diff --git a/.gitignore b/.gitignore
index 2066ba7d5f8a7554184023eb528436996e8cdaa3..798b187d4b326fb24bd4ccccabf7dc84b9bb20fd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,8 @@ examples/*/*/*.mp4
 examples/*/*/*.txt
 examples/*/*/*.rst
 examples/*/*/*.hdf5
+examples/*/*/*.csv
+examples/*/*/*.dot
 examples/*/*/restart/*
 examples/*/*/used_parameters.yml
 examples/*/*/log*
@@ -136,6 +138,8 @@ tests/testOutputList
 tests/testCbrt
 tests/testFormat.sh
 tests/testCooling
+tests/*.png
+tests/*.txt
 
 theory/latex/swift.pdf
 theory/SPH/Kernels/kernels.pdf
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index fe48398e4b0dd4bfdede6781e708bd222b6eedae..db841f347f681c42c1b305c2d130ee0b55d639ae 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -775,6 +775,7 @@ INPUT		       += @top_srcdir@/src/chemistry/EAGLE
 INPUT		       += @top_srcdir@/src/entropy_floor/EAGLE
 INPUT		       += @top_srcdir@/src/star_formation/EAGLE
 INPUT		       += @top_srcdir@/src/tracers/EAGLE
+INPUT		       += @top_srcdir@/src/stars/EAGLE
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
diff --git a/examples/Cooling/CoolingBox/coolingBox.yml b/examples/Cooling/CoolingBox/coolingBox.yml
index 97b452e5ea376ab10bba155e0ff3e9acb8ed02bd..853e480cd4ffba4baa659232e6d5f068b4ea2815 100644
--- a/examples/Cooling/CoolingBox/coolingBox.yml
+++ b/examples/Cooling/CoolingBox/coolingBox.yml
@@ -48,10 +48,12 @@ GrackleCooling:
   ProvideVolumetricHeatingRates: 0 # User provide volumetric heating rates
   ProvideSpecificHeatingRates: 0 # User provide specific heating rates
   SelfShieldingMethod: 0 # Grackle (<= 3) or Gear self shielding method
-  OutputMode: 1 # Write in output corresponding primordial chemistry mode
   MaxSteps: 1000
   ConvergenceLimit: 1e-2
   
+GearChemistry:
+  InitialMetallicity: 0.01295
+
 EAGLECooling:
   dir_name:                ./coolingtables/
   H_reion_z:               11.5
@@ -71,5 +73,3 @@ EAGLEChemistry:             # Solar abundances
   init_abundance_Silicon:   6.825874e-4
   init_abundance_Iron:      1.1032152e-3
 
-GearChemistry:
-  InitialMetallicity: 0.01295
diff --git a/examples/Cooling/CoolingBox/energy_plot.py b/examples/Cooling/CoolingBox/energy_plot.py
deleted file mode 100644
index 45f0b4f6b11c3855a919f6a98fd0ca006a887f82..0000000000000000000000000000000000000000
--- a/examples/Cooling/CoolingBox/energy_plot.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import matplotlib
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-
-# Plot parameters
-params = {'axes.labelsize': 10,
-'axes.titlesize': 10,
-'font.size': 12,
-'legend.fontsize': 12,
-'xtick.labelsize': 10,
-'ytick.labelsize': 10,
-'text.usetex': True,
- 'figure.figsize' : (3.15,3.15),
-'figure.subplot.left'    : 0.145,
-'figure.subplot.right'   : 0.99,
-'figure.subplot.bottom'  : 0.11,
-'figure.subplot.top'     : 0.99,
-'figure.subplot.wspace'  : 0.15,
-'figure.subplot.hspace'  : 0.12,
-'lines.markersize' : 6,
-'lines.linewidth' : 3.,
-'text.latex.unicode': True
-}
-rcParams.update(params)
-rc('font',**{'family':'sans-serif','sans-serif':['Times']})
-
-
-import numpy as np
-import h5py as h5
-import sys
-
-# File containing the total energy
-stats_filename = "./energy.txt"
-
-# First snapshot
-snap_filename = "coolingBox_0000.hdf5"
-
-# Some constants in cgs units
-k_b = 1.38E-16 #boltzmann
-m_p = 1.67e-24 #proton mass
-
-# Initial conditions set in makeIC.py
-T_init = 1.0e5
-
-# Read the initial state of the gas
-f = h5.File(snap_filename,'r')
-rho = np.mean(f["/PartType0/Density"])
-pressure = np.mean(f["/PartType0/Pressure"])
-
-# Read the units parameters from the snapshot
-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)"]
-
-# Read the properties of the cooling function
-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"])
-
-# Read the adiabatic index
-gamma = float(f["HydroScheme"].attrs["Adiabatic index"])
-
-print "Initial density :", rho
-print "Initial pressure:", pressure
-print "Adiabatic index :", gamma
-
-# Read energy and time arrays
-array = np.genfromtxt(stats_filename,skip_header = 1)
-time = array[:,0]
-total_mass = array[:,1]
-total_energy = array[:,2]
-kinetic_energy = array[:,3]
-internal_energy = array[:,4]
-radiated_energy = array[:,8]
-initial_energy = total_energy[0]
-
-# Conversions to cgs
-rho_cgs = rho * unit_mass / (unit_length)**3
-time_cgs = time * unit_time
-total_energy_cgs = total_energy / total_mass[0] * unit_length**2 / (unit_time)**2
-kinetic_energy_cgs = kinetic_energy / total_mass[0] * unit_length**2 / (unit_time)**2
-internal_energy_cgs = internal_energy / total_mass[0] * unit_length**2 / (unit_time)**2
-radiated_energy_cgs = radiated_energy / total_mass[0] * unit_length**2 / (unit_time)**2  
-
-# Find the energy floor
-u_floor_cgs = k_b * min_T / (mu * m_p * (gamma - 1.))
-
-# Find analytic solution
-initial_energy_cgs = initial_energy/total_mass[0] * unit_length**2 / (unit_time)**2 
-n_H_cgs = X_H * rho_cgs / m_p
-du_dt_cgs = -cooling_lambda * n_H_cgs**2 / rho_cgs
-cooling_time_cgs = (initial_energy_cgs/(-du_dt_cgs))[0]
-analytic_time_cgs = np.linspace(0, cooling_time_cgs * 1.8, 1000)
-u_analytic_cgs = du_dt_cgs*analytic_time_cgs + initial_energy_cgs
-u_analytic_cgs[u_analytic_cgs < u_floor_cgs] = u_floor_cgs
-
-print "Cooling time:", cooling_time_cgs, "[s]"
-
-# Read snapshots
-u_snapshots_cgs = zeros(25)
-t_snapshots_cgs = zeros(25)
-for i in range(25):
-    snap = h5.File("coolingBox_%0.4d.hdf5"%i,'r')
-    u_snapshots_cgs[i] = sum(snap["/PartType0/InternalEnergy"][:] * snap["/PartType0/Masses"][:])  / total_mass[0] * unit_length**2 / (unit_time)**2
-    t_snapshots_cgs[i] = snap["/Header"].attrs["Time"] * unit_time
-
-
-figure()
-plot(time_cgs, total_energy_cgs, 'r-', lw=1.6, label="Gas total energy")
-plot(t_snapshots_cgs, u_snapshots_cgs, 'rD', ms=3)
-plot(time_cgs, radiated_energy_cgs, 'g-', lw=1.6, label="Radiated energy")
-plot(time_cgs, total_energy_cgs + radiated_energy_cgs, 'b-', lw=0.6, label="Gas total + radiated")
-
-plot(analytic_time_cgs, u_analytic_cgs, '--', color='k', alpha=0.8, lw=1.0, label="Analytic solution")
-
-legend(loc="upper right", fontsize=8, frameon=False, handlelength=3, ncol=1)
-xlabel("${\\rm{Time~[s]}}$", labelpad=0)
-ylabel("${\\rm{Energy~[erg]}}$")
-xlim(0, 1.5*cooling_time_cgs)
-ylim(0, 1.5*u_analytic_cgs[0])
-
-savefig("energy.png", dpi=200)
-
-
diff --git a/examples/Cooling/CoolingBox/makeIC.py b/examples/Cooling/CoolingBox/makeIC.py
index f863e174b1fcd404ae178fe324c7a165598b4af0..807ad5378d87891ed878a85c1020d5983474e13d 100644
--- a/examples/Cooling/CoolingBox/makeIC.py
+++ b/examples/Cooling/CoolingBox/makeIC.py
@@ -1,58 +1,69 @@
 ###############################################################################
- # This file is part of SWIFT.
- # Copyright (c) 2016 Stefan Arridge (stefan.arridge@durhama.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/>.
- # 
- ##############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2016 Stefan Arridge (stefan.arridge@durhama.ac.uk)
+#                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
 
 import h5py
-import sys
-from numpy import *
+import numpy as np
 
 # Generates a SWIFT IC file with a constant density and pressure
 
 # Parameters
-periodic= 1           # 1 For periodic box
-boxSize = 1           # 1 kiloparsec    
-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" 
-
-#---------------------------------------------------
+periodic = 1  # 1 For periodic box
+boxSize = 1  # 1 kiloparsec
+rho = 3.2e3  # Density in code units (3.2e6 is 0.1 hydrogen atoms per cm^3)
+T = 4e3  # Initial Temperature
+gamma = 5./3.  # Gas adiabatic index
+fileName = "coolingBox.hdf5"
+# ---------------------------------------------------
+
+# defines some constants
+# need to be changed in plotTemperature.py too
+h_frac = 0.76
+mu = 4. / (1. + 3. * h_frac)
+
+m_h_cgs = 1.67e-24
+k_b_cgs = 1.38e-16
+
+# defines units
+unit_length = 3.0857e21  # kpc
+unit_mass = 2.0e33  # solar mass
+unit_time = 3.0857e16  # ~ Gyr
 
 # Read id, position and h from glass
 glass = h5py.File("glassCube_32.hdf5", "r")
 ids = glass["/PartType0/ParticleIDs"][:]
-pos = glass["/PartType0/Coordinates"][:,:] * boxSize
+pos = glass["/PartType0/Coordinates"][:, :] * boxSize
 h = glass["/PartType0/SmoothingLength"][:] * boxSize
 
 # Compute basic properties
-numPart = size(pos) / 3
+numPart = np.size(pos) // 3
 mass = boxSize**3 * rho / numPart
-internalEnergy = P / ((gamma - 1.) * rho)
+internalEnergy = k_b_cgs * T * mu / ((gamma - 1.) * m_h_cgs)
+internalEnergy *= (unit_time / unit_length)**2
 
-#File
-file = h5py.File(fileName, 'w')
+# File
+f = h5py.File(fileName, 'w')
 
 # Header
-grp = file.create_group("/Header")
+grp = f.create_group("/Header")
 grp.attrs["BoxSize"] = boxSize
-grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_Total"] = [numPart, 0, 0, 0, 0, 0]
 grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
 grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 0, 0]
 grp.attrs["Time"] = 0.0
@@ -61,43 +72,43 @@ grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
 grp.attrs["Flag_Entropy_ICs"] = 0
 
 # Runtime parameters
-grp = file.create_group("/RuntimePars")
+grp = f.create_group("/RuntimePars")
 grp.attrs["PeriodicBoundariesOn"] = periodic
 
 # Units
-grp = file.create_group("/Units")
-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.0857e16 
+grp = f.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = unit_length
+grp.attrs["Unit mass in cgs (U_M)"] = unit_mass
+grp.attrs["Unit time in cgs (U_t)"] = unit_time
 grp.attrs["Unit current in cgs (U_I)"] = 1.
 grp.attrs["Unit temperature in cgs (U_T)"] = 1.
 
-#Particle group
-grp = file.create_group("/PartType0")
+# Particle group
+grp = f.create_group("/PartType0")
 
-v  = zeros((numPart, 3))
+v = np.zeros((numPart, 3))
 ds = grp.create_dataset('Velocities', (numPart, 3), 'f')
 ds[()] = v
 
-m = full((numPart, 1), mass)
-ds = grp.create_dataset('Masses', (numPart,1), 'f')
+m = np.full((numPart, 1), mass)
+ds = grp.create_dataset('Masses', (numPart, 1), 'f')
 ds[()] = m
 
-h = reshape(h, (numPart, 1))
+h = np.reshape(h, (numPart, 1))
 ds = grp.create_dataset('SmoothingLength', (numPart, 1), 'f')
 ds[()] = h
 
-u = full((numPart, 1), internalEnergy)
-ds = grp.create_dataset('InternalEnergy', (numPart,1), 'f')
+u = np.full((numPart, 1), internalEnergy)
+ds = grp.create_dataset('InternalEnergy', (numPart, 1), 'f')
 ds[()] = u
 
-ids = reshape(ids, (numPart, 1))
+ids = np.reshape(ids, (numPart, 1))
 ds = grp.create_dataset('ParticleIDs', (numPart, 1), 'L')
 ds[()] = ids
 
 ds = grp.create_dataset('Coordinates', (numPart, 3), 'd')
 ds[()] = pos
 
-file.close()
+f.close()
 
-print numPart
+print("Initial condition generated")
diff --git a/examples/Cooling/CoolingBox/plotEnergy.py b/examples/Cooling/CoolingBox/plotEnergy.py
new file mode 100644
index 0000000000000000000000000000000000000000..9c7af57d3d9dfdcfa222e9f77701f230d25f9ddc
--- /dev/null
+++ b/examples/Cooling/CoolingBox/plotEnergy.py
@@ -0,0 +1,110 @@
+from h5py import File
+import numpy as np
+import matplotlib
+from glob import glob
+matplotlib.use("Agg")
+import matplotlib.pyplot as plt
+
+# Plot parameters
+params = {
+    'axes.labelsize': 10,
+    'axes.titlesize': 10,
+    'font.size': 12,
+    'legend.fontsize': 12,
+    'xtick.labelsize': 10,
+    'ytick.labelsize': 10,
+    'text.usetex': True,
+    'figure.figsize': (5, 5),
+    'figure.subplot.left': 0.145,
+    'figure.subplot.right': 0.99,
+    'figure.subplot.bottom': 0.11,
+    'figure.subplot.top': 0.99,
+    'figure.subplot.wspace': 0.15,
+    'figure.subplot.hspace': 0.12,
+    'lines.markersize': 6,
+    'lines.linewidth': 3.,
+}
+plt.rcParams.update(params)
+
+
+# Some constants in cgs units
+k_b_cgs = 1.38e-16  # boltzmann
+m_h_cgs = 1.67e-24  # proton mass
+
+
+# File containing the total energy
+stats_filename = "./energy.txt"
+
+# First snapshot
+snap_filename = "coolingBox_0000.hdf5"
+
+# Read the initial state of the gas
+f = File(snap_filename, 'r')
+
+# Read the units parameters from the snapshot
+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)"]
+
+# Read the adiabatic index
+gamma = float(f["HydroScheme"].attrs["Adiabatic index"])
+
+
+def energyUnits(u):
+    """ Compute the temperature from the internal energy. """
+    u *= (unit_length / unit_time)**2
+    return u * m_h_cgs / k_b_cgs
+
+
+# Read energy and time arrays
+array = np.genfromtxt(stats_filename, skip_header=1)
+time = array[:, 0] * unit_time
+total_mass = array[:, 1]
+total_energy = array[:, 2]
+kinetic_energy = array[:, 3]
+internal_energy = array[:, 4]
+radiated_energy = array[:, 8]
+initial_energy = total_energy[0]
+
+# Conversions to cgs
+total_energy_cgs = total_energy / total_mass[0]
+total_energy_cgs = energyUnits(total_energy_cgs)
+
+kinetic_energy_cgs = kinetic_energy / total_mass[0]
+kinetic_energy_cgs = energyUnits(kinetic_energy_cgs)
+
+internal_energy_cgs = internal_energy / total_mass[0]
+internal_energy_cgs = energyUnits(internal_energy_cgs)
+
+radiated_energy_cgs = radiated_energy / total_mass[0]
+radiated_energy_cgs = energyUnits(radiated_energy_cgs)
+
+# Read snapshots
+files = glob("coolingBox_*.hdf5")
+N = len(files)
+temp_snap = np.zeros(N)
+time_snap_cgs = np.zeros(N)
+for i in range(N):
+    snap = File(files[i], 'r')
+    u = snap["/PartType0/InternalEnergy"][:] * snap["/PartType0/Masses"][:]
+    u = sum(u) / total_mass[0]
+    temp_snap[i] = energyUnits(u)
+    time_snap_cgs[i] = snap["/Header"].attrs["Time"] * unit_time
+
+
+plt.figure()
+
+Myr_in_yr = 3.15e13
+plt.plot(time, total_energy_cgs, 'r-', lw=1.6, label="Gas total energy")
+plt.plot(time_snap_cgs, temp_snap, 'rD', ms=3)
+plt.plot(time, radiated_energy_cgs, 'g-', lw=1.6, label="Radiated energy")
+plt.plot(time, total_energy_cgs + radiated_energy_cgs, 'b-',
+         lw=0.6, label="Gas total + radiated")
+
+plt.legend(loc="right", fontsize=8, frameon=False,
+           handlelength=3, ncol=1)
+plt.xlabel("${\\rm{Time~[Myr]}}$", labelpad=0)
+plt.ylabel("${\\rm{Internal ~Energy ~(u ~m_H / k_B) ~[K]}}$")
+
+plt.savefig("energy.png", dpi=200)
diff --git a/examples/Cooling/CoolingBox/run.sh b/examples/Cooling/CoolingBox/run.sh
index 8a0d717e49324cdc49301fa0c551d5b7da198d5e..ae5b6d361e3364028864882d3400e702c8e670fb 100755
--- a/examples/Cooling/CoolingBox/run.sh
+++ b/examples/Cooling/CoolingBox/run.sh
@@ -21,7 +21,7 @@ then
 fi
 
 # Run SWIFT
-../../swift --cosmology --hydro --cooling --threads=4 -n 1000 coolingBox.yml
+../../swift --hydro --cooling --threads=4 -n 1000 coolingBox.yml
 
 # Check energy conservation and cooling rate
-python energy_plot.py
+python plotEnergy.py
diff --git a/examples/Cooling/CoolingRates/Makefile.am b/examples/Cooling/CoolingRates/Makefile.am
index 17bcfad7f6b831cdb5f7b222d0e2b6e2cacaed9f..8bb0afa44436c5059f93b585ab6f8893752ce294 100644
--- a/examples/Cooling/CoolingRates/Makefile.am
+++ b/examples/Cooling/CoolingRates/Makefile.am
@@ -15,7 +15,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 # Add the source directory and the non-standard paths to the included library headers to CFLAGS
-AM_CFLAGS = -I$(top_srcdir)/src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS)
+AM_CFLAGS = -I$(top_srcdir)/src -I$(top_builddir)/examples $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS)
 
 AM_LDFLAGS = $(HDF5_LDFLAGS) $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(TCMALLOC_LIBS) $(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PROFILER_LIBS)
 
@@ -27,6 +27,6 @@ bin_PROGRAMS = cooling_rates
 
 # Sources
 cooling_rates_SOURCES = cooling_rates.c
-cooling_rates_CFLAGS = $(MYFLAGS) $(AM_CFLAGS)
+cooling_rates_CFLAGS = $(AM_CFLAGS)
 cooling_rates_LDADD =  ../../../src/.libs/libswiftsim.a $(EXTRA_LIBS)
 
diff --git a/examples/Cooling/CoolingRates/cooling_rates.c b/examples/Cooling/CoolingRates/cooling_rates.c
index f96701f4c5645818bc5c72d0c9b607ce29f1b597..e7ff0340853e5cd5361286bd8af5c547681b4f63 100644
--- a/examples/Cooling/CoolingRates/cooling_rates.c
+++ b/examples/Cooling/CoolingRates/cooling_rates.c
@@ -16,7 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include "config.h"
 
 /* Some standard headers. */
 #include <fenv.h>
diff --git a/examples/EAGLE_ICs/EAGLE_12/run.sh b/examples/EAGLE_ICs/EAGLE_12/run.sh
index bceddf338ae797abcc32c24fb2642320d9091ba9..12c962c29ddd21290624906dfbcca166e171203b 100755
--- a/examples/EAGLE_ICs/EAGLE_12/run.sh
+++ b/examples/EAGLE_ICs/EAGLE_12/run.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
  # Generate the initial conditions if they are not present.
-if [ ! -e EAGLE_ICs_12.hdf5 ]
+if [ ! -e EAGLE_L0012N0188_ICs.hdf5 ]
 then
     echo "Fetching initial conditions for the EAGLE 12Mpc example..."
     ./getIC.sh
diff --git a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml
index 90070f6a342d60d48d8fd2c794014f9d638030be..d09bbb51e90d843dd6731c5fcbd48b9c586713f9 100644
--- a/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml
+++ b/examples/EAGLE_low_z/EAGLE_12/eagle_12.yml
@@ -57,7 +57,7 @@ InitialConditions:
   periodic:   1
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
-
+  
 EAGLEChemistry: 	     # Solar abundances
   init_abundance_metal:      0.014
   init_abundance_Hydrogen:   0.70649785
@@ -76,3 +76,31 @@ EAGLECooling:
   He_reion_z_centre:       3.5
   He_reion_z_sigma:        0.5
   He_reion_eV_p_H:         2.0
+
+# EAGLE star formation parameters
+EAGLEStarFormation:
+  EOS_density_norm_H_p_cm3:          0.1       # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3.
+  EOS_temperature_norm_K:            8000      # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin.
+  EOS_gamma_effective:               1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas.
+  gas_fraction:                      0.3       # The gas fraction used internally by the model.
+  KS_normalisation:                  1.515e-4  # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr.
+  KS_exponent:                       1.4       # The exponent of the Kennicutt-Schmidt law.
+  KS_min_over_density:               57.7      # The over-density above which star-formation is allowed.
+  KS_high_density_threshold_H_p_cm3: 1e3       # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3.
+  KS_high_density_exponent:          2.0       # Slope of the Kennicut-Schmidt law above the high-density threshold.
+  KS_temperature_margin_dex:         0.5       # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars.
+  threshold_norm_H_p_cm3:            0.1       # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  threshold_Z0:                      0.002     # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation.
+  threshold_slope:                   -0.64     # Slope of the metal-dependant star formation threshold
+  threshold_max_density_H_p_cm3:     10.0      # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  
+# Parameters for the EAGLE "equation of state"
+EAGLEEntropyFloor:
+  Jeans_density_threshold_H_p_cm3: 0.1       # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Jeans_over_density_threshold:    10.       # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in.
+  Jeans_temperature_norm_K:        8000      # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin.
+  Jeans_gamma_effective:           1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor
+  Cool_density_threshold_H_p_cm3: 1e-5       # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Cool_over_density_threshold:    10.        # Overdensity above which the EAGLE Cool limiter entropy floor can kick in.
+  Cool_temperature_norm_K:        8000       # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin.
+  Cool_gamma_effective:           1.         # Slope the of the EAGLE Cool limiter entropy floor
diff --git a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml
index 3d7d5de4fe2c98a04c986840bc2804edaf780c17..75799647b4e95ebd75202748c67a2c18c423f532 100644
--- a/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml
+++ b/examples/EAGLE_low_z/EAGLE_25/eagle_25.yml
@@ -84,3 +84,30 @@ EAGLECooling:
   He_reion_z_sigma:        0.5
   He_reion_eV_p_H:         2.0
 
+# EAGLE star formation parameters
+EAGLEStarFormation:
+  EOS_density_norm_H_p_cm3:          0.1       # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3.
+  EOS_temperature_norm_K:            8000      # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin.
+  EOS_gamma_effective:               1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas.
+  gas_fraction:                      0.3       # The gas fraction used internally by the model.
+  KS_normalisation:                  1.515e-4  # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr.
+  KS_exponent:                       1.4       # The exponent of the Kennicutt-Schmidt law.
+  KS_min_over_density:               57.7      # The over-density above which star-formation is allowed.
+  KS_high_density_threshold_H_p_cm3: 1e3       # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3.
+  KS_high_density_exponent:          2.0       # Slope of the Kennicut-Schmidt law above the high-density threshold.
+  KS_temperature_margin_dex:         0.5       # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars.
+  threshold_norm_H_p_cm3:            0.1       # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  threshold_Z0:                      0.002     # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation.
+  threshold_slope:                   -0.64     # Slope of the metal-dependant star formation threshold
+  threshold_max_density_H_p_cm3:     10.0      # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  
+# Parameters for the EAGLE "equation of state"
+EAGLEEntropyFloor:
+  Jeans_density_threshold_H_p_cm3: 0.1       # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Jeans_over_density_threshold:    10.       # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in.
+  Jeans_temperature_norm_K:        8000      # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin.
+  Jeans_gamma_effective:           1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor
+  Cool_density_threshold_H_p_cm3: 1e-5       # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Cool_over_density_threshold:    10.        # Overdensity above which the EAGLE Cool limiter entropy floor can kick in.
+  Cool_temperature_norm_K:        8000       # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin.
+  Cool_gamma_effective:           1.         # Slope the of the EAGLE Cool limiter entropy floor
diff --git a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml
index 8d622998c5ea1c754e877643e42ee3d97bfa720e..6c0c7421ba4f804437a8086b42fb2878bd3904b1 100644
--- a/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml
+++ b/examples/EAGLE_low_z/EAGLE_50/eagle_50.yml
@@ -77,4 +77,32 @@ EAGLECooling:
   H_reion_z:               11.5 
   He_reion_z_centre:       3.5
   He_reion_z_sigma:        0.5
-  He_reion_eV_p_H:         2.0
\ No newline at end of file
+  He_reion_eV_p_H:         2.0
+
+# EAGLE star formation parameters
+EAGLEStarFormation:
+  EOS_density_norm_H_p_cm3:          0.1       # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3.
+  EOS_temperature_norm_K:            8000      # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin.
+  EOS_gamma_effective:               1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas.
+  gas_fraction:                      0.3       # The gas fraction used internally by the model.
+  KS_normalisation:                  1.515e-4  # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr.
+  KS_exponent:                       1.4       # The exponent of the Kennicutt-Schmidt law.
+  KS_min_over_density:               57.7      # The over-density above which star-formation is allowed.
+  KS_high_density_threshold_H_p_cm3: 1e3       # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3.
+  KS_high_density_exponent:          2.0       # Slope of the Kennicut-Schmidt law above the high-density threshold.
+  KS_temperature_margin_dex:         0.5       # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars.
+  threshold_norm_H_p_cm3:            0.1       # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  threshold_Z0:                      0.002     # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation.
+  threshold_slope:                   -0.64     # Slope of the metal-dependant star formation threshold
+  threshold_max_density_H_p_cm3:     10.0      # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  
+# Parameters for the EAGLE "equation of state"
+EAGLEEntropyFloor:
+  Jeans_density_threshold_H_p_cm3: 0.1       # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Jeans_over_density_threshold:    10.       # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in.
+  Jeans_temperature_norm_K:        8000      # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin.
+  Jeans_gamma_effective:           1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor
+  Cool_density_threshold_H_p_cm3: 1e-5       # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Cool_over_density_threshold:    10.        # Overdensity above which the EAGLE Cool limiter entropy floor can kick in.
+  Cool_temperature_norm_K:        8000       # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin.
+  Cool_gamma_effective:           1.         # Slope the of the EAGLE Cool limiter entropy floor
diff --git a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml
index fb885fa44f119bb3e296947997f71581d5ca0f48..313a5a384324eecd56455ea22bbc96d147982d6b 100644
--- a/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml
+++ b/examples/EAGLE_low_z/EAGLE_6/eagle_6.yml
@@ -87,3 +87,30 @@ EAGLECooling:
   He_reion_z_centre:       3.5
   He_reion_z_sigma:        0.5
   He_reion_eV_p_H:         2.0
+# EAGLE star formation parameters
+EAGLEStarFormation:
+  EOS_density_norm_H_p_cm3:          0.1       # Physical density used for the normalisation of the EOS assumed for the star-forming gas in Hydrogen atoms per cm^3.
+  EOS_temperature_norm_K:            8000      # Temperature om the polytropic EOS assumed for star-forming gas at the density normalisation in Kelvin.
+  EOS_gamma_effective:               1.3333333 # Slope the of the polytropic EOS assumed for the star-forming gas.
+  gas_fraction:                      0.3       # The gas fraction used internally by the model.
+  KS_normalisation:                  1.515e-4  # The normalization of the Kennicutt-Schmidt law in Msun / kpc^2 / yr.
+  KS_exponent:                       1.4       # The exponent of the Kennicutt-Schmidt law.
+  KS_min_over_density:               57.7      # The over-density above which star-formation is allowed.
+  KS_high_density_threshold_H_p_cm3: 1e3       # Hydrogen number density above which the Kennicut-Schmidt law changes slope in Hydrogen atoms per cm^3.
+  KS_high_density_exponent:          2.0       # Slope of the Kennicut-Schmidt law above the high-density threshold.
+  KS_temperature_margin_dex:         0.5       # Logarithm base 10 of the maximal temperature difference above the EOS allowed to form stars.
+  threshold_norm_H_p_cm3:            0.1       # Normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  threshold_Z0:                      0.002     # Reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation.
+  threshold_slope:                   -0.64     # Slope of the metal-dependant star formation threshold
+  threshold_max_density_H_p_cm3:     10.0      # Maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  
+# Parameters for the EAGLE "equation of state"
+EAGLEEntropyFloor:
+  Jeans_density_threshold_H_p_cm3: 0.1       # Physical density above which the EAGLE Jeans limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Jeans_over_density_threshold:    10.       # Overdensity above which the EAGLE Jeans limiter entropy floor can kick in.
+  Jeans_temperature_norm_K:        8000      # Temperature of the EAGLE Jeans limiter entropy floor at the density threshold expressed in Kelvin.
+  Jeans_gamma_effective:           1.3333333 # Slope the of the EAGLE Jeans limiter entropy floor
+  Cool_density_threshold_H_p_cm3: 1e-5       # Physical density above which the EAGLE Cool limiter entropy floor kicks in expressed in Hydrogen atoms per cm^3.
+  Cool_over_density_threshold:    10.        # Overdensity above which the EAGLE Cool limiter entropy floor can kick in.
+  Cool_temperature_norm_K:        8000       # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin.
+  Cool_gamma_effective:           1.         # Slope the of the EAGLE Cool limiter entropy floor
diff --git a/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml
index 4c5e2a82b017725929138de011b1f3ed1fe9f1ef..00fd889d4f58a4dadccc52ef5c6d8315ac2a4012 100644
--- a/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml
+++ b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml
@@ -32,7 +32,7 @@ TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   1.  # The end time of the simulation (in internal units).
   dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-3  # The maximal time-step size of the simulation (in internal units).
+  dt_max:     1e-5  # The maximal time-step size of the simulation (in internal units).
   
 # Parameters governing the snapshots
 Snapshots:
diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml
index 2a885f5770f88c3ee6dd75e194d0fa713160e39e..7ba1e601c764d9c12b93178efd8226601af8373c 100644
--- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml
+++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/isolated_galaxy.yml
@@ -36,8 +36,9 @@ Statistics:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:               fid.hdf5 # The file to read
-  periodic:                    0    # Are we running with periodic ICs?
-
+  periodic:                0        # Are we running with periodic ICs?
+  stars_smoothing_length:  0.5
+  
 # Parameters for the hydrodynamics scheme
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
diff --git a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py
index 4e5fcf2ceb2b3f0682871cc533fab93039ff38e9..89a87923148cb6872ab17b6d7229aef597ef3287 100644
--- a/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py
+++ b/examples/IsolatedGalaxy/IsolatedGalaxy_starformation/plotSolution.py
@@ -101,7 +101,7 @@ gas_sSFR = gas_SFR / gas_mass
 # Read the Star properties
 stars_pos = f["/PartType4/Coordinates"][:, :]
 stars_BirthDensity = f["/PartType4/BirthDensity"][:]
-stars_BirthTime = f["/PartType4/Birth_time"][:]
+stars_BirthTime = f["/PartType4/BirthTime"][:]
 stars_XH = f["/PartType4/ElementAbundance"][:,0]
 
 # Centre the box
diff --git a/examples/Makefile.am b/examples/Makefile.am
index bfab73c0e4709b20de3733b08e4fb99901cb996f..1c8415e1302df828133d0459d7dc9e9b4d73ffcb 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -59,7 +59,7 @@ swift_mpi_CFLAGS = $(MYFLAGS) $(AM_CFLAGS) $(MPI_FLAGS) -DENGINE_POLICY="engine_
 swift_mpi_LDADD =  ../src/.libs/libswiftsim_mpi.a ../argparse/.libs/libargparse.a $(MPI_LIBS) $(EXTRA_LIBS)
 
 # Scripts to generate ICs
-EXTRA_DIST = Cooling/CoolingBox/coolingBox.yml Cooling/CoolingBox/energy_plot.py Cooling/CoolingBox/makeIC.py Cooling/CoolingBox/run.sh \
+EXTRA_DIST = Cooling/CoolingBox/coolingBox.yml Cooling/CoolingBox/plotEnergy.py Cooling/CoolingBox/makeIC.py Cooling/CoolingBox/run.sh Cooling/CoolingBox/getGlass.sh \
              Cosmology/ConstantCosmoVolume/run.sh Cosmology/ConstantCosmoVolume/makeIC.py Cosmology/ConstantCosmoVolume/plotSolution.py Cosmology/ConstantCosmoVolume/constant_volume.yml \
              Cosmology/ZeldovichPancake_3D/makeIC.py Cosmology/ZeldovichPancake_3D/zeldovichPancake.yml Cosmology/ZeldovichPancake_3D/run.sh Cosmology/ZeldovichPancake_3D/plotSolution.py \
              EAGLE_low_z/EAGLE_6/eagle_6.yml EAGLE_low_z/EAGLE_6/getIC.sh EAGLE_low_z/EAGLE_6/README EAGLE_low_z/EAGLE_6/run.sh \
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py
index 988ea36163203a50928cc7fd8f9c81f4d3a377ff..a3458ac1598e5657f3f597dfb10b36a7a641e68f 100644
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py
@@ -164,7 +164,9 @@ legend(loc="upper left", frameon=False, handlelength=1.5)
 if cooling_model == "Constant Lambda":
     text(1e-2, 6e4, "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$"%(Lambda/10.**(int(log10(Lambda))), log10(Lambda)), fontsize=7)
 elif cooling_model == "EAGLE":
-    text(1e-2, 6e4, "EAGLE (Wiersma et al. (2009)")
+    text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)")
+elif cooling_model == b"Grackle":
+    text(1e-2, 6e4, "Grackle (Smith et al. 2016)")
 else:
     text(1e-2, 6e4, "No cooling")
     
@@ -187,7 +189,7 @@ minorticks_off()
 xlabel("${\\rm Redshift}~z$", labelpad=0)
 ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
 xlim(9e-3, 1.1)
-ylim(20, 2.5e7)
+ylim(5, 2.5e7)
 
 savefig("Temperature_evolution.png", dpi=200)
 
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh
index 5ce9e2bb3b544eb915f189d4aa417fcc9c316b64..a7ae9dab54975efaf523de323a127b8134663544 100755
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/run.sh
@@ -7,6 +7,11 @@ then
     ./getIC.sh
 fi
 
+if [ ! -e CloudyData_UVB=HM2012.h5 ]
+then
+    ../../Cooling/getCoolingTable.sh 
+fi
+
 if [ ! -e coolingtables ]
 then
     echo "Fetching cooling tables for the small cosmological volume example..."
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml
index 60046cadc3bd3af324b5f967c76315cf5a27fe52..96f10465410ac120b30904a8655da4d8133d09bd 100644
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml
@@ -16,7 +16,7 @@ Cosmology:                      # WMAP9 cosmology
 
 # Parameters governing the time integration
 TimeIntegration:
-  dt_min:     1e-6 
+  dt_min:     1e-8
   dt_max:     1e-2 
 
 # Parameters for the self-gravity scheme
@@ -83,3 +83,18 @@ EAGLEChemistry:
   init_abundance_Magnesium: 0.0
   init_abundance_Silicon:   0.0
   init_abundance_Iron:      0.0
+
+# Cooling with Grackle 3.0
+GrackleCooling:
+  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  WithUVbackground: 1                   # Enable or not the UV background
+  Redshift: -1                           # Redshift to use (-1 means time based redshift)
+  WithMetalCooling: 1                   # Enable or not the metal cooling
+  ProvideVolumetricHeatingRates: 0      # (optional) User provide volumetric heating rates
+  ProvideSpecificHeatingRates: 0        # (optional) User provide specific heating rates
+  SelfShieldingMethod: 0                # (optional) Grackle (<= 3) or Gear self shielding method
+  MaxSteps: 10000                       # (optional) Max number of step when computing the initial composition
+  ConvergenceLimit: 1e-2                # (optional) Convergence threshold (relative) for initial composition
+
+GearChemistry:
+  InitialMetallicity: 0.01295
diff --git a/examples/main.c b/examples/main.c
index 1d46ee954b55c5965a6ab65dae5b5038e4c72394..6fc5b433719822558d531f4ed2691e7127139a79 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -347,7 +347,17 @@ int main(int argc, char *argv[]) {
     if (myrank == 0) {
       argparse_usage(&argparse);
       printf(
-          "\nError: Cannot process feedback without stars, -S must be "
+          "\nError: Cannot process feedback without stars, --stars must be "
+          "chosen.\n");
+    }
+    return 1;
+  }
+
+  if (!with_hydro && with_feedback) {
+    if (myrank == 0) {
+      argparse_usage(&argparse);
+      printf(
+          "\nError: Cannot process feedback without gas, --hydro must be "
           "chosen.\n");
     }
     return 1;
@@ -460,7 +470,6 @@ int main(int argc, char *argv[]) {
 #ifdef WITH_MPI
   if (with_mpole_reconstruction && nr_nodes > 1)
     error("Cannot reconstruct m-poles every step over MPI (yet).");
-  if (with_feedback) error("Can't run with feedback over MPI (yet).");
   if (with_star_formation)
     error("Can't run with star formation over MPI (yet)");
   if (with_limiter) error("Can't run with time-step limiter over MPI (yet)");
@@ -893,10 +902,11 @@ int main(int argc, char *argv[]) {
     if (myrank == 0) cooling_print(&cooling_func);
 
     /* Initialise the star formation law and its properties */
+    bzero(&starform, sizeof(struct star_formation));
     if (with_star_formation)
       starformation_init(params, &prog_const, &us, &hydro_properties,
                          &starform);
-    if (myrank == 0) starformation_print(&starform);
+    if (with_star_formation && myrank == 0) starformation_print(&starform);
 
     /* Initialise the chemistry */
     bzero(&chemistry, sizeof(struct chemistry_global_data));
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 43d6f4818f88d2451e639c60407dbb8a36f5a80a..22bbf3db4f4f49f1cce6c1aa817b8228f829437f 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -65,8 +65,6 @@ Scheduler:
   cell_sub_size_self_hydro:  32000     # (Optional) Maximal number of interactions per sub-self hydro task  (this is the default value).
   cell_sub_size_pair_grav:   256000000 # (Optional) Maximal number of interactions per sub-pair gravity task  (this is the default value).
   cell_sub_size_self_grav:   32000     # (Optional) Maximal number of interactions per sub-self gravity task  (this is the default value).
-  cell_sub_size_pair_stars:  256000000 # (Optional) Maximal number of interactions per sub-pair stars task  (this is the default value).
-  cell_sub_size_self_stars:  32000     # (Optional) Maximal number of interactions per sub-self stars task  (this is the default value).
   cell_split_size:           400       # (Optional) Maximal number of particles per cell (this is the default value).
   cell_subdepth_diff_grav:   4         # (Optional) Maximal depth difference between leaves and a cell that gravity tasks can be pushed down to (this is the default value).
   cell_extra_parts:          0         # (Optional) Number of spare parts per top-level allocated at rebuild time for on-the-fly creation.
@@ -299,7 +297,6 @@ GrackleCooling:
   ProvideVolumetricHeatingRates: 0      # (optional) User provide volumetric heating rates
   ProvideSpecificHeatingRates: 0        # (optional) User provide specific heating rates
   SelfShieldingMethod: 0                # (optional) Grackle (<= 3) or Gear self shielding method
-  OutputMode: 0                         # (optional) Write in output corresponding primordial chemistry mode
   MaxSteps: 10000                       # (optional) Max number of step when computing the initial composition
   ConvergenceLimit: 1e-2                # (optional) Convergence threshold (relative) for initial composition
 
diff --git a/src/active.h b/src/active.h
index 5bbbd3803cb09e7aa05ddb15e2e5c2a15b27602c..6466cd314fdc18ad324bf01a1ff4e73e214e35d5 100644
--- a/src/active.h
+++ b/src/active.h
@@ -85,9 +85,16 @@ __attribute__((always_inline)) INLINE static int cell_are_gpart_drifted(
 __attribute__((always_inline)) INLINE static int cell_are_spart_drifted(
     const struct cell *c, const struct engine *e) {
 
-  /* Currently just use the gpart drift
-   * This function is just for clarity */
-  return cell_are_gpart_drifted(c, e);
+#ifdef SWIFT_DEBUG_CHECKS
+  if (c->stars.ti_old_part > e->ti_current)
+    error(
+        "Cell has been drifted too far forward in time! c->ti_old=%lld (t=%e) "
+        "and e->ti_current=%lld (t=%e)",
+        c->stars.ti_old_part, c->stars.ti_old_part * e->time_base,
+        e->ti_current, e->ti_current * e->time_base);
+#endif
+
+  return (c->stars.ti_old_part == e->ti_current);
 }
 
 /* Are cells / particles active for regular tasks ? */
@@ -125,7 +132,7 @@ __attribute__((always_inline)) INLINE static int cell_is_all_active_hydro(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->hydro.ti_end_max < e->ti_current)
+  if (c->hydro.count > 0 && c->hydro.ti_end_max < e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_end_max=%lld "
         "e->ti_current=%lld",
@@ -181,7 +188,7 @@ __attribute__((always_inline)) INLINE static int cell_is_all_active_gravity(
     const struct cell *c, const struct engine *e) {
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (c->grav.ti_end_max < e->ti_current)
+  if (c->grav.count > 0 && c->grav.ti_end_max < e->ti_current)
     error(
         "cell in an impossible time-zone! c->ti_end_max=%lld "
         "e->ti_current=%lld",
@@ -383,6 +390,28 @@ __attribute__((always_inline)) INLINE static int cell_is_starting_gravity(
   return (c->grav.ti_beg_max == e->ti_current);
 }
 
+/**
+ * @brief Does a cell contain any s-particle starting their time-step now ?
+ *
+ * @param c The #cell.
+ * @param e The #engine containing information about the current time.
+ * @return 1 if the #cell contains at least an active particle, 0 otherwise.
+ */
+__attribute__((always_inline)) INLINE static int cell_is_starting_stars(
+    const struct cell *c, const struct engine *e) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (c->stars.ti_beg_max > e->ti_current)
+    error(
+        "cell in an impossible time-zone! c->ti_beg_max=%lld (t=%e) and "
+        "e->ti_current=%lld (t=%e, a=%e)",
+        c->stars.ti_beg_max, c->stars.ti_beg_max * e->time_base, e->ti_current,
+        e->ti_current * e->time_base, e->cosmology->a);
+#endif
+
+  return (c->stars.ti_beg_max == e->ti_current);
+}
+
 /**
  * @brief Is this particle starting its time-step now ?
  *
diff --git a/src/cache.h b/src/cache.h
index b6735f381da5c6e615d50e0c5768ca201022aa59..e5a62f33b3eb492f9da6e0e98ed767d6b8de32dd 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -123,8 +123,7 @@ __attribute__((always_inline)) INLINE void cache_init(struct cache *c,
                                                       size_t count) {
 
   /* Align cache on correct byte boundary and pad cache size to be a multiple of
-   * the vector size
-   * and include 2 vector lengths for remainder operations. */
+   * the vector size and include 2 vector lengths for remainder operations. */
   size_t pad = 2 * VEC_SIZE, rem = count % VEC_SIZE;
   if (rem > 0) pad += VEC_SIZE - rem;
   size_t sizeBytes = (count + pad) * sizeof(float);
diff --git a/src/cell.c b/src/cell.c
index 4f44abd924be97e54e52fffda8559f7a0dcfe68a..af4edc6a92dd8ed84dd4cd8cc869b8c32c4f81e8 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -381,14 +381,17 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc,
 
   /* Start by packing the data of the current cell. */
   pc->hydro.h_max = c->hydro.h_max;
+  pc->stars.h_max = c->stars.h_max;
   pc->hydro.ti_end_min = c->hydro.ti_end_min;
   pc->hydro.ti_end_max = c->hydro.ti_end_max;
   pc->grav.ti_end_min = c->grav.ti_end_min;
   pc->grav.ti_end_max = c->grav.ti_end_max;
   pc->stars.ti_end_min = c->stars.ti_end_min;
+  pc->stars.ti_end_max = c->stars.ti_end_max;
   pc->hydro.ti_old_part = c->hydro.ti_old_part;
   pc->grav.ti_old_part = c->grav.ti_old_part;
   pc->grav.ti_old_multipole = c->grav.ti_old_multipole;
+  pc->stars.ti_old_part = c->stars.ti_old_part;
   pc->hydro.count = c->hydro.count;
   pc->grav.count = c->grav.count;
   pc->stars.count = c->stars.count;
@@ -485,14 +488,17 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c,
 
   /* Unpack the current pcell. */
   c->hydro.h_max = pc->hydro.h_max;
+  c->stars.h_max = pc->stars.h_max;
   c->hydro.ti_end_min = pc->hydro.ti_end_min;
   c->hydro.ti_end_max = pc->hydro.ti_end_max;
   c->grav.ti_end_min = pc->grav.ti_end_min;
   c->grav.ti_end_max = pc->grav.ti_end_max;
   c->stars.ti_end_min = pc->stars.ti_end_min;
+  c->stars.ti_end_max = pc->stars.ti_end_max;
   c->hydro.ti_old_part = pc->hydro.ti_old_part;
   c->grav.ti_old_part = pc->grav.ti_old_part;
   c->grav.ti_old_multipole = pc->grav.ti_old_multipole;
+  c->stars.ti_old_part = pc->stars.ti_old_part;
   c->hydro.count = pc->hydro.count;
   c->grav.count = pc->grav.count;
   c->stars.count = pc->stars.count;
@@ -619,6 +625,7 @@ int cell_pack_end_step(struct cell *restrict c,
   pcells[0].grav.ti_end_min = c->grav.ti_end_min;
   pcells[0].grav.ti_end_max = c->grav.ti_end_max;
   pcells[0].stars.ti_end_min = c->stars.ti_end_min;
+  pcells[0].stars.ti_end_max = c->stars.ti_end_max;
   pcells[0].hydro.dx_max_part = c->hydro.dx_max_part;
   pcells[0].stars.dx_max_part = c->stars.dx_max_part;
 
@@ -657,6 +664,7 @@ int cell_unpack_end_step(struct cell *restrict c,
   c->grav.ti_end_min = pcells[0].grav.ti_end_min;
   c->grav.ti_end_max = pcells[0].grav.ti_end_max;
   c->stars.ti_end_min = pcells[0].stars.ti_end_min;
+  c->stars.ti_end_max = pcells[0].stars.ti_end_max;
   c->hydro.dx_max_part = pcells[0].hydro.dx_max_part;
   c->stars.dx_max_part = pcells[0].stars.dx_max_part;
 
@@ -1475,7 +1483,7 @@ void cell_check_part_drift_point(struct cell *c, void *data) {
 }
 
 /**
- * @brief Checks that the #gpart and #spart in a cell are at the
+ * @brief Checks that the #gpart in a cell are at the
  * current point in time
  *
  * Calls error() if the cell is not at the current time.
@@ -1506,11 +1514,42 @@ void cell_check_gpart_drift_point(struct cell *c, void *data) {
         c->grav.parts[i].time_bin != time_bin_inhibited)
       error("g-part in an incorrect time-zone! gp->ti_drift=%lld ti_drift=%lld",
             c->grav.parts[i].ti_drift, ti_drift);
+#else
+  error("Calling debugging code without debugging flag activated.");
+#endif
+}
+
+/**
+ * @brief Checks that the #spart in a cell are 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_spart_drift_point(struct cell *c, void *data) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+
+  const integertime_t ti_drift = *(integertime_t *)data;
+
+  /* Only check local cells */
+  if (c->nodeID != engine_rank) return;
+
+  /* Only check cells with content */
+  if (c->stars.count == 0) return;
+
+  if (c->stars.ti_old_part != ti_drift)
+    error(
+        "Cell in an incorrect time-zone! c->stars.ti_old_part=%lld "
+        "ti_drift=%lld",
+        c->stars.ti_old_part, ti_drift);
 
   for (int i = 0; i < c->stars.count; ++i)
     if (c->stars.parts[i].ti_drift != ti_drift &&
         c->stars.parts[i].time_bin != time_bin_inhibited)
-      error("s-part in an incorrect time-zone! sp->ti_drift=%lld ti_drift=%lld",
+      error("g-part in an incorrect time-zone! gp->ti_drift=%lld ti_drift=%lld",
             c->stars.parts[i].ti_drift, ti_drift);
 #else
   error("Calling debugging code without debugging flag activated.");
@@ -1800,6 +1839,8 @@ void cell_clear_drift_flags(struct cell *c, void *data) {
   c->hydro.do_sub_drift = 0;
   c->grav.do_drift = 0;
   c->grav.do_sub_drift = 0;
+  c->stars.do_drift = 0;
+  c->stars.do_sub_drift = 0;
 }
 
 /**
@@ -1896,8 +1937,39 @@ void cell_activate_drift_gpart(struct cell *c, struct scheduler *s) {
  * @brief Activate the #spart drifts on the given cell.
  */
 void cell_activate_drift_spart(struct cell *c, struct scheduler *s) {
-  // MATTHIEU: This will need changing
-  cell_activate_drift_gpart(c, s);
+
+  /* If this cell is already marked for drift, quit early. */
+  if (c->stars.do_drift) return;
+
+  /* Mark this cell for drifting. */
+  c->stars.do_drift = 1;
+
+  /* Set the do_stars_sub_drifts all the way up and activate the super drift
+     if this has not yet been done. */
+  if (c == c->hydro.super) {
+#ifdef SWIFT_DEBUG_CHECKS
+    if (c->stars.drift == NULL)
+      error("Trying to activate un-existing c->stars.drift");
+#endif
+    scheduler_activate(s, c->stars.drift);
+  } else {
+    for (struct cell *parent = c->parent;
+         parent != NULL && !parent->stars.do_sub_drift;
+         parent = parent->parent) {
+
+      /* Mark this cell for drifting */
+      parent->stars.do_sub_drift = 1;
+
+      if (parent == c->hydro.super) {
+#ifdef SWIFT_DEBUG_CHECKS
+        if (parent->stars.drift == NULL)
+          error("Trying to activate un-existing parent->stars.drift");
+#endif
+        scheduler_activate(s, parent->stars.drift);
+        break;
+      }
+    }
+  }
 }
 
 /**
@@ -1905,7 +1977,7 @@ void cell_activate_drift_spart(struct cell *c, struct scheduler *s) {
  */
 void cell_activate_limiter(struct cell *c, struct scheduler *s) {
 
-  /* If this cell is already marked for drift, quit early. */
+  /* If this cell is already marked for limiting, quit early. */
   if (c->hydro.do_limiter) return;
 
   /* Mark this cell for limiting. */
@@ -1990,6 +2062,7 @@ void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s) {
 
   /* Has this cell been sorted at all for the given sid? */
   if (!(c->hydro.sorted & (1 << sid)) || c->nodeID != engine_rank) {
+
     atomic_or(&c->hydro.do_sort, (1 << sid));
     cell_activate_hydro_sorts_up(c, s);
   }
@@ -2000,7 +2073,7 @@ void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s) {
  */
 void cell_activate_stars_sorts_up(struct cell *c, struct scheduler *s) {
 
-  if (c == c->super) {
+  if (c == c->hydro.super) {
 #ifdef SWIFT_DEBUG_CHECKS
     if (c->stars.sorts == NULL)
       error("Trying to activate un-existing c->stars.sorts");
@@ -2008,7 +2081,6 @@ void cell_activate_stars_sorts_up(struct cell *c, struct scheduler *s) {
     scheduler_activate(s, c->stars.sorts);
     if (c->nodeID == engine_rank) {
       // MATTHIEU: to do: do we actually need both drifts here?
-      cell_activate_drift_part(c, s);
       cell_activate_drift_spart(c, s);
     }
   } else {
@@ -2017,16 +2089,13 @@ void cell_activate_stars_sorts_up(struct cell *c, struct scheduler *s) {
          parent != NULL && !parent->stars.do_sub_sort;
          parent = parent->parent) {
       parent->stars.do_sub_sort = 1;
-      if (parent == c->super) {
+      if (parent == c->hydro.super) {
 #ifdef SWIFT_DEBUG_CHECKS
         if (parent->stars.sorts == NULL)
           error("Trying to activate un-existing parents->stars.sorts");
 #endif
         scheduler_activate(s, parent->stars.sorts);
-        if (parent->nodeID == engine_rank) {
-          cell_activate_drift_part(parent, s);
-          cell_activate_drift_spart(parent, s);
-        }
+        if (parent->nodeID == engine_rank) cell_activate_drift_spart(parent, s);
         break;
       }
     }
@@ -2053,6 +2122,7 @@ void cell_activate_stars_sorts(struct cell *c, int sid, struct scheduler *s) {
 
   /* Has this cell been sorted at all for the given sid? */
   if (!(c->stars.sorted & (1 << sid)) || c->nodeID != engine_rank) {
+
     atomic_or(&c->stars.do_sort, (1 << sid));
     cell_activate_stars_sorts_up(c, s);
   }
@@ -2437,10 +2507,14 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj,
   /* Store the current dx_max and h_max values. */
   ci->stars.dx_max_part_old = ci->stars.dx_max_part;
   ci->stars.h_max_old = ci->stars.h_max;
+  ci->hydro.dx_max_part_old = ci->hydro.dx_max_part;
+  ci->hydro.h_max_old = ci->hydro.h_max;
 
   if (cj != NULL) {
     cj->stars.dx_max_part_old = cj->stars.dx_max_part;
     cj->stars.h_max_old = cj->stars.h_max;
+    cj->hydro.dx_max_part_old = cj->hydro.dx_max_part;
+    cj->hydro.h_max_old = cj->hydro.h_max;
   }
 
   /* Self interaction? */
@@ -2478,17 +2552,13 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj,
     /* Should we even bother? */
     if (!cell_is_active_stars(ci, e) && !cell_is_active_stars(cj, e)) return;
 
-    int should_do = ci->stars.count != 0 && cj->hydro.count != 0;
-    should_do |= cj->stars.count != 0 && ci->hydro.count != 0;
-    if (!should_do) return;
-
     /* Get the orientation of the pair. */
     double shift[3];
     int sid = space_getsid(s->space, &ci, &cj, shift);
 
     /* recurse? */
-    if (cell_can_recurse_in_pair_stars_task(ci) &&
-        cell_can_recurse_in_pair_stars_task(cj)) {
+    if (cell_can_recurse_in_pair_stars_task(ci, cj) &&
+        cell_can_recurse_in_pair_stars_task(cj, ci)) {
 
       /* Different types of flags. */
       switch (sid) {
@@ -2767,8 +2837,7 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj,
     /* Otherwise, activate the sorts and drifts. */
     else {
 
-      if (cell_is_active_stars(ci, e) && cj->hydro.count != 0 &&
-          ci->stars.count != 0) {
+      if (cell_is_active_stars(ci, e)) {
         /* We are going to interact this pair, so store some values. */
         atomic_or(&cj->hydro.requires_sorts, 1 << sid);
         atomic_or(&ci->stars.requires_sorts, 1 << sid);
@@ -2785,8 +2854,7 @@ void cell_activate_subcell_stars_tasks(struct cell *ci, struct cell *cj,
         cell_activate_stars_sorts(ci, sid, s);
       }
 
-      if (cell_is_active_stars(cj, e) && ci->hydro.count != 0 &&
-          cj->stars.count != 0) {
+      if (cell_is_active_stars(cj, e)) {
         /* We are going to interact this pair, so store some values. */
         atomic_or(&cj->stars.requires_sorts, 1 << sid);
         atomic_or(&ci->hydro.requires_sorts, 1 << sid);
@@ -3030,7 +3098,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
       }
       /* Store current values of dx_max and h_max. */
       else if (t->type == task_type_sub_pair || t->type == task_type_sub_self) {
-        cell_activate_subcell_hydro_tasks(t->ci, t->cj, s);
+        cell_activate_subcell_hydro_tasks(ci, cj, s);
       }
     }
 
@@ -3156,7 +3224,7 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
     if (c->kick1 != NULL) scheduler_activate(s, c->kick1);
     if (c->kick2 != NULL) scheduler_activate(s, c->kick2);
     if (c->timestep != NULL) scheduler_activate(s, c->timestep);
-    if (c->end_force != NULL) scheduler_activate(s, c->end_force);
+    if (c->hydro.end_force != NULL) scheduler_activate(s, c->hydro.end_force);
     if (c->hydro.cooling != NULL) scheduler_activate(s, c->hydro.cooling);
     if (c->hydro.star_formation != NULL)
       scheduler_activate(s, c->hydro.star_formation);
@@ -3304,11 +3372,11 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
     if (c->kick1 != NULL) scheduler_activate(s, c->kick1);
     if (c->kick2 != NULL) scheduler_activate(s, c->kick2);
     if (c->timestep != NULL) scheduler_activate(s, c->timestep);
-    if (c->end_force != NULL) scheduler_activate(s, c->end_force);
     if (c->grav.down != NULL) scheduler_activate(s, c->grav.down);
     if (c->grav.down_in != NULL) scheduler_activate(s, c->grav.down_in);
     if (c->grav.mesh != NULL) scheduler_activate(s, c->grav.mesh);
     if (c->grav.long_range != NULL) scheduler_activate(s, c->grav.long_range);
+    if (c->grav.end_force != NULL) scheduler_activate(s, c->grav.end_force);
     if (c->logger != NULL) scheduler_activate(s, c->logger);
 
     /* Subgrid tasks */
@@ -3334,9 +3402,14 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s) {
 int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
 
   struct engine *e = s->space->e;
+  const int with_feedback = (e->policy & engine_policy_feedback);
   const int nodeID = e->nodeID;
   int rebuild = 0;
 
+  if (!with_feedback && c->stars.drift != NULL && cell_is_active_stars(c, e)) {
+    cell_activate_drift_spart(c, s);
+  }
+
   /* Un-skip the density tasks involved with this cell. */
   for (struct link *l = c->stars.density; l != NULL; l = l->next) {
     struct task *t = l->t;
@@ -3344,60 +3417,69 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
     struct cell *cj = t->cj;
     const int ci_active = cell_is_active_stars(ci, e);
     const int cj_active = (cj != NULL) ? cell_is_active_stars(cj, e) : 0;
+#ifdef WITH_MPI
+    const int ci_nodeID = ci->nodeID;
+    const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1;
+#else
+    const int ci_nodeID = nodeID;
+    const int cj_nodeID = nodeID;
+#endif
+
+    /* Activate the drifts */
+    if (t->type == task_type_self && ci_active) {
+      cell_activate_drift_part(ci, s);
+      cell_activate_drift_spart(ci, s);
+    }
 
     /* Only activate tasks that involve a local active cell. */
-    if ((ci_active && ci->nodeID == nodeID) ||
-        (cj_active && cj->nodeID == nodeID)) {
+    if ((ci_active || cj_active) &&
+        (ci_nodeID == nodeID || cj_nodeID == nodeID)) {
+
       scheduler_activate(s, t);
 
-      /* Activate drifts */
-      if (t->type == task_type_self) {
-        if (ci->nodeID == nodeID) {
-          cell_activate_drift_part(ci, s);
-          cell_activate_drift_spart(ci, s);
-        }
-      }
+      if (t->type == task_type_pair) {
 
-      /* Set the correct sorting flags and activate hydro drifts */
-      else if (t->type == task_type_pair) {
         /* Do ci */
-        /* stars for ci */
-        atomic_or(&ci->stars.requires_sorts, 1 << t->flags);
-        ci->stars.dx_max_sort_old = ci->stars.dx_max_sort;
+        if (ci_active) {
+          /* stars for ci */
+          atomic_or(&ci->stars.requires_sorts, 1 << t->flags);
+          ci->stars.dx_max_sort_old = ci->stars.dx_max_sort;
 
-        /* hydro for cj */
-        atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
-        cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+          /* hydro for cj */
+          atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+          cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
 
-        /* Activate the drift tasks. */
-        if (ci->nodeID == nodeID) cell_activate_drift_spart(ci, s);
-        if (cj->nodeID == nodeID) cell_activate_drift_part(cj, s);
+          /* Activate the drift tasks. */
+          if (ci_nodeID == nodeID) cell_activate_drift_spart(ci, s);
+          if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
 
-        /* Check the sorts and activate them if needed. */
-        cell_activate_stars_sorts(ci, t->flags, s);
-        cell_activate_hydro_sorts(cj, t->flags, s);
+          /* Check the sorts and activate them if needed. */
+          cell_activate_stars_sorts(ci, t->flags, s);
+          cell_activate_hydro_sorts(cj, t->flags, s);
+        }
 
         /* Do cj */
-        /* hydro for ci */
-        atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
-        ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
-
-        /* stars for cj */
-        atomic_or(&cj->stars.requires_sorts, 1 << t->flags);
-        cj->stars.dx_max_sort_old = cj->stars.dx_max_sort;
+        if (cj_active) {
+          /* hydro for ci */
+          atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+          ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
 
-        /* Activate the drift tasks. */
-        if (cj->nodeID == nodeID) cell_activate_drift_spart(cj, s);
-        if (ci->nodeID == nodeID) cell_activate_drift_part(ci, s);
+          /* stars for cj */
+          atomic_or(&cj->stars.requires_sorts, 1 << t->flags);
+          cj->stars.dx_max_sort_old = cj->stars.dx_max_sort;
 
-        /* Check the sorts and activate them if needed. */
-        cell_activate_hydro_sorts(ci, t->flags, s);
-        cell_activate_stars_sorts(cj, t->flags, s);
+          /* Activate the drift tasks. */
+          if (cj_nodeID == nodeID) cell_activate_drift_spart(cj, s);
+          if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
 
+          /* Check the sorts and activate them if needed. */
+          cell_activate_hydro_sorts(ci, t->flags, s);
+          cell_activate_stars_sorts(cj, t->flags, s);
+        }
       }
-      /* Store current values of dx_max and h_max. */
+
       else if (t->type == task_type_sub_pair || t->type == task_type_sub_self) {
-        cell_activate_subcell_stars_tasks(t->ci, t->cj, s);
+        cell_activate_subcell_stars_tasks(ci, cj, s);
       }
     }
 
@@ -3407,99 +3489,102 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
       /* Check whether there was too much particle motion, i.e. the
          cell neighbour conditions were violated. */
       if (cell_need_rebuild_for_stars_pair(ci, cj)) rebuild = 1;
+      if (cell_need_rebuild_for_stars_pair(cj, ci)) rebuild = 1;
 
 #ifdef WITH_MPI
-      error("MPI with stars not implemented");
-      /* /\* Activate the send/recv tasks. *\/ */
-      /* if (ci->nodeID != nodeID) { */
-
-      /*   /\* If the local cell is active, receive data from the foreign cell.
-       * *\/ */
-      /*   if (cj_active) { */
-      /*     scheduler_activate(s, ci->hydro.recv_xv); */
-      /*     if (ci_active) { */
-      /*       scheduler_activate(s, ci->hydro.recv_rho); */
-
-      /*     } */
-      /*   } */
+      /* Activate the send/recv tasks. */
+      if (ci_nodeID != nodeID) {
 
-      /*   /\* If the foreign cell is active, we want its ti_end values. *\/ */
-      /*   if (ci_active) scheduler_activate(s, ci->mpi.recv_ti); */
+        if (cj_active) {
+          scheduler_activate(s, ci->mpi.hydro.recv_xv);
+          scheduler_activate(s, ci->mpi.hydro.recv_rho);
 
-      /*   /\* Is the foreign cell active and will need stuff from us? *\/ */
-      /*   if (ci_active) { */
+          /* If the local cell is active, more stuff will be needed. */
+          scheduler_activate_send(s, cj->mpi.stars.send, ci_nodeID);
+          cell_activate_drift_spart(cj, s);
 
-      /*     scheduler_activate_send(s, cj->hydro.send_xv, ci->nodeID); */
+          /* If the local cell is active, send its ti_end values. */
+          scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID);
+        }
 
-      /*     /\* Drift the cell which will be sent; note that not all sent */
-      /*        particles will be drifted, only those that are needed. *\/ */
-      /*     cell_activate_drift_part(cj, s); */
+        if (ci_active) {
+          scheduler_activate(s, ci->mpi.stars.recv);
 
-      /*     /\* If the local cell is also active, more stuff will be needed.
-       * *\/ */
-      /*     if (cj_active) { */
-      /*       scheduler_activate_send(s, cj->hydro.send_rho, ci->nodeID); */
+          /* If the foreign cell is active, we want its ti_end values. */
+          scheduler_activate(s, ci->mpi.recv_ti);
 
-      /*     } */
-      /*   } */
+          /* Is the foreign cell active and will need stuff from us? */
+          scheduler_activate_send(s, cj->mpi.hydro.send_xv, ci_nodeID);
+          scheduler_activate_send(s, cj->mpi.hydro.send_rho, ci_nodeID);
 
-      /*   /\* If the local cell is active, send its ti_end values. *\/ */
-      /*   if (cj_active) scheduler_activate_send(s, cj->mpi.send_ti,
-       * ci->nodeID);
-       */
+          /* Drift the cell which will be sent; note that not all sent
+             particles will be drifted, only those that are needed. */
+          cell_activate_drift_part(cj, s);
+        }
 
-      /* } else if (cj->nodeID != nodeID) { */
+      } else if (cj_nodeID != nodeID) {
 
-      /*   /\* If the local cell is active, receive data from the foreign cell.
-       * *\/ */
-      /*   if (ci_active) { */
-      /*     scheduler_activate(s, cj->hydro.recv_xv); */
-      /*     if (cj_active) { */
-      /*       scheduler_activate(s, cj->hydro.recv_rho); */
+        /* If the local cell is active, receive data from the foreign cell. */
+        if (ci_active) {
+          scheduler_activate(s, cj->mpi.hydro.recv_xv);
+          scheduler_activate(s, cj->mpi.hydro.recv_rho);
 
-      /*     } */
-      /*   } */
+          /* If the local cell is active, more stuff will be needed. */
+          scheduler_activate_send(s, ci->mpi.stars.send, cj_nodeID);
+          cell_activate_drift_spart(ci, s);
 
-      /*   /\* If the foreign cell is active, we want its ti_end values. *\/ */
-      /*   if (cj_active) scheduler_activate(s, cj->mpi.recv_ti); */
+          /* If the local cell is active, send its ti_end values. */
+          scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID);
+        }
 
-      /*   /\* Is the foreign cell active and will need stuff from us? *\/ */
-      /*   if (cj_active) { */
+        if (cj_active) {
+          scheduler_activate(s, cj->mpi.stars.recv);
 
-      /*     scheduler_activate_send(s, ci->hydro.send_xv, cj->nodeID); */
+          /* If the foreign cell is active, we want its ti_end values. */
+          scheduler_activate(s, cj->mpi.recv_ti);
 
-      /*     /\* Drift the cell which will be sent; note that not all sent */
-      /*        particles will be drifted, only those that are needed. *\/ */
-      /*     cell_activate_drift_part(ci, s); */
+          /* Is the foreign cell active and will need stuff from us? */
+          scheduler_activate_send(s, ci->mpi.hydro.send_xv, cj_nodeID);
+          scheduler_activate_send(s, ci->mpi.hydro.send_rho, cj_nodeID);
 
-      /*     /\* If the local cell is also active, more stuff will be needed.
-       * *\/ */
-      /*     if (ci_active) { */
+          /* Drift the cell which will be sent; note that not all sent
+             particles will be drifted, only those that are needed. */
+          cell_activate_drift_part(ci, s);
+        }
+      }
+#endif
+    }
+  }
 
-      /*       scheduler_activate_send(s, ci->hydro.send_rho, cj->nodeID); */
+  /* Un-skip the feedback tasks involved with this cell. */
+  for (struct link *l = c->stars.feedback; l != NULL; l = l->next) {
+    struct task *t = l->t;
+    struct cell *ci = t->ci;
+    struct cell *cj = t->cj;
+    const int ci_active = cell_is_active_stars(ci, e);
+    const int cj_active = (cj != NULL) ? cell_is_active_stars(cj, e) : 0;
+#ifdef WITH_MPI
+    const int ci_nodeID = ci->nodeID;
+    const int cj_nodeID = (cj != NULL) ? cj->nodeID : -1;
+#else
+    const int ci_nodeID = nodeID;
+    const int cj_nodeID = nodeID;
+#endif
 
-      /*     } */
-      /*   } */
+    if ((ci_active && cj_nodeID == nodeID) ||
+        (cj_active && ci_nodeID == nodeID)) {
+      scheduler_activate(s, t);
 
-      /*   /\* If the local cell is active, send its ti_end values. *\/ */
-      /*   if (ci_active) scheduler_activate_send(s, ci->mpi.send_ti,
-       * cj->nodeID);
-       */
-      /* } */
-#endif
+      /* Nothing more to do here, all drifts and sorts activated above */
     }
   }
 
   /* Unskip all the other task types. */
   if (c->nodeID == nodeID && cell_is_active_stars(c, e)) {
 
-    /* Un-skip the feedback tasks involved with this cell. */
-    for (struct link *l = c->stars.feedback; l != NULL; l = l->next)
-      scheduler_activate(s, l->t);
-
-    if (c->stars.ghost_in != NULL) scheduler_activate(s, c->stars.ghost_in);
-    if (c->stars.ghost_out != NULL) scheduler_activate(s, c->stars.ghost_out);
     if (c->stars.ghost != NULL) scheduler_activate(s, c->stars.ghost);
+    if (c->stars.stars_in != NULL) scheduler_activate(s, c->stars.stars_in);
+    if (c->stars.stars_out != NULL) scheduler_activate(s, c->stars.stars_out);
     if (c->logger != NULL) scheduler_activate(s, c->logger);
   }
 
@@ -3514,8 +3599,9 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s) {
  */
 void cell_set_super(struct cell *c, struct cell *super) {
 
-  /* Are we in a cell with some kind of self/pair task ? */
-  if (super == NULL && (c->nr_tasks > 0 || c->grav.nr_mm_tasks > 0)) super = c;
+  /* Are we in a cell which is either the hydro or gravity super? */
+  if (super == NULL && (c->hydro.super != NULL || c->grav.super != NULL))
+    super = c;
 
   /* Set the super-cell */
   c->super = super;
@@ -3832,15 +3918,9 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
   const int periodic = e->s->periodic;
   const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]};
   const int with_cosmology = (e->policy & engine_policy_cosmology);
-  const float stars_h_max = e->stars_properties->h_max;
   const integertime_t ti_old_gpart = c->grav.ti_old_part;
   const integertime_t ti_current = e->ti_current;
   struct gpart *const gparts = c->grav.parts;
-  struct spart *const sparts = c->stars.parts;
-
-  float dx_max = 0.f, dx2_max = 0.f;
-  float dx_max_sort = 0.0f, dx2_max_sort = 0.f;
-  float cell_h_max = 0.f;
 
   /* Drift irrespective of cell flags? */
   force |= c->grav.do_drift;
@@ -3878,19 +3958,9 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
 
         /* Recurse */
         cell_drift_gpart(cp, e, force);
-
-        /* Update */
-        dx_max = max(dx_max, cp->stars.dx_max_part);
-        dx_max_sort = max(dx_max_sort, cp->stars.dx_max_sort);
-        cell_h_max = max(cell_h_max, cp->stars.h_max);
       }
     }
 
-    /* Store the values */
-    c->stars.h_max = cell_h_max;
-    c->stars.dx_max_part = dx_max;
-    c->stars.dx_max_sort = dx_max_sort;
-
     /* Update the time of the last drift */
     c->grav.ti_old_part = ti_current;
 
@@ -3948,6 +4018,100 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
       }
     }
 
+    /* Update the time of the last drift */
+    c->grav.ti_old_part = ti_current;
+  }
+
+  /* Clear the drift flags. */
+  c->grav.do_drift = 0;
+  c->grav.do_sub_drift = 0;
+}
+
+/**
+ * @brief Recursively drifts the #spart in a cell hierarchy.
+ *
+ * @param c The #cell.
+ * @param e The #engine (to get ti_current).
+ * @param force Drift the particles irrespective of the #cell flags.
+ */
+void cell_drift_spart(struct cell *c, const struct engine *e, int force) {
+
+  const int periodic = e->s->periodic;
+  const double dim[3] = {e->s->dim[0], e->s->dim[1], e->s->dim[2]};
+  const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const float stars_h_max = e->hydro_properties->h_max;
+  const float stars_h_min = e->hydro_properties->h_min;
+  const integertime_t ti_old_spart = c->stars.ti_old_part;
+  const integertime_t ti_current = e->ti_current;
+  struct spart *const sparts = c->stars.parts;
+
+  float dx_max = 0.f, dx2_max = 0.f;
+  float dx_max_sort = 0.0f, dx2_max_sort = 0.f;
+  float cell_h_max = 0.f;
+
+  /* Drift irrespective of cell flags? */
+  force |= c->stars.do_drift;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Check that we only drift local cells. */
+  if (c->nodeID != engine_rank) error("Drifting a foreign cell is nope.");
+
+  /* Check that we are actually going to move forward. */
+  if (ti_current < ti_old_spart) error("Attempt to drift to the past");
+#endif
+
+  /* Early abort? */
+  if (c->stars.count == 0) {
+
+    /* Clear the drift flags. */
+    c->stars.do_drift = 0;
+    c->stars.do_sub_drift = 0;
+
+    /* Update the time of the last drift */
+    c->stars.ti_old_part = ti_current;
+
+    return;
+  }
+
+  /* Ok, we have some particles somewhere in the hierarchy to drift */
+
+  /* Are we not in a leaf ? */
+  if (c->split && (force || c->stars.do_sub_drift)) {
+
+    /* Loop over the progeny and collect their data. */
+    for (int k = 0; k < 8; k++) {
+      if (c->progeny[k] != NULL) {
+        struct cell *cp = c->progeny[k];
+
+        /* Recurse */
+        cell_drift_spart(cp, e, force);
+
+        /* Update */
+        dx_max = max(dx_max, cp->stars.dx_max_part);
+        dx_max_sort = max(dx_max_sort, cp->stars.dx_max_sort);
+        cell_h_max = max(cell_h_max, cp->stars.h_max);
+      }
+    }
+
+    /* Store the values */
+    c->stars.h_max = cell_h_max;
+    c->stars.dx_max_part = dx_max;
+    c->stars.dx_max_sort = dx_max_sort;
+
+    /* Update the time of the last drift */
+    c->stars.ti_old_part = ti_current;
+
+  } else if (!c->split && force && ti_current > ti_old_spart) {
+
+    /* Drift from the last time the cell was drifted to the current time */
+    double dt_drift;
+    if (with_cosmology) {
+      dt_drift =
+          cosmology_get_drift_factor(e->cosmology, ti_old_spart, ti_current);
+    } else {
+      dt_drift = (ti_current - ti_old_spart) * e->time_base;
+    }
+
     /* Loop over all the star particles in the cell */
     const size_t nr_sparts = c->stars.count;
     for (size_t k = 0; k < nr_sparts; k++) {
@@ -3959,7 +4123,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
       if (spart_is_inhibited(sp, e)) continue;
 
       /* Drift... */
-      drift_spart(sp, dt_drift, ti_old_gpart, ti_current);
+      drift_spart(sp, dt_drift, ti_old_spart, ti_current);
 
 #ifdef SWIFT_DEBUG_CHECKS
       /* Make sure the particle does not drift by more than a box length. */
@@ -3991,6 +4155,7 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
 
       /* Limit h to within the allowed range */
       sp->h = min(sp->h, stars_h_max);
+      sp->h = max(sp->h, stars_h_min);
 
       /* Compute (square of) motion since last cell construction */
       const float dx2 = sp->x_diff[0] * sp->x_diff[0] +
@@ -4007,7 +4172,11 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
       /* Maximal smoothing length */
       cell_h_max = max(cell_h_max, sp->h);
 
-    } /* Note: no need to compute dx_max as all spart have a gpart */
+      /* Get ready for a density calculation */
+      if (spart_is_active(sp, e)) {
+        stars_init_spart(sp);
+      }
+    }
 
     /* Now, get the maximal particle motion from its square */
     dx_max = sqrtf(dx2_max);
@@ -4019,12 +4188,12 @@ void cell_drift_gpart(struct cell *c, const struct engine *e, int force) {
     c->stars.dx_max_sort = dx_max_sort;
 
     /* Update the time of the last drift */
-    c->grav.ti_old_part = ti_current;
+    c->stars.ti_old_part = ti_current;
   }
 
   /* Clear the drift flags. */
-  c->grav.do_drift = 0;
-  c->grav.do_sub_drift = 0;
+  c->stars.do_drift = 0;
+  c->stars.do_sub_drift = 0;
 }
 
 /**
@@ -4099,6 +4268,42 @@ void cell_drift_multipole(struct cell *c, const struct engine *e) {
   c->grav.ti_old_multipole = ti_current;
 }
 
+/**
+ * @brief Resets all the sorting properties for the stars in a given cell
+ * hierarchy.
+ *
+ * @param c The #cell to clean.
+ * @param is_super Is this a super-cell?
+ */
+void cell_clear_stars_sort_flags(struct cell *c, const int is_super) {
+
+  /* Recurse if possible */
+  if (c->split) {
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        cell_clear_stars_sort_flags(c->progeny[k], /*is_super=*/0);
+  }
+
+  /* Free the sorted array at the level where it was allocated */
+  if (is_super) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (c != c->hydro.super) error("Cell is not a super-cell!!!");
+#endif
+
+    for (int i = 0; i < 13; i++) {
+      free(c->stars.sort[i]);
+    }
+  }
+
+  /* Indicate that the cell is not sorted and cancel the pointer sorting arrays.
+   */
+  c->stars.sorted = 0;
+  for (int i = 0; i < 13; i++) {
+    c->stars.sort[i] = NULL;
+  }
+}
+
 /**
  * @brief Recursively checks that all particles in a cell have a time-step
  */
diff --git a/src/cell.h b/src/cell.h
index c5fbc9b8c02b0e008d337189fdbf582faf4fa600..b13889abda54f77ec2030bf2262dca30440bdc4e 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -150,6 +150,12 @@ struct pcell {
     /*! Minimal integer end-of-timestep in this cell for stars tasks */
     integertime_t ti_end_min;
 
+    /*! Maximal integer end-of-timestep in this cell for stars tasks */
+    integertime_t ti_end_max;
+
+    /*! Integer time of the last drift of the #spart in this cell */
+    integertime_t ti_old_part;
+
   } stars;
 
   /*! Maximal depth in that part of the tree */
@@ -198,12 +204,15 @@ struct pcell_step {
   /*! Stars variables */
   struct {
 
-    /*! Maximal distance any #part has travelled since last rebuild */
-    float dx_max_part;
-
     /*! Minimal integer end-of-timestep in this cell (stars) */
     integertime_t ti_end_min;
 
+    /*! Maximal integer end-of-timestep in this cell (stars) */
+    integertime_t ti_end_max;
+
+    /*! Maximal distance any #part has travelled since last rebuild */
+    float dx_max_part;
+
   } stars;
 };
 
@@ -278,6 +287,9 @@ struct cell {
     /*! The extra ghost task for complex hydro schemes */
     struct task *extra_ghost;
 
+    /*! The task to end the force calculation */
+    struct task *end_force;
+
     /*! Task for cooling */
     struct task *cooling;
 
@@ -409,6 +421,9 @@ struct cell {
     /*! Task propagating the multipole to the particles */
     struct task *down;
 
+    /*! The task to end the force calculation */
+    struct task *end_force;
+
     /*! Minimum end of (integer) time step in this cell for gravity tasks. */
     integertime_t ti_end_min;
 
@@ -466,12 +481,6 @@ struct cell {
     /*! Pointer to the #spart data. */
     struct spart *parts;
 
-    /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
-    struct task *ghost_in;
-
-    /*! Dependency implicit task for the star ghost  (in->ghost->out)*/
-    struct task *ghost_out;
-
     /*! The star ghost task itself */
     struct task *ghost;
 
@@ -481,12 +490,25 @@ struct cell {
     /*! Linked list of the tasks computing this cell's star feedback. */
     struct link *feedback;
 
-    /*! The task computing this cell's sorts. */
+    /*! The task computing this cell's sorts before the density. */
     struct task *sorts;
 
+    /*! The drift task for sparts */
+    struct task *drift;
+
+    /*! Implicit tasks marking the entry of the stellar physics block of tasks
+     */
+    struct task *stars_in;
+
+    /*! Implicit tasks marking the exit of the stellar physics block of tasks */
+    struct task *stars_out;
+
     /*! Max smoothing length in this cell. */
     double h_max;
 
+    /*! Last (integer) time the cell's spart were drifted forward in time. */
+    integertime_t ti_old_part;
+
     /*! Spin lock for various uses (#spart case). */
     swift_lock_type lock;
 
@@ -529,6 +551,13 @@ struct cell {
     /*! Maximum end of (integer) time step in this cell for gravity tasks. */
     integertime_t ti_end_min;
 
+    /*! Maximum end of (integer) time step in this cell for star tasks. */
+    integertime_t ti_end_max;
+
+    /*! Maximum beginning of (integer) time step in this cell for star tasks.
+     */
+    integertime_t ti_beg_max;
+
     /*! Number of #spart updated in this cell. */
     int updated;
 
@@ -538,6 +567,12 @@ struct cell {
     /*! Is the #spart data of this cell being used in a sub-cell? */
     int hold;
 
+    /*! Does this cell need to be drifted (stars)? */
+    char do_drift;
+
+    /*! Do any of this cell's sub-cells need to be drifted (stars)? */
+    char do_sub_drift;
+
 #ifdef SWIFT_DEBUG_CHECKS
     /*! Last (integer) time the cell's sort arrays were updated. */
     integertime_t ti_sort;
@@ -580,11 +615,18 @@ struct cell {
     } grav;
 
     struct {
+      /* Task receiving spart data. */
+      struct task *recv;
 
-      /* Task receiving gpart data. */
+      /* Linked list for sending spart data. */
+      struct link *send;
+    } stars;
+
+    struct {
+      /* Task receiving limiter data. */
       struct task *recv;
 
-      /* Linked list for sending gpart data. */
+      /* Linked list for sending limiter data. */
       struct link *send;
     } limiter;
 
@@ -609,9 +651,6 @@ struct cell {
   } mpi;
 #endif
 
-  /*! The task to end the force calculation */
-  struct task *end_force;
-
   /*! The first kick task */
   struct task *kick1;
 
@@ -702,6 +741,7 @@ void cell_check_foreign_multipole(const struct cell *c);
 void cell_clean(struct cell *c);
 void cell_check_part_drift_point(struct cell *c, void *data);
 void cell_check_gpart_drift_point(struct cell *c, void *data);
+void cell_check_spart_drift_point(struct cell *c, void *data);
 void cell_check_multipole_drift_point(struct cell *c, void *data);
 void cell_reset_task_counters(struct cell *c);
 int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s);
@@ -710,6 +750,7 @@ int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s);
 void cell_set_super(struct cell *c, struct cell *super);
 void cell_drift_part(struct cell *c, const struct engine *e, int force);
 void cell_drift_gpart(struct cell *c, const struct engine *e, int force);
+void cell_drift_spart(struct cell *c, const struct engine *e, int force);
 void cell_drift_multipole(struct cell *c, const struct engine *e);
 void cell_drift_all_multipoles(struct cell *c, const struct engine *e);
 void cell_check_timesteps(struct cell *c);
@@ -733,6 +774,7 @@ void cell_clear_limiter_flags(struct cell *c, void *data);
 void cell_set_super_mapper(void *map_data, int num_elements, void *extra_data);
 void cell_check_spart_pos(const struct cell *c,
                           const struct spart *global_sparts);
+void cell_clear_stars_sort_flags(struct cell *c, const int is_super);
 int cell_has_tasks(struct cell *c);
 void cell_remove_part(const struct engine *e, struct cell *c, struct part *p,
                       struct xpart *xp);
@@ -854,17 +896,22 @@ cell_can_recurse_in_self_hydro_task(const struct cell *c) {
  * @brief Can a sub-pair star task recurse to a lower level based
  * on the status of the particles in the cell.
  *
- * @param c The #cell.
+ * @param ci The #cell with stars.
+ * @param cj The #cell with hydro parts.
  */
 __attribute__((always_inline)) INLINE static int
-cell_can_recurse_in_pair_stars_task(const struct cell *c) {
+cell_can_recurse_in_pair_stars_task(const struct cell *ci,
+                                    const struct cell *cj) {
 
   /* Is the cell split ? */
   /* If so, is the cut-off radius plus the max distance the parts have moved */
   /* smaller than the sub-cell sizes ? */
   /* Note: We use the _old values as these might have been updated by a drift */
-  return c->split && ((kernel_gamma * c->stars.h_max_old +
-                       c->stars.dx_max_part_old) < 0.5f * c->dmin);
+  return ci->split && cj->split &&
+         ((kernel_gamma * ci->stars.h_max_old + ci->stars.dx_max_part_old) <
+          0.5f * ci->dmin) &&
+         ((kernel_gamma * cj->hydro.h_max_old + cj->hydro.dx_max_part_old) <
+          0.5f * cj->dmin);
 }
 
 /**
@@ -877,7 +924,8 @@ __attribute__((always_inline)) INLINE static int
 cell_can_recurse_in_self_stars_task(const struct cell *c) {
 
   /* Is the cell split and not smaller than the smoothing length? */
-  return c->split && (kernel_gamma * c->stars.h_max_old < 0.5f * c->dmin);
+  return c->split && (kernel_gamma * c->stars.h_max_old < 0.5f * c->dmin) &&
+         (kernel_gamma * c->hydro.h_max_old < 0.5f * c->dmin);
 }
 
 /**
@@ -895,7 +943,8 @@ __attribute__((always_inline)) INLINE static int cell_can_split_pair_hydro_task(
   /* Note that since tasks are create after a rebuild no need to take */
   /* into account any part motion (i.e. dx_max == 0 here) */
   return c->split &&
-         (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin);
+         (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin) &&
+         (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin);
 }
 
 /**
@@ -913,42 +962,7 @@ __attribute__((always_inline)) INLINE static int cell_can_split_self_hydro_task(
   /* Note: No need for more checks here as all the sub-pairs and sub-self */
   /* tasks will be created. So no need to check for h_max */
   return c->split &&
-         (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin);
-}
-
-/**
- * @brief Can a pair stars task associated with a cell be split into smaller
- * sub-tasks.
- *
- * @param c The #cell.
- */
-__attribute__((always_inline)) INLINE static int cell_can_split_pair_stars_task(
-    const struct cell *c) {
-
-  /* Is the cell split ? */
-  /* If so, is the cut-off radius with some leeway smaller than */
-  /* the sub-cell sizes ? */
-  /* Note that since tasks are create after a rebuild no need to take */
-  /* into account any part motion (i.e. dx_max == 0 here) */
-  return c->split &&
-         (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin);
-}
-
-/**
- * @brief Can a self stars task associated with a cell be split into smaller
- * sub-tasks.
- *
- * @param c The #cell.
- */
-__attribute__((always_inline)) INLINE static int cell_can_split_self_stars_task(
-    const struct cell *c) {
-
-  /* Is the cell split ? */
-  /* If so, is the cut-off radius with some leeway smaller than */
-  /* the sub-cell sizes ? */
-  /* Note: No need for more checks here as all the sub-pairs and sub-self */
-  /* tasks will be created. So no need to check for h_max */
-  return c->split &&
+         (space_stretch * kernel_gamma * c->hydro.h_max < 0.5f * c->dmin) &&
          (space_stretch * kernel_gamma * c->stars.h_max < 0.5f * c->dmin);
 }
 
@@ -992,15 +1006,16 @@ cell_need_rebuild_for_hydro_pair(const struct cell *ci, const struct cell *cj) {
   /* Is the cut-off radius plus the max distance the parts in both cells have */
   /* moved larger than the cell size ? */
   /* Note ci->dmin == cj->dmin */
-  return (kernel_gamma * max(ci->hydro.h_max, cj->hydro.h_max) +
-              ci->hydro.dx_max_part + cj->hydro.dx_max_part >
-          cj->dmin);
+  if (kernel_gamma * max(ci->hydro.h_max, cj->hydro.h_max) +
+          ci->hydro.dx_max_part + cj->hydro.dx_max_part >
+      cj->dmin) {
+    return 1;
+  }
+  return 0;
 }
-
 /**
  * @brief Have star particles in a pair of cells moved too much and require a
- * rebuild
- * ?
+ * rebuild?
  *
  * @param ci The first #cell.
  * @param cj The second #cell.
@@ -1011,9 +1026,12 @@ cell_need_rebuild_for_stars_pair(const struct cell *ci, const struct cell *cj) {
   /* Is the cut-off radius plus the max distance the parts in both cells have */
   /* moved larger than the cell size ? */
   /* Note ci->dmin == cj->dmin */
-  return (kernel_gamma * max(ci->stars.h_max, cj->stars.h_max) +
-              ci->stars.dx_max_part + cj->stars.dx_max_part >
-          cj->dmin);
+  if (kernel_gamma * max(ci->stars.h_max, cj->hydro.h_max) +
+          ci->stars.dx_max_part + cj->hydro.dx_max_part >
+      cj->dmin) {
+    return 1;
+  }
+  return 0;
 }
 
 /**
diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h
index c5a165f4e1663394a0982879292acf91f43a5b68..2632de7e223306a6c6400e350f8cb62a62e58206 100644
--- a/src/cooling/grackle/cooling.h
+++ b/src/cooling/grackle/cooling.h
@@ -24,9 +24,6 @@
  * @brief Cooling using the GRACKLE 3.0 library.
  */
 
-/* Config parameters. */
-#include "../config.h"
-
 /* Some standard headers. */
 #include <float.h>
 #include <math.h>
@@ -49,6 +46,20 @@
 #define GRACKLE_NPART 1
 #define GRACKLE_RANK 3
 
+/* prototype */
+static gr_float cooling_time(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, const struct xpart* restrict xp);
+static gr_float cooling_new_energy(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp, double dt);
+
 /**
  * @brief Common operations performed on the cooling function at a
  * given time-step or redshift.
@@ -58,24 +69,13 @@
  */
 INLINE static void cooling_update(const struct cosmology* cosmo,
                                   struct cooling_function_data* cooling) {
-  // Add content if required.
+  /* set current time */
+  if (cooling->redshift == -1)
+    cooling->units.a_value = cosmo->a;
+  else
+    cooling->units.a_value = 1. / (1. + cooling->redshift);
 }
 
-/* prototypes */
-static gr_float cooling_time(
-    const struct phys_const* restrict phys_const,
-    const struct unit_system* restrict us,
-    const struct cosmology* restrict cosmo,
-    const struct cooling_function_data* restrict cooling,
-    const struct part* restrict p, struct xpart* restrict xp);
-
-static double cooling_rate(const struct phys_const* restrict phys_const,
-                           const struct unit_system* restrict us,
-                           const struct cosmology* restrict cosmo,
-                           const struct cooling_function_data* restrict cooling,
-                           const struct part* restrict p,
-                           struct xpart* restrict xp, double dt);
-
 /**
  * @brief Print the chemical network
  *
@@ -177,7 +177,7 @@ __attribute__((always_inline)) INLINE static void cooling_compute_equilibrium(
   const double alpha = 0.01;
   double dt =
       fabs(cooling_time(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp));
-  cooling_rate(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
+  cooling_new_energy(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
   dt = alpha *
        fabs(cooling_time(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp));
 
@@ -193,7 +193,7 @@ __attribute__((always_inline)) INLINE static void cooling_compute_equilibrium(
     old = *xp;
 
     /* update chemistry */
-    cooling_rate(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
+    cooling_new_energy(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
   } while (step < max_step && !cooling_converged(xp, &old, conv_limit));
 
   if (step == max_step)
@@ -290,7 +290,8 @@ __attribute__((always_inline)) INLINE static void cooling_print_backend(
   message("\tLength       = %g", cooling->units.length_units);
   message("\tDensity      = %g", cooling->units.density_units);
   message("\tTime         = %g", cooling->units.time_units);
-  message("\tScale Factor = %g", cooling->units.a_units);
+  message("\tScale Factor = %g (units: %g)", cooling->units.a_value,
+          cooling->units.a_units);
 }
 
 /**
@@ -487,7 +488,8 @@ __attribute__((always_inline)) INLINE static void cooling_print_backend(
   cooling_copy_from_grackle3(data, p, xp, rho);
 
 /**
- * @brief Compute the cooling rate and update the particle chemistry data
+ * @brief Compute the energy of a particle after dt and update the particle
+ * chemistry data
  *
  * @param phys_const The physical constants in internal units.
  * @param us The internal system of units.
@@ -498,25 +500,15 @@ __attribute__((always_inline)) INLINE static void cooling_print_backend(
  *
  * @return du / dt
  */
-__attribute__((always_inline)) INLINE static gr_float cooling_rate(
+__attribute__((always_inline)) INLINE static gr_float cooling_new_energy(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
     const struct part* restrict p, struct xpart* restrict xp, double dt) {
 
-  if (cosmo->Omega_m != 0. || cosmo->Omega_r != 0. || cosmo->Omega_k != 0. ||
-      cosmo->Omega_lambda != 0. || cosmo->Omega_b != 0.)
-    error(
-        "Check cosmology factors (physical vs. co-moving and drifted vs. "
-        "un-drifted)!");
-
   /* set current time */
   code_units units = cooling->units;
-  if (cooling->redshift == -1)
-    units.a_value = cosmo->a;
-  else
-    units.a_value = 1. / (1. + cooling->redshift);
 
   /* initialize data */
   grackle_field_data data;
@@ -535,8 +527,7 @@ __attribute__((always_inline)) INLINE static gr_float cooling_rate(
 
   /* general particle data */
   gr_float density = hydro_get_physical_density(p, cosmo);
-  const double energy_before =
-      hydro_get_drifted_physical_internal_energy(p, cosmo);
+  const float energy_before = hydro_get_physical_internal_energy(p, xp, cosmo);
   gr_float energy = energy_before;
 
   /* initialize density */
@@ -555,8 +546,7 @@ __attribute__((always_inline)) INLINE static gr_float cooling_rate(
 
   /* solve chemistry */
   chemistry_data chemistry_grackle = cooling->chemistry;
-  chemistry_data_storage chemistry_rates = grackle_rates;
-  if (local_solve_chemistry(&chemistry_grackle, &chemistry_rates, &units, &data,
+  if (local_solve_chemistry(&chemistry_grackle, &grackle_rates, &units, &data,
                             dt) == 0) {
     error("Error in solve_chemistry.");
   }
@@ -564,8 +554,7 @@ __attribute__((always_inline)) INLINE static gr_float cooling_rate(
   /* copy from grackle data to particle */
   cooling_copy_from_grackle(data, p, xp, density);
 
-  /* compute rate */
-  return (energy - energy_before) / dt;
+  return energy;
 }
 
 /**
@@ -582,14 +571,10 @@ __attribute__((always_inline)) INLINE static gr_float cooling_time(
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
-    const struct part* restrict p, struct xpart* restrict xp) {
+    const struct part* restrict p, const struct xpart* restrict xp) {
 
   /* set current time */
   code_units units = cooling->units;
-  if (cooling->redshift == -1)
-    error("TODO time dependant redshift");
-  else
-    units.a_value = 1. / (1. + cooling->redshift);
 
   /* initialize data */
   grackle_field_data data;
@@ -607,7 +592,7 @@ __attribute__((always_inline)) INLINE static gr_float cooling_time(
 
   /* general particle data */
   const gr_float energy_before =
-      hydro_get_drifted_physical_internal_energy(p, cosmo);
+      hydro_get_physical_internal_energy(p, xp, cosmo);
   gr_float density = hydro_get_physical_density(p, cosmo);
   gr_float energy = energy_before;
 
@@ -664,25 +649,50 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     struct part* restrict p, struct xpart* restrict xp, double dt,
     double dt_therm) {
 
-  if (cosmo->Omega_m != 0. || cosmo->Omega_r != 0. || cosmo->Omega_k != 0. ||
-      cosmo->Omega_lambda != 0. || cosmo->Omega_b != 0.)
-    error(
-        "Check cosmology factors (physical vs. co-moving and drifted vs. "
-        "un-drifted)!");
-
+  /* Nothing to do here? */
   if (dt == 0.) return;
 
-  /* Current du_dt */
+  /* Current energy */
+  const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Current du_dt in physical coordinates (internal units) */
   const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo);
 
-  /* compute cooling rate */
-  const float du_dt = cooling_rate(phys_const, us, cosmo, cooling, p, xp, dt);
+  /* Calculate energy after dt */
+  gr_float u_new =
+      cooling_new_energy(phys_const, us, cosmo, cooling, p, xp, dt);
+
+  float delta_u = u_new - u_old + hydro_du_dt * dt_therm;
+
+  /* We now need to check that we are not going to go below any of the limits */
+
+  /* First, check whether we may end up below the minimal energy after
+   * this step 1/2 kick + another 1/2 kick that could potentially be for
+   * a time-step twice as big. We hence check for 1.5 delta_u. */
+  if (u_old + 1.5 * delta_u < hydro_props->minimal_internal_energy) {
+    delta_u = (hydro_props->minimal_internal_energy - u_old) / 1.5;
+  }
+
+  /* Second, check whether the energy used in the prediction could get negative.
+   * We need to check for the 1/2 dt kick followed by a full time-step drift
+   * that could potentially be for a time-step twice as big. We hence check
+   * for 2.5 delta_u but this time against 0 energy not the minimum.
+   * To avoid numerical rounding bringing us below 0., we add a tiny tolerance.
+   */
+  const float rounding_tolerance = 1.0e-4;
+
+  if (u_old + 2.5 * delta_u < 0.) {
+    delta_u = -u_old / (2.5 + rounding_tolerance);
+  }
+
+  /* Turn this into a rate of change (including cosmology term) */
+  const float cooling_du_dt = delta_u / dt_therm;
 
-  /* record energy lost */
-  xp->cooling_data.radiated_energy += -du_dt * dt * hydro_get_mass(p);
+  /* Update the internal energy time derivative */
+  hydro_set_physical_internal_energy_dt(p, cosmo, cooling_du_dt);
 
-  /* Update the internal energy */
-  hydro_set_physical_internal_energy_dt(p, cosmo, hydro_du_dt + du_dt);
+  /* Store the radiated energy */
+  xp->cooling_data.radiated_energy -= hydro_get_mass(p) * cooling_du_dt * dt;
 }
 
 static INLINE float cooling_get_temperature(
@@ -731,7 +741,7 @@ __attribute__((always_inline)) INLINE static void cooling_init_units(
   /* These are conversions from code units to cgs. */
 
   /* first cosmo */
-  cooling->units.a_units = 1.0;  // units for the expansion factor (1/1+zi)
+  cooling->units.a_units = 1.0;  // units for the expansion factor
   cooling->units.a_value = 1.0;
 
   /* We assume here all physical quantities to
@@ -740,12 +750,12 @@ __attribute__((always_inline)) INLINE static void cooling_init_units(
 
   /* then units */
   cooling->units.density_units =
-      us->UnitMass_in_cgs / pow(us->UnitLength_in_cgs, 3);
-  cooling->units.length_units = us->UnitLength_in_cgs;
-  cooling->units.time_units = us->UnitTime_in_cgs;
-  cooling->units.velocity_units = cooling->units.a_units *
-                                  cooling->units.length_units /
-                                  cooling->units.time_units;
+      units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
+  cooling->units.length_units =
+      units_cgs_conversion_factor(us, UNIT_CONV_LENGTH);
+  cooling->units.time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME);
+  cooling->units.velocity_units =
+      units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
 }
 
 /**
@@ -824,6 +834,7 @@ __attribute__((always_inline)) INLINE static void cooling_init_backend(
   /* Set up the units system. */
   cooling_init_units(us, cooling);
 
+  /* Set up grackle */
   cooling_init_grackle(cooling);
 }
 
diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h
index 88235a20a2f9d150b56f59ee32fa9ab91941e659..3905cafd05fb8e15ddf33f4ea688d6144698df73 100644
--- a/src/cooling/grackle/cooling_io.h
+++ b/src/cooling/grackle/cooling_io.h
@@ -19,9 +19,6 @@
 #ifndef SWIFT_COOLING_GRACKLE_IO_H
 #define SWIFT_COOLING_GRACKLE_IO_H
 
-/* Config parameters. */
-#include "../config.h"
-
 /* Local includes */
 #include "cooling_struct.h"
 #include "io_properties.h"
@@ -64,8 +61,6 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles(
 
   int num = 0;
 
-  if (cooling->output_mode == 0) return num;
-
 #if COOLING_GRACKLE_MODE >= 1
   /* List what we want to write */
   list[0] = io_make_output_field("HI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
@@ -89,8 +84,6 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles(
   num += 6;
 #endif
 
-  if (cooling->output_mode == 1) return num;
-
 #if COOLING_GRACKLE_MODE >= 2
   list += num;
 
@@ -106,8 +99,6 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles(
   num += 3;
 #endif
 
-  if (cooling->output_mode == 2) return num;
-
 #if COOLING_GRACKLE_MODE >= 3
   list += num;
 
@@ -156,9 +147,6 @@ __attribute__((always_inline)) INLINE static void cooling_read_parameters(
   cooling->self_shielding_method = parser_get_opt_param_int(
       parameter_file, "GrackleCooling:SelfShieldingMethod", 0);
 
-  cooling->output_mode =
-      parser_get_opt_param_int(parameter_file, "GrackleCooling:OutputMode", 0);
-
   cooling->max_step = parser_get_opt_param_int(
       parameter_file, "GrackleCooling:MaxSteps", 10000);
 
diff --git a/src/cooling/grackle/cooling_struct.h b/src/cooling/grackle/cooling_struct.h
index 6d4b37a6240446d580818e16ff887c8079e319a6..66c385234cccf6532100e86f2c16a508b1112baa 100644
--- a/src/cooling/grackle/cooling_struct.h
+++ b/src/cooling/grackle/cooling_struct.h
@@ -19,8 +19,6 @@
 #ifndef SWIFT_COOLING_STRUCT_GRACKLE_H
 #define SWIFT_COOLING_STRUCT_GRACKLE_H
 
-#include "../config.h"
-
 /* include grackle */
 #include <grackle.h>
 
@@ -61,9 +59,6 @@ struct cooling_function_data {
   /* Self shielding method (<= 3) means grackle modes */
   int self_shielding_method;
 
-  /* Output mode (correspond to primordial chemistry mode */
-  int output_mode;
-
   /* convergence limit for first init */
   float convergence_limit;
 
diff --git a/src/debug.c b/src/debug.c
index 2c1d81b676520c4823a27f3a7a8cc617585ca5f2..d2aff378a174ade46b62a3931f78394a0f41ca41 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -181,6 +181,14 @@ int checkSpacehmax(struct space *s) {
     }
   }
 
+  float cell_stars_h_max = 0.0f;
+  for (int k = 0; k < s->nr_cells; k++) {
+    if (s->cells_top[k].nodeID == s->e->nodeID &&
+        s->cells_top[k].stars.h_max > cell_stars_h_max) {
+      cell_stars_h_max = s->cells_top[k].stars.h_max;
+    }
+  }
+
   /* Now all particles. */
   float part_h_max = 0.0f;
   for (size_t k = 0; k < s->nr_parts; k++) {
@@ -189,10 +197,21 @@ int checkSpacehmax(struct space *s) {
     }
   }
 
+  /* Now all the sparticles. */
+  float spart_h_max = 0.0f;
+  for (size_t k = 0; k < s->nr_sparts; k++) {
+    if (s->sparts[k].h > spart_h_max) {
+      spart_h_max = s->sparts[k].h;
+    }
+  }
+
   /*  If within some epsilon we are OK. */
-  if (fabsf(cell_h_max - part_h_max) <= FLT_EPSILON) return 1;
+  if (fabsf(cell_h_max - part_h_max) <= FLT_EPSILON &&
+      fabsf(cell_stars_h_max - spart_h_max) <= FLT_EPSILON)
+    return 1;
 
   /* There is a problem. Hunt it down. */
+  /* part */
   for (int k = 0; k < s->nr_cells; k++) {
     if (s->cells_top[k].nodeID == s->e->nodeID) {
       if (s->cells_top[k].hydro.h_max > part_h_max) {
@@ -209,6 +228,23 @@ int checkSpacehmax(struct space *s) {
     }
   }
 
+  /* spart */
+  for (int k = 0; k < s->nr_cells; k++) {
+    if (s->cells_top[k].nodeID == s->e->nodeID) {
+      if (s->cells_top[k].stars.h_max > spart_h_max) {
+        message("cell %d is inconsistent (%f > %f)", k,
+                s->cells_top[k].stars.h_max, spart_h_max);
+      }
+    }
+  }
+
+  for (size_t k = 0; k < s->nr_sparts; k++) {
+    if (s->sparts[k].h > cell_stars_h_max) {
+      message("spart %lld is inconsistent (%f > %f)", s->sparts[k].id,
+              s->sparts[k].h, cell_stars_h_max);
+    }
+  }
+
   return 0;
 }
 
@@ -227,6 +263,8 @@ int checkCellhdxmax(const struct cell *c, int *depth) {
 
   float h_max = 0.0f;
   float dx_max = 0.0f;
+  float stars_h_max = 0.0f;
+  float stars_dx_max = 0.0f;
   int result = 1;
 
   const double loc_min[3] = {c->loc[0], c->loc[1], c->loc[2]};
@@ -262,6 +300,33 @@ int checkCellhdxmax(const struct cell *c, int *depth) {
     dx_max = max(dx_max, sqrt(dx2));
   }
 
+  const size_t nr_sparts = c->stars.count;
+  struct spart *sparts = c->stars.parts;
+  for (size_t k = 0; k < nr_sparts; k++) {
+
+    struct spart *const sp = &sparts[k];
+
+    if (sp->x[0] < loc_min[0] || sp->x[0] >= loc_max[0] ||
+        sp->x[1] < loc_min[1] || sp->x[1] >= loc_max[1] ||
+        sp->x[2] < loc_min[2] || sp->x[2] >= loc_max[2]) {
+
+      message(
+          "Inconsistent part position p->x=[%e %e %e], c->loc=[%e %e %e] "
+          "c->width=[%e %e %e]",
+          sp->x[0], sp->x[1], sp->x[2], c->loc[0], c->loc[1], c->loc[2],
+          c->width[0], c->width[1], c->width[2]);
+
+      result = 0;
+    }
+
+    const float dx2 = sp->x_diff[0] * sp->x_diff[0] +
+                      sp->x_diff[1] * sp->x_diff[1] +
+                      sp->x_diff[2] * sp->x_diff[2];
+
+    stars_h_max = max(stars_h_max, sp->h);
+    stars_dx_max = max(stars_dx_max, sqrt(dx2));
+  }
+
   if (c->split) {
     for (int k = 0; k < 8; k++) {
       if (c->progeny[k] != NULL) {
@@ -285,6 +350,19 @@ int checkCellhdxmax(const struct cell *c, int *depth) {
     result = 0;
   }
 
+  if (c->stars.h_max != stars_h_max) {
+    message("%d Inconsistent stars_h_max: cell %f != parts %f", *depth,
+            c->stars.h_max, stars_h_max);
+    message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]);
+    result = 0;
+  }
+  if (c->stars.dx_max_part != stars_dx_max) {
+    message("%d Inconsistent stars_dx_max: %f != %f", *depth,
+            c->stars.dx_max_part, stars_dx_max);
+    message("location: %f %f %f", c->loc[0], c->loc[1], c->loc[2]);
+    result = 0;
+  }
+
   return result;
 }
 
diff --git a/src/drift.h b/src/drift.h
index a4bdf9be74aade4fe0f1349544cf472363c81c99..7e874fe0ceabe5b091cc7c5bb53adbef2c9a3efd 100644
--- a/src/drift.h
+++ b/src/drift.h
@@ -28,6 +28,7 @@
 #include "dimension.h"
 #include "hydro.h"
 #include "part.h"
+#include "stars.h"
 
 /**
  * @brief Perform the 'drift' operation on a #gpart.
@@ -138,6 +139,9 @@ __attribute__((always_inline)) INLINE static void drift_spart(
   sp->x[1] += sp->v[1] * dt_drift;
   sp->x[2] += sp->v[2] * dt_drift;
 
+  /* Predict the values of the extra fields */
+  stars_predict_extra(sp, dt_drift);
+
   /* Compute offsets since last cell construction */
   for (int k = 0; k < 3; k++) {
     const float dx = sp->v[k] * dt_drift;
diff --git a/src/engine.c b/src/engine.c
index bc243bac7bee9d862071c8487134e2a3b7902902..c8fe9e9c777e494540a2b6a9f424157eba06e597 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -2710,16 +2710,21 @@ void engine_skip_force_and_kick(struct engine *e) {
 
     /* Skip everything that updates the particles */
     if (t->type == task_type_drift_part || t->type == task_type_drift_gpart ||
-        t->type == task_type_kick1 || t->type == task_type_kick2 ||
-        t->type == task_type_timestep ||
+        t->type == task_type_drift_spart || t->type == task_type_kick1 ||
+        t->type == task_type_kick2 || t->type == task_type_timestep ||
         t->type == task_type_timestep_limiter ||
         t->subtype == task_subtype_force ||
         t->subtype == task_subtype_limiter || t->subtype == task_subtype_grav ||
-        t->type == task_type_end_force ||
+        t->type == task_type_end_hydro_force ||
+        t->type == task_type_end_grav_force ||
         t->type == task_type_grav_long_range || t->type == task_type_grav_mm ||
-        t->type == task_type_grav_down || t->type == task_type_cooling ||
+        t->type == task_type_grav_down || t->type == task_type_grav_down_in ||
+        t->type == task_type_drift_gpart_out || t->type == task_type_cooling ||
+        t->type == task_type_stars_in || t->type == task_type_stars_out ||
         t->type == task_type_star_formation ||
-        t->type == task_type_extra_ghost || t->subtype == task_subtype_gradient)
+        t->type == task_type_extra_ghost ||
+        t->subtype == task_subtype_gradient ||
+        t->subtype == task_subtype_stars_feedback)
       t->skip = 1;
   }
 
@@ -2743,7 +2748,8 @@ void engine_skip_drift(struct engine *e) {
     struct task *t = &tasks[i];
 
     /* Skip everything that moves the particles */
-    if (t->type == task_type_drift_part || t->type == task_type_drift_gpart)
+    if (t->type == task_type_drift_part || t->type == task_type_drift_gpart ||
+        t->type == task_type_drift_spart)
       t->skip = 1;
   }
 
@@ -2757,7 +2763,6 @@ void engine_skip_drift(struct engine *e) {
  * @param e The #engine.
  */
 void engine_launch(struct engine *e) {
-
   const ticks tic = getticks();
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -3949,6 +3954,78 @@ void engine_split(struct engine *e, struct partition *initial_partition) {
 #endif
 }
 
+#ifdef DEBUG_INTERACTIONS_STARS
+/**
+ * @brief Exchange the feedback counters between stars
+ * @param e The #engine.
+ */
+void engine_collect_stars_counter(struct engine *e) {
+
+#ifdef WITH_MPI
+  if (e->total_nr_sparts > 1e5) {
+    message("WARNING: too many sparts, skipping exchange of counters");
+    return;
+  }
+
+  /* Get number of sparticles for each rank */
+  size_t *n_sparts = (size_t *)malloc(e->nr_nodes * sizeof(size_t));
+
+  int err = MPI_Allgather(&e->s->nr_sparts_foreign, 1, MPI_UNSIGNED_LONG,
+                          n_sparts, 1, MPI_UNSIGNED_LONG, MPI_COMM_WORLD);
+  if (err != MPI_SUCCESS) error("Communication failed");
+
+  /* Compute derivated quantities */
+  int total = 0;
+  int *n_sparts_int = (int *)malloc(e->nr_nodes * sizeof(int));
+  int *displs = (int *)malloc(e->nr_nodes * sizeof(int));
+  for (int i = 0; i < e->nr_nodes; i++) {
+    displs[i] = total;
+    total += n_sparts[i];
+    n_sparts_int[i] = n_sparts[i];
+  }
+
+  /* Get all sparticles */
+  struct spart *sparts = (struct spart *)malloc(total * sizeof(struct spart));
+  err = MPI_Allgatherv(e->s->sparts_foreign, e->s->nr_sparts_foreign,
+                       spart_mpi_type, sparts, n_sparts_int, displs,
+                       spart_mpi_type, MPI_COMM_WORLD);
+  if (err != MPI_SUCCESS) error("Communication failed");
+
+  /* Reset counters */
+  for (size_t i = 0; i < e->s->nr_sparts_foreign; i++) {
+    e->s->sparts_foreign[i].num_ngb_force = 0;
+  }
+
+  /* Update counters */
+  struct spart *local_sparts = e->s->sparts;
+  for (size_t i = 0; i < e->s->nr_sparts; i++) {
+    const long long id_i = local_sparts[i].id;
+
+    for (int j = 0; j < total; j++) {
+      const long long id_j = sparts[j].id;
+
+      if (id_j == id_i) {
+        if (j >= displs[engine_rank] &&
+            j < displs[engine_rank] + n_sparts_int[engine_rank]) {
+          error(
+              "Found a local spart in foreign cell ID=%lli: j=%i, displs=%i, "
+              "n_sparts=%i",
+              id_j, j, displs[engine_rank], n_sparts_int[engine_rank]);
+        }
+
+        local_sparts[i].num_ngb_force += sparts[j].num_ngb_force;
+      }
+    }
+  }
+
+  free(n_sparts);
+  free(n_sparts_in);
+  free(sparts);
+#endif
+}
+
+#endif
+
 /**
  * @brief Writes a snapshot with the current state of the engine
  *
@@ -3985,6 +4062,10 @@ void engine_dump_snapshot(struct engine *e) {
   }
 #endif
 
+#ifdef DEBUG_INTERACTIONS_STARS
+  engine_collect_stars_counter(e);
+#endif
+
 /* Dump... */
 #if defined(HAVE_HDF5)
 #if defined(WITH_MPI)
diff --git a/src/engine_drift.c b/src/engine_drift.c
index 7a842068b57813575c33dd670172059abb1e8fc0..1b0711619d68da02753f307190ca3a0624feecce 100644
--- a/src/engine_drift.c
+++ b/src/engine_drift.c
@@ -124,6 +124,54 @@ void engine_do_drift_all_gpart_mapper(void *map_data, int num_elements,
   }
 }
 
+/**
+ * @brief Mapper function to drift *all* the #spart to the current time.
+ *
+ * @param map_data An array of #cell%s.
+ * @param num_elements Chunk size.
+ * @param extra_data Pointer to an #engine.
+ */
+void engine_do_drift_all_spart_mapper(void *map_data, int num_elements,
+                                      void *extra_data) {
+
+  const struct engine *e = (const struct engine *)extra_data;
+  const int restarting = e->restarting;
+  struct space *s = e->s;
+  struct cell *cells_top;
+  int *local_cells_top;
+
+  if (restarting) {
+
+    /* When restarting, we loop over all top-level cells */
+    cells_top = (struct cell *)map_data;
+    local_cells_top = NULL;
+
+  } else {
+
+    /* In any other case, we use the list of local cells with tasks */
+    cells_top = s->cells_top;
+    local_cells_top = (int *)map_data;
+  }
+
+  for (int ind = 0; ind < num_elements; ind++) {
+
+    struct cell *c;
+
+    /* When restarting, the list of local cells does not
+       yet exist. We use the raw list of top-level cells instead */
+    if (restarting)
+      c = &cells_top[ind];
+    else
+      c = &cells_top[local_cells_top[ind]];
+
+    if (c->nodeID == e->nodeID) {
+
+      /* Drift all the particles */
+      cell_drift_spart(c, e, /* force the drift=*/1);
+    }
+  }
+}
+
 /**
  * @brief Mapper function to drift *all* the multipoles to the current time.
  *
@@ -204,6 +252,11 @@ void engine_drift_all(struct engine *e, const int drift_mpoles) {
                      e->s->local_cells_top, e->s->nr_local_cells, sizeof(int),
                      /* default chunk */ 0, e);
     }
+    if (e->s->nr_sparts > 0) {
+      threadpool_map(&e->threadpool, engine_do_drift_all_spart_mapper,
+                     e->s->local_cells_top, e->s->nr_local_cells, sizeof(int),
+                     /* default chunk */ 0, e);
+    }
     if (drift_mpoles && (e->policy & engine_policy_self_gravity)) {
       threadpool_map(&e->threadpool, engine_do_drift_all_multipole_mapper,
                      e->s->local_cells_with_tasks_top,
diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c
index d1d3be6c12ee47e22da42b41456c822394ed2ba4..6340cb17eb574822957aa4a7e6180cda9a14b5d7 100644
--- a/src/engine_maketasks.c
+++ b/src/engine_maketasks.c
@@ -146,6 +146,7 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
                                0, ci, cj);
       t_rho = scheduler_addtask(s, task_type_send, task_subtype_rho,
                                 ci->mpi.tag, 0, ci, cj);
+
 #ifdef EXTRA_HYDRO_LOOP
       t_gradient = scheduler_addtask(s, task_type_send, task_subtype_gradient,
                                      ci->mpi.tag, 0, ci, cj);
@@ -153,7 +154,7 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
 
 #ifdef EXTRA_HYDRO_LOOP
 
-      scheduler_addunlock(s, t_gradient, ci->super->kick2);
+      scheduler_addunlock(s, t_gradient, ci->hydro.super->hydro.end_force);
 
       scheduler_addunlock(s, ci->hydro.super->hydro.extra_ghost, t_gradient);
 
@@ -169,7 +170,7 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
 
 #else
       /* The send_rho task should unlock the super_hydro-cell's kick task. */
-      scheduler_addunlock(s, t_rho, ci->super->end_force);
+      scheduler_addunlock(s, t_rho, ci->hydro.super->hydro.end_force);
 
       /* The send_rho task depends on the cell's ghost task. */
       scheduler_addunlock(s, ci->hydro.super->hydro.ghost_out, t_rho);
@@ -179,6 +180,8 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
 
 #endif
 
+      scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rho);
+
       /* Drift before you send */
       scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_xv);
     }
@@ -203,6 +206,65 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
 #endif
 }
 
+/**
+ * @brief Add send tasks for the stars pairs to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param ci The sending #cell.
+ * @param cj Dummy cell containing the nodeID of the receiving node.
+ * @param t_feedback The send_feed #task, if it has already been created.
+ */
+void engine_addtasks_send_stars(struct engine *e, struct cell *ci,
+                                struct cell *cj, struct task *t_feedback) {
+
+#ifdef WITH_MPI
+
+  struct link *l = NULL;
+  struct scheduler *s = &e->sched;
+  const int nodeID = cj->nodeID;
+
+  /* Check if any of the density tasks are for the target node. */
+  for (l = ci->stars.density; l != NULL; l = l->next)
+    if (l->t->ci->nodeID == nodeID ||
+        (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
+      break;
+
+  /* If so, attach send tasks. */
+  if (l != NULL) {
+
+    if (t_feedback == NULL) {
+
+      /* Make sure this cell is tagged. */
+      cell_ensure_tagged(ci);
+
+      /* Create the tasks and their dependencies? */
+      t_feedback = scheduler_addtask(s, task_type_send, task_subtype_spart,
+                                     ci->mpi.tag, 0, ci, cj);
+
+      /* The send_stars task should unlock the super_cell's kick task. */
+      scheduler_addunlock(s, t_feedback, ci->hydro.super->stars.stars_out);
+
+      /* Ghost before you send */
+      scheduler_addunlock(s, ci->hydro.super->stars.ghost, t_feedback);
+
+      /* Drift before you send */
+      scheduler_addunlock(s, ci->hydro.super->stars.drift, t_feedback);
+    }
+
+    engine_addlink(e, &ci->mpi.stars.send, t_feedback);
+  }
+
+  /* Recurse? */
+  if (ci->split)
+    for (int k = 0; k < 8; k++)
+      if (ci->progeny[k] != NULL)
+        engine_addtasks_send_stars(e, ci->progeny[k], cj, t_feedback);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
 /**
  * @brief Add send tasks for the time-step to a hierarchy of cells.
  *
@@ -236,6 +298,12 @@ void engine_addtasks_send_timestep(struct engine *e, struct cell *ci,
           (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
         break;
 
+  if (l == NULL)
+    for (l = ci->stars.density; l != NULL; l = l->next)
+      if (l->t->ci->nodeID == nodeID ||
+          (l->t->cj != NULL && l->t->cj->nodeID == nodeID))
+        break;
+
   /* If found anything, attach send tasks. */
   if (l != NULL) {
 
@@ -319,7 +387,10 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c,
   c->mpi.hydro.recv_gradient = t_gradient;
 
   /* Add dependencies. */
-  if (c->hydro.sorts != NULL) scheduler_addunlock(s, t_xv, c->hydro.sorts);
+  if (c->hydro.sorts != NULL) {
+    scheduler_addunlock(s, t_xv, c->hydro.sorts);
+    scheduler_addunlock(s, c->hydro.sorts, t_rho);
+  }
 
   for (struct link *l = c->hydro.density; l != NULL; l = l->next) {
     scheduler_addunlock(s, t_xv, l->t);
@@ -330,13 +401,20 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c,
     scheduler_addunlock(s, t_rho, l->t);
     scheduler_addunlock(s, l->t, t_gradient);
   }
-  for (struct link *l = c->hydro.force; l != NULL; l = l->next)
+  for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
     scheduler_addunlock(s, t_gradient, l->t);
+  }
 #else
-  for (struct link *l = c->hydro.force; l != NULL; l = l->next)
+  for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
     scheduler_addunlock(s, t_rho, l->t);
+  }
 #endif
 
+  /* Make sure the density has been computed before the stars compute theirs. */
+  for (struct link *l = c->stars.density; l != NULL; l = l->next) {
+    scheduler_addunlock(s, t_rho, l->t);
+  }
+
   /* Recurse? */
   if (c->split)
     for (int k = 0; k < 8; k++)
@@ -348,6 +426,59 @@ void engine_addtasks_recv_hydro(struct engine *e, struct cell *c,
 #endif
 }
 
+/**
+ * @brief Add recv tasks for stars pairs to a hierarchy of cells.
+ *
+ * @param e The #engine.
+ * @param c The foreign #cell.
+ * @param t_feedback The recv_feed #task, if it has already been created.
+ */
+void engine_addtasks_recv_stars(struct engine *e, struct cell *c,
+                                struct task *t_feedback) {
+
+#ifdef WITH_MPI
+  struct scheduler *s = &e->sched;
+
+  /* Have we reached a level where there are any stars tasks ? */
+  if (t_feedback == NULL && c->stars.density != NULL) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Make sure this cell has a valid tag. */
+    if (c->mpi.tag < 0) error("Trying to receive from untagged cell.");
+#endif  // SWIFT_DEBUG_CHECKS
+
+    /* Create the tasks. */
+    t_feedback = scheduler_addtask(s, task_type_recv, task_subtype_spart,
+                                   c->mpi.tag, 0, c, NULL);
+  }
+
+  c->mpi.stars.recv = t_feedback;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (c->nodeID == e->nodeID) error("Local cell!");
+#endif
+  if (c->stars.sorts != NULL)
+    scheduler_addunlock(s, t_feedback, c->stars.sorts);
+
+  for (struct link *l = c->stars.density; l != NULL; l = l->next) {
+    scheduler_addunlock(s, l->t, t_feedback);
+  }
+
+  for (struct link *l = c->stars.feedback; l != NULL; l = l->next) {
+    scheduler_addunlock(s, t_feedback, l->t);
+  }
+
+  /* Recurse? */
+  if (c->split)
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL)
+        engine_addtasks_recv_stars(e, c->progeny[k], t_feedback);
+
+#else
+  error("SWIFT was not compiled with MPI support.");
+#endif
+}
+
 /**
  * @brief Add recv tasks for gravity pairs to a hierarchy of cells.
  *
@@ -446,6 +577,9 @@ void engine_addtasks_recv_timestep(struct engine *e, struct cell *c,
     }
   }
 
+  for (struct link *l = c->stars.feedback; l != NULL; l = l->next)
+    scheduler_addunlock(s, l->t, t_ti);
+
   /* Recurse? */
   if (c->split)
     for (int k = 0; k < 8; k++)
@@ -473,8 +607,6 @@ void engine_addtasks_recv_timestep(struct engine *e, struct cell *c,
 void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) {
 
   struct scheduler *s = &e->sched;
-  const int is_with_cooling = (e->policy & engine_policy_cooling);
-  const int is_with_star_formation = (e->policy & engine_policy_star_formation);
   const int with_limiter = (e->policy & engine_policy_limiter);
 
   /* Are we in a super-cell ? */
@@ -499,35 +631,7 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) {
       c->timestep = scheduler_addtask(s, task_type_timestep, task_subtype_none,
                                       0, 0, c, NULL);
 
-      /* Add the task finishing the force calculation */
-      c->end_force = scheduler_addtask(s, task_type_end_force,
-                                       task_subtype_none, 0, 0, c, NULL);
-
-      /* Subgrid tasks */
-      if (is_with_cooling && c->hydro.count_total > 0) {
-
-        c->hydro.cooling = scheduler_addtask(s, task_type_cooling,
-                                             task_subtype_none, 0, 0, c, NULL);
-
-        scheduler_addunlock(s, c->end_force, c->hydro.cooling);
-        scheduler_addunlock(s, c->hydro.cooling, c->kick2);
-
-      } else {
-        scheduler_addunlock(s, c->end_force, c->kick2);
-      }
-
-      if (is_with_star_formation && c->hydro.count_total > 0) {
-
-        c->hydro.star_formation = scheduler_addtask(
-            s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL);
-
-        scheduler_addunlock(s, c->kick2, c->hydro.star_formation);
-        scheduler_addunlock(s, c->hydro.star_formation, c->timestep);
-
-      } else {
-        scheduler_addunlock(s, c->kick2, c->timestep);
-      }
-
+      scheduler_addunlock(s, c->kick2, c->timestep);
       scheduler_addunlock(s, c->timestep, c->kick1);
 
       /* Time-step limiting */
@@ -581,6 +685,11 @@ void engine_make_hierarchical_tasks_gravity(struct engine *e, struct cell *c) {
       c->grav.drift = scheduler_addtask(s, task_type_drift_gpart,
                                         task_subtype_none, 0, 0, c, NULL);
 
+      c->grav.end_force = scheduler_addtask(s, task_type_end_grav_force,
+                                            task_subtype_none, 0, 0, c, NULL);
+
+      scheduler_addunlock(s, c->grav.end_force, c->super->kick2);
+
       if (is_self_gravity) {
 
         /* Initialisation of the multipoles */
@@ -612,7 +721,7 @@ void engine_make_hierarchical_tasks_gravity(struct engine *e, struct cell *c) {
         if (periodic) scheduler_addunlock(s, c->grav.mesh, c->grav.down);
         scheduler_addunlock(s, c->grav.init, c->grav.long_range);
         scheduler_addunlock(s, c->grav.long_range, c->grav.down);
-        scheduler_addunlock(s, c->grav.down, c->super->end_force);
+        scheduler_addunlock(s, c->grav.down, c->grav.super->grav.end_force);
 
         /* Link in the implicit tasks */
         scheduler_addunlock(s, c->grav.init, c->grav.init_out);
@@ -654,34 +763,6 @@ void engine_make_hierarchical_tasks_gravity(struct engine *e, struct cell *c) {
         engine_make_hierarchical_tasks_gravity(e, c->progeny[k]);
 }
 
-/**
- * @brief Recursively add non-implicit star ghost tasks to a cell hierarchy.
- */
-void engine_add_stars_ghosts(struct engine *e, struct cell *c,
-                             struct task *stars_ghost_in,
-                             struct task *stars_ghost_out) {
-
-  /* Abort as there are no star particles here? */
-  if (c->stars.count_total == 0) return;
-
-  /* If we have reached the leaf OR have to few particles to play with*/
-  if (!c->split || c->stars.count_total < engine_max_sparts_per_ghost) {
-
-    /* Add the ghost task and its dependencies */
-    struct scheduler *s = &e->sched;
-    c->stars.ghost = scheduler_addtask(s, task_type_stars_ghost,
-                                       task_subtype_none, 0, 0, c, NULL);
-    scheduler_addunlock(s, stars_ghost_in, c->stars.ghost);
-    scheduler_addunlock(s, c->stars.ghost, stars_ghost_out);
-  } else {
-    /* Keep recursing */
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_add_stars_ghosts(e, c->progeny[k], stars_ghost_in,
-                                stars_ghost_out);
-  }
-}
-
 /**
  * @brief Recursively add non-implicit ghost tasks to a cell hierarchy.
  */
@@ -724,6 +805,10 @@ void engine_add_ghosts(struct engine *e, struct cell *c, struct task *ghost_in,
 void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) {
 
   struct scheduler *s = &e->sched;
+  const int with_stars = (e->policy & engine_policy_stars);
+  const int with_feedback = (e->policy & engine_policy_feedback);
+  const int with_cooling = (e->policy & engine_policy_cooling);
+  const int with_star_formation = (e->policy & engine_policy_star_formation);
 
   /* Are we in a super-cell ? */
   if (c->hydro.super == c) {
@@ -732,6 +817,11 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) {
     c->hydro.sorts =
         scheduler_addtask(s, task_type_sort, task_subtype_none, 0, 0, c, NULL);
 
+    if (with_feedback) {
+      c->stars.sorts = scheduler_addtask(s, task_type_stars_sort,
+                                         task_subtype_none, 0, 0, c, NULL);
+    }
+
     /* Local tasks only... */
     if (c->nodeID == e->nodeID) {
 
@@ -739,6 +829,10 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) {
       c->hydro.drift = scheduler_addtask(s, task_type_drift_part,
                                          task_subtype_none, 0, 0, c, NULL);
 
+      /* Add the task finishing the force calculation */
+      c->hydro.end_force = scheduler_addtask(s, task_type_end_hydro_force,
+                                             task_subtype_none, 0, 0, c, NULL);
+
       /* Generate the ghost tasks. */
       c->hydro.ghost_in =
           scheduler_addtask(s, task_type_ghost_in, task_subtype_none, 0,
@@ -748,57 +842,63 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c) {
                             /* implicit = */ 1, c, NULL);
       engine_add_ghosts(e, c, c->hydro.ghost_in, c->hydro.ghost_out);
 
-#ifdef EXTRA_HYDRO_LOOP
       /* Generate the extra ghost task. */
+#ifdef EXTRA_HYDRO_LOOP
       c->hydro.extra_ghost = scheduler_addtask(
           s, task_type_extra_ghost, task_subtype_none, 0, 0, c, NULL);
 #endif
-    }
 
-  } else { /* We are above the super-cell so need to go deeper */
+      /* Stars */
+      if (with_stars) {
+        c->stars.drift = scheduler_addtask(s, task_type_drift_spart,
+                                           task_subtype_none, 0, 0, c, NULL);
+        scheduler_addunlock(s, c->stars.drift, c->super->kick2);
+      }
 
-    /* Recurse. */
-    if (c->split)
-      for (int k = 0; k < 8; k++)
-        if (c->progeny[k] != NULL)
-          engine_make_hierarchical_tasks_hydro(e, c->progeny[k]);
-  }
-}
+      /* Subgrid tasks: cooling */
+      if (with_cooling) {
 
-/**
- * @brief Generate the stars hierarchical tasks for a hierarchy of cells -
- * i.e. all the O(Npart) tasks -- star version
- *
- * Tasks are only created here. The dependencies will be added later on.
- *
- * Note that there is no need to recurse below the super-cell. Note also
- * that we only add tasks if the relevant particles are present in the cell.
- *
- * @param e The #engine.
- * @param c The #cell.
- */
-void engine_make_hierarchical_tasks_stars(struct engine *e, struct cell *c) {
+        c->hydro.cooling = scheduler_addtask(s, task_type_cooling,
+                                             task_subtype_none, 0, 0, c, NULL);
 
-  struct scheduler *s = &e->sched;
+        scheduler_addunlock(s, c->hydro.end_force, c->hydro.cooling);
+        scheduler_addunlock(s, c->hydro.cooling, c->super->kick2);
 
-  /* Are we in a super-cell ? */
-  if (c->super == c) {
+      } else {
+        scheduler_addunlock(s, c->hydro.end_force, c->super->kick2);
+      }
 
-    /* Add the sort task. */
-    c->stars.sorts = scheduler_addtask(s, task_type_stars_sort,
-                                       task_subtype_none, 0, 0, c, NULL);
+      /* Subgrid tasks: star formation */
+      if (with_star_formation) {
 
-    /* Local tasks only... */
-    if (c->nodeID == e->nodeID) {
+        c->hydro.star_formation = scheduler_addtask(
+            s, task_type_star_formation, task_subtype_none, 0, 0, c, NULL);
 
-      /* Generate the ghost tasks. */
-      c->stars.ghost_in =
-          scheduler_addtask(s, task_type_stars_ghost_in, task_subtype_none, 0,
-                            /* implicit = */ 1, c, NULL);
-      c->stars.ghost_out =
-          scheduler_addtask(s, task_type_stars_ghost_out, task_subtype_none, 0,
-                            /* implicit = */ 1, c, NULL);
-      engine_add_stars_ghosts(e, c, c->stars.ghost_in, c->stars.ghost_out);
+        scheduler_addunlock(s, c->super->kick2, c->hydro.star_formation);
+        scheduler_addunlock(s, c->hydro.star_formation, c->super->timestep);
+      }
+
+      /* Subgrid tasks: feedback */
+      if (with_feedback) {
+
+        c->stars.stars_in =
+            scheduler_addtask(s, task_type_stars_in, task_subtype_none, 0,
+                              /* implicit = */ 1, c, NULL);
+
+        c->stars.stars_out =
+            scheduler_addtask(s, task_type_stars_out, task_subtype_none, 0,
+                              /* implicit = */ 1, c, NULL);
+
+        c->stars.ghost = scheduler_addtask(s, task_type_stars_ghost,
+                                           task_subtype_none, 0, 0, c, NULL);
+
+        scheduler_addunlock(s, c->super->kick2, c->stars.stars_in);
+        scheduler_addunlock(s, c->stars.stars_out, c->super->timestep);
+
+        if (with_star_formation) {
+          scheduler_addunlock(s, c->hydro.star_formation, c->stars.stars_in);
+        }
+      }
     }
   } else { /* We are above the super-cell so need to go deeper */
 
@@ -806,7 +906,27 @@ void engine_make_hierarchical_tasks_stars(struct engine *e, struct cell *c) {
     if (c->split)
       for (int k = 0; k < 8; k++)
         if (c->progeny[k] != NULL)
-          engine_make_hierarchical_tasks_stars(e, c->progeny[k]);
+          engine_make_hierarchical_tasks_hydro(e, c->progeny[k]);
+  }
+}
+
+void engine_make_hierarchical_tasks_mapper(void *map_data, int num_elements,
+                                           void *extra_data) {
+
+  struct engine *e = (struct engine *)extra_data;
+  const int with_hydro = (e->policy & engine_policy_hydro);
+  const int with_self_gravity = (e->policy & engine_policy_self_gravity);
+  const int with_ext_gravity = (e->policy & engine_policy_external_gravity);
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct cell *c = &((struct cell *)map_data)[ind];
+    /* Make the common tasks (time integration) */
+    engine_make_hierarchical_tasks_common(e, c);
+    /* Add the hydro stuff */
+    if (with_hydro) engine_make_hierarchical_tasks_hydro(e, c);
+    /* And the gravity stuff */
+    if (with_self_gravity || with_ext_gravity)
+      engine_make_hierarchical_tasks_gravity(e, c);
   }
 }
 
@@ -973,28 +1093,6 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
   }
 }
 
-void engine_make_hierarchical_tasks_mapper(void *map_data, int num_elements,
-                                           void *extra_data) {
-  struct engine *e = (struct engine *)extra_data;
-  const int is_with_hydro = (e->policy & engine_policy_hydro);
-  const int is_with_self_gravity = (e->policy & engine_policy_self_gravity);
-  const int is_with_external_gravity =
-      (e->policy & engine_policy_external_gravity);
-  const int is_with_feedback = (e->policy & engine_policy_feedback);
-
-  for (int ind = 0; ind < num_elements; ind++) {
-    struct cell *c = &((struct cell *)map_data)[ind];
-    /* Make the common tasks (time integration) */
-    engine_make_hierarchical_tasks_common(e, c);
-    /* Add the hydro stuff */
-    if (is_with_hydro) engine_make_hierarchical_tasks_hydro(e, c);
-    /* And the gravity stuff */
-    if (is_with_self_gravity || is_with_external_gravity)
-      engine_make_hierarchical_tasks_gravity(e, c);
-    if (is_with_feedback) engine_make_hierarchical_tasks_stars(e, c);
-  }
-}
-
 /**
  * @brief Constructs the top-level tasks for the external gravity.
  *
@@ -1056,9 +1154,10 @@ void engine_count_and_link_tasks_mapper(void *map_data, int num_elements,
     /* Link stars sort tasks to all the higher sort task. */
     if (t_type == task_type_stars_sort) {
       for (struct cell *finger = t->ci->parent; finger != NULL;
-           finger = finger->parent)
+           finger = finger->parent) {
         if (finger->stars.sorts != NULL)
           scheduler_addunlock(sched, t, finger->stars.sorts);
+      }
     }
 
     /* Link self tasks to cells. */
@@ -1220,9 +1319,9 @@ void engine_link_gravity_tasks(struct engine *e) {
       if (ci_nodeID != nodeID) error("Non-local self task");
 #endif
 
-      /* drift -----> gravity --> end_force */
+      /* drift -----> gravity --> end_gravity_force */
       scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
-      scheduler_addunlock(sched, t, ci->super->end_force);
+      scheduler_addunlock(sched, t, ci->grav.super->grav.end_force);
     }
 
     /* Otherwise, pair interaction? */
@@ -1271,7 +1370,7 @@ void engine_link_gravity_tasks(struct engine *e) {
 
       /* drift -----> gravity --> end_force */
       scheduler_addunlock(sched, ci->grav.super->grav.drift, t);
-      scheduler_addunlock(sched, t, ci->super->end_force);
+      scheduler_addunlock(sched, t, ci->grav.super->grav.end_force);
     }
 
     /* Otherwise, sub-pair interaction? */
@@ -1368,22 +1467,6 @@ static inline void engine_make_hydro_loops_dependencies(
 }
 
 #endif
-/**
- * @brief Creates the dependency network for the stars tasks of a given cell.
- *
- * @param sched The #scheduler.
- * @param density The star density task to link.
- * @param feedback The star feedback task to link.
- * @param c The cell.
- */
-static inline void engine_make_stars_loops_dependencies(struct scheduler *sched,
-                                                        struct task *density,
-                                                        struct task *feedback,
-                                                        struct cell *c) {
-  /* density loop --> ghost --> feedback loop*/
-  scheduler_addunlock(sched, density, c->super->stars.ghost_in);
-  scheduler_addunlock(sched, c->super->stars.ghost_out, feedback);
-}
 
 /**
  * @brief Duplicates the first hydro loop and construct all the
@@ -1403,638 +1486,544 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
   const int nodeID = e->nodeID;
   const int with_cooling = (e->policy & engine_policy_cooling);
   const int with_limiter = (e->policy & engine_policy_limiter);
+  const int with_feedback = (e->policy & engine_policy_feedback);
 #ifdef EXTRA_HYDRO_LOOP
   struct task *t_gradient = NULL;
 #endif
   struct task *t_force = NULL;
   struct task *t_limiter = NULL;
+  struct task *t_star_density = NULL;
+  struct task *t_star_feedback = NULL;
 
   for (int ind = 0; ind < num_elements; ind++) {
+
     struct task *t = &((struct task *)map_data)[ind];
+    const enum task_types t_type = t->type;
+    const enum task_subtypes t_subtype = t->subtype;
+    const long long flags = t->flags;
+    struct cell *ci = t->ci;
+    struct cell *cj = t->cj;
+
+    /* Sort tasks depend on the drift of the cell (gas version). */
+    if (t_type == task_type_sort && ci->nodeID == nodeID) {
+      scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
+    }
 
-    /* Sort tasks depend on the drift of the cell. */
-    if (t->type == task_type_sort && t->ci->nodeID == engine_rank) {
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
+    /* Sort tasks depend on the drift of the cell (stars version). */
+    else if (t_type == task_type_stars_sort && ci->nodeID == nodeID) {
+      scheduler_addunlock(sched, ci->hydro.super->stars.drift, t);
     }
 
     /* Self-interaction? */
-    else if (t->type == task_type_self && t->subtype == task_subtype_density) {
+    else if (t_type == task_type_self && t_subtype == task_subtype_density) {
 
       /* Make the self-density tasks depend on the drift only. */
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
+      scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
 
-#ifdef EXTRA_HYDRO_LOOP
-      /* Start by constructing the task for the second  and third hydro loop. */
-      t_gradient = scheduler_addtask(sched, task_type_self,
-                                     task_subtype_gradient, 0, 0, t->ci, NULL);
-      t_force = scheduler_addtask(sched, task_type_self, task_subtype_force, 0,
-                                  0, t->ci, NULL);
+      /* Task for the second hydro loop, */
+      t_force = scheduler_addtask(sched, task_type_self, task_subtype_force,
+                                  flags, 0, ci, NULL);
 
-      /* and the task for the time-step limiter */
-      if (with_limiter)
+      /* the task for the time-step limiter */
+      if (with_limiter) {
         t_limiter = scheduler_addtask(sched, task_type_self,
-                                      task_subtype_limiter, 0, 0, t->ci, NULL);
+                                      task_subtype_limiter, flags, 0, ci, NULL);
+      }
+
+      /* The stellar feedback tasks */
+      if (with_feedback) {
+        t_star_density =
+            scheduler_addtask(sched, task_type_self, task_subtype_stars_density,
+                              flags, 0, ci, NULL);
+        t_star_feedback =
+            scheduler_addtask(sched, task_type_self,
+                              task_subtype_stars_feedback, flags, 0, ci, NULL);
+      }
+
+      /* Link the tasks to the cells */
+      engine_addlink(e, &ci->hydro.force, t_force);
+      if (with_limiter) {
+        engine_addlink(e, &ci->hydro.limiter, t_limiter);
+      }
+      if (with_feedback) {
+        engine_addlink(e, &ci->stars.density, t_star_density);
+        engine_addlink(e, &ci->stars.feedback, t_star_feedback);
+      }
+
+#ifdef EXTRA_HYDRO_LOOP
+
+      /* Same work for the additional hydro loop */
+      t_gradient = scheduler_addtask(sched, task_type_self,
+                                     task_subtype_gradient, flags, 0, ci, NULL);
 
       /* Add the link between the new loops and the cell */
-      engine_addlink(e, &t->ci->hydro.gradient, t_gradient);
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
+      engine_addlink(e, &ci->hydro.gradient, t_gradient);
 
       /* Now, build all the dependencies for the hydro */
       engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
-                                           t_limiter, t->ci, with_cooling,
+                                           t_limiter, ci, with_cooling,
                                            with_limiter);
-      scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-      if (with_limiter)
-        scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-      if (with_limiter)
-        scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
 #else
 
-      /* Start by constructing the task for the second hydro loop */
-      t_force = scheduler_addtask(sched, task_type_self, task_subtype_force, 0,
-                                  0, t->ci, NULL);
+      /* Now, build all the dependencies for the hydro */
+      engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
+                                           with_cooling, with_limiter);
+#endif
 
-      /* and the task for the time-step limiter */
-      if (with_limiter)
-        t_limiter = scheduler_addtask(sched, task_type_self,
-                                      task_subtype_limiter, 0, 0, t->ci, NULL);
-
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
+      /* Create the task dependencies */
+      scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
+
+      if (with_feedback) {
+
+        scheduler_addunlock(sched, ci->hydro.super->stars.drift,
+                            t_star_density);
+        scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
+                            t_star_density);
+        scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
+                            t_star_density);
+        scheduler_addunlock(sched, t_star_density,
+                            ci->hydro.super->stars.ghost);
+        scheduler_addunlock(sched, ci->hydro.super->stars.ghost,
+                            t_star_feedback);
+        scheduler_addunlock(sched, t_star_feedback,
+                            ci->hydro.super->stars.stars_out);
+      }
 
-      /* Now, build all the dependencies for the hydro */
-      engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, t->ci,
-                                           with_cooling, with_limiter);
-      scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-      if (with_limiter)
-        scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-      if (with_limiter)
-        scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
-#endif
+      if (with_limiter) {
+        scheduler_addunlock(sched, ci->super->kick2, t_limiter);
+        scheduler_addunlock(sched, t_limiter, ci->super->timestep);
+        scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
+      }
     }
 
     /* Otherwise, pair interaction? */
-    else if (t->type == task_type_pair && t->subtype == task_subtype_density) {
+    else if (t_type == task_type_pair && t_subtype == task_subtype_density) {
 
-      /* Make all density tasks depend on the drift and the sorts. */
-      if (t->ci->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.sorts, t);
-      if (t->ci->hydro.super != t->cj->hydro.super) {
-        if (t->cj->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->cj->hydro.super->hydro.drift, t);
-        scheduler_addunlock(sched, t->cj->hydro.super->hydro.sorts, t);
+      /* Make all density tasks depend on the drift */
+      if (ci->nodeID == nodeID) {
+        scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
+      }
+      if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
+        scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t);
       }
 
-#ifdef EXTRA_HYDRO_LOOP
-      /* Start by constructing the task for the second and third hydro loop */
-      t_gradient = scheduler_addtask(sched, task_type_pair,
-                                     task_subtype_gradient, 0, 0, t->ci, t->cj);
-      t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force, 0,
-                                  0, t->ci, t->cj);
+      /* Make all density tasks depend on the sorts */
+      scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t);
+      if (ci->hydro.super != cj->hydro.super) {
+        scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t);
+      }
+
+      /* New task for the force */
+      t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force,
+                                  flags, 0, ci, cj);
 
       /* and the task for the time-step limiter */
-      if (with_limiter)
+      if (with_limiter) {
         t_limiter = scheduler_addtask(sched, task_type_pair,
-                                      task_subtype_limiter, 0, 0, t->ci, t->cj);
+                                      task_subtype_limiter, flags, 0, ci, cj);
+      }
+
+      /* The stellar feedback tasks */
+      if (with_feedback) {
+        t_star_density =
+            scheduler_addtask(sched, task_type_pair, task_subtype_stars_density,
+                              flags, 0, ci, cj);
+        t_star_feedback =
+            scheduler_addtask(sched, task_type_pair,
+                              task_subtype_stars_feedback, flags, 0, ci, cj);
+      }
+
+      engine_addlink(e, &ci->hydro.force, t_force);
+      engine_addlink(e, &cj->hydro.force, t_force);
+      if (with_limiter) {
+        engine_addlink(e, &ci->hydro.limiter, t_limiter);
+        engine_addlink(e, &cj->hydro.limiter, t_limiter);
+      }
+      if (with_feedback) {
+        engine_addlink(e, &ci->stars.density, t_star_density);
+        engine_addlink(e, &cj->stars.density, t_star_density);
+        engine_addlink(e, &ci->stars.feedback, t_star_feedback);
+        engine_addlink(e, &cj->stars.feedback, t_star_feedback);
+      }
+
+#ifdef EXTRA_HYDRO_LOOP
+
+      /* Start by constructing the task for the second and third hydro loop */
+      t_gradient = scheduler_addtask(sched, task_type_pair,
+                                     task_subtype_gradient, flags, 0, ci, cj);
 
       /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->hydro.gradient, t_gradient);
-      engine_addlink(e, &t->cj->hydro.gradient, t_gradient);
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      engine_addlink(e, &t->cj->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
-      if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter);
+      engine_addlink(e, &ci->hydro.gradient, t_gradient);
+      engine_addlink(e, &cj->hydro.gradient, t_gradient);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
+      if (ci->nodeID == nodeID) {
         engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
-                                             t_limiter, t->ci, with_cooling,
+                                             t_limiter, ci, with_cooling,
+                                             with_limiter);
+      }
+      if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
+        engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
+                                             t_limiter, cj, with_cooling,
                                              with_limiter);
-        scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-        if (with_limiter)
-          scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->hydro.super != t->cj->hydro.super) {
-          engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
-                                               t_limiter, t->cj, with_cooling,
-                                               with_limiter);
-        }
-
-        if (t->ci->super != t->cj->super) {
-          scheduler_addunlock(sched, t_force, t->cj->super->end_force);
-          if (with_limiter)
-            scheduler_addunlock(sched, t->cj->super->kick2, t_limiter);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter, t->cj->super->timestep);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter,
-                                t->cj->super->timestep_limiter);
-        }
       }
-
 #else
 
-      /* Start by constructing the task for the second hydro loop */
-      t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force, 0,
-                                  0, t->ci, t->cj);
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
+                                             with_cooling, with_limiter);
+      }
+      if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
+        engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, cj,
+                                             with_cooling, with_limiter);
+      }
+#endif
 
-      /* and the task for the time-step limiter */
-      if (with_limiter)
-        t_limiter = scheduler_addtask(sched, task_type_pair,
-                                      task_subtype_limiter, 0, 0, t->ci, t->cj);
+      if (with_feedback) {
+        scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
+                            t_star_density);
 
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      engine_addlink(e, &t->cj->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
-      if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter);
+        if (ci->hydro.super != cj->hydro.super) {
+          scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
+                              t_star_density);
+        }
+      }
 
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter,
-                                             t->ci, with_cooling, with_limiter);
-        scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-        if (with_limiter)
-          scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->hydro.super != t->cj->hydro.super) {
-          engine_make_hydro_loops_dependencies(
-              sched, t, t_force, t_limiter, t->cj, with_cooling, with_limiter);
+      if (ci->nodeID == nodeID) {
+        scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
+
+        if (with_feedback) {
+
+          scheduler_addunlock(sched, ci->hydro.super->stars.drift,
+                              t_star_density);
+          scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
+                              t_star_density);
+          scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
+                              t_star_density);
+          scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
+                              t_star_density);
+          scheduler_addunlock(sched, t_star_density,
+                              ci->hydro.super->stars.ghost);
+          scheduler_addunlock(sched, ci->hydro.super->stars.ghost,
+                              t_star_feedback);
+          scheduler_addunlock(sched, t_star_feedback,
+                              ci->hydro.super->stars.stars_out);
         }
 
-        if (t->ci->super != t->cj->super) {
-          scheduler_addunlock(sched, t_force, t->cj->super->end_force);
-          if (with_limiter)
-            scheduler_addunlock(sched, t->cj->super->kick2, t_limiter);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter, t->cj->super->timestep);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter,
-                                t->cj->super->timestep_limiter);
+        if (with_limiter) {
+          scheduler_addunlock(sched, ci->super->kick2, t_limiter);
+          scheduler_addunlock(sched, t_limiter, ci->super->timestep);
+          scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
+        }
+      } else /*(ci->nodeID != nodeID) */ {
+        if (with_feedback) {
+          scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
+                              t_star_feedback);
         }
       }
 
-#endif
+      if (cj->nodeID == nodeID) {
 
-    }
+        if (ci->hydro.super != cj->hydro.super) {
 
-    /* Otherwise, sub-self interaction? */
-    else if (t->type == task_type_sub_self &&
-             t->subtype == task_subtype_density) {
+          scheduler_addunlock(sched, t_force, cj->hydro.super->hydro.end_force);
 
-      /* Make all density tasks depend on the drift and sorts. */
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.sorts, t);
-
-#ifdef EXTRA_HYDRO_LOOP
+          if (with_feedback) {
 
-      /* Start by constructing the task for the second and third hydro loop */
-      t_gradient =
-          scheduler_addtask(sched, task_type_sub_self, task_subtype_gradient,
-                            t->flags, 0, t->ci, NULL);
-      t_force = scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
-                                  t->flags, 0, t->ci, NULL);
+            scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
+                                t_star_density);
+            scheduler_addunlock(sched, cj->hydro.super->stars.drift,
+                                t_star_density);
+            scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
+                                t_star_density);
+            scheduler_addunlock(sched, cj->hydro.super->stars.stars_in,
+                                t_star_density);
+            scheduler_addunlock(sched, t_star_density,
+                                cj->hydro.super->stars.ghost);
+            scheduler_addunlock(sched, cj->hydro.super->stars.ghost,
+                                t_star_feedback);
+            scheduler_addunlock(sched, t_star_feedback,
+                                cj->hydro.super->stars.stars_out);
+          }
 
-      /* and the task for the time-step limiter */
-      if (with_limiter)
-        t_limiter =
-            scheduler_addtask(sched, task_type_sub_self, task_subtype_limiter,
-                              t->flags, 0, t->ci, NULL);
+          if (with_limiter) {
+            scheduler_addunlock(sched, cj->super->kick2, t_limiter);
+            scheduler_addunlock(sched, t_limiter, cj->super->timestep);
+            scheduler_addunlock(sched, t_limiter, cj->super->timestep_limiter);
+          }
+        }
+      } else /*(cj->nodeID != nodeID) */ {
+        if (with_feedback) {
+          scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
+                              t_star_feedback);
+        }
+      }
+    }
 
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->hydro.gradient, t_gradient);
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
+    /* Otherwise, sub-self interaction? */
+    else if (t_type == task_type_sub_self &&
+             t_subtype == task_subtype_density) {
 
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
-                                             t_limiter, t->ci, with_cooling,
-                                             with_limiter);
-        scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-        if (with_limiter)
-          scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter);
-      }
+      /* Make all density tasks depend on the drift and sorts. */
+      scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
+      scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t);
 
-#else
       /* Start by constructing the task for the second hydro loop */
       t_force = scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
-                                  t->flags, 0, t->ci, NULL);
+                                  flags, 0, ci, NULL);
 
       /* and the task for the time-step limiter */
-      if (with_limiter)
-        t_limiter =
-            scheduler_addtask(sched, task_type_sub_self, task_subtype_limiter,
-                              t->flags, 0, t->ci, NULL);
-
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
-
-      /* Now, build all the dependencies for the hydro for the cells */
-      /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter,
-                                             t->ci, with_cooling, with_limiter);
-        scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-        if (with_limiter)
-          scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter);
+      if (with_limiter) {
+        t_limiter = scheduler_addtask(sched, task_type_sub_self,
+                                      task_subtype_limiter, flags, 0, ci, NULL);
       }
-#endif
-    }
 
-    /* Otherwise, sub-pair interaction? */
-    else if (t->type == task_type_sub_pair &&
-             t->subtype == task_subtype_density) {
+      /* The stellar feedback tasks */
+      if (with_feedback) {
+        t_star_density =
+            scheduler_addtask(sched, task_type_sub_self,
+                              task_subtype_stars_density, flags, 0, ci, NULL);
+        t_star_feedback =
+            scheduler_addtask(sched, task_type_sub_self,
+                              task_subtype_stars_feedback, flags, 0, ci, NULL);
+      }
 
-      /* Make all density tasks depend on the drift. */
-      if (t->ci->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.sorts, t);
-      if (t->ci->hydro.super != t->cj->hydro.super) {
-        if (t->cj->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->cj->hydro.super->hydro.drift, t);
-        scheduler_addunlock(sched, t->cj->hydro.super->hydro.sorts, t);
+      /* Add the link between the new loop and the cell */
+      engine_addlink(e, &ci->hydro.force, t_force);
+      if (with_limiter) {
+        engine_addlink(e, &ci->hydro.limiter, t_limiter);
+      }
+      if (with_feedback) {
+        engine_addlink(e, &ci->stars.density, t_star_density);
+        engine_addlink(e, &ci->stars.feedback, t_star_feedback);
       }
 
 #ifdef EXTRA_HYDRO_LOOP
 
       /* Start by constructing the task for the second and third hydro loop */
-      t_gradient =
-          scheduler_addtask(sched, task_type_sub_pair, task_subtype_gradient,
-                            t->flags, 0, t->ci, t->cj);
-      t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
-                                  t->flags, 0, t->ci, t->cj);
+      t_gradient = scheduler_addtask(sched, task_type_sub_self,
+                                     task_subtype_gradient, flags, 0, ci, NULL);
 
-      /* and the task for the time-step limiter */
-      if (with_limiter)
-        t_limiter =
-            scheduler_addtask(sched, task_type_sub_pair, task_subtype_limiter,
-                              t->flags, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->hydro.gradient, t_gradient);
-      engine_addlink(e, &t->cj->hydro.gradient, t_gradient);
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      engine_addlink(e, &t->cj->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
-      if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter);
+      /* Add the link between the new loop and the cell */
+      engine_addlink(e, &ci->hydro.gradient, t_gradient);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
-                                             t_limiter, t->ci, with_cooling,
-                                             with_limiter);
-        scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-        if (with_limiter)
-          scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->hydro.super != t->cj->hydro.super) {
-          engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
-                                               t_limiter, t->cj, with_cooling,
-                                               with_limiter);
-        }
-
-        if (t->ci->super != t->cj->super) {
-          scheduler_addunlock(sched, t_force, t->cj->super->end_force);
-          if (with_limiter)
-            scheduler_addunlock(sched, t->cj->super->kick2, t_limiter);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter, t->cj->super->timestep);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter,
-                                t->cj->super->timestep_limiter);
-        }
-      }
-
+      engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
+                                           t_limiter, ci, with_cooling,
+                                           with_limiter);
 #else
-      /* Start by constructing the task for the second hydro loop */
-      t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
-                                  t->flags, 0, t->ci, t->cj);
-
-      /* and the task for the time-step limiter */
-      if (with_limiter)
-        t_limiter =
-            scheduler_addtask(sched, task_type_sub_pair, task_subtype_limiter,
-                              t->flags, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->hydro.force, t_force);
-      engine_addlink(e, &t->cj->hydro.force, t_force);
-      if (with_limiter) engine_addlink(e, &t->ci->hydro.limiter, t_limiter);
-      if (with_limiter) engine_addlink(e, &t->cj->hydro.limiter, t_limiter);
 
       /* Now, build all the dependencies for the hydro for the cells */
       /* that are local and are not descendant of the same super_hydro-cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter,
-                                             t->ci, with_cooling, with_limiter);
-
-        scheduler_addunlock(sched, t_force, t->ci->super->end_force);
-        if (with_limiter)
-          scheduler_addunlock(sched, t->ci->super->kick2, t_limiter);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep);
-        if (with_limiter)
-          scheduler_addunlock(sched, t_limiter, t->ci->super->timestep_limiter);
-      }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->hydro.super != t->cj->hydro.super) {
-          engine_make_hydro_loops_dependencies(
-              sched, t, t_force, t_limiter, t->cj, with_cooling, with_limiter);
-        }
-
-        if (t->ci->super != t->cj->super) {
-          scheduler_addunlock(sched, t_force, t->cj->super->end_force);
-          if (with_limiter)
-            scheduler_addunlock(sched, t->cj->super->kick2, t_limiter);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter, t->cj->super->timestep);
-          if (with_limiter)
-            scheduler_addunlock(sched, t_limiter,
-                                t->cj->super->timestep_limiter);
-        }
-      }
+      engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
+                                           with_cooling, with_limiter);
 #endif
-    }
-  }
-}
-
-/**
- * @brief Duplicates the first stars loop and construct all the
- * dependencies for the stars part
- *
- * This is done by looping over all the previously constructed tasks
- * and adding another task involving the same cells but this time
- * corresponding to the second stars loop over neighbours.
- * With all the relevant tasks for a given cell available, we construct
- * all the dependencies for that cell.
- */
-void engine_make_extra_starsloop_tasks_mapper(void *map_data, int num_elements,
-                                              void *extra_data) {
 
-  struct engine *e = (struct engine *)extra_data;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-
-  for (int ind = 0; ind < num_elements; ind++) {
-    struct task *t = &((struct task *)map_data)[ind];
-
-    /* Sort tasks depend on the drift and gravity drift of the cell. */
-    if (t->type == task_type_stars_sort && t->ci->nodeID == engine_rank) {
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
-      scheduler_addunlock(sched, t->ci->super->grav.drift, t);
-    }
-
-    /* Self-interaction? */
-    else if (t->type == task_type_self &&
-             t->subtype == task_subtype_stars_density) {
-
-      /* Make the self-density tasks depend on the drift and gravity drift. */
-      scheduler_addunlock(sched, t->ci->hydro.super->hydro.drift, t);
-      scheduler_addunlock(sched, t->ci->super->grav.drift, t);
-
-      /* Start by constructing the task for the second stars loop */
-      struct task *t2 =
-          scheduler_addtask(sched, task_type_self, task_subtype_stars_feedback,
-                            0, 0, t->ci, NULL);
-
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->stars.feedback, t2);
+      /* Create the task dependencies */
+      scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
+
+      if (with_feedback) {
+
+        scheduler_addunlock(sched, ci->hydro.super->stars.drift,
+                            t_star_density);
+        scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
+                            t_star_density);
+        scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
+                            t_star_density);
+        scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
+                            t_star_density);
+        scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
+                            t_star_density);
+        scheduler_addunlock(sched, t_star_density,
+                            ci->hydro.super->stars.ghost);
+        scheduler_addunlock(sched, ci->hydro.super->stars.ghost,
+                            t_star_feedback);
+        scheduler_addunlock(sched, t_star_feedback,
+                            ci->hydro.super->stars.stars_out);
+      }
 
-      /* Now, build all the dependencies for the stars */
-      engine_make_stars_loops_dependencies(sched, t, t2, t->ci);
+      if (with_limiter) {
+        scheduler_addunlock(sched, ci->super->kick2, t_limiter);
+        scheduler_addunlock(sched, t_limiter, ci->super->timestep);
+        scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
+      }
 
-      /* end_force depends on feedback tasks */
-      scheduler_addunlock(sched, t2, t->ci->super->end_force);
     }
 
-    /* Otherwise, pair interaction? */
-    else if (t->type == task_type_pair &&
-             t->subtype == task_subtype_stars_density) {
-
-      /* Make all stars density tasks depend on the hydro drift and sorts,
-       * gravity drift and star sorts. */
-      if (t->ci->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->ci->super->hydro.drift, t);
-      scheduler_addunlock(sched, t->ci->super->hydro.sorts, t);
-      if (t->cj->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->cj->super->grav.drift, t);
-      scheduler_addunlock(sched, t->ci->super->stars.sorts, t);
-
-      if (t->ci->super != t->cj->super) {
-        if (t->cj->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->cj->super->hydro.drift, t);
-        scheduler_addunlock(sched, t->cj->super->hydro.sorts, t);
-        if (t->ci->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->ci->super->grav.drift, t);
-        scheduler_addunlock(sched, t->cj->super->stars.sorts, t);
-      }
-
-      /* Start by constructing the task for the second stars loop */
-      struct task *t2 =
-          scheduler_addtask(sched, task_type_pair, task_subtype_stars_feedback,
-                            0, 0, t->ci, t->cj);
-
-      /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->stars.feedback, t2);
-      engine_addlink(e, &t->cj->stars.feedback, t2);
+    /* Otherwise, sub-pair interaction? */
+    else if (t_type == task_type_sub_pair &&
+             t_subtype == task_subtype_density) {
 
-      /* Now, build all the dependencies for the stars for the cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_stars_loops_dependencies(sched, t, t2, t->ci);
-        scheduler_addunlock(sched, t2, t->ci->super->end_force);
+      /* Make all density tasks depend on the drift */
+      if (ci->nodeID == nodeID) {
+        scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t);
       }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super != t->cj->super)
-          engine_make_stars_loops_dependencies(sched, t, t2, t->cj);
-        if (t->ci->super != t->cj->super)
-          scheduler_addunlock(sched, t2, t->cj->super->end_force);
+      if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
+        scheduler_addunlock(sched, cj->hydro.super->hydro.drift, t);
       }
-    }
 
-    /* Otherwise, sub-self interaction? */
-    else if (t->type == task_type_sub_self &&
-             t->subtype == task_subtype_stars_density) {
+      /* Make all density tasks depend on the sorts */
+      scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t);
+      if (ci->hydro.super != cj->hydro.super) {
+        scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t);
+      }
 
-      /* Make all stars density tasks depend on the hydro drift and sorts,
-       * gravity drift and star sorts. */
-      scheduler_addunlock(sched, t->ci->super->hydro.drift, t);
-      scheduler_addunlock(sched, t->ci->super->hydro.sorts, t);
-      scheduler_addunlock(sched, t->ci->super->grav.drift, t);
-      scheduler_addunlock(sched, t->ci->super->stars.sorts, t);
+      /* New task for the force */
+      t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
+                                  flags, 0, ci, cj);
 
-      /* Start by constructing the task for the second stars loop */
-      struct task *t2 = scheduler_addtask(sched, task_type_sub_self,
-                                          task_subtype_stars_feedback, t->flags,
-                                          0, t->ci, t->cj);
+      /* and the task for the time-step limiter */
+      if (with_limiter) {
+        t_limiter = scheduler_addtask(sched, task_type_sub_pair,
+                                      task_subtype_limiter, flags, 0, ci, cj);
+      }
 
-      /* Add the link between the new loop and the cell */
-      engine_addlink(e, &t->ci->stars.feedback, t2);
+      /* The stellar feedback tasks */
+      if (with_feedback) {
+        t_star_density =
+            scheduler_addtask(sched, task_type_sub_pair,
+                              task_subtype_stars_density, flags, 0, ci, cj);
+        t_star_feedback =
+            scheduler_addtask(sched, task_type_sub_pair,
+                              task_subtype_stars_feedback, flags, 0, ci, cj);
+      }
 
-      /* Now, build all the dependencies for the stars for the cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_stars_loops_dependencies(sched, t, t2, t->ci);
-        scheduler_addunlock(sched, t2, t->ci->super->end_force);
+      engine_addlink(e, &ci->hydro.force, t_force);
+      engine_addlink(e, &cj->hydro.force, t_force);
+      if (with_limiter) {
+        engine_addlink(e, &ci->hydro.limiter, t_limiter);
+        engine_addlink(e, &cj->hydro.limiter, t_limiter);
+      }
+      if (with_feedback) {
+        engine_addlink(e, &ci->stars.density, t_star_density);
+        engine_addlink(e, &cj->stars.density, t_star_density);
+        engine_addlink(e, &ci->stars.feedback, t_star_feedback);
+        engine_addlink(e, &cj->stars.feedback, t_star_feedback);
       }
-    }
 
-    /* Otherwise, sub-pair interaction? */
-    else if (t->type == task_type_sub_pair &&
-             t->subtype == task_subtype_stars_density) {
-
-      /* Make all stars density tasks depend on the hydro drift and sorts,
-       * gravity drift and star sorts. */
-      if (t->cj->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->cj->super->hydro.drift, t);
-      scheduler_addunlock(sched, t->cj->super->hydro.sorts, t);
-      if (t->cj->nodeID == engine_rank)
-        scheduler_addunlock(sched, t->cj->super->grav.drift, t);
-      scheduler_addunlock(sched, t->ci->super->stars.sorts, t);
-
-      if (t->ci->super != t->cj->super) {
-        if (t->ci->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->ci->super->hydro.drift, t);
-        scheduler_addunlock(sched, t->ci->super->hydro.sorts, t);
-        if (t->ci->nodeID == engine_rank)
-          scheduler_addunlock(sched, t->ci->super->grav.drift, t);
-        scheduler_addunlock(sched, t->cj->super->stars.sorts, t);
-      }
-
-      /* Start by constructing the task for the second stars loop */
-      struct task *t2 = scheduler_addtask(sched, task_type_sub_pair,
-                                          task_subtype_stars_feedback, t->flags,
-                                          0, t->ci, t->cj);
+#ifdef EXTRA_HYDRO_LOOP
+
+      /* Start by constructing the task for the second and third hydro loop */
+      t_gradient = scheduler_addtask(sched, task_type_sub_pair,
+                                     task_subtype_gradient, flags, 0, ci, cj);
 
       /* Add the link between the new loop and both cells */
-      engine_addlink(e, &t->ci->stars.feedback, t2);
-      engine_addlink(e, &t->cj->stars.feedback, t2);
+      engine_addlink(e, &ci->hydro.gradient, t_gradient);
+      engine_addlink(e, &cj->hydro.gradient, t_gradient);
 
-      /* Now, build all the dependencies for the stars for the cells */
-      if (t->ci->nodeID == nodeID) {
-        engine_make_stars_loops_dependencies(sched, t, t2, t->ci);
-        scheduler_addunlock(sched, t2, t->ci->super->end_force);
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
+                                             t_limiter, ci, with_cooling,
+                                             with_limiter);
       }
-      if (t->cj->nodeID == nodeID) {
-        if (t->ci->super != t->cj->super)
-          engine_make_stars_loops_dependencies(sched, t, t2, t->cj);
-        if (t->ci->super != t->cj->super)
-          scheduler_addunlock(sched, t2, t->cj->super->end_force);
+      if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
+        engine_make_hydro_loops_dependencies(sched, t, t_gradient, t_force,
+                                             t_limiter, cj, with_cooling,
+                                             with_limiter);
       }
-    }
-  }
-}
-
-/**
- * @brief Constructs the top-level pair tasks for the star loop over
- * neighbours
- *
- * Here we construct all the tasks for all possible neighbouring non-empty
- * local cells in the hierarchy. No dependencies are being added thus far.
- * Additional loop over neighbours can later be added by simply duplicating
- * all the tasks created by this function.
- *
- * @param map_data Offset of first two indices disguised as a pointer.
- * @param num_elements Number of cells to traverse.
- * @param extra_data The #engine.
- */
-void engine_make_starsloop_tasks_mapper(void *map_data, int num_elements,
-                                        void *extra_data) {
+#else
 
-  /* Extract the engine pointer. */
-  struct engine *e = (struct engine *)extra_data;
-  const int periodic = e->s->periodic;
+      /* Now, build all the dependencies for the hydro for the cells */
+      /* that are local and are not descendant of the same super_hydro-cells */
+      if (ci->nodeID == nodeID) {
+        engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, ci,
+                                             with_cooling, with_limiter);
+      }
+      if ((cj->nodeID == nodeID) && (ci->hydro.super != cj->hydro.super)) {
+        engine_make_hydro_loops_dependencies(sched, t, t_force, t_limiter, cj,
+                                             with_cooling, with_limiter);
+      }
+#endif
 
-  struct space *s = e->s;
-  struct scheduler *sched = &e->sched;
-  const int nodeID = e->nodeID;
-  const int *cdim = s->cdim;
-  struct cell *cells = s->cells_top;
+      if (with_feedback) {
+        scheduler_addunlock(sched, ci->hydro.super->hydro.sorts,
+                            t_star_density);
+        if (ci->hydro.super != cj->hydro.super) {
+          scheduler_addunlock(sched, cj->hydro.super->hydro.sorts,
+                              t_star_density);
+        }
+      }
 
-  /* Loop through the elements, which are just byte offsets from NULL. */
-  for (int ind = 0; ind < num_elements; ind++) {
+      if (ci->nodeID == nodeID) {
+        scheduler_addunlock(sched, t_force, ci->hydro.super->hydro.end_force);
+
+        if (with_feedback) {
+
+          scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
+                              t_star_density);
+          scheduler_addunlock(sched, ci->hydro.super->stars.drift,
+                              t_star_density);
+          scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
+                              t_star_density);
+          scheduler_addunlock(sched, ci->hydro.super->stars.stars_in,
+                              t_star_density);
+          scheduler_addunlock(sched, t_star_density,
+                              ci->hydro.super->stars.ghost);
+          scheduler_addunlock(sched, ci->hydro.super->stars.ghost,
+                              t_star_feedback);
+          scheduler_addunlock(sched, t_star_feedback,
+                              ci->hydro.super->stars.stars_out);
+        }
 
-    /* Get the cell index. */
-    const int cid = (size_t)(map_data) + ind;
-    const int i = cid / (cdim[1] * cdim[2]);
-    const int j = (cid / cdim[2]) % cdim[1];
-    const int k = cid % cdim[2];
+        if (with_limiter) {
+          scheduler_addunlock(sched, ci->super->kick2, t_limiter);
+          scheduler_addunlock(sched, t_limiter, ci->super->timestep);
+          scheduler_addunlock(sched, t_limiter, ci->super->timestep_limiter);
+        }
+      } else /* ci->nodeID != nodeID */ {
 
-    /* Get the cell */
-    struct cell *ci = &cells[cid];
+        if (with_feedback) {
+          /* message("%p/%p",ci->hydro.super->stars.sorts, t_star_feedback); */
+          scheduler_addunlock(sched, ci->hydro.super->stars.sorts,
+                              t_star_feedback);
+        }
+      }
 
-    /* Skip cells without particles */
-    if (ci->stars.count == 0 && ci->hydro.count == 0) continue;
+      if (cj->nodeID == nodeID) {
 
-    /* If the cells is local build a self-interaction */
-    if (ci->nodeID == nodeID) {
-      scheduler_addtask(sched, task_type_self, task_subtype_stars_density, 0, 0,
-                        ci, NULL);
-    }
+        if (ci->hydro.super != cj->hydro.super) {
 
-    /* Now loop over all the neighbours of this cell */
-    for (int ii = -1; ii < 2; ii++) {
-      int iii = i + ii;
-      if (!periodic && (iii < 0 || iii >= cdim[0])) continue;
-      iii = (iii + cdim[0]) % cdim[0];
-      for (int jj = -1; jj < 2; jj++) {
-        int jjj = j + jj;
-        if (!periodic && (jjj < 0 || jjj >= cdim[1])) continue;
-        jjj = (jjj + cdim[1]) % cdim[1];
-        for (int kk = -1; kk < 2; kk++) {
-          int kkk = k + kk;
-          if (!periodic && (kkk < 0 || kkk >= cdim[2])) continue;
-          kkk = (kkk + cdim[2]) % cdim[2];
+          scheduler_addunlock(sched, t_force, cj->hydro.super->hydro.end_force);
 
-          /* Get the neighbouring cell */
-          const int cjd = cell_getid(cdim, iii, jjj, kkk);
-          struct cell *cj = &cells[cjd];
+          if (with_feedback) {
 
-          /* Is that neighbour local and does it have particles ? */
-          if (cid >= cjd || (cj->stars.count == 0 && cj->hydro.count == 0) ||
-              (ci->nodeID != nodeID && cj->nodeID != nodeID))
-            continue;
+            scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
+                                t_star_density);
+            scheduler_addunlock(sched, cj->hydro.super->stars.drift,
+                                t_star_density);
+            scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
+                                t_star_density);
+            scheduler_addunlock(sched, cj->hydro.super->stars.stars_in,
+                                t_star_density);
+            scheduler_addunlock(sched, t_star_density,
+                                cj->hydro.super->stars.ghost);
+            scheduler_addunlock(sched, cj->hydro.super->stars.ghost,
+                                t_star_feedback);
+            scheduler_addunlock(sched, t_star_feedback,
+                                cj->hydro.super->stars.stars_out);
+          }
 
-          /* Construct the pair task */
-          const int sid = sortlistID[(kk + 1) + 3 * ((jj + 1) + 3 * (ii + 1))];
-          scheduler_addtask(sched, task_type_pair, task_subtype_stars_density,
-                            sid, 0, ci, cj);
+          if (with_limiter) {
+            scheduler_addunlock(sched, cj->super->kick2, t_limiter);
+            scheduler_addunlock(sched, t_limiter, cj->super->timestep);
+            scheduler_addunlock(sched, t_limiter, cj->super->timestep_limiter);
+          }
+        }
+      } else /* cj->nodeID != nodeID */ {
+        if (with_feedback) {
+          scheduler_addunlock(sched, cj->hydro.super->stars.sorts,
+                              t_star_feedback);
         }
       }
     }
   }
 }
-
 /**
  * @brief Constructs the top-level pair tasks for the first hydro loop over
  * neighbours
@@ -2054,6 +2043,7 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements,
   /* Extract the engine pointer. */
   struct engine *e = (struct engine *)extra_data;
   const int periodic = e->s->periodic;
+  const int with_feedback = (e->policy & engine_policy_feedback);
 
   struct space *s = e->s;
   struct scheduler *sched = &e->sched;
@@ -2075,8 +2065,9 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements,
     /* Get the cell */
     struct cell *ci = &cells[cid];
 
-    /* Skip cells without hydro particles */
-    if (ci->hydro.count == 0) continue;
+    /* Skip cells without hydro or star particles */
+    if ((ci->hydro.count == 0) && (with_feedback && ci->stars.count == 0))
+      continue;
 
     /* If the cell is local build a self-interaction */
     if (ci->nodeID == nodeID) {
@@ -2102,8 +2093,10 @@ void engine_make_hydroloop_tasks_mapper(void *map_data, int num_elements,
           const int cjd = cell_getid(cdim, iii, jjj, kkk);
           struct cell *cj = &cells[cjd];
 
-          /* Is that neighbour local and does it have particles ? */
-          if (cid >= cjd || cj->hydro.count == 0 ||
+          /* Is that neighbour local and does it have gas or star particles ? */
+          if ((cid >= cjd) ||
+              ((cj->hydro.count == 0) &&
+               (with_feedback && cj->stars.count == 0)) ||
               (ci->nodeID != nodeID && cj->nodeID != nodeID))
             continue;
 
@@ -2168,6 +2161,7 @@ struct cell_type_pair {
 
 void engine_addtasks_send_mapper(void *map_data, int num_elements,
                                  void *extra_data) {
+
   struct engine *e = (struct engine *)extra_data;
   const int with_limiter = (e->policy & engine_policy_limiter);
   struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data;
@@ -2186,6 +2180,11 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements,
       engine_addtasks_send_hydro(e, ci, cj, /*t_xv=*/NULL,
                                  /*t_rho=*/NULL, /*t_gradient=*/NULL);
 
+    /* Add the send tasks for the cells in the proxy that have a stars
+     * connection. */
+    if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro))
+      engine_addtasks_send_stars(e, ci, cj, /*t_feedback=*/NULL);
+
     /* Add the send tasks for the cells in the proxy that have a gravity
      * connection. */
     if ((e->policy & engine_policy_self_gravity) &&
@@ -2196,6 +2195,7 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements,
 
 void engine_addtasks_recv_mapper(void *map_data, int num_elements,
                                  void *extra_data) {
+
   struct engine *e = (struct engine *)extra_data;
   const int with_limiter = (e->policy & engine_policy_limiter);
   struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data;
@@ -2212,6 +2212,11 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements,
     if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro))
       engine_addtasks_recv_hydro(e, ci, NULL, NULL, NULL);
 
+    /* Add the recv tasks for the cells in the proxy that have a stars
+     * connection. */
+    if ((e->policy & engine_policy_feedback) && (type & proxy_cell_type_hydro))
+      engine_addtasks_recv_stars(e, ci, NULL);
+
     /* Add the recv tasks for the cells in the proxy that have a gravity
      * connection. */
     if ((e->policy & engine_policy_self_gravity) &&
@@ -2249,18 +2254,6 @@ void engine_maketasks(struct engine *e) {
 
   tic2 = getticks();
 
-  /* Construct the stars hydro loop over neighbours */
-  if (e->policy & engine_policy_feedback) {
-    threadpool_map(&e->threadpool, engine_make_starsloop_tasks_mapper, NULL,
-                   s->nr_cells, 1, 0, e);
-  }
-
-  if (e->verbose)
-    message("Making stellar feedback tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
   /* Add the self gravity tasks. */
   if (e->policy & engine_policy_self_gravity) {
     threadpool_map(&e->threadpool, engine_make_self_gravity_tasks_mapper, NULL,
@@ -2357,19 +2350,6 @@ void engine_maketasks(struct engine *e) {
 
   tic2 = getticks();
 
-  /* Run through the tasks and make stars feedback tasks for each stars density
-     task. Each stars feedback task depends on the stars ghosts and unlocks the
-     kick task of its super-cell. */
-  if (e->policy & engine_policy_stars)
-    threadpool_map(&e->threadpool, engine_make_extra_starsloop_tasks_mapper,
-                   sched->tasks, sched->nr_tasks, sizeof(struct task), 0, e);
-
-  if (e->verbose)
-    message("Making extra starsloop tasks took %.3f %s.",
-            clocks_from_ticks(getticks() - tic2), clocks_getunit());
-
-  tic2 = getticks();
-
   /* Add the dependencies for the gravity stuff */
   if (e->policy & (engine_policy_self_gravity | engine_policy_external_gravity))
     engine_link_gravity_tasks(e);
@@ -2381,9 +2361,6 @@ void engine_maketasks(struct engine *e) {
   tic2 = getticks();
 
 #ifdef WITH_MPI
-  if (e->policy & engine_policy_feedback)
-    error("Cannot run stellar feedback with MPI (yet).");
-
   /* Add the communication tasks if MPI is being used. */
   if (e->policy & engine_policy_mpi) {
 
diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c
index 3a26dbb2f47f9503aa0b93fa28d679f5eebaeede..c02eb5d2bd272111808701269faed07cef505449 100644
--- a/src/engine_marktasks.c
+++ b/src/engine_marktasks.c
@@ -84,7 +84,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
       /* Local pointer. */
       struct cell *ci = t->ci;
 
-      if (ci->nodeID != engine_rank) error("Non-local self task found");
+      if (ci->nodeID != nodeID) error("Non-local self task found");
 
       /* Activate the hydro drift */
       if (t_type == task_type_self && t_subtype == task_subtype_density) {
@@ -124,7 +124,6 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
         if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
       }
 
-#ifdef EXTRA_HYDRO_LOOP
       else if (t_type == task_type_self && t_subtype == task_subtype_gradient) {
         if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
       }
@@ -133,7 +132,6 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
                t_subtype == task_subtype_gradient) {
         if (cell_is_active_hydro(ci, e)) scheduler_activate(s, t);
       }
-#endif
 
       /* Activate the star density */
       else if (t_type == task_type_self &&
@@ -154,7 +152,6 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
         }
       }
 
-      /* Activate the star feedback */
       else if (t_type == task_type_self &&
                t_subtype == task_subtype_stars_feedback) {
         if (cell_is_active_stars(ci, e)) {
@@ -162,13 +159,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
         }
       }
 
-      /* Store current values of dx_max and h_max. */
       else if (t_type == task_type_sub_self &&
                t_subtype == task_subtype_stars_feedback) {
-        if (cell_is_active_stars(ci, e)) {
-          scheduler_activate(s, t);
-          cell_activate_subcell_stars_tasks(ci, NULL, s);
-        }
+        if (cell_is_active_stars(ci, e)) scheduler_activate(s, t);
       }
 
       /* Activate the gravity drift */
@@ -258,14 +251,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
       }
 
       /* Stars density */
-      if (t_subtype == task_subtype_stars_density &&
-          ((ci_active_stars && ci->nodeID == engine_rank) ||
-           (cj_active_stars && cj->nodeID == engine_rank))) {
-
-        // MATTHIEU: The logic here can be improved.
-        // If ci is active for stars but not cj, then we can only drift the
-        // stars in ci and parts in cj. (and vice-versa). The same logic can be
-        // applied in cell_unskip_stars().
+      else if ((t_subtype == task_subtype_stars_density) &&
+               (ci_active_stars || cj_active_stars) &&
+               (ci_nodeID == nodeID || cj_nodeID == nodeID)) {
 
         scheduler_activate(s, t);
 
@@ -273,59 +261,65 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
         if (t_type == task_type_pair) {
 
           /* Do ci */
-          /* Store some values. */
-          atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
-          atomic_or(&ci->stars.requires_sorts, 1 << t->flags);
-
-          cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
-          ci->stars.dx_max_sort_old = ci->stars.dx_max_sort;
+          if (ci_active_stars) {
 
-          /* Activate the hydro drift tasks. */
-          if (ci_nodeID == nodeID) cell_activate_drift_spart(ci, s);
+            /* stars for ci */
+            atomic_or(&ci->stars.requires_sorts, 1 << t->flags);
+            ci->stars.dx_max_sort_old = ci->stars.dx_max_sort;
 
-          if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
+            /* hydro for cj */
+            atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+            cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
 
-          /* Check the sorts and activate them if needed. */
-          cell_activate_hydro_sorts(cj, t->flags, s);
+            /* Activate the drift tasks. */
+            if (ci_nodeID == nodeID) cell_activate_drift_spart(ci, s);
+            if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
 
-          cell_activate_stars_sorts(ci, t->flags, s);
+            /* Check the sorts and activate them if needed. */
+            cell_activate_hydro_sorts(cj, t->flags, s);
+            cell_activate_stars_sorts(ci, t->flags, s);
+          }
 
           /* Do cj */
-          /* Store some values. */
-          atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
-          atomic_or(&cj->stars.requires_sorts, 1 << t->flags);
+          if (cj_active_stars) {
 
-          ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
-          cj->stars.dx_max_sort_old = cj->stars.dx_max_sort;
+            /* hydro for ci */
+            atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+            ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
 
-          /* Activate the hydro drift tasks. */
-          if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
+            /* stars for cj */
+            atomic_or(&cj->stars.requires_sorts, 1 << t->flags);
+            cj->stars.dx_max_sort_old = cj->stars.dx_max_sort;
 
-          if (cj_nodeID == nodeID) cell_activate_drift_spart(cj, s);
+            /* Activate the drift tasks. */
+            if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
+            if (cj_nodeID == nodeID) cell_activate_drift_spart(cj, s);
 
-          /* Check the sorts and activate them if needed. */
-          cell_activate_hydro_sorts(ci, t->flags, s);
-          cell_activate_stars_sorts(cj, t->flags, s);
+            /* Check the sorts and activate them if needed. */
+            cell_activate_hydro_sorts(ci, t->flags, s);
+            cell_activate_stars_sorts(cj, t->flags, s);
+          }
         }
 
         /* Store current values of dx_max and h_max. */
-        else if (t_type == task_type_sub_pair) {
-          cell_activate_subcell_stars_tasks(t->ci, t->cj, s);
+        else if (t_type == task_type_sub_pair &&
+                 t_subtype == task_subtype_stars_density) {
+          cell_activate_subcell_stars_tasks(ci, cj, s);
         }
       }
 
       /* Stars feedback */
-      if (t_subtype == task_subtype_stars_feedback &&
-          ((ci_active_stars && ci->nodeID == engine_rank) ||
-           (cj_active_stars && cj->nodeID == engine_rank))) {
+      else if ((t_subtype == task_subtype_stars_feedback) &&
+               ((ci_active_stars && ci_nodeID == nodeID) ||
+                (cj_active_stars && cj_nodeID == nodeID))) {
 
         scheduler_activate(s, t);
       }
 
       /* Gravity */
-      if ((t_subtype == task_subtype_grav) &&
-          ((ci_active_gravity && ci_nodeID == nodeID) ||
-           (cj_active_gravity && cj_nodeID == nodeID))) {
+      else if ((t_subtype == task_subtype_grav) &&
+               ((ci_active_gravity && ci_nodeID == nodeID) ||
+                (cj_active_gravity && cj_nodeID == nodeID))) {
 
         scheduler_activate(s, t);
 
@@ -368,7 +362,6 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 
           /* Is the foreign cell active and will need stuff from us? */
           if (ci_active_hydro) {
-
             struct link *l =
                 scheduler_activate_send(s, cj->mpi.hydro.send_xv, ci_nodeID);
 
@@ -396,6 +389,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 
           /* If the local cell is active, receive data from the foreign cell. */
           if (ci_active_hydro) {
+
             scheduler_activate(s, cj->mpi.hydro.recv_xv);
             if (cj_active_hydro) {
               scheduler_activate(s, cj->mpi.hydro.recv_rho);
@@ -439,16 +433,78 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
       }
 
       /* Only interested in stars_density tasks as of here. */
-      if (t->subtype == task_subtype_stars_density) {
+      else if (t->subtype == task_subtype_stars_density) {
 
         /* Too much particle movement? */
         if (cell_need_rebuild_for_stars_pair(ci, cj)) *rebuild_space = 1;
+        if (cell_need_rebuild_for_stars_pair(cj, ci)) *rebuild_space = 1;
+
+#ifdef WITH_MPI
+        /* Activate the send/recv tasks. */
+        if (ci_nodeID != nodeID) {
+
+          if (cj_active_stars) {
+            scheduler_activate(s, ci->mpi.hydro.recv_xv);
+            scheduler_activate(s, ci->mpi.hydro.recv_rho);
 
-        // LOIC: Need implementing MPI case
+            /* If the local cell is active, more stuff will be needed. */
+            scheduler_activate_send(s, cj->mpi.stars.send, ci_nodeID);
+            cell_activate_drift_spart(cj, s);
+
+            /* If the local cell is active, send its ti_end values. */
+            scheduler_activate_send(s, cj->mpi.send_ti, ci_nodeID);
+          }
+
+          if (ci_active_stars) {
+            scheduler_activate(s, ci->mpi.stars.recv);
+
+            /* If the foreign cell is active, we want its ti_end values. */
+            scheduler_activate(s, ci->mpi.recv_ti);
+
+            /* Is the foreign cell active and will need stuff from us? */
+            scheduler_activate_send(s, cj->mpi.hydro.send_xv, ci_nodeID);
+            scheduler_activate_send(s, cj->mpi.hydro.send_rho, ci_nodeID);
+
+            /* Drift the cell which will be sent; note that not all sent
+               particles will be drifted, only those that are needed. */
+            cell_activate_drift_part(cj, s);
+          }
+
+        } else if (cj_nodeID != nodeID) {
+
+          /* If the local cell is active, receive data from the foreign cell. */
+          if (ci_active_stars) {
+            scheduler_activate(s, cj->mpi.hydro.recv_xv);
+            scheduler_activate(s, cj->mpi.hydro.recv_rho);
+
+            /* If the local cell is active, more stuff will be needed. */
+            scheduler_activate_send(s, ci->mpi.stars.send, cj_nodeID);
+            cell_activate_drift_spart(ci, s);
+
+            /* If the local cell is active, send its ti_end values. */
+            scheduler_activate_send(s, ci->mpi.send_ti, cj_nodeID);
+          }
+
+          if (cj_active_stars) {
+            scheduler_activate(s, cj->mpi.stars.recv);
+
+            /* If the foreign cell is active, we want its ti_end values. */
+            scheduler_activate(s, cj->mpi.recv_ti);
+
+            /* Is the foreign cell active and will need stuff from us? */
+            scheduler_activate_send(s, ci->mpi.hydro.send_xv, cj_nodeID);
+            scheduler_activate_send(s, ci->mpi.hydro.send_rho, cj_nodeID);
+
+            /* Drift the cell which will be sent; note that not all sent
+               particles will be drifted, only those that are needed. */
+            cell_activate_drift_part(ci, s);
+          }
+        }
+#endif
       }
 
       /* Only interested in gravity tasks as of here. */
-      if (t_subtype == task_subtype_grav) {
+      else if (t_subtype == task_subtype_grav) {
 
 #ifdef WITH_MPI
         /* Activate the send/recv tasks. */
@@ -504,11 +560,16 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
       }
     }
 
-    /* End force ? */
-    else if (t_type == task_type_end_force) {
+    /* End force for hydro ? */
+    else if (t_type == task_type_end_hydro_force) {
 
-      if (cell_is_active_hydro(t->ci, e) || cell_is_active_gravity(t->ci, e))
-        scheduler_activate(s, t);
+      if (cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t);
+    }
+
+    /* End force for gravity ? */
+    else if (t_type == task_type_end_grav_force) {
+
+      if (cell_is_active_gravity(t->ci, e)) scheduler_activate(s, t);
     }
 
     /* Kick ? */
@@ -563,9 +624,12 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
     }
 
     /* Star ghost tasks ? */
-    else if (t_type == task_type_stars_ghost ||
-             t_type == task_type_stars_ghost_in ||
-             t_type == task_type_stars_ghost_out) {
+    else if (t_type == task_type_stars_ghost) {
+      if (cell_is_active_stars(t->ci, e)) scheduler_activate(s, t);
+    }
+
+    /* Feedback implicit tasks? */
+    else if (t_type == task_type_stars_in || t_type == task_type_stars_out) {
       if (cell_is_active_stars(t->ci, e)) scheduler_activate(s, t);
     }
 
diff --git a/src/hydro/AnarchyPU/hydro_io.h b/src/hydro/AnarchyPU/hydro_io.h
index 99fea6a9feb2722da3c82482d4f5e79330e5c7e6..b2a411cf408a2067ea5490869be004f9fd26954d 100644
--- a/src/hydro/AnarchyPU/hydro_io.h
+++ b/src/hydro/AnarchyPU/hydro_io.h
@@ -207,7 +207,7 @@ INLINE static void hydro_write_flavour(hid_t h_grpsph) {
   /* Nothing in this minimal model... */
   io_write_attribute_s(h_grpsph, "Thermal Conductivity Model", "No treatment");
   io_write_attribute_s(h_grpsph, "Viscosity Model",
-                       "Minimal treatment as in Monaghan (1992)");
+                       "Simplified version of Cullen & Denhen (2011)");
 
   /* Time integration properties */
   io_write_attribute_f(h_grpsph, "Maximal Delta u change over dt",
diff --git a/src/partition.c b/src/partition.c
index 1dde46dcd4ad2eb397bd26905fd8b57240088857..606f64e4c2f1c057520ed7ff893db10905f5aa8e 100644
--- a/src/partition.c
+++ b/src/partition.c
@@ -1244,9 +1244,10 @@ static void partition_gather_weights(void *map_data, int num_elements,
     if (t->type == task_type_drift_part || t->type == task_type_drift_gpart ||
         t->type == task_type_ghost || t->type == task_type_extra_ghost ||
         t->type == task_type_kick1 || t->type == task_type_kick2 ||
-        t->type == task_type_end_force || t->type == task_type_cooling ||
-        t->type == task_type_timestep || t->type == task_type_init_grav ||
-        t->type == task_type_grav_down ||
+        t->type == task_type_end_hydro_force ||
+        t->type == task_type_end_grav_force || t->type == task_type_cooling ||
+        t->type == task_type_star_formation || t->type == task_type_timestep ||
+        t->type == task_type_init_grav || t->type == task_type_grav_down ||
         t->type == task_type_grav_long_range) {
 
       /* Particle updates add only to vertex weight. */
@@ -2149,9 +2150,10 @@ static void check_weights(struct task *tasks, int nr_tasks,
     if (t->type == task_type_drift_part || t->type == task_type_drift_gpart ||
         t->type == task_type_ghost || t->type == task_type_extra_ghost ||
         t->type == task_type_kick1 || t->type == task_type_kick2 ||
-        t->type == task_type_end_force || t->type == task_type_cooling ||
-        t->type == task_type_timestep || t->type == task_type_init_grav ||
-        t->type == task_type_grav_down ||
+        t->type == task_type_end_hydro_force ||
+        t->type == task_type_end_grav_force || t->type == task_type_cooling ||
+        t->type == task_type_star_formation || t->type == task_type_timestep ||
+        t->type == task_type_init_grav || t->type == task_type_grav_down ||
         t->type == task_type_grav_long_range) {
 
       /* Particle updates add only to vertex weight. */
diff --git a/src/random.h b/src/random.h
index 4d665a2697076a139c6e4e614b223302b04ad7a6..660ae21db8dc78a8bde78b3f541bff6b621253cd 100644
--- a/src/random.h
+++ b/src/random.h
@@ -1,6 +1,7 @@
 /*******************************************************************************
  * This file is part of SWIFT.
  * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ *               2019 Folkert Nobels    (nobels@strw.leidenuniv.nl)
  *
  * 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
@@ -28,15 +29,23 @@
 /**
  * @brief The categories of random number generated.
  *
- * The values of the fields are carefully chose prime
- * numbers. Only change them if you know what you are
- * doing!
+ * The values of the fields are carefully chose numbers
+ * the numbers are very large primes such that the IDs
+ * will not have a prime factorization with this coefficient
+ * this results in a very high period for the random number
+ * generator.
+ * Only change when you know what you are doing, changing
+ * the numbers to bad values will break the random number
+ * generator.
+ * In case new numbers need to be added other possible
+ * numbers could be:
+ * 4947009007, 5947309451, 6977309513
  */
 enum random_number_type {
-  random_number_star_formation = 7,
-  random_number_stellar_feedback = 53,
-  random_number_stellar_enrichment = 197,
-  random_number_BH_feedback = 491
+  random_number_star_formation = 0LL,
+  random_number_stellar_feedback = 3947008991LL,
+  random_number_stellar_enrichment = 2936881973LL,
+  random_number_BH_feedback = 1640531371LL
 };
 
 /**
@@ -59,15 +68,45 @@ INLINE static double random_unit_interval(const long long int id,
   /* Range used for the seeds. Best if prime */
   static const long long seed_range = RAND_MAX;
   static const double RAND_MAX_inv = 1. / ((double)RAND_MAX);
+  static const long long mwc_number = (1LL << 32) - 1LL;
 
   /* Calculate the seed */
   /* WARNING: Only change the math if you really know what you are doing!
-     The numbers are carefully chosen prime numbers that prevent correlation
-     with either the current integer time or the particle IDs.
-     The calculation overflows on purpose.  */
-  unsigned int seed = ((937LL * id + 1109LL) % 2147987LL +
-                       (ti_current - 1LL) % 1514917LL + (long long)type) %
-                      seed_range;
+   * The numbers are carefully chosen prime numbers that prevent correlation
+   * with either the current integer time or the particle IDs. The current
+   * method also prevents any correlation between different random number
+   * types.
+   * The calculation overflows on purpose.
+   * 1. The first step is calculating the seed by using a multiply with carry
+   * (MWC) method, this method depends on the type of random number and
+   * this therefore also prevents that there is any correlation between
+   * the different types of random numbers.
+   * 2. After this we use the 64 bit Xorshift method to randomize the seeds
+   * even more.
+   * 3. We calculate a prime multiplication for the id with a quadratic
+   * term.
+   * 4. We calculate the seed by using a Quadratic congruential generator,
+   * in which we use the id part and the current time step bin.
+   */
+  unsigned long long number = ti_current;
+
+  /* Multiply with carry (MWC), (adviced variables by NR) */
+  number = 4294957665LL * (number & (mwc_number)) + (number >> 32);
+
+  /* 64-bit Xorshift (adviced variables by NR) */
+  number ^= number << 21;
+  number ^= number >> 35;
+  number ^= number << 4;
+
+  /* Add constant to ID */
+  const unsigned long long idt = id + type;
+
+  /* Nonlinear congruential generator */
+  const unsigned long long idpart =
+      3457LL * idt + 593LL * idt * ti_current + 5417LL * idt * idt;
+  unsigned int seed =
+      (937LL * number + 5171LL * number * number + idpart + 1109LL) %
+      9996361LL % seed_range;
 
   /* Generate a random number between 0 and 1. */
   return rand_r(&seed) * RAND_MAX_inv;
diff --git a/src/runner.c b/src/runner.c
index d3d76d3942cc616a31cff7d7fc6c068211196e89..83e489caba0140d941826fef3e8fedb094f212ea 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -110,7 +110,9 @@
 
 /* Import the stars density loop functions. */
 #define FUNCTION density
+#define UPDATE_STARS 1
 #include "runner_doiact_stars.h"
+#undef UPDATE_STARS
 #undef FUNCTION
 
 /* Import the stars feedback loop functions. */
@@ -131,11 +133,12 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
   struct spart *restrict sparts = c->stars.parts;
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
-  const struct stars_props *stars_properties = e->stars_properties;
-  const float stars_h_max = stars_properties->h_max;
-  const float eps = stars_properties->h_tolerance;
-  const float stars_eta_dim = pow_dimension(stars_properties->eta_neighbours);
-  const int max_smoothing_iter = stars_properties->max_smoothing_iterations;
+  const float stars_h_max = e->hydro_properties->h_max;
+  const float stars_h_min = e->hydro_properties->h_min;
+  const float eps = e->stars_properties->h_tolerance;
+  const float stars_eta_dim =
+      pow_dimension(e->stars_properties->eta_neighbours);
+  const int max_smoothing_iter = e->stars_properties->max_smoothing_iterations;
   int redo = 0, scount = 0;
 
   TIMER_TIC;
@@ -151,11 +154,23 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
 
     /* Init the list of active particles that have to be updated. */
     int *sid = NULL;
+    float *h_0 = NULL;
+    float *left = NULL;
+    float *right = NULL;
     if ((sid = (int *)malloc(sizeof(int) * c->stars.count)) == NULL)
       error("Can't allocate memory for sid.");
+    if ((h_0 = (float *)malloc(sizeof(float) * c->stars.count)) == NULL)
+      error("Can't allocate memory for h_0.");
+    if ((left = (float *)malloc(sizeof(float) * c->stars.count)) == NULL)
+      error("Can't allocate memory for left.");
+    if ((right = (float *)malloc(sizeof(float) * c->stars.count)) == NULL)
+      error("Can't allocate memory for right.");
     for (int k = 0; k < c->stars.count; k++)
       if (spart_is_active(&sparts[k], e)) {
         sid[scount] = k;
+        h_0[scount] = sparts[k].h;
+        left[scount] = 0.f;
+        right[scount] = stars_h_max;
         ++scount;
       }
 
@@ -179,9 +194,11 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
 #endif
 
         /* Get some useful values */
+        const float h_init = h_0[i];
         const float h_old = sp->h;
         const float h_old_dim = pow_dimension(h_old);
         const float h_old_dim_minus_one = pow_dimension_minus_one(h_old);
+
         float h_new;
         int has_no_neighbours = 0;
 
@@ -192,6 +209,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
 
           /* Double h and try again */
           h_new = 2.f * h_old;
+
         } else {
 
           /* Finish the density calculation */
@@ -205,8 +223,45 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
               sp->density.wcount_dh * h_old_dim +
               hydro_dimension * sp->density.wcount * h_old_dim_minus_one;
 
+          /* Improve the bisection bounds */
+          if (n_sum < n_target)
+            left[i] = max(left[i], h_old);
+          else if (n_sum > n_target)
+            right[i] = min(right[i], h_old);
+
+#ifdef SWIFT_DEBUG_CHECKS
+          /* Check the validity of the left and right bounds */
+          if (left[i] > right[i])
+            error("Invalid left (%e) and right (%e)", left[i], right[i]);
+#endif
+
+          /* Skip if h is already h_max and we don't have enough neighbours */
+          /* Same if we are below h_min */
+          if (((sp->h >= stars_h_max) && (f < 0.f)) ||
+              ((sp->h <= stars_h_min) && (f > 0.f))) {
+
+            stars_reset_feedback(sp);
+
+            /* Ok, we are done with this particle */
+            continue;
+          }
+
+          /* Normal case: Use Newton-Raphson to get a better value of h */
+
           /* Avoid floating point exception from f_prime = 0 */
           h_new = h_old - f / (f_prime + FLT_MIN);
+
+          /* Be verbose about the particles that struggle to converge */
+          if (num_reruns > max_smoothing_iter - 10) {
+
+            message(
+                "Smoothing length convergence problem: iter=%d p->id=%lld "
+                "h_init=%12.8e h_old=%12.8e h_new=%12.8e f=%f f_prime=%f "
+                "n_sum=%12.8e n_target=%12.8e left=%12.8e right=%12.8e",
+                num_reruns, sp->id, h_init, h_old, h_new, f, f_prime, n_sum,
+                n_target, left[i], right[i]);
+          }
+
 #ifdef SWIFT_DEBUG_CHECKS
           if ((f > 0.f && h_new > h_old) || (f < 0.f && h_new < h_old))
             error(
@@ -216,19 +271,39 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
           /* Safety check: truncate to the range [ h_old/2 , 2h_old ]. */
           h_new = min(h_new, 2.f * h_old);
           h_new = max(h_new, 0.5f * h_old);
+
+          /* Verify that we are actually progrssing towards the answer */
+          h_new = max(h_new, left[i]);
+          h_new = min(h_new, right[i]);
         }
 
         /* Check whether the particle has an inappropriate smoothing length */
         if (fabsf(h_new - h_old) > eps * h_old) {
 
           /* Ok, correct then */
-          sp->h = h_new;
+
+          /* Case where we have been oscillating around the solution */
+          if ((h_new == left[i] && h_old == right[i]) ||
+              (h_old == left[i] && h_new == right[i])) {
+
+            /* Bissect the remaining interval */
+            sp->h = pow_inv_dimension(
+                0.5f * (pow_dimension(left[i]) + pow_dimension(right[i])));
+
+          } else {
+
+            /* Normal case */
+            sp->h = h_new;
+          }
 
           /* If below the absolute maximum, try again */
-          if (sp->h < stars_h_max) {
+          if (sp->h < stars_h_max && sp->h > stars_h_min) {
 
             /* Flag for another round of fun */
             sid[redo] = sid[i];
+            h_0[redo] = h_0[i];
+            left[redo] = left[i];
+            right[redo] = right[i];
             redo += 1;
 
             /* Re-initialise everything */
@@ -237,7 +312,12 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
             /* Off we go ! */
             continue;
 
-          } else {
+          } else if (sp->h <= stars_h_min) {
+
+            /* Ok, this particle is a lost cause... */
+            sp->h = stars_h_min;
+
+          } else if (sp->h >= stars_h_max) {
 
             /* Ok, this particle is a lost cause... */
             sp->h = stars_h_max;
@@ -246,13 +326,19 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
             if (has_no_neighbours) {
               stars_spart_has_no_neighbours(sp, cosmo);
             }
+
+          } else {
+            error(
+                "Fundamental problem with the smoothing length iteration "
+                "logic.");
           }
         }
 
         /* We now have a particle whose smoothing length has converged */
+        stars_reset_feedback(sp);
 
         /* Compute the stellar evolution  */
-        stars_evolve_spart(sp, stars_properties, cosmo);
+        stars_evolve_spart(sp, e->stars_properties, cosmo);
       }
 
       /* We now need to treat the particles whose smoothing length had not
@@ -316,7 +402,10 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
     }
 
     /* Be clean */
+    free(left);
+    free(right);
     free(sid);
+    free(h_0);
   }
 
   if (timer) TIMER_TOC(timer_dostars_ghost);
@@ -489,6 +578,7 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) {
   struct cooling_function_data *restrict cooling = e->cooling_func;
   const double time_base = e->time_base;
   const integertime_t ti_current = e->ti_current;
+  const int current_stars_count = c->stars.count;
 
   TIMER_TIC;
 
@@ -556,6 +646,13 @@ void runner_do_star_formation(struct runner *r, struct cell *c, int timer) {
     }     /* Loop over particles */
   }
 
+  /* If we formed any stars, the star sorts are now invalid. We need to
+   * re-compute them. */
+  if ((c == c->hydro.super) && (current_stars_count != c->stars.count)) {
+    cell_clear_stars_sort_flags(c, /*is_super=*/1);
+    runner_do_stars_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0);
+  }
+
   if (timer) TIMER_TOC(timer_do_star_formation);
 }
 
@@ -918,8 +1015,9 @@ void runner_do_stars_sort(struct runner *r, struct cell *c, int flags,
   if (flags == 0 && !c->stars.do_sub_sort) return;
 
   /* Check that the particles have been moved to the current time */
-  if (flags && !cell_are_spart_drifted(c, r->e))
+  if (flags && !cell_are_spart_drifted(c, r->e)) {
     error("Sorting un-drifted cell c->nodeID=%d", c->nodeID);
+  }
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Make sure the sort flags are consistent (downward). */
@@ -955,10 +1053,10 @@ void runner_do_stars_sort(struct runner *r, struct cell *c, int flags,
     for (int k = 0; k < 8; k++) {
       if (c->progeny[k] != NULL && c->progeny[k]->stars.count > 0) {
         /* Only propagate cleanup if the progeny is stale. */
-        runner_do_stars_sort(r, c->progeny[k], flags,
-                             cleanup && (c->progeny[k]->stars.dx_max_sort_old >
-                                         space_maxreldx * c->progeny[k]->dmin),
-                             0);
+        const int cleanup_prog =
+            cleanup && (c->progeny[k]->stars.dx_max_sort_old >
+                        space_maxreldx * c->progeny[k]->dmin);
+        runner_do_stars_sort(r, c->progeny[k], flags, cleanup_prog, 0);
         dx_max_sort = max(dx_max_sort, c->progeny[k]->stars.dx_max_sort);
         dx_max_sort_old =
             max(dx_max_sort_old, c->progeny[k]->stars.dx_max_sort_old);
@@ -1327,8 +1425,10 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
               hydro_dimension * p->density.wcount * h_old_dim_minus_one;
 
           /* Improve the bisection bounds */
-          if (n_sum < n_target) left[i] = max(left[i], h_old);
-          if (n_sum > n_target) right[i] = min(right[i], h_old);
+          if (n_sum < n_target)
+            left[i] = max(left[i], h_old);
+          else if (n_sum > n_target)
+            right[i] = min(right[i], h_old);
 
 #ifdef SWIFT_DEBUG_CHECKS
           /* Check the validity of the left and right bounds */
@@ -1337,6 +1437,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 #endif
 
           /* Skip if h is already h_max and we don't have enough neighbours */
+          /* Same if we are below h_min */
           if (((p->h >= hydro_h_max) && (f < 0.f)) ||
               ((p->h <= hydro_h_min) && (f > 0.f))) {
 
@@ -1480,6 +1581,11 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
               star_formation_part_has_no_neighbours(p, xp, star_formation,
                                                     cosmo);
             }
+
+          } else {
+            error(
+                "Fundamental problem with the smoothing length iteration "
+                "logic.");
           }
         }
 
@@ -1717,6 +1823,7 @@ void runner_do_unskip_mapper(void *map_data, int num_elements,
     }
   }
 }
+
 /**
  * @brief Drift all part in a cell.
  *
@@ -1749,6 +1856,21 @@ void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer) {
   if (timer) TIMER_TOC(timer_drift_gpart);
 }
 
+/**
+ * @brief Drift all spart in a cell.
+ *
+ * @param r The runner thread.
+ * @param c The cell.
+ * @param timer Are we timing this ?
+ */
+void runner_do_drift_spart(struct runner *r, struct cell *c, int timer) {
+
+  TIMER_TIC;
+
+  cell_drift_spart(c, r->e, 0);
+
+  if (timer) TIMER_TOC(timer_drift_spart);
+}
 /**
  * @brief Perform the first half-kick on all the active particles in a cell.
  *
@@ -1776,7 +1898,9 @@ void runner_do_kick1(struct runner *r, struct cell *c, int timer) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (!cell_is_starting_hydro(c, e) && !cell_is_starting_gravity(c, e)) return;
+  if (!cell_is_starting_hydro(c, e) && !cell_is_starting_gravity(c, e) &&
+      !cell_is_starting_stars(c, e))
+    return;
 
   /* Recurse? */
   if (c->split) {
@@ -1958,7 +2082,9 @@ void runner_do_kick2(struct runner *r, struct cell *c, int timer) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e)) return;
+  if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) &&
+      !cell_is_active_stars(c, e))
+    return;
 
   /* Recurse? */
   if (c->split) {
@@ -2152,7 +2278,8 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e)) {
+  if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) &&
+      !cell_is_active_stars(c, e)) {
     c->hydro.updated = 0;
     c->grav.updated = 0;
     c->stars.updated = 0;
@@ -2165,7 +2292,8 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
                 ti_hydro_beg_max = 0;
   integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0,
                 ti_gravity_beg_max = 0;
-  integertime_t ti_stars_end_min = max_nr_timesteps;
+  integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0,
+                ti_stars_beg_max = 0;
 
   /* No children? */
   if (!c->split) {
@@ -2343,13 +2471,13 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
         s_updated++;
         g_updated++;
 
-        /* What is the next sync-point ? */
+        ti_stars_end_min = min(ti_current + ti_new_step, ti_stars_end_min);
+        ti_stars_end_max = max(ti_current + ti_new_step, ti_stars_end_max);
         ti_gravity_end_min = min(ti_current + ti_new_step, ti_gravity_end_min);
         ti_gravity_end_max = max(ti_current + ti_new_step, ti_gravity_end_max);
 
-        ti_stars_end_min = min(ti_current + ti_new_step, ti_stars_end_min);
-
         /* What is the next starting point for this cell ? */
+        ti_stars_beg_max = max(ti_current, ti_stars_beg_max);
         ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max);
 
         /* star particle is inactive but not inhibited */
@@ -2361,23 +2489,23 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
         const integertime_t ti_end =
             get_integer_time_end(ti_current, sp->time_bin);
 
-        /* What is the next sync-point ? */
-        ti_gravity_end_min = min(ti_end, ti_gravity_end_min);
-        ti_gravity_end_max = max(ti_end, ti_gravity_end_max);
-
-        ti_stars_end_min = min(ti_end, ti_stars_end_min);
-
         const integertime_t ti_beg =
             get_integer_time_begin(ti_current + 1, sp->time_bin);
 
+        ti_stars_end_min = min(ti_end, ti_stars_end_min);
+        ti_stars_end_max = max(ti_end, ti_stars_end_max);
+        ti_gravity_end_min = min(ti_end, ti_gravity_end_min);
+        ti_gravity_end_max = max(ti_end, ti_gravity_end_max);
+
         /* What is the next starting point for this cell ? */
+        ti_stars_beg_max = max(ti_beg, ti_stars_beg_max);
         ti_gravity_beg_max = max(ti_beg, ti_gravity_beg_max);
       }
     }
   } else {
 
     /* Loop over the progeny. */
-    for (int k = 0; k < 8; k++)
+    for (int k = 0; k < 8; k++) {
       if (c->progeny[k] != NULL) {
         struct cell *restrict cp = c->progeny[k];
 
@@ -2398,7 +2526,10 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
         ti_gravity_end_max = max(cp->grav.ti_end_max, ti_gravity_end_max);
         ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max);
         ti_stars_end_min = min(cp->stars.ti_end_min, ti_stars_end_min);
+        ti_stars_end_max = max(cp->grav.ti_end_max, ti_stars_end_max);
+        ti_stars_beg_max = max(cp->grav.ti_beg_max, ti_stars_beg_max);
       }
+    }
   }
 
   /* Store the values. */
@@ -2415,6 +2546,8 @@ void runner_do_timestep(struct runner *r, struct cell *c, int timer) {
   c->grav.ti_end_max = ti_gravity_end_max;
   c->grav.ti_beg_max = ti_gravity_beg_max;
   c->stars.ti_end_min = ti_stars_end_min;
+  c->stars.ti_end_max = ti_stars_end_max;
+  c->stars.ti_beg_max = ti_stars_beg_max;
 
 #ifdef SWIFT_DEBUG_CHECKS
   if (c->hydro.ti_end_min == e->ti_current &&
@@ -2570,46 +2703,32 @@ void runner_do_limiter(struct runner *r, struct cell *c, int force, int timer) {
 }
 
 /**
- * @brief End the force calculation of all active particles in a cell
+ * @brief End the hydro force calculation of all active particles in a cell
  * by multiplying the acccelerations by the relevant constants
  *
  * @param r The #runner thread.
  * @param c The #cell.
  * @param timer Are we timing this ?
  */
-void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
+void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer) {
 
   const struct engine *e = r->e;
-  const struct space *s = e->s;
-  const struct cosmology *cosmo = e->cosmology;
-  const int count = c->hydro.count;
-  const int gcount = c->grav.count;
-  const int scount = c->stars.count;
-  struct part *restrict parts = c->hydro.parts;
-  struct gpart *restrict gparts = c->grav.parts;
-  struct spart *restrict sparts = c->stars.parts;
-  const int periodic = s->periodic;
-  const float G_newton = e->physical_constants->const_newton_G;
 
   TIMER_TIC;
 
-  /* Potential normalisation in the case of periodic gravity */
-  float potential_normalisation = 0.;
-  if (periodic && (e->policy & engine_policy_self_gravity)) {
-    const double volume = s->dim[0] * s->dim[1] * s->dim[2];
-    const double r_s = e->mesh->r_s;
-    potential_normalisation = 4. * M_PI * e->total_mass * r_s * r_s / volume;
-  }
-
   /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e)) return;
+  if (!cell_is_active_hydro(c, e)) return;
 
   /* Recurse? */
   if (c->split) {
     for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL) runner_do_end_force(r, c->progeny[k], 0);
+      if (c->progeny[k] != NULL) runner_do_end_hydro_force(r, c->progeny[k], 0);
   } else {
 
+    const struct cosmology *cosmo = e->cosmology;
+    const int count = c->hydro.count;
+    struct part *restrict parts = c->hydro.parts;
+
     /* Loop over the gas particles in this cell. */
     for (int k = 0; k < count; k++) {
 
@@ -2622,6 +2741,48 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
         hydro_end_force(p, cosmo);
       }
     }
+  }
+
+  if (timer) TIMER_TOC(timer_end_hydro_force);
+}
+
+/**
+ * @brief End the gravity force calculation of all active particles in a cell
+ * by multiplying the acccelerations by the relevant constants
+ *
+ * @param r The #runner thread.
+ * @param c The #cell.
+ * @param timer Are we timing this ?
+ */
+void runner_do_end_grav_force(struct runner *r, struct cell *c, int timer) {
+
+  const struct engine *e = r->e;
+
+  TIMER_TIC;
+
+  /* Anything to do here? */
+  if (!cell_is_active_gravity(c, e)) return;
+
+  /* Recurse? */
+  if (c->split) {
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL) runner_do_end_grav_force(r, c->progeny[k], 0);
+  } else {
+
+    const struct space *s = e->s;
+    const int periodic = s->periodic;
+    const float G_newton = e->physical_constants->const_newton_G;
+
+    /* Potential normalisation in the case of periodic gravity */
+    float potential_normalisation = 0.;
+    if (periodic && (e->policy & engine_policy_self_gravity)) {
+      const double volume = s->dim[0] * s->dim[1] * s->dim[2];
+      const double r_s = e->mesh->r_s;
+      potential_normalisation = 4. * M_PI * e->total_mass * r_s * r_s / volume;
+    }
+
+    const int gcount = c->grav.count;
+    struct gpart *restrict gparts = c->grav.parts;
 
     /* Loop over the g-particles in this cell. */
     for (int k = 0; k < gcount; k++) {
@@ -2699,21 +2860,8 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
 #endif
       }
     }
-
-    /* Loop over the stars particles in this cell. */
-    for (int k = 0; k < scount; k++) {
-
-      /* Get a handle on the spart. */
-      struct spart *restrict sp = &sparts[k];
-      if (spart_is_active(sp, e)) {
-
-        /* Finish the force loop */
-        stars_end_force(sp);
-      }
-    }
   }
-
-  if (timer) TIMER_TOC(timer_endforce);
+  if (timer) TIMER_TOC(timer_end_grav_force);
 }
 
 /**
@@ -2726,7 +2874,6 @@ void runner_do_end_force(struct runner *r, struct cell *c, int timer) {
  */
 void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts,
                          int timer) {
-
 #ifdef WITH_MPI
 
   const struct part *restrict parts = c->hydro.parts;
@@ -2878,69 +3025,79 @@ void runner_do_recv_gpart(struct runner *r, struct cell *c, int timer) {
  *
  * @param r The runner thread.
  * @param c The cell.
+ * @param clear_sorts Should we clear the sort flag and hence trigger a sort ?
  * @param timer Are we timing this ?
  */
-void runner_do_recv_spart(struct runner *r, struct cell *c, int timer) {
+void runner_do_recv_spart(struct runner *r, struct cell *c, int clear_sorts,
+                          int timer) {
 
 #ifdef WITH_MPI
 
-  const struct spart *restrict sparts = c->stars.parts;
+  struct spart *restrict sparts = c->stars.parts;
   const size_t nr_sparts = c->stars.count;
   const integertime_t ti_current = r->e->ti_current;
 
-  error("Need to add h_max computation");
-
   TIMER_TIC;
 
-  integertime_t ti_gravity_end_min = max_nr_timesteps;
-  integertime_t ti_gravity_end_max = 0;
+  integertime_t ti_stars_end_min = max_nr_timesteps;
+  integertime_t ti_stars_end_max = 0;
   timebin_t time_bin_min = num_time_bins;
   timebin_t time_bin_max = 0;
+  float h_max = 0.f;
 
 #ifdef SWIFT_DEBUG_CHECKS
   if (c->nodeID == engine_rank) error("Updating a local cell!");
 #endif
 
+  /* Clear this cell's sorted mask. */
+  if (clear_sorts) c->stars.sorted = 0;
+
   /* If this cell is a leaf, collect the particle data. */
   if (!c->split) {
 
     /* Collect everything... */
     for (size_t k = 0; k < nr_sparts; k++) {
+#ifdef DEBUG_INTERACTIONS_STARS
+      sparts[k].num_ngb_force = 0;
+#endif
       if (sparts[k].time_bin == time_bin_inhibited) continue;
       time_bin_min = min(time_bin_min, sparts[k].time_bin);
       time_bin_max = max(time_bin_max, sparts[k].time_bin);
+      h_max = max(h_max, sparts[k].h);
     }
 
     /* Convert into a time */
-    ti_gravity_end_min = get_integer_time_end(ti_current, time_bin_min);
-    ti_gravity_end_max = get_integer_time_end(ti_current, time_bin_max);
+    ti_stars_end_min = get_integer_time_end(ti_current, time_bin_min);
+    ti_stars_end_max = get_integer_time_end(ti_current, time_bin_max);
   }
 
   /* Otherwise, recurse and collect. */
   else {
     for (int k = 0; k < 8; k++) {
       if (c->progeny[k] != NULL && c->progeny[k]->stars.count > 0) {
-        runner_do_recv_spart(r, c->progeny[k], 0);
-        ti_gravity_end_min =
-            min(ti_gravity_end_min, c->progeny[k]->grav.ti_end_min);
-        ti_gravity_end_max =
-            max(ti_gravity_end_max, c->progeny[k]->grav.ti_end_max);
+        runner_do_recv_spart(r, c->progeny[k], clear_sorts, 0);
+        ti_stars_end_min =
+            min(ti_stars_end_min, c->progeny[k]->stars.ti_end_min);
+        ti_stars_end_max =
+            max(ti_stars_end_max, c->progeny[k]->grav.ti_end_max);
+        h_max = max(h_max, c->progeny[k]->stars.h_max);
       }
     }
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
-  if (ti_gravity_end_min < ti_current)
+  if (ti_stars_end_min < ti_current)
     error(
         "Received a cell at an incorrect time c->ti_end_min=%lld, "
         "e->ti_current=%lld.",
-        ti_gravity_end_min, ti_current);
+        ti_stars_end_min, ti_current);
 #endif
 
   /* ... and store. */
   // c->grav.ti_end_min = ti_gravity_end_min;
   // c->grav.ti_end_max = ti_gravity_end_max;
-  c->grav.ti_old_part = ti_current;
+  c->stars.ti_old_part = ti_current;
+  c->stars.h_max = h_max;
 
   if (timer) TIMER_TOC(timer_dorecv_spart);
 
@@ -3028,9 +3185,9 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_external_grav)
             runner_do_grav_external(r, ci, 1);
           else if (t->subtype == task_subtype_stars_density)
-            runner_doself_stars_density(r, ci, 1);
+            runner_doself_branch_stars_density(r, ci);
           else if (t->subtype == task_subtype_stars_feedback)
-            runner_doself_stars_feedback(r, ci, 1);
+            runner_doself_branch_stars_feedback(r, ci);
           else
             error("Unknown/invalid task subtype (%d).", t->subtype);
           break;
@@ -3049,9 +3206,9 @@ void *runner_main(void *data) {
           else if (t->subtype == task_subtype_grav)
             runner_dopair_recursive_grav(r, ci, cj, 1);
           else if (t->subtype == task_subtype_stars_density)
-            runner_dopair_stars_density(r, ci, cj, 1);
+            runner_dopair_branch_stars_density(r, ci, cj);
           else if (t->subtype == task_subtype_stars_feedback)
-            runner_dopair_stars_feedback(r, ci, cj, 1);
+            runner_dopair_branch_stars_feedback(r, ci, cj);
           else
             error("Unknown/invalid task subtype (%d).", t->subtype);
           break;
@@ -3127,6 +3284,9 @@ void *runner_main(void *data) {
         case task_type_drift_part:
           runner_do_drift_part(r, ci, 1);
           break;
+        case task_type_drift_spart:
+          runner_do_drift_spart(r, ci, 1);
+          break;
         case task_type_drift_gpart:
           runner_do_drift_gpart(r, ci, 1);
           break;
@@ -3136,8 +3296,11 @@ void *runner_main(void *data) {
         case task_type_kick2:
           runner_do_kick2(r, ci, 1);
           break;
-        case task_type_end_force:
-          runner_do_end_force(r, ci, 1);
+        case task_type_end_hydro_force:
+          runner_do_end_hydro_force(r, ci, 1);
+          break;
+        case task_type_end_grav_force:
+          runner_do_end_grav_force(r, ci, 1);
           break;
         case task_type_logger:
           runner_do_logger(r, ci, 1);
@@ -3169,7 +3332,7 @@ void *runner_main(void *data) {
           } else if (t->subtype == task_subtype_gpart) {
             runner_do_recv_gpart(r, ci, 1);
           } else if (t->subtype == task_subtype_spart) {
-            runner_do_recv_spart(r, ci, 1);
+            runner_do_recv_spart(r, ci, 1, 1);
           } else if (t->subtype == task_subtype_multipole) {
             cell_unpack_multipoles(ci, (struct gravity_tensors *)t->buff);
             free(t->buff);
diff --git a/src/runner.h b/src/runner.h
index 6af0cd227374afd616b3329a8dbd527634902922..60466dc922f445e79bd37b855a10ffb6751aa371 100644
--- a/src/runner.h
+++ b/src/runner.h
@@ -77,7 +77,7 @@ void runner_do_drift_part(struct runner *r, struct cell *c, int timer);
 void runner_do_drift_gpart(struct runner *r, struct cell *c, int timer);
 void runner_do_kick1(struct runner *r, struct cell *c, int timer);
 void runner_do_kick2(struct runner *r, struct cell *c, int timer);
-void runner_do_end_force(struct runner *r, struct cell *c, int timer);
+void runner_do_end_hydro_force(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);
diff --git a/src/runner_doiact.h b/src/runner_doiact.h
index a111b6e9c7e5764e955180c5a4f7f271cffe32e8..854f8b898df93be93bb020a46606a415170fe980 100644
--- a/src/runner_doiact.h
+++ b/src/runner_doiact.h
@@ -817,6 +817,9 @@ void DOPAIR_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci,
 
   const struct engine *e = r->e;
 
+  /* Anything to do here? */
+  if (cj->hydro.count == 0) return;
+
   /* Get the relative distance between the pairs, wrapping. */
   double shift[3] = {0.0, 0.0, 0.0};
   for (int k = 0; k < 3; k++) {
@@ -1204,6 +1207,9 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
 
   const struct engine *restrict e = r->e;
 
+  /* Anything to do here? */
+  if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
+
   /* Anything to do here? */
   if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
 
@@ -1748,6 +1754,9 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
 
   const struct engine *restrict e = r->e;
 
+  /* Anything to do here? */
+  if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
+
   /* Anything to do here? */
   if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
 
@@ -1998,6 +2007,9 @@ void DOSELF1_BRANCH(struct runner *r, struct cell *c) {
 
   const struct engine *restrict e = r->e;
 
+  /* Anything to do here? */
+  if (c->hydro.count == 0) return;
+
   /* Anything to do here? */
   if (!cell_is_active_hydro(c, e)) return;
 
@@ -2175,6 +2187,9 @@ void DOSELF2_BRANCH(struct runner *r, struct cell *c) {
 
   const struct engine *restrict e = r->e;
 
+  /* Anything to do here? */
+  if (c->hydro.count == 0) return;
+
   /* Anything to do here? */
   if (!cell_is_active_hydro(c, e)) return;
 
diff --git a/src/runner_doiact_stars.h b/src/runner_doiact_stars.h
index e816d80399a0fef85645c914168dd4038f55988c..f208f14ac98a31a55df6741a2cc5f9cb0a829762 100644
--- a/src/runner_doiact_stars.h
+++ b/src/runner_doiact_stars.h
@@ -74,6 +74,11 @@
  * @param timer 1 if the time is to be recorded.
  */
 void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (c->nodeID != engine_rank) error("Should be run on a different node");
+#endif
+
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
 
@@ -95,6 +100,8 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) {
 
     /* Get a hold of the ith spart in ci. */
     struct spart *restrict si = &sparts[sid];
+    if (!spart_is_active(si, e) || spart_is_inhibited(si, e)) continue;
+
     const float hi = si->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float six[3] = {(float)(si->x[0] - c->loc[0]),
@@ -138,6 +145,14 @@ void DOSELF1_STARS(struct runner *r, struct cell *c, int timer) {
 void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci,
                            struct cell *restrict cj) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+#ifdef UPDATE_STARS
+  if (ci->nodeID != engine_rank) error("Should be run on a different node");
+#else
+  if (cj->nodeID != engine_rank) error("Should be run on a different node");
+#endif
+#endif
+
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
 
@@ -167,6 +182,7 @@ void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci,
 
     /* Get a hold of the ith spart in ci. */
     struct spart *restrict si = &sparts_i[sid];
+    if (!spart_is_active(si, e) || spart_is_inhibited(si, e)) continue;
     const float hi = si->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float six[3] = {(float)(si->x[0] - (cj->loc[0] + shift[0])),
@@ -202,9 +218,17 @@ void DO_NONSYM_PAIR1_STARS(struct runner *r, struct cell *restrict ci,
 void DOPAIR1_STARS(struct runner *r, struct cell *restrict ci,
                    struct cell *restrict cj, int timer) {
 
-  if (ci->stars.count != 0 && cj->hydro.count != 0)
+#ifdef UPDATE_STARS
+  const int ci_local = ci->nodeID == engine_rank;
+  const int cj_local = cj->nodeID == engine_rank;
+#else
+  /* here we are updating the hydro -> switch ci, cj */
+  const int ci_local = cj->nodeID == engine_rank;
+  const int cj_local = ci->nodeID == engine_rank;
+#endif
+  if (ci_local && ci->stars.count != 0 && cj->hydro.count != 0)
     DO_NONSYM_PAIR1_STARS(r, ci, cj);
-  if (cj->stars.count != 0 && ci->hydro.count != 0)
+  if (cj_local && cj->stars.count != 0 && ci->hydro.count != 0)
     DO_NONSYM_PAIR1_STARS(r, cj, ci);
 }
 
@@ -227,6 +251,9 @@ void DOPAIR1_SUBSET_STARS(struct runner *r, struct cell *restrict ci,
                           int scount, struct cell *restrict cj,
                           const double *shift) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ci->nodeID != engine_rank) error("Should be run on a different node");
+#endif
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
 
@@ -293,6 +320,10 @@ void DOSELF1_SUBSET_STARS(struct runner *r, struct cell *restrict ci,
                           struct spart *restrict sparts, int *restrict ind,
                           int scount) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ci->nodeID != engine_rank) error("Should be run on a different node");
+#endif
+
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
 
@@ -446,8 +477,8 @@ void DOSUB_SUBSET_STARS(struct runner *r, struct cell *ci, struct spart *sparts,
   else {
 
     /* Recurse? */
-    if (cell_can_recurse_in_pair_stars_task(ci) &&
-        cell_can_recurse_in_pair_stars_task(cj)) {
+    if (cell_can_recurse_in_pair_stars_task(ci, cj) &&
+        cell_can_recurse_in_pair_stars_task(cj, ci)) {
 
       /* Get the type of pair if not specified explicitly. */
       double shift[3] = {0.0, 0.0, 0.0};
@@ -956,10 +987,13 @@ void DOSUB_SUBSET_STARS(struct runner *r, struct cell *ci, struct spart *sparts,
     }
 
     /* Otherwise, compute the pair directly. */
-    else if (cell_is_active_stars(ci, e) || cell_is_active_stars(cj, e)) {
+    else if (cell_is_active_stars(ci, e)) {
 
       /* Do any of the cells need to be drifted first? */
-      if (!cell_are_part_drifted(cj, e)) error("Cell should be drifted!");
+      if (cell_is_active_stars(ci, e)) {
+        if (!cell_are_spart_drifted(ci, e)) error("Cell should be drifted!");
+        if (!cell_are_part_drifted(cj, e)) error("Cell should be drifted!");
+      }
 
       DOPAIR1_SUBSET_BRANCH_STARS(r, ci, sparts, ind, scount, cj);
     }
@@ -979,6 +1013,9 @@ void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c) {
 
   const struct engine *restrict e = r->e;
 
+  /* Anything to do here? */
+  if (c->stars.count == 0) return;
+
   /* Anything to do here? */
   if (!cell_is_active_stars(c, e)) return;
 
@@ -995,6 +1032,8 @@ void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c) {
                                                                             \
     for (int pjd = 0; pjd < cj->TYPE.count; pjd++) {                        \
       const struct PART *p = &cj->TYPE.parts[sort_j[pjd].i];                \
+      if (PART##_is_inhibited(p, e)) continue;                              \
+                                                                            \
       const float d = p->x[0] * runner_shift[sid][0] +                      \
                       p->x[1] * runner_shift[sid][1] +                      \
                       p->x[2] * runner_shift[sid][2];                       \
@@ -1007,9 +1046,12 @@ void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c) {
             "cj->nodeID=%d "                                                \
             "ci->nodeID=%d d=%e sort_j[pjd].d=%e cj->" #TYPE                \
             ".dx_max_sort=%e "                                              \
-            "cj->" #TYPE ".dx_max_sort_old=%e",                             \
+            "cj->" #TYPE                                                    \
+            ".dx_max_sort_old=%e, cellID=%i super->cellID=%i"               \
+            "cj->depth=%d cj->maxdepth=%d",                                 \
             cj->nodeID, ci->nodeID, d, sort_j[pjd].d, cj->TYPE.dx_max_sort, \
-            cj->TYPE.dx_max_sort_old);                                      \
+            cj->TYPE.dx_max_sort_old, cj->cellID, cj->hydro.super->cellID,  \
+            cj->depth, cj->maxdepth);                                       \
     }                                                                       \
   })
 
@@ -1026,18 +1068,29 @@ void DOSELF1_BRANCH_STARS(struct runner *r, struct cell *c) {
 void DOPAIR1_BRANCH_STARS(struct runner *r, struct cell *ci, struct cell *cj) {
 
   const struct engine *restrict e = r->e;
+
+  /* Get the sort ID. */
+  double shift[3] = {0.0, 0.0, 0.0};
+  const int sid = space_getsid(e->s, &ci, &cj, shift);
+
   const int ci_active = cell_is_active_stars(ci, e);
   const int cj_active = cell_is_active_stars(cj, e);
-  const int do_ci = (ci->stars.count != 0 && cj->hydro.count != 0 && ci_active);
-  const int do_cj = (cj->stars.count != 0 && ci->hydro.count != 0 && cj_active);
+#ifdef UPDATE_STARS
+  const int ci_local = ci->nodeID == engine_rank;
+  const int cj_local = cj->nodeID == engine_rank;
+#else
+  /* here we are updating the hydro -> switch ci, cj */
+  const int ci_local = cj->nodeID == engine_rank;
+  const int cj_local = ci->nodeID == engine_rank;
+#endif
+  const int do_ci =
+      (ci->stars.count != 0 && cj->hydro.count != 0 && ci_active && ci_local);
+  const int do_cj =
+      (cj->stars.count != 0 && ci->hydro.count != 0 && cj_active && cj_local);
 
   /* Anything to do here? */
   if (!do_ci && !do_cj) return;
 
-  /* Get the sort ID. */
-  double shift[3] = {0.0, 0.0, 0.0};
-  const int sid = space_getsid(e->s, &ci, &cj, shift);
-
   /* Check that cells are drifted. */
   if (do_ci &&
       (!cell_are_spart_drifted(ci, e) || !cell_are_part_drifted(cj, e)))
@@ -1099,19 +1152,19 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj,
   const struct engine *e = r->e;
 
   /* Should we even bother? */
-  int should_do = ci->stars.count != 0 && cj->hydro.count != 0 &&
-                  cell_is_active_stars(ci, e);
-  should_do |= cj->stars.count != 0 && ci->hydro.count != 0 &&
-               cell_is_active_stars(cj, e);
-  if (!should_do) return;
+  const int should_do_ci = ci->stars.count != 0 && cj->hydro.count != 0 &&
+                           cell_is_active_stars(ci, e);
+  const int should_do_cj = cj->stars.count != 0 && ci->hydro.count != 0 &&
+                           cell_is_active_stars(cj, e);
+  if (!should_do_ci && !should_do_cj) return;
 
   /* Get the type of pair if not specified explicitly. */
   double shift[3];
   sid = space_getsid(s, &ci, &cj, shift);
 
   /* Recurse? */
-  if (cell_can_recurse_in_pair_stars_task(ci) &&
-      cell_can_recurse_in_pair_stars_task(cj)) {
+  if (cell_can_recurse_in_pair_stars_task(ci, cj) &&
+      cell_can_recurse_in_pair_stars_task(cj, ci)) {
 
     /* Different types of flags. */
     switch (sid) {
@@ -1314,10 +1367,18 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj,
   /* Otherwise, compute the pair directly. */
   else {
 
+#ifdef UPDATE_STARS
+    const int ci_local = ci->nodeID == engine_rank;
+    const int cj_local = cj->nodeID == engine_rank;
+#else
+    /* here we are updating the hydro -> switch ci, cj */
+    const int ci_local = cj->nodeID == engine_rank;
+    const int cj_local = ci->nodeID == engine_rank;
+#endif
     const int do_ci = ci->stars.count != 0 && cj->hydro.count != 0 &&
-                      cell_is_active_stars(ci, e);
+                      cell_is_active_stars(ci, e) && ci_local;
     const int do_cj = cj->stars.count != 0 && ci->hydro.count != 0 &&
-                      cell_is_active_stars(cj, e);
+                      cell_is_active_stars(cj, e) && cj_local;
 
     if (do_ci) {
 
@@ -1330,8 +1391,9 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj,
 
       /* Do any of the cells need to be sorted first? */
       if (!(ci->stars.sorted & (1 << sid)) ||
-          ci->stars.dx_max_sort_old > ci->dmin * space_maxreldx)
+          ci->stars.dx_max_sort_old > ci->dmin * space_maxreldx) {
         error("Interacting unsorted cell (sparts).");
+      }
 
       if (!(cj->hydro.sorted & (1 << sid)) ||
           cj->hydro.dx_max_sort_old > cj->dmin * space_maxreldx)
@@ -1349,12 +1411,14 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj,
 
       /* Do any of the cells need to be sorted first? */
       if (!(ci->hydro.sorted & (1 << sid)) ||
-          ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx)
+          ci->hydro.dx_max_sort_old > ci->dmin * space_maxreldx) {
         error("Interacting unsorted cell (parts).");
+      }
 
       if (!(cj->stars.sorted & (1 << sid)) ||
-          cj->stars.dx_max_sort_old > cj->dmin * space_maxreldx)
+          cj->stars.dx_max_sort_old > cj->dmin * space_maxreldx) {
         error("Interacting unsorted cell (sparts).");
+      }
     }
 
     if (do_ci || do_cj) DOPAIR1_BRANCH_STARS(r, ci, cj);
@@ -1370,6 +1434,11 @@ void DOSUB_PAIR1_STARS(struct runner *r, struct cell *ci, struct cell *cj,
  */
 void DOSUB_SELF1_STARS(struct runner *r, struct cell *ci, int gettimer) {
 
+#ifdef SWIFT_DEBUG_CHECKS
+  if (ci->nodeID != engine_rank)
+    error("This function should not be called on foreign cells");
+#endif
+
   /* Should we even bother? */
   if (ci->hydro.count == 0 || ci->stars.count == 0 ||
       !cell_is_active_stars(ci, r->e))
diff --git a/src/scheduler.c b/src/scheduler.c
index 1083be1996f9ba3986a601ecbe47b1e6a224239a..229c94b2088814352487032816f610a1bdf74724 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -541,6 +541,11 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose) {
  */
 static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
 
+  /* Are we considering both stars and hydro when splitting? */
+  /* Note this is not very clean as the scheduler should not really
+     access the engine... */
+  const int with_feedback = (s->space->e->policy & engine_policy_feedback);
+
   /* Iterate on this task until we're done with it. */
   int redo = 1;
   while (redo) {
@@ -548,9 +553,20 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
     /* Reset the redo flag. */
     redo = 0;
 
+    /* Is this a non-empty self-task? */
+    const int is_self =
+        (t->type == task_type_self) && (t->ci != NULL) &&
+        ((t->ci->hydro.count > 0) || (with_feedback && t->ci->stars.count > 0));
+
+    /* Is this a non-empty pair-task? */
+    const int is_pair =
+        (t->type == task_type_pair) && (t->ci != NULL) && (t->cj != NULL) &&
+        ((t->ci->hydro.count > 0) ||
+         (with_feedback && t->ci->stars.count > 0)) &&
+        ((t->cj->hydro.count > 0) || (with_feedback && t->cj->stars.count > 0));
+
     /* Empty task? */
-    if ((t->ci == NULL) || (t->type == task_type_pair && t->cj == NULL) ||
-        t->ci->hydro.count == 0 || (t->cj != NULL && t->cj->hydro.count == 0)) {
+    if (!is_self && !is_pair) {
       t->type = task_type_none;
       t->subtype = task_subtype_none;
       t->cj = NULL;
@@ -589,24 +605,46 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
           int first_child = 0;
           while (ci->progeny[first_child] == NULL) first_child++;
           t->ci = ci->progeny[first_child];
-          for (int k = first_child + 1; k < 8; k++)
-            if (ci->progeny[k] != NULL && ci->progeny[k]->hydro.count)
+          for (int k = first_child + 1; k < 8; k++) {
+
+            /* Do we have a non-empty progenitor? */
+            if (ci->progeny[k] != NULL &&
+                (ci->progeny[k]->hydro.count ||
+                 (with_feedback && ci->progeny[k]->stars.count))) {
+
               scheduler_splittask_hydro(
                   scheduler_addtask(s, task_type_self, t->subtype, 0, 0,
                                     ci->progeny[k], NULL),
                   s);
+            }
+          }
 
           /* Make a task for each pair of progeny */
-          for (int j = 0; j < 8; j++)
-            if (ci->progeny[j] != NULL && ci->progeny[j]->hydro.count)
-              for (int k = j + 1; k < 8; k++)
-                if (ci->progeny[k] != NULL && ci->progeny[k]->hydro.count)
+          for (int j = 0; j < 8; j++) {
+
+            /* Do we have a non-empty progenitor? */
+            if (ci->progeny[j] != NULL &&
+                (ci->progeny[j]->hydro.count ||
+                 (with_feedback && ci->progeny[j]->stars.count))) {
+
+              for (int k = j + 1; k < 8; k++) {
+
+                /* Do we have a second non-empty progenitor? */
+                if (ci->progeny[k] != NULL &&
+                    (ci->progeny[k]->hydro.count ||
+                     (with_feedback && ci->progeny[k]->stars.count))) {
+
                   scheduler_splittask_hydro(
                       scheduler_addtask(s, task_type_pair, t->subtype,
                                         sub_sid_flag[j][k], 0, ci->progeny[j],
                                         ci->progeny[k]),
                       s);
+                }
+              }
+            }
+          }
         }
+
       } /* Cell is split */
 
     } /* Self interaction */
@@ -1014,478 +1052,6 @@ static void scheduler_splittask_hydro(struct task *t, struct scheduler *s) {
   }   /* iterate over the current task. */
 }
 
-/**
- * @brief Split a stars task if too large.
- *
- * @param t The #task
- * @param s The #scheduler we are working in.
- */
-static void scheduler_splittask_stars(struct task *t, struct scheduler *s) {
-
-  /* Iterate on this task until we're done with it. */
-  int redo = 1;
-  while (redo) {
-
-    /* Reset the redo flag. */
-    redo = 0;
-
-    /* Empty task? */
-    if ((t->ci == NULL) || (t->type == task_type_pair && t->cj == NULL) ||
-        t->ci->stars.count == 0 || (t->cj != NULL && t->cj->stars.count == 0)) {
-      t->type = task_type_none;
-      t->subtype = task_subtype_none;
-      t->cj = NULL;
-      t->skip = 1;
-      break;
-    }
-
-    /* Self-interaction? */
-    if (t->type == task_type_self) {
-
-      /* Get a handle on the cell involved. */
-      struct cell *ci = t->ci;
-
-      /* Foreign task? */
-      if (ci->nodeID != s->nodeID) {
-        t->skip = 1;
-        break;
-      }
-
-      /* Is this cell even split and the task does not violate h ? */
-      if (cell_can_split_self_stars_task(ci)) {
-
-        /* Make a sub? */
-        if (scheduler_dosub && ci->stars.count < space_subsize_self_stars) {
-
-          /* convert to a self-subtask. */
-          t->type = task_type_sub_self;
-
-          /* Otherwise, make tasks explicitly. */
-        } else {
-
-          /* Take a step back (we're going to recycle the current task)... */
-          redo = 1;
-
-          /* Add the self tasks. */
-          int first_child = 0;
-          while (ci->progeny[first_child] == NULL) first_child++;
-          t->ci = ci->progeny[first_child];
-          for (int k = first_child + 1; k < 8; k++)
-            if (ci->progeny[k] != NULL && ci->progeny[k]->stars.count)
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_self, t->subtype, 0, 0,
-                                    ci->progeny[k], NULL),
-                  s);
-
-          /* Make a task for each pair of progeny */
-          for (int j = 0; j < 8; j++)
-            if (ci->progeny[j] != NULL && ci->progeny[j]->stars.count)
-              for (int k = j + 1; k < 8; k++)
-                if (ci->progeny[k] != NULL && ci->progeny[k]->stars.count)
-                  scheduler_splittask_stars(
-                      scheduler_addtask(s, task_type_pair, t->subtype,
-                                        sub_sid_flag[j][k], 0, ci->progeny[j],
-                                        ci->progeny[k]),
-                      s);
-        }
-      } /* Cell is split */
-
-    } /* Self interaction */
-
-    /* Pair interaction? */
-    else if (t->type == task_type_pair) {
-
-      /* Get a handle on the cells involved. */
-      struct cell *ci = t->ci;
-      struct cell *cj = t->cj;
-
-      /* Foreign task? */
-      if (ci->nodeID != s->nodeID && cj->nodeID != s->nodeID) {
-        t->skip = 1;
-        break;
-      }
-
-      /* Get the sort ID, use space_getsid and not t->flags
-         to make sure we get ci and cj swapped if needed. */
-      double shift[3];
-      const int sid = space_getsid(s->space, &ci, &cj, shift);
-
-      /* Should this task be split-up? */
-      if (cell_can_split_pair_stars_task(ci) &&
-          cell_can_split_pair_stars_task(cj)) {
-
-        /* Replace by a single sub-task? */
-        if (scheduler_dosub && /* Use division to avoid integer overflow. */
-            ci->stars.count * sid_scale[sid] <
-                space_subsize_pair_stars / cj->stars.count &&
-            !sort_is_corner(sid)) {
-
-          /* Make this task a sub task. */
-          t->type = task_type_sub_pair;
-
-          /* Otherwise, split it. */
-        } else {
-
-          /* Take a step back (we're going to recycle the current task)... */
-          redo = 1;
-
-          /* For each different sorting type... */
-          switch (sid) {
-
-            case 0: /* (  1 ,  1 ,  1 ) */
-              t->ci = ci->progeny[7];
-              t->cj = cj->progeny[0];
-              t->flags = 0;
-              break;
-
-            case 1: /* (  1 ,  1 ,  0 ) */
-              t->ci = ci->progeny[6];
-              t->cj = cj->progeny[0];
-              t->flags = 1;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[7], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[6], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[7], cj->progeny[0]),
-                  s);
-              break;
-
-            case 2: /* (  1 ,  1 , -1 ) */
-              t->ci = ci->progeny[6];
-              t->cj = cj->progeny[1];
-              t->flags = 2;
-              break;
-
-            case 3: /* (  1 ,  0 ,  1 ) */
-              t->ci = ci->progeny[5];
-              t->cj = cj->progeny[0];
-              t->flags = 3;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[7], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[5], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[7], cj->progeny[0]),
-                  s);
-              break;
-
-            case 4: /* (  1 ,  0 ,  0 ) */
-              t->ci = ci->progeny[4];
-              t->cj = cj->progeny[0];
-              t->flags = 4;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[5], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[6], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[7], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[4], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
-                                    ci->progeny[5], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[6], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[7], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[4], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[5], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
-                                    ci->progeny[6], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[7], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[4], cj->progeny[3]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[5], cj->progeny[3]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[6], cj->progeny[3]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 4, 0,
-                                    ci->progeny[7], cj->progeny[3]),
-                  s);
-              break;
-
-            case 5: /* (  1 ,  0 , -1 ) */
-              t->ci = ci->progeny[4];
-              t->cj = cj->progeny[1];
-              t->flags = 5;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[6], cj->progeny[3]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[4], cj->progeny[3]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[6], cj->progeny[1]),
-                  s);
-              break;
-
-            case 6: /* (  1 , -1 ,  1 ) */
-              t->ci = ci->progeny[5];
-              t->cj = cj->progeny[2];
-              t->flags = 6;
-              break;
-
-            case 7: /* (  1 , -1 ,  0 ) */
-              t->ci = ci->progeny[4];
-              t->cj = cj->progeny[3];
-              t->flags = 6;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[5], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[4], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[5], cj->progeny[3]),
-                  s);
-              break;
-
-            case 8: /* (  1 , -1 , -1 ) */
-              t->ci = ci->progeny[4];
-              t->cj = cj->progeny[3];
-              t->flags = 8;
-              break;
-
-            case 9: /* (  0 ,  1 ,  1 ) */
-              t->ci = ci->progeny[3];
-              t->cj = cj->progeny[0];
-              t->flags = 9;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[7], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[3], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[7], cj->progeny[0]),
-                  s);
-              break;
-
-            case 10: /* (  0 ,  1 ,  0 ) */
-              t->ci = ci->progeny[2];
-              t->cj = cj->progeny[0];
-              t->flags = 10;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[3], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[6], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[7], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[2], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
-                                    ci->progeny[3], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[6], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 7, 0,
-                                    ci->progeny[7], cj->progeny[1]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[2], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[3], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
-                                    ci->progeny[6], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[7], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[2], cj->progeny[5]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 1, 0,
-                                    ci->progeny[3], cj->progeny[5]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[6], cj->progeny[5]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 10, 0,
-                                    ci->progeny[7], cj->progeny[5]),
-                  s);
-              break;
-
-            case 11: /* (  0 ,  1 , -1 ) */
-              t->ci = ci->progeny[2];
-              t->cj = cj->progeny[1];
-              t->flags = 11;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[6], cj->progeny[5]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[2], cj->progeny[5]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[6], cj->progeny[1]),
-                  s);
-              break;
-
-            case 12: /* (  0 ,  0 ,  1 ) */
-              t->ci = ci->progeny[1];
-              t->cj = cj->progeny[0];
-              t->flags = 12;
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[3], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[5], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 2, 0,
-                                    ci->progeny[7], cj->progeny[0]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[1], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
-                                    ci->progeny[3], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 8, 0,
-                                    ci->progeny[5], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 5, 0,
-                                    ci->progeny[7], cj->progeny[2]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[1], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 6, 0,
-                                    ci->progeny[3], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
-                                    ci->progeny[5], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 11, 0,
-                                    ci->progeny[7], cj->progeny[4]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                    ci->progeny[1], cj->progeny[6]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 3, 0,
-                                    ci->progeny[3], cj->progeny[6]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 9, 0,
-                                    ci->progeny[5], cj->progeny[6]),
-                  s);
-              scheduler_splittask_stars(
-                  scheduler_addtask(s, task_type_pair, t->subtype, 12, 0,
-                                    ci->progeny[7], cj->progeny[6]),
-                  s);
-              break;
-          } /* switch(sid) */
-        }
-
-        /* Otherwise, break it up if it is too large? */
-      } else if (scheduler_doforcesplit && ci->split && cj->split &&
-                 (ci->stars.count > space_maxsize / cj->stars.count)) {
-
-        /* Replace the current task. */
-        t->type = task_type_none;
-
-        for (int j = 0; j < 8; j++)
-          if (ci->progeny[j] != NULL && ci->progeny[j]->stars.count)
-            for (int k = 0; k < 8; k++)
-              if (cj->progeny[k] != NULL && cj->progeny[k]->stars.count) {
-                struct task *tl =
-                    scheduler_addtask(s, task_type_pair, t->subtype, 0, 0,
-                                      ci->progeny[j], cj->progeny[k]);
-                scheduler_splittask_stars(tl, s);
-                tl->flags = space_getsid(s->space, &t->ci, &t->cj, shift);
-              }
-      }
-    } /* pair interaction? */
-  }   /* iterate over the current task. */
-}
-
 /**
  * @brief Split a gravity task if too large.
  *
@@ -1670,8 +1236,6 @@ void scheduler_splittasks_mapper(void *map_data, int num_elements,
       scheduler_splittask_gravity(t, s);
     } else if (t->type == task_type_grav_mesh) {
       /* For future use */
-    } else if (t->subtype == task_subtype_stars_density) {
-      scheduler_splittask_stars(t, s);
     } else {
 #ifdef SWIFT_DEBUG_CHECKS
       error("Unexpected task sub-type");
@@ -2071,8 +1635,11 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
       case task_type_grav_mm:
         cost = wscale * (gcount_i + gcount_j);
         break;
-      case task_type_end_force:
-        cost = wscale * count_i + wscale * gcount_i;
+      case task_type_end_hydro_force:
+        cost = wscale * count_i;
+        break;
+      case task_type_end_grav_force:
+        cost = wscale * gcount_i;
         break;
       case task_type_kick1:
         cost = wscale * count_i + wscale * gcount_i;
@@ -2286,9 +1853,6 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
           err = MPI_Irecv(t->ci->hydro.parts, t->ci->hydro.count, part_mpi_type,
                           t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype],
                           &t->req);
-          // message( "receiving %i parts with tag=%i from %i to %i." ,
-          //     t->ci->hydro.count , t->flags , t->ci->nodeID , s->nodeID );
-          // fflush(stdout);
         } else if (t->subtype == task_subtype_gpart) {
           err = MPI_Irecv(t->ci->grav.parts, t->ci->grav.count, gpart_mpi_type,
                           t->ci->nodeID, t->flags, subtaskMPI_comms[t->subtype],
@@ -2342,9 +1906,6 @@ void scheduler_enqueue(struct scheduler *s, struct task *t) {
             err = MPI_Issend(t->ci->hydro.parts, t->ci->hydro.count,
                              part_mpi_type, t->cj->nodeID, t->flags,
                              subtaskMPI_comms[t->subtype], &t->req);
-          // message( "sending %i parts with tag=%i from %i to %i." ,
-          //     t->ci->hydro.count , t->flags , s->nodeID , t->cj->nodeID );
-          // fflush(stdout);
         } else if (t->subtype == task_subtype_gpart) {
           if ((t->ci->grav.count * sizeof(struct gpart)) > s->mpi_message_limit)
             err = MPI_Isend(t->ci->grav.parts, t->ci->grav.count,
diff --git a/src/scheduler.h b/src/scheduler.h
index f1e130c6ce2a8538b0126e86ee0cbd79cf5a0e0d..3fb1c2e43bddd73a08edd40c9c2969ea20ee6d39 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -120,6 +120,7 @@ struct scheduler {
  */
 __attribute__((always_inline)) INLINE static void scheduler_activate(
     struct scheduler *s, struct task *t) {
+
   if (atomic_cas(&t->skip, 1, 0)) {
     t->wait = 0;
     int ind = atomic_inc(&s->active_count);
@@ -143,7 +144,9 @@ scheduler_activate_send(struct scheduler *s, struct link *link, int nodeID) {
   struct link *l = NULL;
   for (l = link; l != NULL && l->t->cj->nodeID != nodeID; l = l->next)
     ;
-  if (l == NULL) error("Missing link to send task.");
+  if (l == NULL) {
+    error("Missing link to send task.");
+  }
   scheduler_activate(s, l->t);
   return l;
 }
diff --git a/src/space.c b/src/space.c
index 481f3eea43451ad64a47f16510de26164ac0959a..33e88a0b679b6b1500e5ce5ac657348843f46964 100644
--- a/src/space.c
+++ b/src/space.c
@@ -68,8 +68,6 @@ int space_subsize_pair_hydro = space_subsize_pair_hydro_default;
 int space_subsize_self_hydro = space_subsize_self_hydro_default;
 int space_subsize_pair_grav = space_subsize_pair_grav_default;
 int space_subsize_self_grav = space_subsize_self_grav_default;
-int space_subsize_pair_stars = space_subsize_pair_stars_default;
-int space_subsize_self_stars = space_subsize_self_stars_default;
 int space_subdepth_diff_grav = space_subdepth_diff_grav_default;
 int space_maxsize = space_maxsize_default;
 
@@ -217,8 +215,6 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->hydro.ghost_in = NULL;
     c->hydro.ghost_out = NULL;
     c->hydro.ghost = NULL;
-    c->stars.ghost_in = NULL;
-    c->stars.ghost_out = NULL;
     c->stars.ghost = NULL;
     c->stars.density = NULL;
     c->stars.feedback = NULL;
@@ -226,8 +222,11 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->kick2 = NULL;
     c->timestep = NULL;
     c->timestep_limiter = NULL;
-    c->end_force = NULL;
+    c->hydro.end_force = NULL;
     c->hydro.drift = NULL;
+    c->stars.drift = NULL;
+    c->stars.stars_in = NULL;
+    c->stars.stars_out = NULL;
     c->grav.drift = NULL;
     c->grav.drift_out = NULL;
     c->hydro.cooling = NULL;
@@ -235,6 +234,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->grav.down_in = NULL;
     c->grav.down = NULL;
     c->grav.mesh = NULL;
+    c->grav.end_force = NULL;
     c->super = c;
     c->hydro.super = c;
     c->grav.super = c;
@@ -244,8 +244,9 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->stars.parts = NULL;
     c->hydro.do_sub_sort = 0;
     c->stars.do_sub_sort = 0;
-    c->grav.do_sub_drift = 0;
     c->hydro.do_sub_drift = 0;
+    c->grav.do_sub_drift = 0;
+    c->stars.do_sub_drift = 0;
     c->hydro.do_sub_limiter = 0;
     c->hydro.do_limiter = 0;
     c->hydro.ti_end_min = -1;
@@ -253,6 +254,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->grav.ti_end_min = -1;
     c->grav.ti_end_max = -1;
     c->stars.ti_end_min = -1;
+    c->stars.ti_end_max = -1;
 #ifdef SWIFT_DEBUG_CHECKS
     c->cellID = 0;
 #endif
@@ -275,6 +277,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->mpi.hydro.recv_rho = NULL;
     c->mpi.hydro.recv_gradient = NULL;
     c->mpi.grav.recv = NULL;
+    c->mpi.stars.recv = NULL;
     c->mpi.recv_ti = NULL;
     c->mpi.limiter.recv = NULL;
 
@@ -282,6 +285,7 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->mpi.hydro.send_rho = NULL;
     c->mpi.hydro.send_gradient = NULL;
     c->mpi.grav.send = NULL;
+    c->mpi.stars.send = NULL;
     c->mpi.send_ti = NULL;
     c->mpi.limiter.send = NULL;
 #endif
@@ -540,6 +544,7 @@ void space_regrid(struct space *s, int verbose) {
           c->grav.super = c;
           c->hydro.ti_old_part = ti_current;
           c->grav.ti_old_part = ti_current;
+          c->stars.ti_old_part = ti_current;
           c->grav.ti_old_multipole = ti_current;
 #ifdef WITH_MPI
           c->mpi.tag = -1;
@@ -549,6 +554,8 @@ void space_regrid(struct space *s, int verbose) {
           c->mpi.hydro.send_xv = NULL;
           c->mpi.hydro.send_rho = NULL;
           c->mpi.hydro.send_gradient = NULL;
+          c->mpi.stars.send = NULL;
+          c->mpi.stars.recv = NULL;
           c->mpi.grav.recv = NULL;
           c->mpi.grav.send = NULL;
 #endif  // WITH_MPI
@@ -1527,6 +1534,7 @@ void space_rebuild(struct space *s, int repartitioned, int verbose) {
     c->hydro.ti_old_part = ti_current;
     c->grav.ti_old_part = ti_current;
     c->grav.ti_old_multipole = ti_current;
+    c->stars.ti_old_part = ti_current;
 
 #ifdef SWIFT_DEBUG_CHECKS
     c->cellID = -last_cell_id;
@@ -2593,7 +2601,8 @@ void space_split_recursive(struct space *s, struct cell *c,
                 ti_hydro_beg_max = 0;
   integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_end_max = 0,
                 ti_gravity_beg_max = 0;
-  integertime_t ti_stars_end_min = max_nr_timesteps;
+  integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_end_max = 0,
+                ti_stars_beg_max = 0;
   struct part *parts = c->hydro.parts;
   struct gpart *gparts = c->grav.parts;
   struct spart *sparts = c->stars.parts;
@@ -2686,6 +2695,7 @@ void space_split_recursive(struct space *s, struct cell *c,
       cp->hydro.ti_old_part = c->hydro.ti_old_part;
       cp->grav.ti_old_part = c->grav.ti_old_part;
       cp->grav.ti_old_multipole = c->grav.ti_old_multipole;
+      cp->stars.ti_old_part = c->stars.ti_old_part;
       cp->loc[0] = c->loc[0];
       cp->loc[1] = c->loc[1];
       cp->loc[2] = c->loc[2];
@@ -2713,6 +2723,7 @@ void space_split_recursive(struct space *s, struct cell *c,
       cp->stars.do_sub_sort = 0;
       cp->grav.do_sub_drift = 0;
       cp->hydro.do_sub_drift = 0;
+      cp->stars.do_sub_drift = 0;
       cp->hydro.do_sub_limiter = 0;
       cp->hydro.do_limiter = 0;
 #ifdef WITH_MPI
@@ -2763,6 +2774,8 @@ void space_split_recursive(struct space *s, struct cell *c,
         ti_gravity_end_max = max(ti_gravity_end_max, cp->grav.ti_end_max);
         ti_gravity_beg_max = max(ti_gravity_beg_max, cp->grav.ti_beg_max);
         ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min);
+        ti_stars_end_max = min(ti_stars_end_max, cp->stars.ti_end_max);
+        ti_stars_beg_max = min(ti_stars_beg_max, cp->stars.ti_beg_max);
 
         /* Increase the depth */
         if (cp->maxdepth > maxdepth) maxdepth = cp->maxdepth;
@@ -2991,6 +3004,8 @@ void space_split_recursive(struct space *s, struct cell *c,
   c->grav.ti_end_max = ti_gravity_end_max;
   c->grav.ti_beg_max = ti_gravity_beg_max;
   c->stars.ti_end_min = ti_stars_end_min;
+  c->stars.ti_end_max = ti_stars_end_max;
+  c->stars.ti_beg_max = ti_stars_beg_max;
   c->stars.h_max = stars_h_max;
   c->maxdepth = maxdepth;
 
@@ -3515,6 +3530,8 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count,
   const ptrdiff_t delta = sp - s->sparts;
 #endif
 
+  const float initial_h = s->initial_spart_h;
+
   const int with_feedback = (e->policy & engine_policy_feedback);
 
   const struct cosmology *cosmo = e->cosmology;
@@ -3527,6 +3544,11 @@ void space_first_init_sparts_mapper(void *restrict map_data, int count,
     sp[k].v[1] *= a_factor_vel;
     sp[k].v[2] *= a_factor_vel;
 
+    /* Imposed smoothing length from parameter file */
+    if (initial_h != -1.f) {
+      sp[k].h = initial_h;
+    }
+
 #ifdef HYDRO_DIMENSION_2D
     sp[k].x[2] = 0.f;
     sp[k].v[2] = 0.f;
@@ -3822,12 +3844,6 @@ void space_init(struct space *s, struct swift_params *params,
   space_subsize_self_grav =
       parser_get_opt_param_int(params, "Scheduler:cell_sub_size_self_grav",
                                space_subsize_self_grav_default);
-  space_subsize_pair_stars =
-      parser_get_opt_param_int(params, "Scheduler:cell_sub_size_pair_stars",
-                               space_subsize_pair_stars_default);
-  space_subsize_self_stars =
-      parser_get_opt_param_int(params, "Scheduler:cell_sub_size_self_stars",
-                               space_subsize_self_stars_default);
   space_splitsize = parser_get_opt_param_int(
       params, "Scheduler:cell_split_size", space_splitsize_default);
   space_subdepth_diff_grav =
@@ -3848,8 +3864,6 @@ void space_init(struct space *s, struct swift_params *params,
             space_subsize_pair_hydro, space_subsize_self_hydro);
     message("sub_size_pair_grav set to %d, sub_size_self_grav set to %d",
             space_subsize_pair_grav, space_subsize_self_grav);
-    message("sub_size_pair_stars set to %d, sub_size_self_stars set to %d",
-            space_subsize_pair_stars, space_subsize_self_stars);
   }
 
   /* Apply h scaling */
@@ -3860,6 +3874,13 @@ void space_init(struct space *s, struct swift_params *params,
     for (size_t k = 0; k < Npart; k++) parts[k].h *= scaling;
   }
 
+  /* Read in imposed star smoothing length */
+  s->initial_spart_h = parser_get_opt_param_float(
+      params, "InitialConditions:stars_smoothing_length", -1.f);
+  if (s->initial_spart_h != -1.f) {
+    message("Imposing a star smoothing length of %e", s->initial_spart_h);
+  }
+
   /* Apply shift */
   double shift[3] = {0.0, 0.0, 0.0};
   parser_get_opt_param_double_array(params, "InitialConditions:shift", 3,
@@ -4289,6 +4310,7 @@ void space_check_drift_point(struct space *s, integertime_t ti_drift,
   /* Recursively check all cells */
   space_map_cells_pre(s, 1, cell_check_part_drift_point, &ti_drift);
   space_map_cells_pre(s, 1, cell_check_gpart_drift_point, &ti_drift);
+  space_map_cells_pre(s, 1, cell_check_spart_drift_point, &ti_drift);
   if (multipole)
     space_map_cells_pre(s, 1, cell_check_multipole_drift_point, &ti_drift);
 #else
diff --git a/src/space.h b/src/space.h
index 98ab2523668c9789bb644f0ebe300cf73ef6f182..fe47a2b8b995e5872e79e755b5b8075a409795b8 100644
--- a/src/space.h
+++ b/src/space.h
@@ -53,8 +53,6 @@ struct cosmology;
 #define space_subsize_self_hydro_default 32000
 #define space_subsize_pair_grav_default 256000000
 #define space_subsize_self_grav_default 32000
-#define space_subsize_pair_stars_default 256000000
-#define space_subsize_self_stars_default 32000
 #define space_subdepth_diff_grav_default 4
 #define space_max_top_level_cells_default 12
 #define space_stretch 1.10f
@@ -226,6 +224,9 @@ struct space {
   /*! Sum of the norm of the velocity of all the #spart */
   float sum_spart_vel_norm;
 
+  /*! Initial value of the smoothing length read from the parameter file */
+  float initial_spart_h;
+
   /*! General-purpose lock for this space. */
   swift_lock_type lock;
 
diff --git a/src/stars/Default/stars.h b/src/stars/Default/stars.h
index ab4c6c7013d47a5731440bc953ad6a1101c7d2a4..586a87f75600a08acfd84b0f7ecc57fc4573281f 100644
--- a/src/stars/Default/stars.h
+++ b/src/stars/Default/stars.h
@@ -65,6 +65,26 @@ __attribute__((always_inline)) INLINE static void stars_init_spart(
   sp->density.wcount_dh = 0.f;
 }
 
+/**
+ * @brief Predict additional particle fields forward in time when drifting
+ *
+ * @param sp The particle
+ * @param dt_drift The drift time-step for positions.
+ */
+__attribute__((always_inline)) INLINE static void stars_predict_extra(
+    struct spart* restrict sp, float dt_drift) {
+
+  // MATTHIEU
+  /* const float h_inv = 1.f / sp->h; */
+
+  /* /\* Predict smoothing length *\/ */
+  /* const float w1 = sp->feedback.h_dt * h_inv * dt_drift; */
+  /* if (fabsf(w1) < 0.2f) */
+  /*   sp->h *= approx_expf(w1); /\* 4th order expansion of exp(w) *\/ */
+  /* else */
+  /*   sp->h *= expf(w1); */
+}
+
 /**
  * @brief Sets the values to be predicted in the drifts to their values at a
  * kick time
@@ -77,12 +97,13 @@ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values(
 /**
  * @brief Finishes the calculation of (non-gravity) forces acting on stars
  *
- * Multiplies the forces and accelerations by the appropiate constants
- *
  * @param sp The particle to act upon
  */
-__attribute__((always_inline)) INLINE static void stars_end_force(
-    struct spart* sp) {}
+__attribute__((always_inline)) INLINE static void stars_end_feedback(
+    struct spart* sp) {
+
+  sp->feedback.h_dt *= sp->h * hydro_dimension_inv;
+}
 
 /**
  * @brief Kick the additional variables
@@ -147,4 +168,25 @@ __attribute__((always_inline)) INLINE static void stars_evolve_spart(
     struct spart* restrict sp, const struct stars_props* stars_properties,
     const struct cosmology* cosmo) {}
 
+/**
+ * @brief Reset acceleration fields of a particle
+ *
+ * This is the equivalent of hydro_reset_acceleration.
+ * We do not compute the acceleration on star, therefore no need to use it.
+ *
+ * @param p The particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void stars_reset_feedback(
+    struct spart* restrict p) {
+
+  /* Reset time derivative */
+  p->feedback.h_dt = 0.f;
+
+#ifdef DEBUG_INTERACTIONS_STARS
+  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
+    p->ids_ngbs_force[i] = -1;
+  p->num_ngb_force = 0;
+#endif
+}
+
 #endif /* SWIFT_DEFAULT_STARS_H */
diff --git a/src/stars/Default/stars_iact.h b/src/stars/Default/stars_iact.h
index 9e27f86028245a230cfd777dfc46da7b7d2f3915..994ff98d01920124081598d7e6758c929c6d0b2f 100644
--- a/src/stars/Default/stars_iact.h
+++ b/src/stars/Default/stars_iact.h
@@ -35,6 +35,8 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj,
   /* Update ngb counters */
   if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_STARS)
     si->ids_ngbs_density[si->num_ngb_density] = pj->id;
+
+  /* Update ngb counters */
   ++si->num_ngb_density;
 #endif
 }
@@ -54,4 +56,34 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj,
 __attribute__((always_inline)) INLINE static void
 runner_iact_nonsym_stars_feedback(float r2, const float *dx, float hi, float hj,
                                   struct spart *restrict si,
-                                  struct part *restrict pj, float a, float H) {}
+                                  struct part *restrict pj, float a, float H) {
+
+  const float mj = pj->mass;
+  const float rhoj = pj->rho;
+  const float r = sqrtf(r2);
+  const float ri = 1.f / r;
+
+  /* Get the kernel for hi. */
+  float hi_inv = 1.0f / hi;
+  float hid_inv = pow_dimension_plus_one(hi_inv); /* 1/h^(d+1) */
+  float xi = r * hi_inv;
+  float wi, wi_dx;
+  kernel_deval(xi, &wi, &wi_dx);
+  float wi_dr = hid_inv * wi_dx;
+
+  /* Compute dv dot r */
+  float dvdr = (si->v[0] - pj->v[0]) * dx[0] + (si->v[1] - pj->v[1]) * dx[1] +
+               (si->v[2] - pj->v[2]) * dx[2];
+
+  /* Get the time derivative for h. */
+  si->feedback.h_dt -= mj * dvdr * ri / rhoj * wi_dr;
+
+#ifdef DEBUG_INTERACTIONS_STARS
+  /* Update ngb counters */
+  if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_STARS)
+    si->ids_ngbs_force[si->num_ngb_force] = pj->id;
+
+  /* Update ngb counters */
+  ++si->num_ngb_force;
+#endif
+}
diff --git a/src/stars/Default/stars_io.h b/src/stars/Default/stars_io.h
index a6c2768f715e3dc6e870ee92e7d8a5e9458a5d11..26a42b8c0f80beb32695e2cb00716f283289663d 100644
--- a/src/stars/Default/stars_io.h
+++ b/src/stars/Default/stars_io.h
@@ -60,10 +60,10 @@ INLINE static void stars_write_particles(const struct spart *sparts,
                                          struct io_props *list,
                                          int *num_fields) {
 
-  /* Say how much we want to read */
+  /* Say how much we want to write */
   *num_fields = 5;
 
-  /* List what we want to read */
+  /* List what we want to write */
   list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH,
                                  sparts, x);
   list[1] =
@@ -74,6 +74,23 @@ INLINE static void stars_write_particles(const struct spart *sparts,
                                  sparts, id);
   list[4] = io_make_output_field("SmoothingLength", FLOAT, 1, UNIT_CONV_LENGTH,
                                  sparts, h);
+
+#ifdef DEBUG_INTERACTIONS_STARS
+
+  list += *num_fields;
+  *num_fields += 4;
+
+  list[0] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS,
+                                 sparts, num_ngb_density);
+  list[1] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS,
+                                 sparts, num_ngb_force);
+  list[2] = io_make_output_field("Ids_ngb_density", LONGLONG,
+                                 MAX_NUM_OF_NEIGHBOURS_STARS,
+                                 UNIT_CONV_NO_UNITS, sparts, ids_ngbs_density);
+  list[3] = io_make_output_field("Ids_ngb_force", LONGLONG,
+                                 MAX_NUM_OF_NEIGHBOURS_STARS,
+                                 UNIT_CONV_NO_UNITS, sparts, ids_ngbs_force);
+#endif
 }
 
 /**
@@ -108,9 +125,6 @@ INLINE static void stars_props_init(struct stars_props *sp,
       (pow_dimension(delta_eta) - pow_dimension(sp->eta_neighbours)) *
       kernel_norm;
 
-  /* Maximal smoothing length */
-  sp->h_max = parser_get_opt_param_float(params, "Stars:h_max", p->h_max);
-
   /* Number of iterations to converge h */
   sp->max_smoothing_iterations = parser_get_opt_param_int(
       params, "Stars:max_ghost_iterations", p->max_smoothing_iterations);
@@ -143,9 +157,6 @@ INLINE static void stars_props_print(const struct stars_props *sp) {
       "(max|dlog(h)/dt|=%f).",
       pow_dimension(expf(sp->log_max_h_change)), sp->log_max_h_change);
 
-  if (sp->h_max != FLT_MAX)
-    message("Maximal smoothing length allowed: %.4f", sp->h_max);
-
   message("Maximal iterations in ghost task set to %d",
           sp->max_smoothing_iterations);
 }
@@ -161,7 +172,6 @@ INLINE static void stars_props_print_snapshot(hid_t h_grpstars,
   io_write_attribute_f(h_grpstars, "Kernel eta", sp->eta_neighbours);
   io_write_attribute_f(h_grpstars, "Smoothing length tolerance",
                        sp->h_tolerance);
-  io_write_attribute_f(h_grpstars, "Maximal smoothing length", sp->h_max);
   io_write_attribute_f(h_grpstars, "Volume log(max(delta h))",
                        sp->log_max_h_change);
   io_write_attribute_f(h_grpstars, "Volume max change time-step",
diff --git a/src/stars/Default/stars_part.h b/src/stars/Default/stars_part.h
index eb253fd3c9f0a44efeafbfdd2b002f4b2694c4e3..bed2e14756ff2b2b83dbd1f5de821aae4ca7be51 100644
--- a/src/stars/Default/stars_part.h
+++ b/src/stars/Default/stars_part.h
@@ -61,6 +61,7 @@ struct spart {
   timebin_t time_bin;
 
   struct {
+
     /* Number of neighbours. */
     float wcount;
 
@@ -69,6 +70,13 @@ struct spart {
 
   } density;
 
+  struct {
+
+    /* Change in smoothing length over time. */
+    float h_dt;
+
+  } feedback;
+
   /*! Tracer structure */
   struct tracers_xpart_data tracers_data;
 
@@ -86,11 +94,17 @@ struct spart {
 #endif
 
 #ifdef DEBUG_INTERACTIONS_STARS
+  /*! Number of interactions in the density SELF and PAIR */
+  int num_ngb_density;
+
   /*! List of interacting particles in the density SELF and PAIR */
   long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS];
 
-  /*! Number of interactions in the density SELF and PAIR */
-  int num_ngb_density;
+  /*! Number of interactions in the force SELF and PAIR */
+  int num_ngb_force;
+
+  /*! List of interacting particles in the force SELF and PAIR */
+  long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_STARS];
 #endif
 
 } SWIFT_STRUCT_ALIGN;
@@ -112,9 +126,6 @@ struct stars_props {
   /*! Tolerance on neighbour number  (for info only)*/
   float delta_neighbours;
 
-  /*! Maximal smoothing length */
-  float h_max;
-
   /*! Maximal number of iterations to converge h */
   int max_smoothing_iterations;
 
diff --git a/src/stars/EAGLE/stars.h b/src/stars/EAGLE/stars.h
index 4acfef360cedf8993702d27a748a364288052410..ea63dd84453d7c02efc1232a2d96ef14af840b29 100644
--- a/src/stars/EAGLE/stars.h
+++ b/src/stars/EAGLE/stars.h
@@ -33,6 +33,25 @@ __attribute__((always_inline)) INLINE static float stars_compute_timestep(
   return FLT_MAX;
 }
 
+/**
+ * @brief Prepares a s-particle for its interactions
+ *
+ * @param sp The particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void stars_init_spart(
+    struct spart* sp) {
+
+#ifdef DEBUG_INTERACTIONS_STARS
+  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
+    sp->ids_ngbs_density[i] = -1;
+  sp->num_ngb_density = 0;
+#endif
+
+  sp->density.wcount = 0.f;
+  sp->density.wcount_dh = 0.f;
+  sp->rho_gas = 0.f;
+}
+
 /**
  * @brief Initialises the s-particles for the first time
  *
@@ -46,24 +65,29 @@ __attribute__((always_inline)) INLINE static void stars_first_init_spart(
 
   sp->time_bin = 0;
   sp->birth_density = -1.f;
+  sp->birth_time = -1.f;
+
+  stars_init_spart(sp);
 }
 
 /**
- * @brief Prepares a s-particle for its interactions
+ * @brief Predict additional particle fields forward in time when drifting
  *
- * @param sp The particle to act upon
+ * @param sp The particle
+ * @param dt_drift The drift time-step for positions.
  */
-__attribute__((always_inline)) INLINE static void stars_init_spart(
-    struct spart* sp) {
-
-#ifdef DEBUG_INTERACTIONS_STARS
-  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
-    sp->ids_ngbs_density[i] = -1;
-  sp->num_ngb_density = 0;
-#endif
-
-  sp->density.wcount = 0.f;
-  sp->density.wcount_dh = 0.f;
+__attribute__((always_inline)) INLINE static void stars_predict_extra(
+    struct spart* restrict sp, float dt_drift) {
+
+  // MATTHIEU
+  /* const float h_inv = 1.f / sp->h; */
+
+  /* /\* Predict smoothing length *\/ */
+  /* const float w1 = sp->feedback.h_dt * h_inv * dt_drift; */
+  /* if (fabsf(w1) < 0.2f) */
+  /*   sp->h *= approx_expf(w1); /\* 4th order expansion of exp(w) *\/ */
+  /* else */
+  /*   sp->h *= expf(w1); */
 }
 
 /**
@@ -82,8 +106,11 @@ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values(
  *
  * @param sp The particle to act upon
  */
-__attribute__((always_inline)) INLINE static void stars_end_force(
-    struct spart* sp) {}
+__attribute__((always_inline)) INLINE static void stars_end_feedback(
+    struct spart* sp) {
+
+  sp->feedback.h_dt *= sp->h * hydro_dimension_inv;
+}
 
 /**
  * @brief Kick the additional variables
@@ -110,6 +137,7 @@ __attribute__((always_inline)) INLINE static void stars_end_density(
   const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
 
   /* Finish the calculation by inserting the missing h-factors */
+  sp->rho_gas *= h_inv_dim;
   sp->density.wcount *= h_inv_dim;
   sp->density.wcount_dh *= h_inv_dim_plus_one;
 }
@@ -124,14 +152,10 @@ __attribute__((always_inline)) INLINE static void stars_end_density(
 __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours(
     struct spart* restrict sp, const struct cosmology* cosmo) {
 
-  /* Some smoothing length multiples. */
-  const float h = sp->h;
-  const float h_inv = 1.0f / h;                 /* 1/h */
-  const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
-
   /* Re-set problematic values */
-  sp->density.wcount = kernel_root * h_inv_dim;
+  sp->density.wcount = 0.f;
   sp->density.wcount_dh = 0.f;
+  sp->rho_gas = 0.f;
 }
 
 /**
@@ -148,4 +172,25 @@ __attribute__((always_inline)) INLINE static void stars_evolve_spart(
     struct spart* restrict sp, const struct stars_props* stars_properties,
     const struct cosmology* cosmo) {}
 
+/**
+ * @brief Reset acceleration fields of a particle
+ *
+ * This is the equivalent of hydro_reset_acceleration.
+ * We do not compute the acceleration on star, therefore no need to use it.
+ *
+ * @param p The particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void stars_reset_feedback(
+    struct spart* restrict p) {
+
+  /* Reset time derivative */
+  p->feedback.h_dt = 0.f;
+
+#ifdef DEBUG_INTERACTIONS_STARS
+  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
+    p->ids_ngbs_force[i] = -1;
+  p->num_ngb_force = 0;
+#endif
+}
+
 #endif /* SWIFT_EAGLE_STARS_H */
diff --git a/src/stars/EAGLE/stars_iact.h b/src/stars/EAGLE/stars_iact.h
index 9e27f86028245a230cfd777dfc46da7b7d2f3915..5c4d94c2060ec42633a5eec472d844ca919b56eb 100644
--- a/src/stars/EAGLE/stars_iact.h
+++ b/src/stars/EAGLE/stars_iact.h
@@ -16,6 +16,9 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj,
                                  const struct part *restrict pj, float a,
                                  float H) {
 
+  /* Get the gas mass. */
+  const float mj = pj->mass;
+
   float wi, wi_dx;
 
   /* Get r and 1/r. */
@@ -31,6 +34,9 @@ runner_iact_nonsym_stars_density(float r2, const float *dx, float hi, float hj,
   si->density.wcount += wi;
   si->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx);
 
+  /* Compute contribution to the density */
+  si->rho_gas += mj * wi;
+
 #ifdef DEBUG_INTERACTIONS_STARS
   /* Update ngb counters */
   if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_STARS)
diff --git a/src/stars/EAGLE/stars_io.h b/src/stars/EAGLE/stars_io.h
index 64fde1a779966cc072a750fab89c74c5329bc03b..d93b4bf7cf81c56bb65d7d5d8801f843a492ead0 100644
--- a/src/stars/EAGLE/stars_io.h
+++ b/src/stars/EAGLE/stars_io.h
@@ -62,7 +62,7 @@ INLINE static void stars_write_particles(const struct spart *sparts,
                                          int *num_fields) {
 
   /* Say how much we want to write */
-  *num_fields = 8;
+  *num_fields = 9;
 
   /* List what we want to write */
   list[0] = io_make_output_field("Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH,
@@ -77,10 +77,12 @@ INLINE static void stars_write_particles(const struct spart *sparts,
                                  sparts, h);
   list[5] = io_make_output_field("BirthDensity", FLOAT, 1, UNIT_CONV_DENSITY,
                                  sparts, birth_density);
-  list[6] = io_make_output_field("Initial_Masses", FLOAT, 1, UNIT_CONV_MASS,
+  list[6] = io_make_output_field("InitialMasses", FLOAT, 1, UNIT_CONV_MASS,
                                  sparts, mass_init);
-  list[7] = io_make_output_field("Birth_time", FLOAT, 1, UNIT_CONV_TIME, sparts,
+  list[7] = io_make_output_field("BirthTime", FLOAT, 1, UNIT_CONV_TIME, sparts,
                                  birth_time);
+  list[8] = io_make_output_field("GasDensity", FLOAT, 1, UNIT_CONV_DENSITY,
+                                 sparts, rho_gas);
 }
 
 /**
@@ -115,9 +117,6 @@ INLINE static void stars_props_init(struct stars_props *sp,
       (pow_dimension(delta_eta) - pow_dimension(sp->eta_neighbours)) *
       kernel_norm;
 
-  /* Maximal smoothing length */
-  sp->h_max = parser_get_opt_param_float(params, "Stars:h_max", p->h_max);
-
   /* Number of iterations to converge h */
   sp->max_smoothing_iterations = parser_get_opt_param_int(
       params, "Stars:max_ghost_iterations", p->max_smoothing_iterations);
@@ -153,9 +152,6 @@ INLINE static void stars_props_print(const struct stars_props *sp) {
       "(max|dlog(h)/dt|=%f).",
       pow_dimension(expf(sp->log_max_h_change)), sp->log_max_h_change);
 
-  if (sp->h_max != FLT_MAX)
-    message("Maximal smoothing length allowed: %.4f", sp->h_max);
-
   message("Maximal iterations in ghost task set to %d",
           sp->max_smoothing_iterations);
 }
@@ -171,7 +167,6 @@ INLINE static void stars_props_print_snapshot(hid_t h_grpstars,
   io_write_attribute_f(h_grpstars, "Kernel eta", sp->eta_neighbours);
   io_write_attribute_f(h_grpstars, "Smoothing length tolerance",
                        sp->h_tolerance);
-  io_write_attribute_f(h_grpstars, "Maximal smoothing length", sp->h_max);
   io_write_attribute_f(h_grpstars, "Volume log(max(delta h))",
                        sp->log_max_h_change);
   io_write_attribute_f(h_grpstars, "Volume max change time-step",
diff --git a/src/stars/EAGLE/stars_part.h b/src/stars/EAGLE/stars_part.h
index a18a55e959078201ada3c7589db92e5b4946ad25..664b5c0d03dd5b762aab0a0f582f22312b2196c6 100644
--- a/src/stars/EAGLE/stars_part.h
+++ b/src/stars/EAGLE/stars_part.h
@@ -58,13 +58,17 @@ struct spart {
   /*! Initial star mass */
   float mass_init;
 
-  /* Particle cutoff radius. */
+  /*! Particle smoothing length. */
   float h;
 
+  /*! Density of the gas surrounding the star. */
+  float rho_gas;
+
   /*! Particle time bin */
   timebin_t time_bin;
 
   struct {
+
     /* Number of neighbours. */
     float wcount;
 
@@ -73,8 +77,16 @@ struct spart {
 
   } density;
 
+  struct {
+
+    /* Change in smoothing length over time. */
+    float h_dt;
+
+  } feedback;
+
   /*! Union for the birth time and birht scale factor */
   union {
+
     /*! Birth time */
     float birth_time;
 
@@ -128,9 +140,6 @@ struct stars_props {
   /*! Tolerance on neighbour number  (for info only)*/
   float delta_neighbours;
 
-  /*! Maximal smoothing length */
-  float h_max;
-
   /*! Maximal number of iterations to converge h */
   int max_smoothing_iterations;
 
diff --git a/src/task.c b/src/task.c
index 69837548be104f33891f5bef97f8f5c499b62812..34c636b48ed6ff3fefdf1e7847a67ca56ea79c89 100644
--- a/src/task.c
+++ b/src/task.c
@@ -61,9 +61,10 @@ const char *taskID_names[task_type_count] = {"none",
                                              "ghost_out",
                                              "extra_ghost",
                                              "drift_part",
+                                             "drift_spart",
                                              "drift_gpart",
                                              "drift_gpart_out",
-                                             "end_force",
+                                             "end_hydro_force",
                                              "kick1",
                                              "kick2",
                                              "timestep",
@@ -75,9 +76,12 @@ const char *taskID_names[task_type_count] = {"none",
                                              "grav_down_in",
                                              "grav_down",
                                              "grav_mesh",
+                                             "grav_end_force",
                                              "cooling",
                                              "star_formation",
                                              "logger",
+                                             "stars_in",
+                                             "stars_out",
                                              "stars_ghost_in",
                                              "stars_ghost",
                                              "stars_ghost_out",
@@ -144,12 +148,14 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
     case task_type_extra_ghost:
     case task_type_timestep_limiter:
     case task_type_cooling:
+    case task_type_end_hydro_force:
       return task_action_part;
       break;
 
     case task_type_star_formation:
       return task_action_all;
 
+    case task_type_drift_spart:
     case task_type_stars_ghost:
     case task_type_stars_sort:
       return task_action_spart;
@@ -179,13 +185,15 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
           break;
 
         default:
-          error("Unknow task_action for task");
+#ifdef SWIFT_DEBUG_CHECKS
+          error("Unknown task_action for task %s/%s", taskID_names[t->type],
+                subtaskID_names[t->subtype]);
+#endif
           return task_action_none;
           break;
       }
       break;
 
-    case task_type_end_force:
     case task_type_kick1:
     case task_type_kick2:
     case task_type_logger:
@@ -198,8 +206,11 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
         return task_action_part;
       else if (t->ci->grav.count > 0)
         return task_action_gpart;
-      else
+      else {
+#ifdef SWIFT_DEBUG_CHECKS
         error("Task without particles");
+#endif
+      }
       break;
 
     case task_type_init_grav:
@@ -210,18 +221,25 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
 
     case task_type_drift_gpart:
     case task_type_grav_down:
+    case task_type_end_grav_force:
     case task_type_grav_mesh:
       return task_action_gpart;
       break;
 
     default:
-      error("Unknown task_action for task");
+#ifdef SWIFT_DEBUG_CHECKS
+      error("Unknown task_action for task %s/%s", taskID_names[t->type],
+            subtaskID_names[t->subtype]);
+#endif
       return task_action_none;
       break;
   }
 
-  /* Silence compiler warnings */
-  error("Unknown task_action for task");
+#ifdef SWIFT_DEBUG_CHECKS
+  error("Unknown task_action for task %s/%s", taskID_names[t->type],
+        subtaskID_names[t->subtype]);
+#endif
+  /* Silence compiler warnings. We should never get here. */
   return task_action_none;
 }
 
@@ -265,6 +283,8 @@ float task_overlap(const struct task *restrict ta,
     if (tb->ci != NULL) size_union += tb->ci->hydro.count;
     if (tb->cj != NULL) size_union += tb->cj->hydro.count;
 
+    if (size_union == 0) return 0.f;
+
     /* Compute the intersection of the cell data. */
     const size_t size_intersect = task_cell_overlap_part(ta->ci, tb->ci) +
                                   task_cell_overlap_part(ta->ci, tb->cj) +
@@ -284,6 +304,8 @@ float task_overlap(const struct task *restrict ta,
     if (tb->ci != NULL) size_union += tb->ci->grav.count;
     if (tb->cj != NULL) size_union += tb->cj->grav.count;
 
+    if (size_union == 0) return 0.f;
+
     /* Compute the intersection of the cell data. */
     const size_t size_intersect = task_cell_overlap_gpart(ta->ci, tb->ci) +
                                   task_cell_overlap_gpart(ta->ci, tb->cj) +
@@ -303,6 +325,8 @@ float task_overlap(const struct task *restrict ta,
     if (tb->ci != NULL) size_union += tb->ci->stars.count;
     if (tb->cj != NULL) size_union += tb->cj->stars.count;
 
+    if (size_union == 0) return 0.f;
+
     /* Compute the intersection of the cell data. */
     const size_t size_intersect = task_cell_overlap_spart(ta->ci, tb->ci) +
                                   task_cell_overlap_spart(ta->ci, tb->cj) +
@@ -330,7 +354,6 @@ void task_unlock(struct task *t) {
   /* Act based on task type. */
   switch (type) {
 
-    case task_type_end_force:
     case task_type_kick1:
     case task_type_kick2:
     case task_type_logger:
@@ -342,12 +365,14 @@ void task_unlock(struct task *t) {
     case task_type_drift_part:
     case task_type_sort:
     case task_type_ghost:
+    case task_type_end_hydro_force:
     case task_type_timestep_limiter:
       cell_unlocktree(ci);
       break;
 
     case task_type_drift_gpart:
     case task_type_grav_mesh:
+    case task_type_end_grav_force:
       cell_gunlocktree(ci);
       break;
 
@@ -453,7 +478,6 @@ int task_lock(struct task *t) {
 #endif
       break;
 
-    case task_type_end_force:
     case task_type_kick1:
     case task_type_kick2:
     case task_type_logger:
@@ -469,6 +493,7 @@ int task_lock(struct task *t) {
     case task_type_drift_part:
     case task_type_sort:
     case task_type_ghost:
+    case task_type_end_hydro_force:
     case task_type_timestep_limiter:
       if (ci->hydro.hold) return 0;
       if (cell_locktree(ci) != 0) return 0;
@@ -480,6 +505,7 @@ int task_lock(struct task *t) {
       break;
 
     case task_type_drift_gpart:
+    case task_type_end_grav_force:
     case task_type_grav_mesh:
       if (ci->grav.phold) return 0;
       if (cell_glocktree(ci) != 0) return 0;
@@ -655,7 +681,11 @@ void task_get_group_name(int type, int subtype, char *cluster) {
       strcpy(cluster, "Density");
       break;
     case task_subtype_gradient:
-      strcpy(cluster, "Gradient");
+      if (type == task_type_send || type == task_type_recv) {
+        strcpy(cluster, "None");
+      } else {
+        strcpy(cluster, "Gradient");
+      }
       break;
     case task_subtype_force:
       strcpy(cluster, "Force");
@@ -667,7 +697,10 @@ void task_get_group_name(int type, int subtype, char *cluster) {
       strcpy(cluster, "Timestep_limiter");
       break;
     case task_subtype_stars_density:
-      strcpy(cluster, "Stars");
+      strcpy(cluster, "StarsDensity");
+      break;
+    case task_subtype_stars_feedback:
+      strcpy(cluster, "StarsFeedback");
       break;
     default:
       strcpy(cluster, "None");
diff --git a/src/task.h b/src/task.h
index 35b46bd383221d767e313fa38d838777e61f0a99..704d1a5ef80f1208bce69d0acf7625fb36fa19e1 100644
--- a/src/task.h
+++ b/src/task.h
@@ -52,9 +52,10 @@ enum task_types {
   task_type_ghost_out, /* Implicit */
   task_type_extra_ghost,
   task_type_drift_part,
+  task_type_drift_spart,
   task_type_drift_gpart,
   task_type_drift_gpart_out, /* Implicit */
-  task_type_end_force,
+  task_type_end_hydro_force,
   task_type_kick1,
   task_type_kick2,
   task_type_timestep,
@@ -66,12 +67,15 @@ enum task_types {
   task_type_grav_down_in, /* Implicit */
   task_type_grav_down,
   task_type_grav_mesh,
+  task_type_end_grav_force,
   task_type_cooling,
   task_type_star_formation,
   task_type_logger,
-  task_type_stars_ghost_in,
+  task_type_stars_in,       /* Implicit */
+  task_type_stars_out,      /* Implicit */
+  task_type_stars_ghost_in, /* Implicit */
   task_type_stars_ghost,
-  task_type_stars_ghost_out,
+  task_type_stars_ghost_out, /* Implicit */
   task_type_stars_sort,
   task_type_count
 } __attribute__((packed));
diff --git a/src/timestep.h b/src/timestep.h
index e9943a41a0536b65944f0256c827d43386aadd88..b98ce06d5e69a2e2b5cb8503322c025dc69f92c7 100644
--- a/src/timestep.h
+++ b/src/timestep.h
@@ -201,8 +201,14 @@ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep(
     new_dt_self = gravity_compute_timestep_self(
         sp->gpart, a_hydro, e->gravity_properties, e->cosmology);
 
+  /* Limit change in smoothing length */
+  const float dt_h_change = (sp->feedback.h_dt != 0.0f)
+                                ? fabsf(e->stars_properties->log_max_h_change *
+                                        sp->h / sp->feedback.h_dt)
+                                : FLT_MAX;
+
   /* Take the minimum of all */
-  float new_dt = min3(new_dt_stars, new_dt_self, new_dt_ext);
+  float new_dt = min4(new_dt_stars, new_dt_self, new_dt_ext, dt_h_change);
 
   /* Apply the maximal displacement constraint (FLT_MAX  if non-cosmological)*/
   new_dt = min(new_dt, e->dt_max_RMS_displacement);
@@ -212,9 +218,10 @@ __attribute__((always_inline)) INLINE static integertime_t get_spart_timestep(
 
   /* Limit timestep within the allowed range */
   new_dt = min(new_dt, e->dt_max);
-  if (new_dt < e->dt_min)
+  if (new_dt < e->dt_min) {
     error("spart (id=%lld) wants a time-step (%e) below dt_min (%e)", sp->id,
           new_dt, e->dt_min);
+  }
 
   /* Convert to integer time */
   const integertime_t new_dti = make_integer_timestep(
diff --git a/tests/test125cells.c b/tests/test125cells.c
index 3b49e71163d05afdc0e33a33306121dc48a22283..7fc743725a58ed82c815dc97b3a7b89351c3f2c0 100644
--- a/tests/test125cells.c
+++ b/tests/test125cells.c
@@ -809,7 +809,7 @@ int main(int argc, char *argv[]) {
     timings[26] += getticks() - self_tic;
 
     /* Finally, give a gentle kick */
-    runner_do_end_force(&runner, main_cell, 0);
+    runner_do_end_hydro_force(&runner, main_cell, 0);
     const ticks toc = getticks();
     time += toc - tic;
 
@@ -960,7 +960,7 @@ int main(int argc, char *argv[]) {
   self_all_force(&runner, main_cell);
 
   /* Finally, give a gentle kick */
-  runner_do_end_force(&runner, main_cell, 0);
+  runner_do_end_hydro_force(&runner, main_cell, 0);
   // runner_do_kick2(&runner, main_cell, 0);
 
   const ticks toc = getticks();
diff --git a/tests/test27cellsStars.c b/tests/test27cellsStars.c
index 0377fc49edfedc8b1d9ce0630821622117187c9b..d94de68932a4c6e1f7381fd14105ce540be2de50 100644
--- a/tests/test27cellsStars.c
+++ b/tests/test27cellsStars.c
@@ -382,7 +382,6 @@ int main(int argc, char *argv[]) {
   struct stars_props stars_p;
   stars_p.eta_neighbours = h;
   stars_p.h_tolerance = 1e0;
-  stars_p.h_max = FLT_MAX;
   stars_p.max_smoothing_iterations = 1;
 
   struct engine engine;
diff --git a/tests/testRandom.c b/tests/testRandom.c
index 4ac3230705f7c119ce2a4868d2d131375ff20858..1b2a6a5d480be23998a4278443b095eed1fc9755 100644
--- a/tests/testRandom.c
+++ b/tests/testRandom.c
@@ -1,6 +1,7 @@
 /*******************************************************************************
  * This file is part of SWIFT.
  * Copyright (C) 2019 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ *               2019 Folkert Nobels    (nobels@strw.leidenuniv.nl)
  *
  * 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
@@ -25,6 +26,63 @@
 /* Local headers. */
 #include "swift.h"
 
+/**
+ * @brief Compute the Pearson correlation coefficient for two sets of numbers
+ *
+ * The pearson correlation coefficient between two sets of numbers can be
+ * calculated as:
+ *
+ *           <x*y> - <x>*<y>
+ * r_xy = ----------------------
+ *         (var(x) * var(y))^.5
+ *
+ * In the case that both sets are purely uncorrelated the value of the
+ * Pearson correlation function is expected to be close to 0. In the case that
+ * there is positive correlation r_xy > 0 and in the case of negative
+ * correlation, the function has r_xy < 0.
+ *
+ * @param mean1 average of first series of numbers
+ * @param mean2 average of second series of numbers
+ * @param total12 sum of x_i * y_i of both series of numbers
+ * @param var1 variance of the first series of numbers
+ * @param var2 variance of the second series of numbers
+ * @param number of elements in both series
+ * @return the Pearson correlation coefficient
+ * */
+double pearsonfunc(double mean1, double mean2, double total12, double var1,
+                   double var2, int counter) {
+
+  const double mean12 = total12 / (double)counter;
+  const double correlation = (mean12 - mean1 * mean2) / sqrt(var1 * var2);
+  return fabs(correlation);
+}
+
+/**
+ * @brief Test to check that the pseodo-random numbers in SWIFT are random
+ * enough for our purpose.
+ *
+ * The test initializes with the current time and than creates 20 ID numbers
+ * it runs the test using these 20 ID numbers. Using these 20 ID numbers it
+ * Checks 4 different things:
+ * 1. The mean and variance are correct for random numbers generated by this
+ *    ID number.
+ * 2. The random numbers from this ID number do not cause correlation in time.
+ *    Correlation is checked using the Pearson correlation coefficient which
+ *    should be sufficiently close to zero.
+ * 3. A small offset in ID number of 2, doesn't cause correlation between
+ *    the two sets of random numbers (again with the Pearson correlation
+ *    coefficient) and the mean and variance of this set is
+ *    also correct.
+ * 4. Different physical processes in random.h are also uncorrelated and
+ *    produce the correct mean and variance as expected. Again the correlation
+ *    is calculated using the Pearson correlation coefficient.
+ *
+ * More information about the Pearson correlation coefficient can be found in
+ * the function pearsonfunc above this function.
+ *
+ * @param none
+ * @return 0 if everything is fine, 1 if random numbers are not random enough.
+ */
 int main(int argc, char* argv[]) {
 
   /* Initialize CPU frequency, this also starts time. */
@@ -49,12 +107,39 @@ int main(int argc, char* argv[]) {
 
     const long long id = rand() * (1LL << 31) + rand();
     const integertime_t increment = (1LL << time_bin);
+    const long long idoffset = id + 2;
 
     message("Testing id=%lld time_bin=%d", id, time_bin);
 
     double total = 0., total2 = 0.;
     int count = 0;
 
+    /* Pearson correlation variables for different times */
+    double sum_previous_current = 0.;
+    double previous = 0.;
+
+    /* Pearson correlation for two different IDs */
+    double pearsonIDs = 0.;
+    double totalID = 0.;
+    double total2ID = 0.;
+
+    /* Pearson correlation for different processes */
+    double pearson_star_sf = 0.;
+    double pearson_star_se = 0.;
+    double pearson_star_bh = 0.;
+    double pearson_sf_se = 0.;
+    double pearson_sf_bh = 0.;
+    double pearson_se_bh = 0.;
+
+    /* Calculate the mean and <x^2> for these processes */
+    double total_sf = 0.;
+    double total_se = 0.;
+    double total_bh = 0.;
+
+    double total2_sf = 0.;
+    double total2_se = 0.;
+    double total2_bh = 0.;
+
     /* Check that the numbers are uniform over the full-range of useful
      * time-steps */
     for (integertime_t ti_current = 0LL; ti_current < max_nr_timesteps;
@@ -68,18 +153,151 @@ int main(int argc, char* argv[]) {
       total += r;
       total2 += r * r;
       count++;
+
+      /* Calculate for correlation between time.
+       * For this we use the pearson correlation of time i and i-1 */
+      sum_previous_current += r * previous;
+      previous = r;
+
+      /* Calculate if there is a correlation between different ids */
+      const double r_2ndid = random_unit_interval(idoffset, ti_current,
+                                                  random_number_star_formation);
+
+      /* Pearson correlation for small different IDs */
+      pearsonIDs += r * r_2ndid;
+      totalID += r_2ndid;
+      total2ID += r_2ndid * r_2ndid;
+
+      /* Calculate random numbers for the different processes and check
+       * that they are uncorrelated */
+
+      const double r_sf =
+          random_unit_interval(id, ti_current, random_number_stellar_feedback);
+
+      const double r_se = random_unit_interval(
+          id, ti_current, random_number_stellar_enrichment);
+
+      const double r_bh =
+          random_unit_interval(id, ti_current, random_number_BH_feedback);
+
+      /* Calculate the correlation between the different processes */
+      total_sf += r_sf;
+      total_se += r_se;
+      total_bh += r_bh;
+
+      total2_sf += r_sf * r_sf;
+      total2_se += r_se * r_se;
+      total2_bh += r_bh * r_bh;
+
+      pearson_star_sf += r * r_sf;
+      pearson_star_se += r * r_se;
+      pearson_star_bh += r * r_bh;
+      pearson_sf_se += r_sf * r_se;
+      pearson_sf_bh += r_sf * r_bh;
+      pearson_se_bh += r_se * r_bh;
     }
 
     const double mean = total / (double)count;
     const double var = total2 / (double)count - mean * mean;
 
+    /* Pearson correlation calculation for different times */
+    // const double mean_xy = sum_previous_current / ((double)count - 1.f);
+    // const double correlation = (mean_xy - mean * mean) / var;
+    const double correlation =
+        pearsonfunc(mean, mean, sum_previous_current, var, var, count - 1);
+
+    /* Mean for different IDs */
+    const double meanID = totalID / (double)count;
+    const double varID = total2ID / (double)count - meanID * meanID;
+
+    /* Pearson correlation between different IDs*/
+    const double correlationID =
+        pearsonfunc(mean, meanID, pearsonIDs, var, varID, count);
+
+    /* Mean and <x^2> for different processes */
+    const double mean_sf = total_sf / (double)count;
+    const double mean_se = total_se / (double)count;
+    const double mean_bh = total_bh / (double)count;
+
+    const double var_sf = total2_sf / (double)count - mean_sf * mean_sf;
+    const double var_se = total2_se / (double)count - mean_se * mean_se;
+    const double var_bh = total2_bh / (double)count - mean_bh * mean_bh;
+
+    /* Correlation between different processes */
+    const double corr_star_sf =
+        pearsonfunc(mean, mean_sf, pearson_star_sf, var, var_sf, count);
+    const double corr_star_se =
+        pearsonfunc(mean, mean_se, pearson_star_se, var, var_se, count);
+    const double corr_star_bh =
+        pearsonfunc(mean, mean_bh, pearson_star_bh, var, var_bh, count);
+    const double corr_sf_se =
+        pearsonfunc(mean_sf, mean_se, pearson_sf_se, var_sf, var_se, count);
+    const double corr_sf_bh =
+        pearsonfunc(mean_sf, mean_bh, pearson_sf_bh, var_sf, var_bh, count);
+    const double corr_se_bh =
+        pearsonfunc(mean_se, mean_bh, pearson_se_bh, var_se, var_bh, count);
+
     /* Verify that the mean and variance match the expected values for a uniform
      * distribution */
-    if ((fabs(mean - 0.5) / 0.5 > 1e-4) ||
-        (fabs(var - 1. / 12.) / (1. / 12.) > 1e-4)) {
+    const double tolmean = 2e-4;
+    const double tolvar = 1e-3;
+    const double tolcorr = 4e-4;
+
+    if ((fabs(mean - 0.5) / 0.5 > tolmean) ||
+        (fabs(var - 1. / 12.) / (1. / 12.) > tolvar) ||
+        (correlation > tolcorr) || (correlationID > tolcorr) ||
+        (fabs(meanID - 0.5) / 0.5 > tolmean) ||
+        (fabs(varID - 1. / 12.) / (1. / 12.) > tolvar) ||
+        (corr_star_sf > tolcorr) || (corr_star_se > tolcorr) ||
+        (corr_star_bh > tolcorr) || (corr_sf_se > tolcorr) ||
+        (corr_sf_bh > tolcorr) || (corr_se_bh > tolcorr) ||
+        (fabs(mean_sf - 0.5) / 0.5 > tolmean) ||
+        (fabs(mean_se - 0.5) / 0.5 > tolmean) ||
+        (fabs(mean_bh - 0.5) / 0.5 > tolmean) ||
+        (fabs(var_sf - 1. / 12.) / (1. / 12.) > tolvar) ||
+        (fabs(var_se - 1. / 12.) / (1. / 12.) > tolvar) ||
+        (fabs(var_bh - 1. / 12.) / (1. / 12.) > tolvar)) {
       message("Test failed!");
-      message("Result:    count=%d mean=%f var=%f", count, mean, var);
-      message("Expected:  count=%d mean=%f var=%f", count, 0.5f, 1. / 12.);
+      message("Global result:");
+      message("Result:    count=%d mean=%f var=%f, correlation=%f", count, mean,
+              var, correlation);
+      message("Expected:  count=%d mean=%f var=%f, correlation=%f", count, 0.5f,
+              1. / 12., 0.);
+      message("ID part");
+      message(
+          "Result:     count%d mean=%f var=%f"
+          " correlation=%f",
+          count, meanID, varID, correlationID);
+      message(
+          "Expected:   count%d mean=%f var=%f"
+          " correlation=%f",
+          count, .5f, 1. / 12., 0.);
+      message("Different physical processes:");
+      message(
+          "Means:    stars=%f stellar feedback=%f stellar "
+          " enrichement=%f black holes=%f",
+          mean, mean_sf, mean_se, mean_bh);
+      message(
+          "Expected: stars=%f stellar feedback=%f stellar "
+          " enrichement=%f black holes=%f",
+          .5f, .5f, .5f, .5f);
+      message(
+          "Var:      stars=%f stellar feedback=%f stellar "
+          " enrichement=%f black holes=%f",
+          var, var_sf, var_se, var_bh);
+      message(
+          "Expected: stars=%f stellar feedback=%f stellar "
+          " enrichement=%f black holes=%f",
+          1. / 12., 1. / 12., 1 / 12., 1. / 12.);
+      message(
+          "Correlation: stars-sf=%f stars-se=%f stars-bh=%f"
+          "sf-se=%f sf-bh=%f se-bh=%f",
+          corr_star_sf, corr_star_se, corr_star_bh, corr_sf_se, corr_sf_bh,
+          corr_se_bh);
+      message(
+          "Expected:    stars-sf=%f stars-se=%f stars-bh=%f"
+          "sf-se=%f sf-bh=%f se-bh=%f",
+          0., 0., 0., 0., 0., 0.);
       return 1;
     }
   }
diff --git a/tools/plot_task_dependencies.py b/tools/plot_task_dependencies.py
index fd7c8f8f7c4165f38947828f64a7f3fd26f41ee5..14ba7c99f621c0c62c3c9cb8e8d3b0c78e491401 100644
--- a/tools/plot_task_dependencies.py
+++ b/tools/plot_task_dependencies.py
@@ -167,6 +167,8 @@ def taskIsHydro(name):
         return True
     if "rho" in name:
         return True
+    if "gradient" in name:
+        return True
     if "force" in name:
         return True
     if "xv" in name:
@@ -177,6 +179,9 @@ def taskIsHydro(name):
         "ghost_in",
         "ghost",
         "ghost_out",
+        "extra_ghost",
+        "cooling",
+        "star_formation"
     ]
     if name in task_name:
         return True
diff --git a/tools/task_plots/analyse_tasks.py b/tools/task_plots/analyse_tasks.py
index e897424a95be8937073bd16adf108fa4fa1456ad..fc9df0e4797cfb16e883df551af30dc0d3244edc 100755
--- a/tools/task_plots/analyse_tasks.py
+++ b/tools/task_plots/analyse_tasks.py
@@ -76,9 +76,10 @@ TASKTYPES = [
     "ghost_out",
     "extra_ghost",
     "drift_part",
+    "drift_spart",
     "drift_gpart",
     "drift_gpart_out",
-    "end_force",
+    "hydro_end_force",
     "kick1",
     "kick2",
     "timestep",
@@ -90,9 +91,12 @@ TASKTYPES = [
     "grav_down_in",
     "grav_down",
     "grav_mesh",
+    "grav_end_force",
     "cooling",
     "star_formation",
     "logger",
+    "stars_in",
+    "stars_out",
     "stars_ghost_in",
     "stars_ghost",
     "stars_ghost_out",
diff --git a/tools/task_plots/plot_tasks.py b/tools/task_plots/plot_tasks.py
index 12fd4d241a268c9d45fd72f5cdda2727221ba94d..54f34b2f828895d894b84253e366173827c03158 100755
--- a/tools/task_plots/plot_tasks.py
+++ b/tools/task_plots/plot_tasks.py
@@ -161,9 +161,10 @@ TASKTYPES = [
     "ghost_out",
     "extra_ghost",
     "drift_part",
+    "drift_spart",
     "drift_gpart",
     "drift_gpart_out",
-    "end_force",
+    "hydro_end_force",
     "kick1",
     "kick2",
     "timestep",
@@ -175,9 +176,12 @@ TASKTYPES = [
     "grav_down_in",
     "grav_down",
     "grav_mesh",
+    "grav_end_force",
     "cooling",
     "star_formation",
     "logger",
+    "stars_in",
+    "stars_out",
     "stars_ghost_in",
     "stars_ghost",
     "stars_ghost_out",
@@ -232,6 +236,8 @@ FULLTYPES = [
     "send/tend",
     "recv/gpart",
     "send/gpart",
+    "recv/spart",
+    "send/spart",
     "self/stars_density",
     "pair/stars_density",
     "sub_self/stars_density",