diff --git a/.gitignore b/.gitignore
index 7164fcfecf16c83e7079d011a3be1f19d9ab605c..438ea6c0b497f4939c67e35f4d4046d7346e33e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,6 +69,10 @@ examples/Cooling/CoolingRates/cooling_output.dat
 examples/SubgridTests/StellarEvolution/StellarEvolutionSolution*
 examples/SubgridTests/CosmologicalStellarEvolution/StellarEvolutionSolution*
 examples/SmallCosmoVolume/SmallCosmoVolume_DM/power_spectra
+examples/SmallCosmoVolume/SmallCosmoVolume_cooling/snapshots/
+examples/SmallCosmoVolume/SmallCosmoVolume_hydro/snapshots/
+examples/SmallCosmoVolume/SmallCosmoVolume_cooling/CloudyData_UVB=HM2012.h5
+examples/SmallCosmoVolume/SmallCosmoVolume_cooling/CloudyData_UVB=HM2012_shielded.h5
 
 tests/testActivePair
 tests/testActivePair.sh
@@ -133,6 +137,7 @@ tests/testAtomic
 tests/testRandom
 tests/testRandomSpacing
 tests/testRandomPoisson
+tests/testRandomCone
 tests/testThreadpool
 tests/testParser
 tests/testFeedback
@@ -269,6 +274,9 @@ src/equation_of_state/planetary/*.txt
 *.out
 *.toc
 
+## Figures
+*.svg
+
 ## Intermediate documents:
 *.dvi
 *-converted-to.*
diff --git a/AUTHORS b/AUTHORS
index 07c7dd91c9f2b3ddcfcd517b4f4c08c6fad8d0c1..5520d77633752e7ea863766984e516a67cef5a87 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -24,4 +24,5 @@ Evgenii Chaikin		chaikin@strw.leidenuniv.nl
 Sylvia Ploeckinger	ploeckinger@lorentz.leidenuniv.nl
 Willem Elbers		willem.h.elbers@durham.ac.uk
 TK Chan			chantsangkeung@gmail.com
-Marcel van Daalen	daalen@strw.leidenuniv.nl
\ No newline at end of file
+Marcel van Daalen	daalen@strw.leidenuniv.nl
+Filip Husko 		filip.husko@durham.ac.uk
\ No newline at end of file
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 0000000000000000000000000000000000000000..74de823ecba2dddb5bd394359ab450cf9b486959
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,46 @@
+cff-version: 1.2.0
+title: >-
+  SWIFT: SPH With Inter-dependent Fine-grained
+  Tasking
+message: 'If you use this software, please cite it as below'
+type: software
+authors:
+  - given-names: Matthieu
+    family-names: Schaller
+    email: schaller@strw.leidenuniv.nl
+    affiliation: Leiden University
+    orcid: 'https://orcid.org/0000-0002-2395-4902'
+  - given-names: Gonnet
+    family-names: Pedro
+  - given-names: Peter
+    family-names: Draper
+  - given-names: Aidan
+    family-names: Chalk
+  - given-names: Bert
+    family-names: Vandenbroucke
+  - given-names: James
+    family-names: Willis
+  - given-names: Richard
+    family-names: Bower
+  - given-names: Josh
+    family-names: Borrow
+  - given-names: Loic
+    family-names: Hausammann
+  - given-names: Yves
+    family-names: Revaz
+  - given-names: Jacob
+    family-names: Kegerreis
+  - given-names: Mladen
+    family-names: Ivkovic
+  - given-names: Stuart
+    family-names: McAlpine
+  - given-names: Folkert
+    family-names: Nobels
+  - given-names: John
+    family-names: Helly
+  - given-names: Yannick
+    family-names: Bahé
+  - given-names: Willem
+    family-names: Elbers
+  - given-names: Filip
+    family-names: Husko
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f1e9e535da6003cdc6f736caa6769f9896817a71..6db6cfbdaacd8f68cbd505b07a3982e8409fb5f9 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,5 +1,5 @@
 The SWIFT source code is using a variation of the 'Google' formatting style. 
-The script 'format.sh' in the root directory applies the clang-format-10
+The script 'format.sh' in the root directory applies the clang-format-13
 tool with our style choices to all the SWIFT C source file. Please apply 
 the formatting script to the files before submitting a merge request.
 
diff --git a/COPYING.LESSER b/COPYING.LESSER
index 0927556b54440825c63fbf61b9e6179894a5547a..65c5ca88a67c30becee01c5a8816d964b03862f9 100644
--- a/COPYING.LESSER
+++ b/COPYING.LESSER
@@ -1,156 +1,164 @@
-### GNU LESSER GENERAL PUBLIC LICENSE
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
 
-Version 3, 29 June 2007
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
 
-Copyright (C) 2007 Free Software Foundation, Inc.
-<https://fsf.org/>
 
-Everyone is permitted to copy and distribute verbatim copies of this
-license document, but changing it is not allowed.
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
 
-This version of the GNU Lesser General Public License incorporates the
-terms and conditions of version 3 of the GNU General Public License,
-supplemented by the additional permissions listed below.
+  0. Additional Definitions.
 
-#### 0. Additional Definitions.
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
 
-As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the
-GNU General Public License.
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
 
-"The Library" refers to a covered work governed by this License, other
-than an Application or a Combined Work as defined below.
-
-An "Application" is any work that makes use of an interface provided
+  An "Application" is any work that makes use of an interface provided
 by the Library, but which is not otherwise based on the Library.
 Defining a subclass of a class defined by the Library is deemed a mode
 of using an interface provided by the Library.
 
-A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
 with which the Combined Work was made is also called the "Linked
 Version".
 
-The "Minimal Corresponding Source" for a Combined Work means the
+  The "Minimal Corresponding Source" for a Combined Work means the
 Corresponding Source for the Combined Work, excluding any source code
 for portions of the Combined Work that, considered in isolation, are
 based on the Application, and not on the Linked Version.
 
-The "Corresponding Application Code" for a Combined Work means the
+  The "Corresponding Application Code" for a Combined Work means the
 object code and/or source code for the Application, including any data
 and utility programs needed for reproducing the Combined Work from the
 Application, but excluding the System Libraries of the Combined Work.
 
-#### 1. Exception to Section 3 of the GNU GPL.
+  1. Exception to Section 3 of the GNU GPL.
 
-You may convey a covered work under sections 3 and 4 of this License
+  You may convey a covered work under sections 3 and 4 of this License
 without being bound by section 3 of the GNU GPL.
 
-#### 2. Conveying Modified Versions.
+  2. Conveying Modified Versions.
 
-If you modify a copy of the Library, and, in your modifications, a
+  If you modify a copy of the Library, and, in your modifications, a
 facility refers to a function or data to be supplied by an Application
 that uses the facility (other than as an argument passed when the
 facility is invoked), then you may convey a copy of the modified
 version:
 
--   a) under this License, provided that you make a good faith effort
-    to ensure that, in the event an Application does not supply the
-    function or data, the facility still operates, and performs
-    whatever part of its purpose remains meaningful, or
--   b) under the GNU GPL, with none of the additional permissions of
-    this License applicable to that copy.
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
 
-#### 3. Object Code Incorporating Material from Library Header Files.
+  3. Object Code Incorporating Material from Library Header Files.
 
-The object code form of an Application may incorporate material from a
-header file that is part of the Library. You may convey such object
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
 code under terms of your choice, provided that, if the incorporated
 material is not limited to numerical parameters, data structure
 layouts and accessors, or small macros, inline functions and templates
 (ten or fewer lines in length), you do both of the following:
 
--   a) Give prominent notice with each copy of the object code that
-    the Library is used in it and that the Library and its use are
-    covered by this License.
--   b) Accompany the object code with a copy of the GNU GPL and this
-    license document.
-
-#### 4. Combined Works.
-
-You may convey a Combined Work under terms of your choice that, taken
-together, effectively do not restrict modification of the portions of
-the Library contained in the Combined Work and reverse engineering for
-debugging such modifications, if you also do each of the following:
-
--   a) Give prominent notice with each copy of the Combined Work that
-    the Library is used in it and that the Library and its use are
-    covered by this License.
--   b) Accompany the Combined Work with a copy of the GNU GPL and this
-    license document.
--   c) For a Combined Work that displays copyright notices during
-    execution, include the copyright notice for the Library among
-    these notices, as well as a reference directing the user to the
-    copies of the GNU GPL and this license document.
--   d) Do one of the following:
-    -   0) Convey the Minimal Corresponding Source under the terms of
-        this License, and the Corresponding Application Code in a form
-        suitable for, and under terms that permit, the user to
-        recombine or relink the Application with a modified version of
-        the Linked Version to produce a modified Combined Work, in the
-        manner specified by section 6 of the GNU GPL for conveying
-        Corresponding Source.
-    -   1) Use a suitable shared library mechanism for linking with
-        the Library. A suitable mechanism is one that (a) uses at run
-        time a copy of the Library already present on the user's
-        computer system, and (b) will operate properly with a modified
-        version of the Library that is interface-compatible with the
-        Linked Version.
--   e) Provide Installation Information, but only if you would
-    otherwise be required to provide such information under section 6
-    of the GNU GPL, and only to the extent that such information is
-    necessary to install and execute a modified version of the
-    Combined Work produced by recombining or relinking the Application
-    with a modified version of the Linked Version. (If you use option
-    4d0, the Installation Information must accompany the Minimal
-    Corresponding Source and Corresponding Application Code. If you
-    use option 4d1, you must provide the Installation Information in
-    the manner specified by section 6 of the GNU GPL for conveying
-    Corresponding Source.)
-
-#### 5. Combined Libraries.
-
-You may place library facilities that are a work based on the Library
-side by side in a single library together with other library
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
 facilities that are not Applications and are not covered by this
 License, and convey such a combined library under terms of your
 choice, if you do both of the following:
 
--   a) Accompany the combined library with a copy of the same work
-    based on the Library, uncombined with any other library
-    facilities, conveyed under the terms of this License.
--   b) Give prominent notice with the combined library that part of it
-    is a work based on the Library, and explaining where to find the
-    accompanying uncombined form of the same work.
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
 
-#### 6. Revised Versions of the GNU Lesser General Public License.
+  6. Revised Versions of the GNU Lesser General Public License.
 
-The Free Software Foundation may publish revised and/or new versions
+  The Free Software Foundation may publish revised and/or new versions
 of the GNU Lesser General Public License from time to time. Such new
 versions will be similar in spirit to the present version, but may
 differ in detail to address new problems or concerns.
 
-Each version is given a distinguishing version number. If the Library
-as you received it specifies that a certain numbered version of the
-GNU Lesser General Public License "or any later version" applies to
-it, you have the option of following the terms and conditions either
-of that published version or of any later version published by the
-Free Software Foundation. If the Library as you received it does not
-specify a version number of the GNU Lesser General Public License, you
-may choose any version of the GNU Lesser General Public License ever
-published by the Free Software Foundation.
-
-If the Library as you received it specifies that a proxy can decide
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
 whether future versions of the GNU Lesser General Public License shall
 apply, that proxy's public statement of acceptance of any version is
 permanent authorization for you to choose that version for the
diff --git a/INSTALL.swift b/INSTALL.swift
index bfc815db107a935b470893b198bcf37607d84574..403dbc74069ad37474e4f238f4b9b3baf6efbf98 100644
--- a/INSTALL.swift
+++ b/INSTALL.swift
@@ -189,7 +189,7 @@ before you can build it.
                              ==================
 
 The SWIFT source code uses a variation of 'Google' style. The script
-'format.sh' in the root directory applies the clang-format-10 tool with our
+'format.sh' in the root directory applies the clang-format-13 tool with our
 style choices to all the SWIFT C source file. Please apply the formatting
 script to the files before submitting a merge request.
 
diff --git a/argparse/argparse.c b/argparse/argparse.c
index 5760e42fda065e865d4247c6e5a20b177d1741ad..483a8a84605c043b1f4082599e0378a072d34f20 100644
--- a/argparse/argparse.c
+++ b/argparse/argparse.c
@@ -7,8 +7,9 @@
  */
 #include "argparse.h"
 
-#include "config.h"
+#include <config.h>
 
+/* System includes */
 #include <assert.h>
 #include <errno.h>
 #include <stdio.h>
diff --git a/configure.ac b/configure.ac
index 7c27b614fbcd98156a773dcb57b408e840e2d7ee..672dcd5680708de4abaf436be283e4e429e011a9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -31,10 +31,13 @@ AC_CONFIG_MACRO_DIR([m4])
 : ${CFLAGS=""}
 
 # Generate header file.
-AM_CONFIG_HEADER(config.h)
+AC_CONFIG_HEADERS([config.h])
+
 
 # Find and test the compiler.
 AX_CHECK_ENABLE_DEBUG
+# Enable POSIX and platform extension preprocessor macros.
+AC_USE_SYSTEM_EXTENSIONS
 AC_PROG_CC
 AM_PROG_CC_C_O
 AC_OPENMP
@@ -157,9 +160,11 @@ AC_ARG_ENABLE([mpi],
     [enable_mpi="$enableval"],
     [enable_mpi="yes"]
 )
+# Use extra flags set by AC_PROG_CC as part of $CC. Currently undocumented as ac_cv_prog_cc_stdc, 
+# so could change.
 good_mpi="yes"
 if test "$enable_mpi" = "yes"; then
-    AX_MPI([CC="$MPICC" AC_DEFINE(HAVE_MPI, 1, [Define if you have the MPI library.]) ])
+    AX_MPI([CC="$MPICC $ac_cv_prog_cc_stdc" AC_DEFINE(HAVE_MPI, 1, [Define if you have the MPI library.]) ], [enable_mpi="no"])
     MPI_LIBRARY="Unknown MPI"
 
     # Various MPI implementations require additional libraries when also using
@@ -224,10 +229,13 @@ AC_ARG_VAR(MPIRUN, Path to the mpirun command if non-standard)
 # Add libtool support (now that CC is defined). Disable shared libraries by default.
 LT_INIT([disable-shared])
 
-# Need C99 and inline support.
-AC_PROG_CC_C99
+# Need C99 and inline support. Only for autoconfs to version 2.69.
+m4_version_prereq([2.71], [], [AC_PROG_CC_C99])
+
+# Need inline support.
 AC_C_INLINE
 
+
 # If debugging try to show inlined functions.
 if test "x$enable_debug" = "xyes"; then
    #  Show inlined functions.
@@ -411,6 +419,20 @@ elif test "$stars_density_checks" != "no"; then
    AC_DEFINE_UNQUOTED([SWIFT_STARS_DENSITY_CHECKS], [$enableval] ,[Enable stars density brute-force checks])
 fi
 
+# Check if ghost statistics are enabled
+AC_ARG_ENABLE([ghost-statistics],
+   [AS_HELP_STRING([--enable-ghost-statistics],
+     [Gather statistics about the ghost iterations for hydro, stars and black holes in N bins @<:@N@:>@]
+   )],
+   [ghost_stats="$enableval"],
+   [ghost_stats="no"]
+)
+if test "$ghost_stats" == "yes"; then
+   AC_MSG_ERROR(Need to specify the number of bins when using --enable-ghost-statistics!)
+elif test "$ghost_stats" != "no"; then
+   AC_DEFINE_UNQUOTED([SWIFT_GHOST_STATS], [$enableval] ,[Enable ghost statistics for hydro, stars and black holes])
+fi
+
 # Check whether we want to switch on glass making
 AC_ARG_ENABLE([glass-making],
    [AS_HELP_STRING([--enable-glass-making],
@@ -696,7 +718,8 @@ AC_ARG_ENABLE([mpi-mesh-gravity],
 # Autoconf stuff.
 AC_PROG_INSTALL
 AC_PROG_MAKE_SET
-AC_HEADER_STDC
+AC_PROG_EGREP
+
 
 # Check for the libraries we will need.
 AC_CHECK_LIB(m,sqrt,,AC_MSG_ERROR(something is wrong with the math library!))
@@ -1574,8 +1597,33 @@ AC_CHECK_HEADERS([altivec.h], [], [],
 ])
 
 # Check for timing functions needed by cycle.h.
-AC_HEADER_TIME
-AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h])
+AC_CHECK_HEADERS_ONCE([sys/time.h])
+if test $ac_cv_header_sys_time_h = yes; then
+  AC_DEFINE([TIME_WITH_SYS_TIME],[1],[Define to 1 if you can safely include both <sys/time.h>
+	     and <time.h>.  This macro is obsolete.])
+fi
+
+AC_CHECK_HEADERS([sys/time.h], [], [],
+[#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+])
+AC_CHECK_HEADERS([c_asm.h], [], [],
+[#ifdef HAVE_C_ASM_H
+# include <c_asm.h>
+#endif
+])
+AC_CHECK_HEADERS([intrinsics.h], [], [],
+[#ifdef HAVE_INTRINSICS_H
+# include <intrinsics.h>
+#endif
+])
+AC_CHECK_HEADERS([mach/mach_time.h], [], [],
+[#ifdef HAVE_MACH_MACH_TIME_H
+# include <mach/mach_time.h>
+#endif
+])
+
 AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t
 is defined in <sys/time.h>])],,
 [#if HAVE_SYS_TIME_H
@@ -1593,12 +1641,12 @@ AC_LINK_IFELSE([AC_LANG_PROGRAM(
 AC_MSG_RESULT($rtc_ok)
 
 # Special timers for the ARM v7 platforms (taken from FFTW-3 to match their cycle.h)
-AC_ARG_ENABLE(armv7a-cntvct, [AC_HELP_STRING([--enable-armv7a-cntvct],[enable the cycle counter on Armv7a via the CNTVCT register])], have_armv7acntvct=$enableval)
+AC_ARG_ENABLE(armv7a-cntvct, [AS_HELP_STRING([--enable-armv7a-cntvct],[enable the cycle counter on Armv7a via the CNTVCT register])], have_armv7acntvct=$enableval)
 if test "$have_armv7acntvct"x = "yes"x; then
 	AC_DEFINE(HAVE_ARMV7A_CNTVCT,1,[Define if you have enabled the CNTVCT cycle counter on ARMv7a])
 fi
 
-AC_ARG_ENABLE(armv7a-pmccntr, [AC_HELP_STRING([--enable-armv7a-pmccntr],[enable the cycle counter on Armv7a via the PMCCNTR register])], have_armv7apmccntr=$enableval)
+AC_ARG_ENABLE(armv7a-pmccntr, [AS_HELP_STRING([--enable-armv7a-pmccntr],[enable the cycle counter on Armv7a via the PMCCNTR register])], have_armv7apmccntr=$enableval)
 if test "$have_armv7apmccntr"x = "yes"x; then
 	AC_DEFINE(HAVE_ARMV7A_PMCCNTR,1,[Define if you have enabled the PMCCNTR cycle counter on ARMv7a])
 fi
@@ -1722,7 +1770,7 @@ AC_SUBST([SUNDIALS_INCS])
 # As an example for this, see the call to AC_ARG_WITH for cooling.
 AC_ARG_WITH([subgrid],
 	[AS_HELP_STRING([--with-subgrid=<subgrid>],
-		[Master switch for subgrid methods. Inexperienced user should start here. Options are: @<:@none, GEAR, QLA, QLA-EAGLE, EAGLE, EAGLE-XL default: none@:>@]
+		[Master switch for subgrid methods. Inexperienced user should start here. Options are: @<:@none, GEAR, QLA, QLA-EAGLE, EAGLE, EAGLE-XL, SPIN_JET_EAGLE default: none@:>@]
 	)],
 	[with_subgrid="$withval"],
 	[with_subgrid=none]
@@ -1809,6 +1857,19 @@ case "$with_subgrid" in
 	with_subgrid_extra_io=none
 	enable_fof=yes
    ;;
+   SPIN_JET_EAGLE)
+	with_subgrid_cooling=EAGLE
+	with_subgrid_chemistry=EAGLE
+	with_subgrid_tracers=EAGLE
+	with_subgrid_entropy_floor=EAGLE
+	with_subgrid_stars=EAGLE
+	with_subgrid_star_formation=EAGLE
+	with_subgrid_feedback=EAGLE
+	with_subgrid_black_holes=SPIN_JET
+	with_subgrid_sink=none
+	with_subgrid_extra_io=none
+	enable_fof=yes
+   ;;
    *)
       AC_MSG_ERROR([Unknown subgrid choice: $with_subgrid])
    ;;
@@ -2456,7 +2517,7 @@ AC_DEFINE_UNQUOTED([FEEDBACK_NR_RAYS_AGN], [$with_number_of_AGN_rays], [Number o
 # Black hole model.
 AC_ARG_WITH([black-holes],
    [AS_HELP_STRING([--with-black-holes=<model>],
-      [Black holes model to use @<:@none, EAGLE default: none@:>@]
+      [Black holes model to use @<:@none, EAGLE, SPIN_JET default: none@:>@]
    )],
    [with_black_holes="$withval"],
    [with_black_holes="none"]
@@ -2477,6 +2538,10 @@ case "$with_black_holes" in
    EAGLE)
       AC_DEFINE([BLACK_HOLES_EAGLE], [1], [EAGLE black hole model])
    ;;
+   SPIN_JET)
+      AC_DEFINE([BLACK_HOLES_SPIN_JET], [1], [Spin and jet black hole model])
+      with_black_holes="SPIN_JETS (Husko+22)"
+   ;;
    *)
       AC_MSG_ERROR([Unknown black-hole model: $with_black_holes])
    ;;
@@ -2947,6 +3012,7 @@ AC_MSG_RESULT([
    Boundary particles          : $boundary_particles
    Fixed boundary particles    : $fixed_boundary_particles
    Planetary fixed entropy     : $planetary_fixed_entropy
+   Ghost statistics            : $ghost_stats
 
    Zoom region support  : $with_zoom_region
    Continuous Sim. Data Stream : $with_csds
diff --git a/doc/RTD/source/AnalysisTools/index.rst b/doc/RTD/source/AnalysisTools/index.rst
index f86a79307228d8065a85d6c249758caabf703dc8..15922608cf93fd0deb04769272b8d23307d5b74b 100644
--- a/doc/RTD/source/AnalysisTools/index.rst
+++ b/doc/RTD/source/AnalysisTools/index.rst
@@ -235,3 +235,50 @@ The ``.dump<.rank>`` files once seen are deleted, so dumping can be done more
 than once. For a non-MPI run the file is simply called ``.dump``, note for MPI
 you need to create one file per rank, so ``.dump.0``, ``.dump.1`` and so on.
 
+
+Neighbour search statistics
+---------------------------
+
+One of the core algorithms in SWIFT is an iterative neighbour search 
+whereby we try to find an appropriate radius around a particle's 
+position so that the weighted sum over neighbouring particles within 
+that radius is equal to some target value. The most obvious example of 
+this iterative neighbour search is the SPH density loop, but various 
+sub-grid models employ a very similar iterative neighbour search. The 
+computational cost of this iterative search is significantly affected by 
+the number of iterations that is required, and it can therefore be 
+useful to analyse the progression of the iterative scheme in detail.
+
+When configured with ``--enable-ghost-statistics=X``, SWIFT will be 
+compiled with additional diagnostics that statistically track the number 
+of iterations required to find a converged answer. Here, ``X`` is a 
+fixed number of bins to use to collect the required statistics 
+(``ghost`` refers to the fact that the iterations take place inside the 
+ghost tasks). In practice, this means that every cell in the SWIFT tree 
+will be equipped with an additional ``struct`` containing three sets of 
+``X`` bins (one set for each iterative neighbour loop: hydro, stellar 
+feedback, AGN feedback). For each bin ``i``, we store the number of 
+particles that required updating during iteration ``i``, the number of 
+particles that could not find a single neighbouring particle, the 
+minimum and maximum smoothing length of all particles that required 
+updating, and the sum of all their search radii and all their search 
+radii squared. This allows us to calculate the upper and lower limits, 
+as well as the mean and standard deviation on the search radius for each 
+iteration and for each cell. Note that there could be more iterations 
+required than the number of bins ``X``; in this case the additional 
+iterations will be accumulated in the final bin. At the end of each time 
+step, a text file is produced (one per MPI rank) that contains the 
+information for all cells that had any relevant activity. This text file 
+is named ``ghost_stats_ssss_rrrr.txt``, where ``ssss`` is the step 
+counter for that time step and ``rrrr`` is the MPI rank.
+
+The script ``tools/plot_ghost_stats.py`` takes one or multiple 
+``ghost_stats.txt`` files and computes global statistics for all the 
+cells in those files. The script also takes the name of an output file 
+where it will save those statistics as a set of plots, and an optional 
+label that will be displayed as the title of the plots. Note that there 
+are no restrictions on the number of input files or how they relate; 
+different files could represent different MPI ranks, but also different 
+time steps or even different simulations (which would make little 
+sense). It is up to the user to make sure that the input is actually 
+relevant.
diff --git a/doc/RTD/source/ExternalPotentials/index.rst b/doc/RTD/source/ExternalPotentials/index.rst
index 0cdfefdeba1350c4cd17fca941540747c44d6bf8..0454da53ab5a591452bb68bc1860859ecd83dc26 100644
--- a/doc/RTD/source/ExternalPotentials/index.rst
+++ b/doc/RTD/source/ExternalPotentials/index.rst
@@ -150,22 +150,22 @@ The parameters of the model are:
 6. Hernquist potential (``hernquist``)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-A potential that is given by the Hernquist (1990)
-potential: 
+We can set up a potential as given by Hernquist (1990): 
 
-:math:`\Phi(r) = - \frac{G_{\rm N}M}{r+a}.`
+:math:`\Phi(r) = - \frac{G_{\rm N}M}{r+a},`
 
-The free parameters of the Hernquist potential are mass, scale length,
-and softening. The potential can be set at any position in the box. 
-The potential add an additional time step constrain that limits the 
-time step to a maximum of a specified fraction of the circular orbital 
+where :math:`M` is the total Hernquist mass and :math: `a` is the Hernquist-
+equivalent scale radius of the potential. The potential can be set at any 
+position in the box. It adds an additional time step constraint that limits
+the time step to a maximum of a specified fraction of the circular orbital 
 time at the current radius of the particle. The other criteria 
-(CFL, self-gravity, ...) are applied on top of this criterion.
+(CFL, self-gravity, ...) are applied on top of this criterion. For example, a
+fraction of 0.01 means that an orbital period would be resolved by 100 time steps.
 
-The Hernquist potential can be run in the most basic version, then only the 
-Hernquist mass, scale length, softening length and fraction of the 
-orbital time for the time step limit are used, the parameters of the
-model in this case are:
+In the most basic version, the Hernquist potential can be run by providing 
+only the Hernquist mass, scale length, softening length and fraction of the 
+orbital time for the time stepping. In this case the model parameters may 
+look something like:
 
 .. code:: YAML
 
@@ -173,20 +173,23 @@ model in this case are:
         useabspos:       0              # 0 -> positions based on centre, 1 -> absolute positions 
         position:        [0.,0.,0.]     # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units)
         mass:            1e12           # Mass of the Hernquist potential
-        scalelength:     2.0            # scale length a
-        timestep_mult:   0.01           # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration
+        scalelength:     2.0            # scale length a (internal units)
+        timestep_mult:   0.01           # Dimensionless pre-factor for the time-step condition, determines the fraction of the orbital time we use to do the time integration; fraction of 0.01 means we resolve an orbit with 100 timesteps
         epsilon:         0.2            # Softening size (internal units)
 
 Besides the basic version, it is also possible to run the Hernquist 
 potential for idealised disk galaxies that follow the approach of 
-Springel+ 2005. The default Hernquist potential uses a corrected value 
-for the formulation that improves the match with the NFW (below) with
-the same M200 (Nobels+ in prep). Contrary to above, the idealised disk 
-setup runs with a specified M200, concentration and reduced Hubble 
-constant that set both the mass and scale length parameter. The reduced
-Hubble constant is only used to determine R200. 
-
-The parameters of the model are:
+`Springel, Di Matteo & Hernquist (2005)
+<https://ui.adsabs.harvard.edu/abs/2005MNRAS.361..776S/abstract>`_. This 
+potential, however, uses a corrected value of the formulation that improves 
+the match with the NFW profile (below) with the same M200 (Nobels+ in prep). 
+Contrary to the above (idealizeddisk: 0 setup), the idealised disk setup runs 
+by specifying one out of :math:`M_{200}`, :math:`V_{200}`, or :math:`R_{200}`, 
+plus concentration and reduced Hubble constant.
+
+In this case, we don't provide the 'mass' and 'scalelength' parameters, but
+'M200' (or 'V200', or 'R200') and 'concentration' :math:`c`, as well as reduced Hubble
+constant :math:`h` to define the potential. The parameters of the model may look something like:
 
 .. code:: YAML
 
@@ -199,21 +202,52 @@ The parameters of the model are:
         concentration:   9.0            # concentration of the Halo
         diskfraction:    0.040          # Disk mass fraction
         bulgefraction:   0.0            # Bulge mass fraction
-        timestep_mult:   0.01           # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration
+        timestep_mult:   0.01           # Dimensionless pre-factor for the time-step condition, determines the fraction of the orbital time we use to do the time integration; fraction of 0.01 means we resolve an orbit with 100 timesteps
         epsilon:         0.2            # Softening size (internal units)
 
+The user should specify one out of 'M200', 'V200', or 'R200' to define
+the potential. The reduced Hubble constant is then used to determine the
+other two. Then, the scale radius is calculated as :math:`R_s = R_{200}/c`,
+where :math:`c` is the concentration, and the Hernquist-equivalent scale-length
+is calculated as:
+
+:math:`a = \frac{b+\sqrt{b}}{1-b} R_{200}`,
+
+where :math:`b = \frac{2}{c^2}(\ln(1+c) - \frac{c}{1+c})`.
+
+Two examples using the Hernquist potential can be found in ``swiftsim/examples/GravityTests/``. 
+The ``Hernquist_radialinfall`` example puts 5 particles with zero velocity in a Hernquist 
+potential, resulting in radial orbits. The ``Hernquist_circularorbit``example puts three
+particles on a circular orbit in a Hernquist potential, one at the inner region, one at the
+maximal circular velocity, and one in the outer region. To run these examples, SWIFT must
+be configured with the flag ``--with-ext-potential=hernquist``, or ``hernquist-sdmh05``
+(see below).
 
-7. Hernquist potential (``hernquist-sdmh05``)
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+7. Hernquist SDMH05 potential (``hernquist-sdmh05``)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 This is the same potential as Hernquist with the difference being the
 way that the idealised disk part is calculated. This potential uses
 exactly the same approach as `Springel, Di Matteo & Hernquist (2005)
 <https://ui.adsabs.harvard.edu/abs/2005MNRAS.361..776S/abstract>`_,
-this means that ICs generated with the original `MakeNewDisk` code can
-be used when using this potential. Contrary to the updated Hernquist
-potential (above) it is not possible to have an identically matched
-NFW potential.
+which means that ICs generated with the original `MakeNewDisk` code can
+be used with this potential. Contrary to the updated Hernquist
+potential (above), it is not possible to have an identically matched
+NFW potential in this case.
+
+The parameters needed for this potential are the same set of variables as 
+above, i.e. 'mass' and 'scalelength' when we don't use the idealised
+disk, and 'concentration' plus one out of 'M200', 'V200', or 'R200' if 
+we do. As one of the three is provided, the reduced Hubble constant is
+used to calculate the other two. Then, the scale radius is calculated
+using the NFW definition, :math:`R_s = R_{200}/c`, and the Hernquist-
+equivalent scale length is given by
+
+:math:`a = R_s \sqrt{2(\ln(1+c) - \frac{c}{1+c})}.`
+
+Runs that provide e.g. M200 and c (using idealised disk) are thus equivalent
+to providing mass and scale length if calculated by the above equation (without
+idealized disk). 
 
    
 8. Navarro-Frenk-White potential (``nfw``):
diff --git a/doc/RTD/source/ParameterFiles/lossy_filters.rst b/doc/RTD/source/ParameterFiles/lossy_filters.rst
index e4c0fe6a9f9de1f9057ad0da3a29aa4d23e7ec6b..61915e990d190db75dce5d69f1b88e7a2e9fa5f0 100644
--- a/doc/RTD/source/ParameterFiles/lossy_filters.rst
+++ b/doc/RTD/source/ParameterFiles/lossy_filters.rst
@@ -43,6 +43,8 @@ sufficient (The extra "+1" is for the sign bit).
 
 SWIFT implements 5 variants of this filter:
 
+ * ``Nbit32`` stores the 32 most significant bits (Numbers up to
+   :math:`2\times10^{9}`, comp. ratio: 2)
  * ``Nbit36`` stores the 36 most significant bits (Numbers up to
    :math:`3.4\times10^{10}`, comp. ratio: 1.78)
  * ``Nbit40`` stores the 40 most significant bits (Numbers up to
diff --git a/doc/RTD/source/ParameterFiles/parameter_description.rst b/doc/RTD/source/ParameterFiles/parameter_description.rst
index 6ab3a7e3c429bdcfacae7637d91a83d9a338302e..9af0ba21a5c318eb1126f0e9d7f6e19208f80b94 100644
--- a/doc/RTD/source/ParameterFiles/parameter_description.rst
+++ b/doc/RTD/source/ParameterFiles/parameter_description.rst
@@ -275,19 +275,32 @@ The time-step of a given particle is given by :math:`\Delta t =
 <http://adsabs.harvard.edu/abs/2003MNRAS.338...14P>`_ recommend using
 :math:`\eta=0.025`.
 
-The last tree-related parameters are:
+Two further parameters determine when the gravity tree is reconstructed:
 
 * The tree rebuild frequency: ``rebuild_frequency``.
+* The fraction of active particles to trigger a rebuild:
+  ``rebuild_active_fraction``.
+
+The tree rebuild frequency is an optional parameter defaulting to
+:math:`0.01`. It is used to trigger the re-construction of the tree every
+time a fraction of the particles have been integrated (kicked) forward in
+time. The second parameter is also optional and determines a separate rebuild
+criterion, based on the fraction of particles that is active at the
+beginning of a step. This can be seen as a forward-looking version of the
+first criterion, which can be useful for runs with very fast particles.
+The second criterion is not used for values :math:`>1`, which is the default
+assumption.
+
+
+The last tree-related parameters are:
+
 * Whether or not to use the approximate gravity from the FMM tree below the
   softening scale: ``use_tree_below_softening`` (default: 0)
 * Whether or not the truncated force estimator in the adaptive tree-walk
   considers the exponential mesh-related cut-off:
   ``allow_truncation_in_MAC`` (default: 0)
 
-The tree rebuild frequency is an optional parameter defaulting to
-:math:`0.01`. It is used to trigger the re-construction of the tree every
-time a fraction of the particles have been integrated (kicked) forward in
-time.  The other two parameters default to good all-around choices. See the
+These parameters default to good all-around choices. See the
 theory documentation about their exact effects.
 
 Simulations using periodic boundary conditions use additional parameters for the
diff --git a/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst b/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst
index 8f8e89889abc4253eaa7233a2934754e0e1fa33d..58dde33968340796caeabff81c97f7e598ed3e60 100644
--- a/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst
+++ b/doc/RTD/source/RadiativeTransfer/GEAR_RT.rst
@@ -22,7 +22,7 @@ Compiling for GEAR RT
 -   You need to choose a Riemann solver for the RT equations. You can choose
     between the ``GLF`` and ``HLL`` solver. For the time being, I recommend 
     sticking to the ``GLF`` solver as the ``HLL`` solver is more expensive,
-    but seemingly offers no advantage, although this remains to be comfirmed
+    but seemingly offers no advantage, although this remains to be confirmed
     in further testing.
 
 -   GEAR RT is only compatible with the Meshless Finite Volume scheme. You'll
@@ -30,7 +30,7 @@ Compiling for GEAR RT
     you to select a hydro Riemann solver, e.g ``--with-riemann-solver=hllc``.
 
 -   The thermochemistry requires the `grackle <https://github.com/grackle-project/grackle>`_ 
-    library. Grackle is a chemistry and cooling library presented in 
+    library version 3.2. Grackle is a chemistry and cooling library presented in 
     `B. Smith et al. 2017 <https://ui.adsabs.harvard.edu/abs/2017MNRAS.466.2217S>`_.
     Please note that the current implementation is not (yet) as
     advanced as the :ref:`GEAR subgrid model grackle cooling <gear_grackle_cooling>`, 
@@ -49,31 +49,42 @@ You need to provide the following runtime parameters in the yaml file:
 
    GEARRT:
        photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15]  # Photon frequency group bin edges in Hz
-       use_const_emission_rates: 1 
-       star_emission_rates_LSol: [1., 1., 1., 1.]         # stellar emission rates for each photon 
-                                                          # frequency bin in units of solar luminosity
+       stellar_spectrum_type: 0                           # Which radiation spectrum to use. 
+                                                          #   0: constant. 
+                                                          #   1: blackbody spectrum.
+       stellar_luminosity_model: const                    # Which luminosity model to use.
+       const_stellar_luminosities_LSol: [1., 1., 1.]      # stellar emission rates for each photon 
+                                                          #   frequency bin in units of solar luminosity
+                                                          #   for the 'const' luminosity model
        f_reduce_c: 1e-3                                   # reduce the speed of light by this factor
        CFL_condition: 0.9                                 # CFL condition for time integration
        hydrogen_mass_fraction:  0.76                      # total hydrogen (H + H+) mass fraction in the 
                                                           # metal-free portion of the gas
 
-       stellar_spectrum_type: 0                           # Which radiation spectrum to use. 0: constant. 1: blackbody spectrum.
 
-The ``photon_groups`` need to be ``N - 1`` frequency edges (floats) to separate 
-the spectrum into ``N`` groups. The outer limits of zero and infinity are 
-assumed.
+   TimeIntegration:
+       max_nr_rt_subcycles: 128         # maximal number of RT subcycles per hydro step
 
-At the moment, the only way to define star emission rates is to use constant
-star emission rates that need to be provided in the parameter file. The star 
-emission rates need to be defined for each photon frequency group individually.
-The first entry of the array is for the photon group with frequency 
-``[0, <first entry of photon_groups_Hz>)``. Each star particle will then emit
-the given energies, independent of their other properties.
 
-Furthermore, even though the parameter ``use_const_emission_rates`` is 
-intended to be optional in the future, **for now it needs to be set to 1.**, and
-it requires you to manually set the stellar emission rates via the
-``star_emission_rates_LSol`` parameter.
+The ``photon_groups_Hz`` need to be ``N`` frequency edges (floats) to separate 
+the spectrum into ``N`` groups, where ``N`` is the same number you configured
+with using ``--with_rt=GEAR_N``. The edges are **lower** edges of the bins, and
+need to be sorted in increasing order. The final upper edge is defined in a 
+different manner, and depends on the stellar spectrum type you assume (see below
+for more details).
+
+To specify the radiation emitted by stars, there are two main parameters:
+``stellar_luminosity_model`` defines which model to use to obtain star 
+luminosities, while ``stellar_spectrum_type`` determines the spectrum of the
+radiation.
+At the moment, the only way to define star emission rates is to use constant
+stellar luminosities by setting ``stellar_luminosity_model: const``. [#f3]_
+The constant star emission rates need to be provided in the parameter file and
+to be defined for each photon frequency group individually using the 
+``const_stellar_luminosities_LSol`` parameter. The luminosities are expected to
+be in units of solar luminosities. Each star particle will then emit the given 
+luminosities, independent of their other properties, e.g. the stellar age, 
+metallicity, redshift, etc.
 
 When solving the thermochemistry, we need to assume some form of stellar
 spectrum so we may integrate over frequency bins to obtain average interaction
@@ -81,14 +92,56 @@ rates. The parameter ``stellar_spectrum_type`` is hence required, and allows you
 to select between:
 
 - constant spectrum (``stellar_spectrum_type: 0``)
+    - Assume same energy density for any frequency.
     - This choice additionally requires you to provide a maximal frequency for
       the spectrum after which it'll be cut off via the 
       ``stellar_spectrum_const_max_frequency_Hz`` parameter
 
 - blackbody spectrum (``stellar_spectrum_type: 1``)
+    - Assume the spectrum is a blackbody spectrum
     - In this case, you need to provide also temperature of the blackbody via the 
       ``stellar_spectrum_blackbody_temperature_K`` parameter.
+    - The assumed maximal considered frequency :math:`\nu_{max}` for this spectrum 
+      is equal to 10 times :math:`\nu_{peak}`, the frequency at which the blackbody 
+      spectrum has its maximum, i.e.
+
+.. math::
+
+     \nu_{peak} = 2.82144 \times k_{B} \times T / h_{Planck}
+
+     \nu_{max} = 10 \times \nu_{peak}
+
+
+.. warning::
+   The ``stellar_spectrum_type`` parameter also determines the averaged photon 
+   interaction cross sections, as they are being computed by integrating a 
+   parametrization of the cross section multiplied by the assumed spectrum. See
+   e.g. equations 9 - 11 in `Rosdahl et al. 2013. 
+   <https://ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R/abstract>`_
+
+Finally, you will also need to provide an upper threshold for the number of 
+RT-subcycles w.r.t. a single hydro step via ``TimeIntegration:max_nr_rt_subcycles``.
+For more details, refer to :ref:`the subcycling documentation <rt_subcycling>`.
+
+
 
+Choice of Internal Units
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The choice of internal units requires a bit of special attention. Part of the 
+reason is that the exponents of the gas and radiation variables can quickly 
+change by several dozens and cause overflows and other errors. Furthermore, the 
+grackle library may have some other troubles with the units, e.g. when trying to
+find a converging solution. [#f2]_
+
+For this reason, I **strongly encourage** you to run the Internal Units check for 
+GEAR-RT which you can find in the 
+`swiftsim-rt-tools <https://github.com/SWIFTSIM/swiftsim-rt-tools/GEARRTUnitCheck>`_ 
+repository under ``/GEARRTUnitsCheck``. The test should take no more than a 
+minute to run, and requires only two yaml parameter files: the yaml parameter 
+file that you intend to run your simulation with, and one that a provided script 
+can extract automatically from the initial conditions hdf5 file. This test can 
+save you a lot of headaches down the line.
 
 
 
@@ -349,3 +402,19 @@ useful:
    The addition of the particle volume term for the radiation flux was made so
    that the initial conditions are compatible with the SPHM1RT conventions, and
    both methods can run on the exact same ICs.
+
+
+.. [#f2] For example, choosing cgs units as the internal units may lead to
+   trouble with grackle. (Trouble like a gas at 10^6K without any heating
+   sources heating up instead of cooling down.) The library is set up to work 
+   with units geared towards cosmology. According to Britton Smith (private comm), 
+   a decent rule of thumb is density_units ~ proton mass in g, time_units ~ 1 Myr 
+   to 1 Gyr in s, length_units ~ 1 kpc to 1 Mpc in cm. This should keep you in a 
+   relatively safe range.
+   This is the state of things at 08.2022, with grackle being at version 3.2 (commit
+   ``a089c837b8649c97b53ed3c51c84b1decf5073d8``)
+    
+.. [#f3] Technically there is also the model used for "Test 4" from the 
+   `I. Iliev et al. 2006 <https://ui.adsabs.harvard.edu/abs/2006MNRAS.369.1625I>`_ 
+   paper, but that is very specialized and shouldn't have much use in real 
+   applications.
diff --git a/doc/RTD/source/RadiativeTransfer/RTTaskDependencies.webp b/doc/RTD/source/RadiativeTransfer/RTTaskDependencies.webp
new file mode 100644
index 0000000000000000000000000000000000000000..b47231dcaf53bdbad0fe47b83a0f474e85dad396
Binary files /dev/null and b/doc/RTD/source/RadiativeTransfer/RTTaskDependencies.webp differ
diff --git a/doc/RTD/source/RadiativeTransfer/RTTaskDependenciesFull-simplified.png b/doc/RTD/source/RadiativeTransfer/RTTaskDependenciesFull-simplified.png
new file mode 100644
index 0000000000000000000000000000000000000000..349ab164179b4fe7c8ea240f881e9d821869647a
Binary files /dev/null and b/doc/RTD/source/RadiativeTransfer/RTTaskDependenciesFull-simplified.png differ
diff --git a/doc/RTD/source/RadiativeTransfer/RTWorkflow.png b/doc/RTD/source/RadiativeTransfer/RTWorkflow.png
new file mode 100644
index 0000000000000000000000000000000000000000..f3956de04d3313e46deff26901f0840255caa302
Binary files /dev/null and b/doc/RTD/source/RadiativeTransfer/RTWorkflow.png differ
diff --git a/doc/RTD/source/RadiativeTransfer/RT_notes_for_developers.rst b/doc/RTD/source/RadiativeTransfer/RT_notes_for_developers.rst
new file mode 100644
index 0000000000000000000000000000000000000000..286418d09d8976ba8900f312d0d4633114137a88
--- /dev/null
+++ b/doc/RTD/source/RadiativeTransfer/RT_notes_for_developers.rst
@@ -0,0 +1,490 @@
+.. RT developer notes
+    Mladen Ivkovic 07.2022
+
+.. _rt_dev:
+   
+Notes for Developers
+========================
+
+
+.. _rt_workflow:
+
+The Radiative Transfer Workflow
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This section is intended to give a superficial overview on the general workflow 
+that the RT tasks are going through. It should be sufficient to give you a general
+idea of how things work, and allow you to plan and implement your own RT scheme
+into SWIFT.
+
+For a more complete documentation on the tasking system used for RT, please refer
+to the :ref:`subsequent section <rt_task_system>`.
+
+.. figure:: RTWorkflow.png
+    :width: 400px
+    :align: center
+    :figclass: align-center
+    :alt: Workflow for the Radiative Transfer in SWIFT.
+
+    This figure shows the general RT workflow in SWIFT.
+    The green nodes are the ones related to the RT.
+
+
+- There are some additions to star tasks: 
+
+  1)    During the star density loop, we gather additional neighbour information
+        that are required for the star-gas interaction.
+
+  2)    In the star ghost tasks, stars compute how much radiation they will 
+        inject in the surrounding gas. The radiation injection scheme follows 
+        the philosophy of the stellar feedback, and only stars that are 
+        currently active will inject radiation into neighbouring gas particles, 
+        regardless of whether the gas particle is currently active (as opposed 
+        to active gas particles "pulling" radiation from possibly inactive 
+        stars). So the each star needs to compute how much radiation it will 
+        emit during its current time step.
+
+  3)    During the stellar feedback interaction loop, the stars inject radiation
+        onto neighbouring gas particles.
+
+- ``Ghost1`` tasks operate on individual particles, and are intended to finish up
+  any leftover work from the injection.
+
+- ``Gradient`` tasks are particle-particle interaction tasks, intended for 
+  particles to gather data from its own neighbours, e.g. so we can estimate the 
+  current local gradients.  This is an interaction of "type 1", meaning that any 
+  particle will only interact with neighbours which are within its own compact 
+  support radius.
+
+- ``Ghost2`` tasks operate on individual particles, and are intended to finish 
+  up any leftover work from the "gradients".
+
+- ``Transport`` tasks are particle-particle interaction tasks, intended to 
+  propagate the radiation. This is an interaction of "type 2", meaning that any 
+  particle will interact with any neighbours within either particles' compact 
+  support radius.
+
+- ``thermochemistry`` tasks operate on individual particles, and are intended
+  to solve the thermochemistry equations.
+
+
+
+
+
+
+
+.. _rt_task_system:
+
+Current Task System
+~~~~~~~~~~~~~~~~~~~~
+
+Some RT tasks featured in the full task graphs below, like the 
+``rt_advance_cell_time``, ``rt_collect_times``, and ``rt_sorts``, have not been 
+mentioned in the previous section. They are necessary for internal machinations 
+of the RT subcycling scheme, and do not affect the RT scheme itself. If you are
+implementing a new RT scheme into SWIFT, you should not need to touch those
+tasks. For more documentation on them, please refer to the :ref:`subsequent
+section <rt_subcycling_documentation>`.
+
+
+.. figure:: RTTaskDependenciesFull-simplified.png
+    :width: 400px
+    :align: center
+    :figclass: align-center
+    :alt: Task dependencies for the sink scheme.
+
+    This figure shows the task dependencies for the radiative transfer scheme.
+    Some tasks with little or no relevance to the RT scheme have been simplified
+    and condensed for clarity.
+    This was done with SWIFT v0.9.0.
+
+
+.. figure:: RTTaskDependencies.webp
+    :width: 400px
+    :align: center
+    :figclass: align-center
+    :alt: Task dependencies for the sink scheme.
+
+    This figure shows the full task dependencies for the radiative transfer scheme
+    with self-gravity.
+    This was done with SWIFT v0.9.0.
+
+
+
+
+
+
+.. _rt_subcycling_documentation:
+
+Notes on Subcycling
+~~~~~~~~~~~~~~~~~~~~~
+
+Note: This section is directed towards developers and maintainers, not
+necessarily towards users.
+
+How it works
+`````````````````
+
+A subcycle is basically a SWIFT time step where only radiative transfer is being
+run.
+
+After a normal SWIFT time step (i.e. after a call to ``engine_launch()`` and the
+global collection and communication) is complete, the starting time of the
+following global time step is known. We also collect the current minimal RT time 
+step size, which allows us to determine how many sub-cycles we need to complete
+before the next normal SWIFT time step is launched. Particles are not drifted 
+during a subcycle, and the propagation velocity (aka the speed of light) is 
+taken to be constant, so the number of subcycles is fixed at the end of a normal 
+step. For each subcycle, we then unskip the RT tasks, and make a new call to
+``engine_launch()``.
+
+For the time integration to work correctly, the time integration variables of
+particles like the time-bins are kept independently from the hydro ones. The same
+goes for the respective quantities of cells, like the next integer end time of
+the cell, or the minimal RT time step size in the cell. Furthermore, the global
+time variables that are stored in the engine (e.g. current integer time, current
+max active bin...) have a copy that is being kept up-to-date outside of normal
+SWIFT steps in the same manner the non-RT variables are being updated each
+normal step. The RT subcycling scheme never touches or changes any global time
+integration related variable.
+
+Since the time stepping variables of particles and cells are taken to be
+constant during subcycles (because there are no drifts, constant speed of
+light), the ``timestep`` tasks are not being run during a sub-cycle. This
+effectively means that the particle time bins can only be changed in a normal
+step when the particle is also hydro-active. Furthermore, there are no MPI 
+communications after the tasks have finished executing to update any global 
+times etc. for the same reason. There are some functionalities of the
+``timestep`` and the ``collect`` tasks which are still necessary though:
+
+- The ``timestep`` task also updates the cell's next integer end time after it
+  has been determined during the task. During a subcycle, the next end time is
+  simply the current time plus the minimal time step size of the cell, but we
+  need a task to actually update the cell at the end of each subcycle. The
+  ``rt_advance_cell_time`` task does exactly that, and in that sense does the
+  ``timestep`` task's job during subcycles.
+
+- The ``collect`` task propagates sub-cell data like the minimal end time or the
+  RT time step size from the super level to the top level. This functionality is
+  replaced with the ``rt_collect_times`` tasks during subcycles. Note that the
+  ``rt_collect_times`` tasks aren't being activated during normal steps, as the
+  ``collect`` tasks already do the job just fine.
+
+Something special about the ``rt_advance_cell_time`` tasks is that they are
+also created and run on foreign cells. During a subcycle, the ``tend`` tasks
+don't run and don't update the cell time variables from the original cell, so
+during the subsequent unskipping, the data will be wrong, leading to all sorts
+of trouble. We can do that on foreign cells during sub-cycles because all the
+cell's time step sizes stay fixed between two regular SWIFT steps, and hence
+the number of sub-cycles all the sub-cycles' end times are predictable.
+
+
+
+
+RT Sorts
+````````````````
+
+The sorting of particles required for pair-type interaction tasks requires some
+special attention. The issues arise because a subcycle step of a cell can
+coincide with the main step of another cell. To illustrate, suppose we have two
+cells, ``A`` and ``B``. Let cell ``A`` have a hydro time step of size 4, and 
+cell ``B`` a hydro time step of size 8. Let both cells do 2 RT subcycles per
+hydro step each. In the graph below, an ``X`` represents when a cell will be
+updated:
+
+.. code::
+
+   Cell A
+     Hydro active:    X               X               X               X               X       
+     RT active:       X       X       X       X       X       X       X       X       X       X
+
+   Cell B
+     Hydro active:    X                               X                               X
+     RT active:       X               X               X               X               X       
+    
+    
+    ------------------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
+    t                 0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18
+
+
+Note that e.g. at ``t`` = 4, cell ``B`` is only RT active, while cell ``A`` is
+also hydro active. It being hydro active means that we will have a SWIFT main
+step at that time.
+
+Now suppose cell cells ``A`` and ``B`` are neighbouring cells that undergo hydro
+interactions, but are on different MPI ranks. For the hydro interactions in the
+normal SWIFT step at ``t`` = 4, cell ``B`` will be sent over to the rank of 
+cell ``A``. Once it was received, it will be sorted, because after being 
+received, the ``recv_xv`` task resets the arrived cell's sort tasks, and a hydro 
+sort is activated. This is the default hydrodynamics workflow.
+
+
+
+Complications however arise when several conditions coincide:
+
+- a foreign cell has been drifted on its "home" domain due to some reason other 
+  than hydro, e.g. for gravity or for stellar feedback. 
+- the foreign cell and all of its neighbours are not hydro active at the current
+  main step, so no ``recv_xv`` task has been run on the local rank, no ``sort``
+  task has been run, and the sorting flags have not been reset for the cell.
+- the foreign cell is involved in an RT interaction that coincides with the 
+  current main step, i.e. the cell or one of its neighbours is RT active during
+  a main step. (E.g. like cell ``A`` at ``t`` = 2 in the graph above.)
+
+If these conditions are met, the cell will undergo an interaction while
+unsorted. Obviously that's a no-no.
+
+
+To illustrate this problem, consider the following scenario. Say we have cells ``A``,
+``B``, and ``C``. Cell ``A`` is "home" on MPI rank 0, while cell ``B`` is on MPI rank 1. 
+In the current step in this scenario, cell ``A`` and ``B`` are both active for RT, and 
+interact with each other. ``C`` has an active star, which inject energy into particles of
+``B``.  Cell ``A`` and ``C`` do *not* interact with each other:
+
+
+.. code::
+
+   rank 0       | rank 1  
+                |
+      RT interaction        Star Feedback  
+   A <----------|---> B <------------------ C
+                |
+                |
+
+
+Since ``A`` and ``B`` interact, but are separated between different MPI ranks, both ``A``
+and ``B`` will have local copies of each other available on their local ranks, respectively.
+These cells are referred to as "foreign cells". Let's denote them with an additional ``f``:
+
+.. code::
+
+    rank 0                      | rank 1  
+                                |
+             RT interaction     |        RT interaction      Star Feedback  
+           A <-------------> Bf |   Af  <-------------> B <------------------ C
+                             ^  |   ^
+    (this is a foreign cell) |  |   | (this is a foreign cell)
+
+
+Now let's first have a look at the default workflow when hydrodynamics is involved. 
+Assume that all cells ``A``, ``B``, and ``C`` are hydro *and* RT active for this step.
+Once again cells ``A`` and ``B`` interact, and ``B`` and ``C`` interact, but ``A`` 
+and ``C`` *don't* interact. Cell ``C`` contains an active star particle.
+
+**Without** MPI, the order of operations for each cell would look like this: (operations 
+where two cells interact with each other are marked with an arrow)
+
+
+.. code::
+
+    A                   B                  C
+    ----------------------------------------------------
+    Drift               Drift               Drift
+    Sort                Sort                Sort
+    Density Loop <----> Density Loop <----> Density Loop
+    Ghost               Ghost               Ghost
+    Force Loop <------> Force Loop <------> Force Loop
+    End Force           End Force           End Force
+    Kick2               Kick2               Kick2
+
+                                            Star Drift
+                                            Star Sort
+                        (inactive) <------> Star Density
+                                            Star Ghost
+                        (inactive) <------> Star Feedback
+
+    RT Ghost1           RT Ghost1           RT Ghost1
+    RT Gradient <-----> RT Gradient <-----> RT Gradient
+    RT Ghost2           RT Ghost2           RT Ghost2
+    RT Transport <----> RT Transport <----> RT Transport
+    RT Tchem            RT Tchem            RT Tchem
+    Timestep            Timestep            Timestep
+    Kick1               Kick1               Kick1
+
+
+Now  **with** MPI communications, cells ``A`` and ``B`` need to send over the 
+up-to-date data to their foreign counterparts ``Af`` and ``Bf``, respectively, 
+*before* each interaction type task (the ones with arrows in the sketch above). 
+The order of operations should look like this:
+(The foreign cell ``Af`` on rank 1 is omitted for clarity, but follows the same
+principle as ``Bf`` on rank 0)
+
+.. code::
+
+    rank 0                                  |  rank 1
+    A                   Bf                  |  B                   C
+    ----------------------------------------|----------------------------------------
+    Drift                                   |  Drift               Drift
+                        Recv XV  <------------ Send XV
+    Sort                Sort                |  Sort                Sort
+    Density Loop <----> Density Loop        |  Density Loop <----> Density Loop
+    Ghost                                   |  Ghost               Ghost
+                        Recv Density <-------- Send Density
+    Force Loop <------> Force Loop          |  Force Loop <------> Force Loop
+    End Force                               |  End Force           End Force
+    Kick2                                   |  Kick2               Kick2
+                                            |
+                                            |                      Star Drift
+                                            |                      Star Sort
+                                            |  (inactive) <------> Star Density
+                                            |                      Star Ghost
+                                            |  (inactive) <------> Star Feedback
+                                            |
+    RT Ghost1                               |  RT Ghost1           RT Ghost1
+                        Recv RT Gradient <---- Send RT Gradient
+    RT Gradient <-----> RT Gradient         |  RT Gradient <-----> RT Gradient
+    RT Ghost2                               |  RT Ghost2           RT Ghost2
+                        Recv RT Transport <--- Send RT Transport
+    RT Transport <----> RT Transport        |  RT Transport <----> RT Transport
+    RT Tchem                                |  RT Tchem            RT Tchem
+    Timestep                                |  Timestep            Timestep
+                        Recv tend <----------- Send tend
+    Kick1                                   |  Kick1               Kick1
+
+
+Finally, let's look at the scenario which causes problems with the sort. This 
+is the case, as described above, when (a) cells ``A`` and ``B`` are RT active during
+a main step (like in the sketch above), (b) aren't hydro active during a main step 
+(unlike what is drawn above), (c) one of these cells is foreign (in this case, ``Bf``),
+while the "home" cell (cell ``B``) get drifted during a main step for some reason
+other than hydrodynamics, e.g. because a star interaction with cell ``C`` requested it.
+
+In this case, the workflow looks like this:
+
+.. code::
+
+    rank 0                                  |  rank 1
+    A                   Bf                  |  B                   C
+    ----------------------------------------|----------------------------------------
+                                            |  Drift               Drift
+                                            |  Sort                Sort
+                                            |                      Kick2
+                                            |
+                                            |                      Star Drift
+                                            |                      Star Sort
+                                            |  (inactive) <------> Star Density
+                                            |                      Star Ghost
+                                            |  (inactive) <------> Star Feedback
+                                            |
+    RT Ghost1                               |  RT Ghost1           RT Ghost1
+                        Recv RT Gradient <---- Send RT Gradient
+    RT Gradient <-----> RT Gradient         |  RT Gradient <-----> RT Gradient
+    RT Ghost2                               |  RT Ghost2           RT Ghost2
+                        Recv RT Transport <--- Send RT Transport
+    RT Transport <----> RT Transport        |  RT Transport <----> RT Transport
+    RT Tchem                                |  RT Tchem            RT Tchem
+    Timestep                                |  Timestep            Timestep
+                        Recv tend <----------- Send tend
+    Kick1                                   |  Kick1               Kick1
+
+The issue is that with the missing hydro communication tasks, the first communication
+between cell ``B`` and its foreign counterpart ``Bf`` is the ``recv_rt_gradient`` task.
+Recall that during a communication, we always send over all particle data of a cell.
+This includes all the particle positions, which may have been updated during a drift.
+However, the sorting information is not stored in particles, but in the cell itself.
+For this reason, a ``sort`` task is *always* run directly after a cell finishes the 
+``recv_xv`` task, which, until the sub-cycling was added, was always the first task any 
+foreign cell would run, with a subsequent ``sort``.
+
+The RT sub-cycling now however allows the ``recv_xv`` task to not run at all
+during a main step, since it's not always necessary, as shown in the example above.
+All the required data for the RT interactions can be sent over with
+``send/recv_rt_gradient`` tasks. An unintended consequence however is that in a
+scenario as sketched above, at the time of the ``A <---> Bf`` RT Gradient
+interaction, cell ``Bf`` will not be sorted. That's a problem.
+
+To solve this issue, a new task has been added, named ``rt_sorts``. It is only
+required for foreign cells, like cell ``Bf``, and only during normal/main steps 
+(as we don't drift during subcycles, there won't be any reason to re-sort.) On
+local cells, each time a drift is activated for an interaction type task, the 
+sort task is also activated. So there is no need for ``rt_sorts`` tasks on local
+cells.
+An additional advantage to adding a new sort task like the ``rt_sorts`` is that
+it allows us to sidestep possible deadlocks. Suppose that as an alternative to
+the ``rt_sort`` tasks we instead use the regular hydro ``sort`` task. The default
+hydro ``sort`` task is set up to run before the other hydro tasks, and in 
+particular before the ``kick2`` task. However the RT and star tasks are executed 
+*after* the ``kick2``. This means that there are scenarios where a cell with a 
+foreign counterpart like cells ``B`` and ``Bf`` can deadlock when ``Bf`` is waiting
+for the ``recv_rt_gradient`` to arrive so it may sort the data, while ``B`` is
+waiting for ``Bf`` to finish the sorting and proceed past the ``kick2`` stage so
+it can run the ``send_rt_gradient`` data which would allow ``Bf`` to run the
+sorts.
+
+
+The ``rt_sorts`` tasks are executed after the first RT related ``recv``, in this 
+case the ``recv_rt_gradient``. The order of operations should now look like this:
+
+
+.. code::
+
+    rank 0                                  |  rank 1
+    A                   Bf                  |  B                   C
+    ----------------------------------------|----------------------------------------
+                                            |  Drift               Drift
+                                            |  Sort                Sort
+                                            |                      Kick2
+                                            |
+                                            |                      Star Drift
+                                            |                      Star Sort
+                                            |  (inactive) <------> Star Density
+                                            |                      Star Ghost
+                                            |  (inactive) <------> Star Feedback
+                                            |
+    RT Ghost1                               |  RT Ghost1           RT Ghost1
+                        Recv RT Gradient <---- Send RT Gradient
+                        rt_sort             |
+    RT Gradient <-----> RT Gradient         |  RT Gradient <-----> RT Gradient
+    RT Ghost2                               |  RT Ghost2           RT Ghost2
+                        Recv RT Transport <--- Send RT Transport
+    RT Transport <----> RT Transport        |  RT Transport <----> RT Transport
+    RT Tchem                                |  RT Tchem            RT Tchem
+    Timestep                                |  Timestep            Timestep
+                        Recv tend <----------- Send tend
+    Kick1                                   |  Kick1               Kick1
+
+
+
+In order to minimize unnecessary work, three new cell flags concerning the RT
+sorts have been added:
+ 
+- ``cell_flag_do_rt_sub_sort``: tracks whether we need an RT sub sort, which is 
+  equivalent to the ``cell_flag_do_sub_sort`` flag for hydro. We can't use the 
+  hydro flag though because the hydro flag is also used to early-exit walking up 
+  the cell hierarchy when activating hydro subcell sorts. In particular, this
+  condition in ``cell_unskip.c:cell_activate_hydro_sorts_up():``
+
+.. code:: 
+
+   void cell_activate_hydro_sorts_up(struct cell *c, struct scheduler *s) {
+       /* omitted lines */
+
+       for (struct cell *parent = c->parent;
+           parent != null && !cell_get_flag(parent, cell_flag_do_hydro_sub_sort); parent = parent->parent) {
+       /* !! this is the problem ---^ */
+   
+       /* omitted lines */
+     }
+   }
+
+The sort activation for RT and for hydro can run concurrently. So there is no
+guarantee that when the hydro sort activation sets the
+``cell_flag_do_hydro_sub_sort`` flag, the RT sorting tasks will be activated
+correctly, which occurs at the top of the cell hierarchy tree walk.
+So we need an independent flag for RT here to not abort the tree walk early
+and in error.
+
+- ``cell_flag_do_rt_sort``: tracks whether the call to the 
+  ``runner_do_hydro_sort()`` function was requested by an RT sort. (Both the (hydro) 
+  ``sort`` and the ``rt_sort`` tasks call the same function.) This flag is used to 
+  discriminate during the actual sorting in the ``runner_do_hydro_sort()``
+  function if the internal check whether the cell is drifted to the current time
+  may be disregarded. When an RT subcycle coincides with a main step, the particles 
+  won't necessarily be drifted to the current time as there is no need to drift them 
+  for RT only. This is intended behaviour, so we allow ``runner_do_hydro_sort()`` to
+  skip this drift check in the case where the sorting was requested for RT purposes.
+
+- ``cell_flag_skip_rt_sort``: Tracks whether a regular hydro sort has been
+  activated for this cell. If it has, then there is no need to run an RT sort as
+  well, and we skip it.
+
diff --git a/doc/RTD/source/RadiativeTransfer/RT_subcycling.rst b/doc/RTD/source/RadiativeTransfer/RT_subcycling.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d5e8a5151ad2c9d09f996cc8fe19bc1315dd274c
--- /dev/null
+++ b/doc/RTD/source/RadiativeTransfer/RT_subcycling.rst
@@ -0,0 +1,54 @@
+.. RT Subcycling
+    Mladen Ivkovic 07.2022
+
+.. _rt_subcycling:
+   
+RT Subcycling
+-------------
+
+.. warning::
+    The radiative transfer schemes are still in development and are not useable
+    at this moment. This page is currently a placeholder to document new
+    features and requirements as the code grows.
+
+
+SWIFT allows to sub-cycle the solution of radiative transfer steps (both 
+photon propagation and thermochemistry) with respect to the hydrodynamics
+time steps. Basically you can tell SWIFT to run up to X radiative transfer
+steps during a single hydrodynamics step for all particles in the simulation.
+The aim is to not waste time doing unnecessary hydrodynamics updates, which
+typically allow for much higher time steps compared to radiation due to the
+propagation speed of the respective advected quantity.
+
+You will need to provide an upper limit on how many RT subcycles per hydro
+step you want to allow. That is governed by the
+
+.. code:: yaml
+
+   TimeIntegration:
+       max_nr_rt_subcycles: 128         # maximal number of RT subcycles per hydro step
+
+parameter, which is mandatory for any RT runs. To turn off subcycling and 
+couple the radiative transfer and the hydrodynamics time steps one-to-one,
+set this parameter to either 0 or 1.
+
+Due to the discretization of individual particle time steps in time bins
+with a factor of 2 difference in time step size from a lower to a higher
+time bin, the ``max_nr_rt_subcycles`` parameter itself is required to be
+a power of 2 as well.
+
+Note that this parameter will set an upper limit to the number of subcycles
+per hydro step. If the ratio of hydro-to-RT time step is greater than what
+``max_nr_rt_subcycles`` allows for, then the hydro time step will be reduced
+to fit the maximal threshold. If it is smaller, the particle will simply do 
+fewer subcycles.
+
+.. warning::
+   Contrary to the documentation above, in the current implementation the 
+   ``max_nr_rt_subcycles`` parameters is abused as the fixed number of RT 
+   subcycles per hydro step. This means that RT time steps will be reduced
+   to lower values than necessary to fit the exact ratio should they be too
+   large to begin with.
+   Once the development advances, the behaviour will be set to be as 
+   documented above.
+
diff --git a/doc/RTD/source/RadiativeTransfer/additional_tools.rst b/doc/RTD/source/RadiativeTransfer/additional_tools.rst
new file mode 100644
index 0000000000000000000000000000000000000000..bbfc5c356d422916f8469bc1125ffdbbe97f3768
--- /dev/null
+++ b/doc/RTD/source/RadiativeTransfer/additional_tools.rst
@@ -0,0 +1,15 @@
+.. GEAR Radiative Transfer
+    Mladen Ivkovic 08.2022
+
+.. _rt_additional_tools:
+
+
+Additional Tools
+-------------------
+
+A plethora of additional RT related tools is hosted in the 
+`the swiftsim-rt-tools <https://github.com/SWIFTSIM/swiftsim-rt-tools>`_
+repository. For example, it contains tools to convert injected photon number 
+rates into luminosities, compute integrated interaction cross sections for 
+various spectra, some rudimentary thermochemistry python functions, etc.
+
diff --git a/doc/RTD/source/RadiativeTransfer/index.rst b/doc/RTD/source/RadiativeTransfer/index.rst
index e25c3fc29b4079818fd36f7839a41309b6b75b1a..2f465b735f34b9bb63c5bde0a006fba4eb1441e8 100644
--- a/doc/RTD/source/RadiativeTransfer/index.rst
+++ b/doc/RTD/source/RadiativeTransfer/index.rst
@@ -24,3 +24,6 @@ schemes.
    requirements
    GEAR_RT
    SPHM1_RT
+   RT_subcycling
+   RT_notes_for_developers
+   additional_tools
diff --git a/doc/RTD/source/Snapshots/index.rst b/doc/RTD/source/Snapshots/index.rst
index 6c9e1fd4117b7fe7f98ef35012f89aa836888ef0..30cc87fa1d0e29311fab8c1c7fde7be374ece27b 100644
--- a/doc/RTD/source/Snapshots/index.rst
+++ b/doc/RTD/source/Snapshots/index.rst
@@ -38,6 +38,11 @@ number of sub-snapshot files (always 1 unless a distributed snapshot was asked
 for) and ``ThisFile`` the id of that specific file (always 0 unless a distributed
 snapshot was asked for). 
 
+The field ``TotalNumberOfParticles`` gives the total number of particles of each type
+as a 64 bit integer. This allows the total number of particles to be read directly
+with no calculation required even if there are 2^31 or more particles. This field is
+equal to ``NumPart_ThisFile`` if the snapshot is not distributed over multiple files.
+
 The field ``InitialMassTable`` contains the *mean* initial mass of each of the
 particle types present in the initial conditions. This can be used as estimator
 of the mass resolution of the run. The masses are expressed in internal units.
diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/index.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d7c83ca5117f8be7a663a7e34eaf6167e0101a11
--- /dev/null
+++ b/doc/RTD/source/SubgridModels/AGNSpinJets/index.rst
@@ -0,0 +1,46 @@
+.. AGN spin and jet model
+   Filip Husko, 1 April 2022
+
+.. AGN_spin_jet:
+
+AGN jets and black hole spin in hydrodynamical simulations
+==========================================================
+
+Model summary
+-------------
+
+
+The main feature of this model, in terms of the effects on galaxy populations, is the addition of an AGN jet 
+mode of feedback. In order to launch realistic jets, black hole spin is tracked and evolved for all BH 
+particles in the simulation.
+
+Jet powers, in addition to depending on spin, also depend on which accretion state the black hole is in. We 
+include three accretion states: the thick, thin and slim disk. The thick disk appears at low accretion rates, 
+has very strong jets and is inefficient at spinning up the black hole. The thin disk, appearing at 
+intermediate accretion rates, has weak jets, strong radiation and efficiently spins up the black hole. The 
+slim disk, corresponding to super-Eddington accretion, has features of both, and has both strong radiation and 
+jets. Slim disks can be turned off in the model, but the thick and thin disks are intimitely tied to their 
+feedback modes (jets and radiation, respectively).
+
+In ``theory.rst`` we outline all of the theory which is implemented as part of the model. This includes when 
+the black holes transition from one state to another, the strength of feedback in each state, how spin is 
+evolved in terms of magnitude and direction, etc. In ``numerics.rst`` we discuss how jet launching is 
+implemented, and additional black hole time steps introduced into the code. In ``params.rst`` we list and 
+discuss all parameters used by the model. In ``output.rst`` we list additional arrays output for the BHs and 
+tracers. Below we outline how to configure and run the model.
+
+Compiling and running the model
+-------------------------------
+
+The model can be run with either the EAGLE or COLIBRE models. You can configure the model with ``--with-black-holes=SPIN_JET`` in combination with other configure options, or you can configure the full EAGLE or COLIBRE models with the new spin/jet physics as ``--with-subgrid=SPIN_JET_EAGLE`` and ``--with-subgrid=SPIN_JET_COLIBRE``, respectively. The model will then run as long as ``--black-holes`` is among the runtime options.
+
+For cosmological simulations you do not need to do anything special, but for isolated runs (or any runs with black holes in the initial conditions), the ICs must include two new fields for all black holes: a scalar field representing black hole spins called ``Spins`` and a vector field representing the directions of the spin called ``AngularMomentumDirections``. The former should be between 0 and 1, while the latter should be normalized to 1.
+
+A full list of all relevant parameters of the model is in ``params.rst``. We also briefly describe the most important parameters which need to be set to run the model, as well as how to run it in different configurations.
+
+.. toctree::
+
+  theory
+  numerics
+  params
+  output
diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/numerics.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/numerics.rst
new file mode 100644
index 0000000000000000000000000000000000000000..6d37b8aec2510b37a40629f4eb12ff61609e586f
--- /dev/null
+++ b/doc/RTD/source/SubgridModels/AGNSpinJets/numerics.rst
@@ -0,0 +1,31 @@
+.. AGN spin and jet model
+   Filip Husko, 1 April 2022
+
+.. AGN_spin_jet:
+
+Jet launching algorithm
+-----------------------
+
+In order to launch jets, we introduce a jet reservoir that functions identically to the thermal reservoir used in EAGLE and COLIBRE. When the jet reservoir exceeds the value :math:`N_\mathrm{j}\overline{m}_\mathrm{ngb}v_\mathrm{j}^2/2`, kicks are given out to :math:`N_\mathrm{j}` particles. Here, :math:`N_\mathrm{j}` is the target number of particles to kick per each kicking event (typically equal to 2, but should always be a multiple of 2 to ensure approximately symmetrical jets). :math:`\overline{m}_\mathrm{ngb}v_\mathrm{j}^2/2` is the average kinetic energy of one kicking event, with :math:`\overline{m}_\mathrm{ngb}` the mean neighbour mass of gas particles in the BH smoothing kernel and :math:`v_\mathrm{j}` the target kicking velocity.
+
+These kicks are handed out to particles in a symmetric way with respect to the spin vector of the BH. :math:`N_\mathrm{j}/2` particles are kicked from the 'upper' hemisphere relative to the spin vector, and the other half from the lower hemisphere. The particles to be kicked can be any in the smoothing kernel. We include four different choices: the particles kicked are: 1) the closest to the BH, 2) the farthest from the BH, 3) the ones of minimal density and 4) the ones closest to the spin axis, in terms of angular distance. Note that these sortings are done for each hemisphere seperately. 
+
+The particles chosen are always given velocities based on the same algorithm, regardless of their positions in the kernel. We perform the actual kicks in the following way. Velocity kicks are chosen at random from a cone around the current spin vector with a (half-)opening angle of :math:`\theta_\mathrm{j}`. In particular, we first choose the kick vector around the z-axis as :math:`\vec{v}_\mathrm{kick}=(\sin\theta\cos\phi,\hspace{0.3mm}\sin\theta\sin\phi,\hspace{0.3mm}\cos \theta)`. Here, :math:`\cos\theta` is chosen uniformly from the interval :math:`[\cos\theta_\mathrm{j},1]`, and :math:`\sin\theta=\sqrt{1-\cos\theta^2}`. :math:`\phi` is chosen uniformly from :math:`[0,2\pi]`. This random vector, now representing a random kick within a cone around the z-axis, is rotated into the frame of the spin vector so that the cone is pointing in the right direction. For particles being kicked from the 'negative' side of the BH hemisphere, the final kick vector is simply multiplied by :math:`-1`.
+
+We then add the kick vector to the particle's current velocity. We do this in a way that conserves energy, so that the magnitude of the final velocity is computed from
+
+.. math::
+    \frac{1}{2}m_\mathrm{gas}\vec{v}_\mathrm{fin}^2=\frac{1}{2}m_\mathrm{gas}\vec{v}_\mathrm{0}^2 + \frac{1}{2}m_\mathrm{gas}\vec{v}_\mathrm{kick}^2,
+    
+while its direction is computed from conservation of momentum:
+
+.. math::
+    m_\mathrm{gas}\vec{v}_\mathrm{fin}=m_\mathrm{gas}\vec{v}_\mathrm{0} + m_\mathrm{gas}\vec{v}_\mathrm{kick}.
+
+Black hole time steps
+---------------------
+
+Black holes will generally have time steps based on their gravitational interactions, but also based on their current accretion rate and the expected time interval until which the thermal feedback reservoir (representing radiative feedback) will have grown enough to heat 1 particle. We introduce a similar time step, but based on when the jet reservoir grows enough to kick :math:`N_\mathrm{j}` particles. We then take the minimum of those two. 
+
+On top of that, we add a time step that makes sure the BH spin doesn't get evolved too much over one time step. Its magnitude is actually not a problem, since the growth of the spin magnitude is always tied with the growth of mass. However, the direction is more problematic (especially in the thin disk case, where alignment can occur very quickly, with little mass or spin growth, due to large warp radii). For this reason, we make sure that the amount of warp angular momentum interacting with the BH over the next time-step, :math:`\Delta_J=(J_\mathrm{warp}/M_\mathrm{warp})\Delta M`, is a small fraction of the current BH angular momentum :math:`J_\mathrm{BH}` (e.g. :math:`0.01`).
+
diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/output.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/output.rst
new file mode 100644
index 0000000000000000000000000000000000000000..fe03f042cf3aafcb71e3fe6142dd9b9ce4f1b5d7
--- /dev/null
+++ b/doc/RTD/source/SubgridModels/AGNSpinJets/output.rst
@@ -0,0 +1,88 @@
+.. AGN spin and jet model
+   Filip Husko, 1 April 2022
+
+.. AGN_spin_jet:
+
+Black holes
+-----------
+
+
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| Name                                  | Description                         | Units     | Comments                    |
++=======================================+=====================================+===========+=============================+
+| ``AngularMomentumDirections``         | | The direction of the angular      | [-]       | | Array of length           |
+|                                       | | momentum (spin) vector of the BH  |           | | 3 for each particle       |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``AccretionDiscAspectRatios``         | | Aspect ratio, H/R, of the subgrid | [-]       |                             |
+|                                       | | accretion disk surrounding each   |           |                             |
+|                                       | | black hole                        |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``AccretionModes``                    | | Type of subgrid accretion disk    | [-]       |                             |
+|                                       | | surrounding the black holes       |           |                             |
+|                                       | | 0 - Thick disk, 1 - Thin disk,    |           |                             |
+|                                       | | 2 - Slim disk                     |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``CosAccretionDiskAngle``             | | Cosine of the angle between the   | [-]       |                             |
+|                                       | | spin vector and the gas angular   |           |                             |
+|                                       | | momentum vector around the BH     |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``InjectedJetEnergies``               | | Total jet energy injected into    | [U_M U_L  |                             |
+|                                       | | surroundings of this BH           | ^2 U_t^-2]|                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``JetEfficiencies``                   | | The efficiency of jet launching,  | [-]       |                             |
+|                                       | | i.e. the jet power divided by the |           |                             |
+|                                       | | accretion rate times c*c          |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``JetReservoirs``                     | | The remaining jet energy left to  | [U_M U_L  |                             |
+|                                       | | be launched from the BH           | ^2 U_t^-2]|                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``JetTimeSteps``                      | | Jet-limited time steps of the BHs | [U_t]     |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``LastAGNJetScaleFactors``            | | Last AGN jet scale factors when   | [-]       |                             |
+|                                       | | the BH did jet feedback, if       |           |                             |
+|                                       | | cosmology is turned on            |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``LastAGNJetTimes``                   | | Last AGN jet times when the BH    | [U_t]     |                             |
+|                                       | | did jet feedback, if cosmology is |           |                             |
+|                                       | | turned off                        |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``NumberOfAGNJetEvents``              | | Total number of times this BH did | [-]       |                             |
+|                                       | | jet feedback                      |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``NumberOfJetParticlesLaunched``      | | Total number of times this BH     | [-]       |                             |
+|                                       | | launched particles as part of jet |           |                             |
+|                                       | | feedback                          |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``Spins``                             | | Dimensionless spin parameters of  | [-]       |                             |
+|                                       | | the black holes. Negative values  |           |                             |
+|                                       | | indicate retrograde accretion     |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``RadiativeEfficiencies``             | | The efficiency of radiative       | [-]       |                             |
+|                                       | | feedback, i.e. the radiative      |           |                             |
+|                                       | | power divided by the accretion    |           |                             |
+|                                       | | rate times c*c                    |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+
+
+Tracers (gas and stars)
+-----------------------
+
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| Name                                  | Description                         | Units     | Comments                    |
++=======================================+=====================================+===========+=============================+
+| ``EnergiesReceivedFromJetFeedback``   | | The total energy this particle    | [U_M U_L  |                             |
+|                                       | | has received by being kicked as   | ^2 U_t^-2]|                             |
+|                                       | | part of jet feedback              |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``LastAGNJetFeedbackScaleFactors``    | | Scale factor when the particle    | [-]       |                             |
+|                                       | | was last kicked as part of jet    |           |                             |
+|                                       | | feedback, if with cosmology       |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``LastAGNJetFeedbackTimes``           | | Times when the particle was last  | [U_t]     |                             |
+|                                       | | last kicked as part of jet        |           |                             |
+|                                       | | feedback, if without cosmology    |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
+| ``KickedByJetFeedback``               | | How many times this particle has  | [-]       |                             |
+|                                       | | been kicked as                    |           |                             |
+|                                       | | part of jet feedback              |           |                             |
++---------------------------------------+-------------------------------------+-----------+-----------------------------+
diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/params.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/params.rst
new file mode 100644
index 0000000000000000000000000000000000000000..114a5d4cc7a978e32a7326759564d2914fb93c84
--- /dev/null
+++ b/doc/RTD/source/SubgridModels/AGNSpinJets/params.rst
@@ -0,0 +1,80 @@
+.. AGN spin and jet model
+   Filip Husko, 1 April 2022
+
+.. AGN_spin_jet:
+
+Model parameters
+----------------
+
+Below we give an example of parameter choices applicable for e.g. a 50 Mpc box. The new parameters are from ``include_jets`` and below. Their descriptions are given next to the parameters.
+
+.. code:: YAML
+
+    SPINJETAGN:
+        subgrid_seed_mass_Msun:             1e5        # Black hole subgrid mass at creation time in solar masses.
+        use_subgrid_mass_from_ics:          1          # (Optional) Use subgrid masses specified in ICs [1, default], or initialise them to particle masses [0]?
+        with_subgrid_mass_check:            1          # (Optional) Verify that initial black hole subgrid masses are positive [1, default]. Only used if use_subgrid_mass_from_ics is 1.
+        use_multi_phase_bondi:              0          # Compute Bondi rates per neighbour particle [1] or for the smoothed ambient gas around the black hole [0]?
+        use_subgrid_gas_properties:         1          # Use subgrid density [1] or dynamical density [0] to calculate BH accretion rates?
+        use_krumholz:                       1          # Use Krumholz et al. (2006) [1] or standard Bondi-Hoyle-Lyttleton formula [0] for black hole accretion rates? Only used if multi_phase_bondi is 0.
+        with_krumholz_vorticity:            0          # Include the vorticity term in Krumholz et al. formula? Only used if use_multi_phase_bondi is 0.
+        with_angmom_limiter:                0          # Are we applying the Rosas-Guevara (2015) viscous time-scale reduction term?
+        viscous_alpha:                      1e6        # Normalisation constant of the viscous time-scale in the accretion reduction term. Only used if with_angmom_limiter is 1.
+        with_boost_factor:                  0          # Are we using the model from Booth, Schaye (2009)?
+        boost_alpha:                        1.         # Lowest value for the accretion effeciency for the Booth, Schaye 2009 accretion model.
+        boost_beta:                         2.         # Slope of the power law for the Booth, Schaye 2009 model, set beta to zero for constant alpha models.
+        boost_n_h_star_cm3:                 0.1        # Normalization of the power law for the Booth Schaye 2009 model in cgs (cm^-3).
+        eddington_fraction_for_recording:   0.1        # Record the last time BHs reached an Eddington ratio above this threshold.
+        use_nibbling:                       1          # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? 
+        min_gas_mass_for_nibbling_Msun:     9e5        # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1.
+        coupling_efficiency:                0.15       # Fraction of the radiated energy that couples to the gas in feedback events.
+        AGN_delta_T_K:                      1e8        # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin.
+        AGN_num_ngb_to_heat:                1.         # Target number of gas neighbours to heat in an AGN feedback event.
+        with_potential_correction:          1          # Subtract BH's own contribution to the potential of neighbours when determining repositioning targets.
+        max_reposition_mass_Msun:           2e8        # Maximal BH mass considered for BH repositioning in solar masses.
+        max_reposition_distance_ratio:      3.0        # Maximal distance a BH can be repositioned, in units of the softening length.
+        with_reposition_velocity_threshold: 0          # Should we only reposition to particles that move slowly w.r.t. the black hole?
+        max_reposition_velocity_ratio:      0.25       # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1.
+        min_reposition_velocity_threshold_km_p_s: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1.
+        set_reposition_speed:               0          # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum?
+        reposition_coefficient_upsilon:     0.001      # Repositioning speed normalisation [km/s/M_sun]. Only meaningful if set_reposition_speed is 1.
+        reposition_exponent_xi:             1.0        # (Optional) Scaling of repositioning velocity with BH subgrid mass (default: 1.0, linear). Only meaningful if set_reposition_speed is 1.
+        threshold_major_merger:             0.333      # Mass ratio threshold to consider a BH merger as 'major'
+        threshold_minor_merger:             0.1        # Mass ratio threshold to consider a BH merger as 'minor'
+        merger_threshold_type:              DynamicalEscapeVelocity   # Type of velocity threshold for BH mergers (CircularVelocity as in EAGLE, EscapeVelocity, or DynamicalEscapeVelocity)
+        merger_max_distance_ratio:          3.0        # Maximal distance over which two BHs can merge, in units of the softening length.
+        AGN_use_deterministic_feedback:     1          # Deterministic (1) or stochastic (0) AGN feedback model
+        AGN_feedback_model:                 Isotropic  # AGN feedback model (Isotropic or MinimumDistance)
+        minimum_timestep_yr:                1000.0     # Minimum time-step of black-hole particles
+        max_eddington_fraction:             1.        # Maximal allowed accretion rate in units of the Eddington rate.
+        include_jets:                       1          # Global switch whether to include jet feedback [1] or not [0].
+        turn_off_radiative_feedback:        0          # Global switch whether to turn off radiative (thermal) feedback [1] or not [0]. This should only be used if 'include_jets' is set to 1, since we want feedback in some form or another.
+        alpha_acc:                          0.2        # Viscous alpha of the subgrid accretion disks. Likely to be within the 0.1-0.3 range. The main effect is that it sets the transition accretion rate between thin and thick disk, as dot(m) = 0.1 * alpha^2 + 0.035 * alpha.
+        AGN_jet_velocity_model:             BlackHoleMass          # How AGN jet velocities are calculated. If 'Constant', a single value is used. If 'BlackHoleMass', then an empirical relation between halo mass and black hole mass is used to calculate jet velocities. 'HaloMass' is currently not supported. 
+        v_jet_km_p_s:                       10000.     # Jet velocity to use if 'AGN_jet_velocity_model' is 'Constant'. Units are km/s.
+        v_jet_cs_ratio:                     10.        # This sets the jet velocity to v_jet_cs_ratio times the sound speed of the hot gas of the parent halo the black hole is in. This is used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+        v_jet_BH_mass_scaling_reference_mass_Msun: 3.4e3 # The reference mass used in the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+        v_jet_BH_mass_scaling_slope:        0.65       # The slope of the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+        v_jet_min_km_p_s:                   500        # The minimal jet velocity. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+        opening_angle_in_degrees:           7.5        # The half-opening angle of the jet in degrees. Should use values < 15 unless for tests.
+        N_jet:                              2          # Target number of particles to kick as part of a single jet feedback event. Should be a multiple of 2 to ensure approximate momentum conservation (we always kick particles in pairs, one from each 'side' of the BH, relative to the spin vector).
+        AGN_jet_feedback_model:             SpinAxis   # Which particles to kick from the black hole smoothing kernels. Should be 'SpinAxis', 'MinimumDistance', 'MaximumDistance' or 'MinimumDensity'
+        eps_f_jet:                          1.         # Coupling efficiency for jet feedback. No reason to expect this to be less than 1.
+        fix_jet_efficiency:                 0          # Global switch whether to fix jet efficiency to a particular value [1], or use a spin-dependant formula [0]. If used, jets will be launched exclusively along the z axis. Should be set to 1 only for tests.
+        jet_efficiency:                     0.1        # The constant jet efficiency used if 'fix_jet_efficiency' is set to 1.
+        fix_radiative_efficiency:           0          # Global switch whether to fix the radiative efficiency to a particular value [1], or use a spin-dependant formula [0]. 
+        radiative_efficiency:               0.1        # The constant jet efficiency used if 'fix_radiative_efficiency' is set to 1. Otherwise, this value is used to define the Eddington accretion rate.
+        TD_region:                          C          # How to treat the subgrid accretion disk if it is thin, according to the Shakura & Sunyaev (1973) model. If set to B, region b will be used. If set to C, region c will be used. 
+        include_GRMHD_spindown:             1          # Whether to include high jet spindown rates from GRMHD simulations [1], or use an analytical formula that assumes extraction of energy from the rotational mass/energy of the BH.
+        include_ADIOS_suppression:          0          # Whether to suppress the accretion rate in the fully thick disc regime [1] (Eddington rate below 0.2alpha^2) by the amount expected to be taken away by isotropic kinetic disk winds.
+        ADIOS_R_in:                         30.        # If include_ADIOS_accr_suppression is set to 1, this parameter controls the inner radius within which winds are not important.
+        ADIOS_s:                            0.4        # Slope of the accretion rate - radius relationship if include_ADIOS_accr_suppression is set to 1.
+        turn_off_secondary_feedback:        1          # If set to 1, there will be only radiative (thermal) feedback in the thin disk mode, and only jets in the thick disk mode.
+        jet_h_r_slope:                      1.         # The slope of the dependence of jet efficiency on aspect ratio of the subgrid accretion disk, H/R. Default value is 1, and another reasonable value is 0 (same jet efficiency for all disks). Reality could be anything in between. This parameter is only used if turn_off_secondary_feedback is set to 0.
+        delta_ADAF:                         0.2        # Electron heating parameter, which controls the strength of radiative feedback in thick disks. Should be between 0.1 and 0.5. This parameter is only used if turn_off_secondary_feedback is set to 0.
+        include_slim_disk:                  0          # Global switch whether to include super-Eddington accretion, modeled as the slim disk. If set to 0, disks will be considered thin even at very large accretion rates.
+        TD_SD_eps_r_threshold:              0.75       # Parameter controlling the transition from thin to slim disk. Accretion disk will be slim if radiative efficiency satisfies eps_slim < TD_SD_eps_r_threshold * eps_thin. This parameter is only used if include_slim_disk is set to 1.
+
+Most of these parameters should work well generally, and should not be changed except for tests. We will discuss only some of the more important ones. You can choose whether to have only the thick and thin disk (low and high BH accretion rates, respectively), or you can also include the slim disk at super-Eddington rates with ``include_slim_disk``. You can control what type of feedback you (do not) want with ``include_jets`` and ``turn_off_radiative_feedback``. If you choose to turn off jets, everything will be modeled as a thin disk (regardless of accretion rate), since jets go hand-in-hand with the thick and the slim disk. Similarly, if you turn off radiation, everything will be treated as a thick disk.
+
+If you set ``use_var_v_jet:   0``, you will need to change ``v_jet``, the kicking velocity of particles, depending on what system you are simulating. You should typically choose values at least 10 times larger than the sound speed of the hot gas in your most massive haloes (e.g. 1500 km/s for a MW-type galaxy and 10 000 km/s for a :math:`10^{14}` :math:`\mathrm{M}_\odot` halo). If, on the other hand, you set ``use_var_v_jet:   1``, the launching velocities will vary on their own depending on the typical sound speed (virial velocity) of the hot gas in the haloes. You then need to set ``v_jet_cs_ratio`` to values :math:`\gg1` (10-30 works well) in order to have significant shocking.
diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/plots.py b/doc/RTD/source/SubgridModels/AGNSpinJets/plots.py
new file mode 100644
index 0000000000000000000000000000000000000000..c16a46651c32a9fd9c358b26813017c5c3e33dd3
--- /dev/null
+++ b/doc/RTD/source/SubgridModels/AGNSpinJets/plots.py
@@ -0,0 +1,834 @@
+import os
+
+if (
+    os.path.exists("efficiencies.svg")
+    and os.path.exists("modes.svg")
+    and os.path.exists("spec_ang_mom.svg")
+    and os.path.exists("spinup.svg")
+):
+    # no need to rerun script
+    exit()
+
+import numpy as np
+from scipy.optimize import fsolve
+
+
+def Z1(x):
+    return 1 + (1 - x ** 2) ** 0.3333 * (
+        (1 + np.absolute(x)) ** 0.3333 + (1 - np.absolute(x)) ** 0.3333
+    )
+
+
+def Z2(x):
+    return np.sqrt(3 * x ** 2 + Z1(x) ** 2)
+
+
+def r_hor(x):
+    return 1 + np.sqrt(1 - x ** 2)
+
+
+def r_isco(x):
+    return 3 + Z2(x) - np.sign(x) * np.sqrt((3 - Z1(x)) * (3 + Z1(x) + 2 * Z2(x)))
+
+
+def eps_NT(x):
+    return 1 - np.sqrt(1 - 2 / 3 * 1 / r_isco(x))
+
+
+def eff_sd(m, a, limit):
+    return limit - (
+        1
+        / 10
+        * 1
+        / m
+        * (
+            0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / m)
+            + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / m)
+        )
+        * (0.9663 - 0.9292 * a) ** -0.5693
+    ) / (1 - np.sqrt(1 - 2 / (3 * r_isco(a))))
+
+
+def find_root(a, limit):
+    return fsolve(eff_sd, x0=1, args=(a, limit))[0]
+
+
+def A(x):
+    return (0.9663 - 0.9292 * x) ** -0.5693
+
+
+def B(x):
+    return (4.627 - 4.445 * x) ** -0.5524
+
+
+def C(x):
+    return (827.3 - 718.1 * x) ** -0.706
+
+
+def m_dot_crit1(x, limit):
+    C1 = C(x) / B(x)
+    eps_1 = 16 * limit / (0.985 * A(x)) * eps_NT(x)
+    N1 = 0.015 / 0.985
+    res1 = (
+        1.6
+        / B(x)
+        * 1
+        / (2 * C1 * eps_1)
+        * (
+            np.sqrt(
+                (C1 * (1 - eps_1) + N1 - eps_1) ** 2 + 4 * eps_1 * C1 * (N1 - eps_1 + 1)
+            )
+            + C1 * (1 - eps_1)
+            + N1
+            - eps_1
+        )
+    )
+    return res1
+
+
+def m_dot_crit2(x, limit):
+    C1 = C(x) / B(x)
+    eps_1 = 16 * limit / (0.985 * A(x)) * eps_NT(x)
+    N1 = 0.015 / 0.985
+    res1 = (
+        1.6
+        / B(x)
+        * 1
+        / (2 * C1 * eps_1)
+        * (
+            -1
+            * np.sqrt(
+                (C1 * (1 - eps_1) + N1 - eps_1) ** 2 + 4 * eps_1 * C1 * (N1 - eps_1 + 1)
+            )
+            + C1 * (eps_1 - 1)
+            + N1
+            - eps_1
+        )
+    )
+    return res1
+
+
+def beta(alfa):
+    return 1 / (1 + 2 * alfa)
+
+
+def gamma(alpha):
+    return (8 - 3 * beta(alpha)) / (6 - 3 * beta(alpha))
+
+
+def t1(alpha):
+    return -0.2703 * gamma(alpha) + 1.3603
+
+
+def t2(alpha):
+    return -0.94 + 4.475 * (gamma(alpha) - 1.444) - 5.1402 * (gamma(alpha) - 1.444) ** 2
+
+
+def t3(alpha):
+    return -0.84 * np.log10(alpha) - 0.919 + 0.643 * np.exp(-0.209 / alpha)
+
+
+def t4(x):
+    return (0.6365 * r_isco(x) - 0.4828) * (1 + 11.9 * np.exp(-0.838 * r_isco(x) ** 4))
+
+
+def t5(x):
+    return 1.444 * np.exp(-1.01 * r_isco(x) ** 0.86) + 0.1
+
+
+def T(x, alpha):
+    return (
+        0.31
+        * (1 + (t4(x) / r_hor(x)) ** 0.9) ** (t2(alpha) + t3(alpha))
+        * 1
+        / (r_hor(x) - t5(x)) ** (t1(alpha))
+    )
+
+
+def eta(x, alpha):
+    return 1 + gamma(alpha) / (gamma(alpha) - 1) * T(x, alpha)
+
+
+def f1(x):
+    return 0.0871 * r_isco(x) - 0.1082
+
+
+def f2(alpha):
+    return 0.5 - 7.798 * (gamma(alpha) - 1.333) ** 1.26
+
+
+def f3(x):
+    return 0.153 * (r_isco(x) - 0.6) ** 0.3 + 0.105
+
+
+def f4(x, alpha):
+    return (
+        f3(x)
+        * (0.9 * gamma(alpha) - 0.2996)
+        * (1.202 - 0.08 * (np.log10(alpha) + 2.5) ** 2.6)
+    )
+
+
+def f5(alpha):
+    return -1.8 * gamma(alpha) + 4.299 - 0.018 + 0.018 * (np.log10(alpha) + 2) ** 3.571
+
+
+def f6(x, alpha):
+    return (
+        f4(x, alpha)
+        * (((0.14 * np.log10(r_hor(x) ** f5(alpha)) + 0.23) / f4(x, alpha)) ** 10 + 1)
+        ** 0.1
+    )
+
+
+def L_adv(x, alpha):
+    return (
+        f2(alpha)
+        + (f1(x) + 10 ** f6(x, alpha)) * (1.15 - 0.03 * (np.log10(alpha) + 3) ** 2.37)
+    ) / eta(x, alpha)
+
+
+a = np.arange(-1, 1, 0.0001)
+mdotcrit1 = m_dot_crit1(a, 0.5)
+mdotcrit2 = m_dot_crit2(a, 0.5)
+m_a_90 = [find_root(x, 0.9) for x in a]
+m_a_75 = [find_root(x, 0.75) for x in a]
+m_a_50 = [find_root(x, 0.5) for x in a]
+
+import matplotlib
+
+matplotlib.use("Agg")
+import matplotlib.pyplot as plt
+import matplotlib.gridspec as gridspec
+
+fig = plt.figure(figsize=(8, 6))
+
+plt.style.use("classic")
+plt.fill_between(a, [0.0001 for x in a], [0.028 for x in a], color="blue", alpha=0.2)
+plt.fill_between(a, [0.028 for x in a], mdotcrit1, color="red", alpha=0.2)
+plt.fill_between(a, mdotcrit1, [375 for x in a], color="orange", alpha=0.2)
+plt.ylabel("$\dot{m}$", fontsize=24, usetex=True)
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.yscale("log")
+plt.axis([-1, 1, 0.001, 100])
+plt.text(-0.22, 0.004, "Thick disc", fontsize=20)
+plt.text(-0.2, 0.33, "Thin disc", fontsize=20)
+plt.text(-0.2, 18, "Slim disc", fontsize=20)
+plt.minorticks_on()
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+
+plt.savefig("modes.svg", bbox_inches="tight")
+plt.close()
+
+phi = -20.2 * a ** 3 - 14.9 * a ** 2 + 34.0 * a + 52.6
+horizon_ang_vel = a / (2 * (1 + np.sqrt(1 - a ** 2)))
+jet_factor = (
+    0.05
+    / (4.0 * np.pi)
+    * 1
+    / 0.3
+    * phi ** 2
+    * horizon_ang_vel ** 2
+    * (1.0 + 1.38 * horizon_ang_vel ** 2 - 9.2 * horizon_ang_vel ** 4)
+)
+Z1_j = np.array(
+    [
+        1 + (1 - x ** 2) ** 0.333 * ((1 + abs(x)) ** 0.333 + (1 - abs(x)) ** 0.333)
+        for x in a
+    ]
+)
+Z2_j = np.array(np.sqrt(3 * a ** 2 + Z1_j ** 2))
+r_iso = 3 + Z2_j - np.sign(np.array(a)) * np.sqrt((3 - Z1_j) * (3 + Z1_j + 2 * Z2_j))
+eps_TD = 1 - np.sqrt(1 - 2 / (3 * r_iso))
+eps_ADAF1 = 0.144 * (6 / r_iso) * eps_TD * min(1, 0.028 / 0.0044)
+eps_ADAF2 = 0.144 * (6 / r_iso) * eps_TD * min(1, 0.001 / 0.0044)
+Jet_ADAF = jet_factor * 0.3
+Jet_SD = 0.22 * jet_factor
+Jet_TD1 = 10 ** -3 * 0.1 ** (-0.1) * 100 ** 0.2 * 10 ** (2 * 0.1) * jet_factor
+Jet_TD2 = 10 ** -3 * 0.1 ** (-0.1) * 10 ** (-1 * 0.1) * jet_factor
+eps_SD1 = (
+    1
+    / 10
+    * 1
+    / 1
+    * (
+        0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / 1)
+        + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / 1)
+    )
+    * (0.9663 - 0.9292 * a) ** -0.5693
+)
+eps_SD2 = (
+    1
+    / 10
+    * 1
+    / 50
+    * (
+        0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / 50)
+        + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / 50)
+    )
+    * (0.9663 - 0.9292 * a) ** -0.5693
+)
+
+mdot_bh_ADAF1 = (1 - Jet_ADAF / 4.447) * (1 - eps_ADAF1 - Jet_ADAF)
+mdot_bh_ADAF2 = (1 - Jet_ADAF / 4.447) * (1 - eps_ADAF2 - Jet_ADAF)
+mdot_bh_ADAF3 = (1 - Jet_ADAF / 11.56) * (1 - eps_ADAF1 - Jet_ADAF)
+mdot_bh_ADAF4 = (1 - Jet_ADAF / 11.56) * (1 - eps_ADAF2 - Jet_ADAF)
+mdot_bh_TD1 = (1 - Jet_TD1 / 4.447) * (1 - eps_TD - Jet_TD1)
+mdot_bh_TD2 = (1 - Jet_TD2 / 4.447) * (1 - eps_TD - Jet_TD2)
+
+
+fig = plt.figure(figsize=(18, 4))
+fig.subplots_adjust(wspace=0, hspace=0, top=1, bottom=0)
+gs = gridspec.GridSpec(1, 3, width_ratios=[1, 1, 1])
+plt.style.use("classic")
+
+plt.subplot(gs[0])
+plt.plot(
+    a,
+    eps_ADAF2,
+    linewidth=2,
+    label="$\epsilon_\mathrm{rad}(\dot{m}<0.0044)$",
+    color="red",
+)
+plt.plot(
+    a,
+    eps_ADAF1,
+    linewidth=2,
+    label="$\epsilon_\mathrm{rad}(\dot{m}=0.028)$",
+    color="red",
+    linestyle="--",
+)
+plt.plot(a, 0.97 * Jet_ADAF, linewidth=2, label="$\epsilon_\mathrm{jet}$", color="blue")
+plt.fill_between(a, eps_ADAF1, eps_ADAF2, color="red", alpha=0.2)
+plt.ylabel("$\epsilon_\mathrm{feedback}$", fontsize=24, usetex=True)
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.legend(loc="upper left", prop={"size": 13})
+plt.xticks([-1, -0.5, 0, 0.5, 1], [-1, -0.5, 0, 0.5, 1])
+plt.yticks(
+    [0.0001, 0.001, 0.01, 0.1, 1, 10, 100],
+    ["", 10 ** (-3), 10 ** (-2), 10 ** (-1), 10 ** (-0), 10 ** (1), 10 ** (2)],
+)
+plt.minorticks_on()
+plt.axis([-1, 1, 0.0001, 10])
+plt.yscale("log")
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.title("Thick disc", fontsize=16)
+
+plt.subplot(gs[1])
+plt.plot(a, 0.97 * eps_TD, linewidth=2, label="$\epsilon_\mathrm{rad}$", color="red")
+plt.plot(
+    a,
+    Jet_TD2,
+    linewidth=2,
+    label="$\epsilon_\mathrm{jet}(\dot{m}=0.028,M_\mathrm{BH}=10^9 \mathrm{M}_\odot)$",
+    color="blue",
+)
+plt.plot(
+    a,
+    Jet_TD1,
+    linewidth=2,
+    label="$\epsilon_\mathrm{jet}(\dot{m}=1,M_\mathrm{BH}=10^6 \mathrm{M}_\odot)$",
+    color="blue",
+    linestyle="--",
+)
+plt.fill_between(a, Jet_TD1, Jet_TD2, color="blue", alpha=0.2)
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.yscale("log")
+plt.legend(loc="upper left", prop={"size": 13})
+plt.xticks([-1, -0.5, 0, 0.5, 1], ["", -0.5, 0, 0.5, 1])
+plt.axis([-1, 1, 0.0001, 10])
+plt.yticks([0.001, 0.01, 0.1, 1, 10], ["", "", "", "", ""])
+plt.minorticks_on()
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.title("Thin disc", fontsize=16)
+
+plt.subplot(gs[2])
+plt.plot(
+    a, eps_SD1, linewidth=2, label="$\epsilon_\mathrm{rad}(\dot{m}=1)$", color="red"
+)
+plt.plot(
+    a,
+    eps_SD2,
+    linewidth=2,
+    label="$\epsilon_\mathrm{rad}(\dot{m}=50)$",
+    color="red",
+    linestyle="--",
+)
+plt.plot(a, Jet_SD, linewidth=2, label="$\epsilon_\mathrm{jet}$", color="blue")
+plt.fill_between(a, eps_SD1, eps_SD2, color="red", alpha=0.2)
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.yscale("log")
+plt.legend(loc="upper left", prop={"size": 13})
+plt.xticks([-1, -0.5, 0, 0.5, 1], ["", -0.5, 0, 0.5, 1])
+plt.axis([-1, 1, 0.0001, 10])
+plt.yticks([0.001, 0.01, 0.1, 1, 10], ["", "", "", "", ""])
+plt.minorticks_on()
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.title("Slim disc", fontsize=16)
+
+plt.savefig("efficiencies.svg", bbox_inches="tight")
+
+L_isco1 = [2 / 3 * 1 / np.sqrt(3) * (1 + 2 * np.sqrt(3 * r_isco(x) - 2)) for x in a]
+
+plt.style.use("classic")
+fig = plt.figure(figsize=(8, 6), linewidth=4)
+plt.plot(a, L_isco1, linewidth=2, label="$\ell_\mathrm{ISCO}$", color="red")
+plt.plot(
+    a,
+    0.45 * np.array(L_isco1),
+    linewidth=3,
+    linestyle="--",
+    label=r"$0.45\ell_\mathrm{ISCO}$",
+    color="red",
+)
+plt.plot(
+    a,
+    [L_adv(x, 0.1) for x in a],
+    linewidth=2,
+    label=r"$\ell_\mathrm{adv},\alpha=0.1$",
+    color="green",
+)
+plt.plot(
+    a,
+    [L_adv(x, 0.2) for x in a],
+    linewidth=2,
+    label=r"$\ell_\mathrm{adv},\alpha=0.2$",
+    color="teal",
+)
+plt.plot(
+    a,
+    [L_adv(x, 0.3) for x in a],
+    linewidth=2,
+    label=r"$\ell_\mathrm{adv},\alpha=0.3$",
+    color="purple",
+)
+plt.ylabel("$\ell_\mathrm{in}$", fontsize=24, usetex=True)
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.legend(loc="upper right", prop={"size": 14})
+plt.minorticks_on()
+plt.axis([-1, 1, 0, 5])
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+
+plt.savefig("spec_ang_mom.svg", bbox_inches="tight")
+plt.close()
+
+z1 = np.array(
+    [
+        1 + (1 - x ** 2) ** 0.333 * ((1 + abs(x)) ** 0.333 + (1 - abs(x)) ** 0.333)
+        for x in a
+    ]
+)
+z2 = np.array(np.sqrt(3 * a ** 2 + z1 ** 2))
+r_iso = 3 + z2 - np.sign(np.array(a)) * np.sqrt((3 - z1) * (3 + z1 + 2 * z2))
+
+phi = -20.2 * a ** 3 - 14.9 * a ** 2 + 34.0 * a + 52.6
+horizon_ang_vel = a / (2 * (1 + np.sqrt(1 - a ** 2)))
+jet_factor = (
+    0.05
+    / (4.0 * np.pi)
+    * 1
+    / 0.3
+    * phi ** 2
+    * horizon_ang_vel ** 2
+    * (1.0 + 1.38 * horizon_ang_vel ** 2 - 9.2 * horizon_ang_vel ** 4)
+)
+
+da_TD_acc_only = 2 / 3 * 1 / np.sqrt(3) * (
+    1 + 2 * np.sqrt(3 * r_iso - 2)
+) - 2 * a * np.sqrt(1 - 2 / (3 * r_iso))
+da_TD_Benson = (
+    2 / 3 * 1 / np.sqrt(3) * (1 + 2 * np.sqrt(3 * r_iso - 2))
+    - 2 * a * np.sqrt(1 - 2 / (3 * r_iso))
+    - (1.25 * 10 ** -3 * 0.1 ** (-0.1) * 100 ** 0.2 * 10 ** (2 * 0.1) * jet_factor)
+    * 2
+    / a
+    * (np.sqrt(1 - a ** 2))
+    * (1 + np.sqrt(1 - a ** 2))
+)
+da_ADAF_acc_only = L_adv(a, 0.1) - 2 * a
+da_ADAF_Benson = (
+    L_adv(a, 0.1)
+    - 2 * a
+    - (jet_factor * 0.3) * 2 / a * (np.sqrt(1 - a ** 2)) * (1 + np.sqrt(1 - a ** 2))
+)
+eps_SD = (
+    1
+    / 10
+    * 1
+    / 10
+    * (
+        0.985 / ((4.627 - 4.445 * a) ** -0.5524 + 1.6 * 1 / 10)
+        + 0.015 / ((827.3 - 718.1 * a) ** -0.706 + 1.6 * 1 / 10)
+    )
+    * (0.9663 - 0.9292 * a) ** -0.5693
+)
+da_SD_acc_only = L_adv(a, 0.1) - 2 * a * (1 - eps_SD)
+da_SD_Benson = (
+    L_adv(a, 0.1)
+    - 2 * a * (1 - eps_SD)
+    - (jet_factor * 0.22) * 2 / a * (np.sqrt(1 - a ** 2)) * (1 + np.sqrt(1 - a ** 2))
+)
+
+
+fig = plt.figure(figsize=(18, 4))
+fig.subplots_adjust(wspace=0, hspace=0, top=1, bottom=0)
+gs = gridspec.GridSpec(1, 3, width_ratios=[1, 1, 1])
+plt.style.use("classic")
+
+plt.subplot(gs[0])
+plt.plot(a, da_TD_acc_only, linewidth=2, label="Accretion only", color="black")
+plt.plot(a, da_TD_Benson, linewidth=1.5, label="Jet spindown included", color="blue")
+plt.plot(a, [0 for x in a], linewidth=1.5, color="black", linestyle="--")
+plt.ylabel("$\mathrm{d}a/\mathrm{d}\ln M_\mathrm{BH,0}$", fontsize=24, usetex=True)
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.legend(loc="lower left", prop={"size": 15})
+plt.minorticks_on()
+plt.axis([-1, 1, -4, 7])
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.title("Thin disc", fontsize=16)
+
+plt.subplot(gs[1])
+plt.plot(a, da_ADAF_acc_only, linewidth=2, label="Accretion only", color="black")
+plt.plot(a, da_ADAF_Benson, linewidth=1.5, label="Jet spindown included", color="blue")
+plt.plot(a, [0 for x in a], linewidth=1.5, color="black", linestyle="--")
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.xticks([-1.0, -0.5, 0.0, 0.5, 1.0], ["", -0.5, 0.0, 0.5, 1.0])
+plt.yticks([-8, -6, -4, -2, 0, 2, 4, 6, 8], ["", "", "", "", "", "", "", "", ""])
+plt.minorticks_on()
+plt.axis([-1, 1, -4, 7])
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.title("Thick disc", fontsize=16)
+
+plt.subplot(gs[2])
+plt.plot(a, da_SD_acc_only, linewidth=2, label="Accretion only", color="black")
+plt.plot(a, da_SD_Benson, linewidth=1.5, label="Jet spindown included", color="blue")
+plt.plot(a, [0 for x in a], linewidth=1.5, color="black", linestyle="--")
+plt.xlabel("$a$", fontsize=24, usetex=True)
+plt.tick_params(axis="y", right=True, direction="in")
+plt.xticks([-1.0, -0.5, 0.0, 0.5, 1.0], ["", -0.5, 0.0, 0.5, 1.0])
+plt.yticks([-8, -6, -4, -2, 0, 2, 4, 6, 8], ["", "", "", "", "", "", "", "", ""])
+plt.minorticks_on()
+plt.axis([-1, 1, -4, 7])
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=8,
+    width=1.2,
+    which="major",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="x",
+    direction="in",
+    bottom=True,
+    top=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.tick_params(
+    axis="y",
+    direction="in",
+    left=True,
+    right=True,
+    length=4,
+    width=0.9,
+    which="minor",
+    labelsize=16,
+)
+plt.title("Slim disc", fontsize=16)
+
+plt.savefig("spinup.svg", bbox_inches="tight")
diff --git a/doc/RTD/source/SubgridModels/AGNSpinJets/theory.rst b/doc/RTD/source/SubgridModels/AGNSpinJets/theory.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d57747b958abb5c0c91d562e442e6e295b43e3c6
--- /dev/null
+++ b/doc/RTD/source/SubgridModels/AGNSpinJets/theory.rst
@@ -0,0 +1,251 @@
+.. AGN spin and jet model
+   Filip Husko, 1 April 2022
+
+.. AGN_spin_jet:
+
+
+Model summary
+-------------
+
+Here we provide a comprehensive summary of the model. In order to more easily visualize the model, it is recommended to run the python script in order to generate plots that show various aspects of the model.
+
+Any model for realistic AGN jets must include black hole spin since jet powers depend steeply on spin, and because including spin provides a well-defined direction for the jets to be launched in. The spin (angular momentum) of BHs is best represented through the dimensionlesss spin parameter :math:`a=J_\mathrm{BH}c/M_\mathrm{BH}^2 G`, where :math:`J_\mathrm{BH}` is its angular momentum. For theoretical reasons, its magnitude cannot grow above 1. It can be positive, representing prograde accretion, or negative, representing retrograde accretion.
+
+Jet powers, in addition to depending on spin, also depend on which accretion state the black hole is in. We refer to these states by the shape of the accretion disk that surrounds the BH. We include three accretion states: the thick (or advection-dominated accretion flow; ADAF), thin (standard) and slim disk. Our main reference points for these disks are the following papers: `Shakura & Sunyaev (1973) <https://ui.adsabs.harvard.edu/abs/1973A%26A....24..337S/abstract>`_, `Narayan & Yi (1994) <https://ui.adsabs.harvard.edu/abs/1994ApJ...428L..13N/abstract>`_ and `Wang & Zhou. (1999) <https://ui.adsabs.harvard.edu/abs/1999ApJ...516..420W/abstract>`_, respectively.
+
+The thick disk appears at low accretion rates, has very strong jets and is inefficient at spinning up the black hole. The thin disk, appearing at intermediate accretion rates, typically has weak jets, strong radiation and efficiently spins up the black hole. The slim disk, corresponding to super-Eddington accretion, has features of both: in terms of geometry, orbits and angular momentum, it is similar to the thick disk. It is optically thin, leading to strong radiation. However, it also has strong jets. We assume that each subgrid accretion disk launches jets and radiates at the same time, regardless of the type it is. However, we use expressions for the jet and radiative efficiencies that depend on the type of the disk, and which are physically motivated.
+
+Transitions from one accretion state to another
+-----------------------------------------------
+
+.. figure:: modes.svg
+    :width: 600px
+    :align: center
+    :figclass: align-center
+    :alt: Accretion regimes
+
+    The type of accretion disk surrounding the BHs depending on their accretion rates and spins. The transition between the thick and thin disk is calculated assuming the viscosity parameter :math:`\alpha=0.2`, while the transition from thin to slim disk is assumed to occur when the latter is :math:`F=0.5` times as radiatively efficienct as the former.
+
+The state of the subgrid accretion disk depends mostly on the Eddington fraction, i.e. the (dimensionless) accretion rate of the BH in units of the Eddington accretion rate, which we denote as :math:`\dot{m}`. We assume that the subgrid accretion disk is thick for :math:`\dot{m}<0.03`, based on observations (`Russell et al. 2013 <https://ui.adsabs.harvard.edu/abs/2013MNRAS.432..530R/abstract>`_). This also allows us to constrain the value of one of the main parameters in any accretion model: the viscosity parameter :math:`\alpha` (which is related to the kinematic viscosity :math:`\nu`and sound speed :math:`c_\mathrm{s}` through :math:`\nu=\alpha c_\mathrm{s}H`, with :math:`c_\mathrm{s}` the sound speed and :math:`H` the disk half-thickness). Numerical calculations suggest that thick disks are present for :math:`\dot{m}<0.4\alpha^2` (`Yuan & Narayan 2014 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_), and this agrees with observations if :math:`\alpha=0.25-0.3`. These values agree very well with more direct observational estimates, which suggest :math:`\alpha=0.2-0.3` (`Martin et al. 2019 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_).
+
+The transition from the thin to the slim disk should occur around :math:`\dot{m}\approx 1`. However, the exact physics of this transition is not well understood. There is likely some spin dependence of the critical accretion rate, due to different radiative physics depending on spin. One of the main properties of slim disks is that they are less radiatively efficient than thin disks (`Sadowski et al. 2014 <https://ui.adsabs.harvard.edu/abs/2014MNRAS.439..503S/abstract>`_). We thus assume that the transition occurs when the radiative efficiency of a slim disk, :math:`\epsilon_\mathrm{r,SD}`, falls below some fraction of the radiative efficiency of a thin disk, :math:`\epsilon_\mathrm{r,TD}`. We quantify this as :math:`\epsilon_\mathrm{r,SD}<F\epsilon_\mathrm{r,SD}`, with :math:`F\approx 0.5` a free parameter. We give the expressions for both of the efficiencies below.
+
+Jet efficiencies
+----------------
+
+The jet efficiency is related to the jet power through :math:`\epsilon_\mathrm{j}=P_\mathrm{j}/\dot{M}_\mathrm{BH,0}c^2`, where :math:`\dot{M}_\mathrm{BH,0}` is the accretion rate measured in the simulation, e.g. the Bondi rate). We use the formula for the jet efficiency based on general-relativistic, magneto-hydrodynamical (GRMHD) simulations by `Tchekhovskoy et al. (2010) <https://ui.adsabs.harvard.edu/abs/2010ApJ...711...50T/abstract>`_:
+
+.. math::
+    \epsilon_\mathrm{j}=\frac{\kappa}{4\pi}\bigg(\frac{H/R}{0.3}\bigg)^\eta \phi_\mathrm{BH}^2\Omega_\mathrm{BH}^2\big(1+1.38\Omega_\mathrm{BH}^2-9.2\Omega_\mathrm{BH}^4\big),
+
+where :math:`\kappa\approx0.05` is a numerical factor which depends on the initial geometry of the magnetic field, :math:`\phi_\mathrm{BH}` is the dimensionless magnetic flux threading the horizon (see original paper for precise definition), and :math:`\Omega_\mathrm{BH}=a/2r_\mathrm{H}` is the (dimensionless) angular velocity of the black hole event horizon. Here, :math:`r_\mathrm{H}=1+\sqrt{1-a^2}` is the radius of the horizon in units of the gravitational radius :math:`R_\mathrm{G}=M_\mathrm{BH}G/c^2`. The formula above, for the jet efficiency, agrees very well with the results from higher-resolution simulations performed by `Narayan et al. (2021) <https://ui.adsabs.harvard.edu/abs/2010ApJ...711...50T/abstract>`_, who provide the following fit for the magnetic flux as a function of spin:
+
+.. math::
+    \phi_\mathrm{BH}(a)=-20.2a^3-14.9a^2+34a+52.6.
+    
+The `Tchekhovskoy et al. (2010) <https://ui.adsabs.harvard.edu/abs/2010ApJ...711...50T/abstract>`_ jet efficiency depends very steeply on spin (:math:`\epsilon_\mathrm{j}\propto a^2` for small spin and :math:`\epsilon_\mathrm{j}\propto a^6` near :math:`a=1`). It can reach values above 100 per cent for large spins, and is also different (weaker) for negative spins.
+
+The dependence of the jet efficiency on the type of accretion disk comes through the factor that depends on the aspect ratio :math:`H/R`, since accretion disks differ in this quantity. Theoretical, self-similar models of thick disks suggest :math:`H/R=0.5` (`Narayan & Yi 1995b <https://ui.adsabs.harvard.edu/abs/1995ApJ...452..710N/abstract>`_), but we instead take :math:`H/R=0.3`, more in line with simulations. For slim disks, which have received less attention in simulations, we assume the value :math:`H/R=1/(2\sqrt{5})\approx 0.2` (based on the self-similar model by `Wang & Zhou. 1999 <https://ui.adsabs.harvard.edu/abs/1999ApJ...516..420W/abstract>`_).
+
+Thin disks are, not surprisingly, much thinner. The value of :math:`H/R` in this regime is not a constant, but rather depends on the BH mass and accretion rate, slightly on radius and also on the viscosity parameter :math:`\alpha`. Thin disks have three different regions in the `Shakura & Sunyaev (1973) <https://ui.adsabs.harvard.edu/abs/1973A%26A....24..337S/abstract>`_ model. For simplicity, we model the whole disk as being represented with only one region. In region a), the innermost one, radiation dominates over gas pressure. It is typically very small or doesn't exist at all, so we disregard it as a possibility. In regions b) and c), gas pressure dominates over radiation pressure. In b), electrons dominate in the opacity, while in c), free-free absorption dominates. We leave both regions as a possibility, and leave the choice as a free parameter in the model (not likely to lead to large differences in galaxy/BH evolution). The expressions for the aspect ratio in these regions are
+
+.. math::
+    \bigg(\frac{H}{R}\bigg)_\mathrm{TD,b} = 1.25\times10^{-3} \alpha^{-1/10}\dot{m}^{1/5}\bigg(\frac{M_\mathrm{BH}}{10^8\hspace{0.5mm}\mathrm{M}_\odot}\bigg)^{-1/10}\bigg(\frac{R}{2R_\mathrm{G}}\bigg)^{1/20}
+
+in region b) and
+
+.. math::
+    \bigg(\frac{H}{R}\bigg)_\mathrm{TD,c} = 1.15\times10^{-3} \alpha^{-1/10}\dot{m}^{3/20}\bigg(\frac{M_\mathrm{BH}}{10^8\hspace{0.5mm}\mathrm{M}_\odot}\bigg)^{-1/10}\bigg(\frac{R}{2R_\mathrm{G}}\bigg)^{1/8}
+
+in region c). 
+
+The jet efficiency also depends on the slope :math:`\eta`. Classical jet theory (`Meier 2001 <https://ui.adsabs.harvard.edu/abs/2001Sci...291...84M/abstract>`_) suggests that jet powers depend on the aspect ratio linearly, so :math:`\eta=1`. This is also in line with some simulations finding a reduction in jet efficiencies with the aspect ratio (e.g. `Tchekhovskoy et al. 2014 <https://ui.adsabs.harvard.edu/abs/2014MNRAS.437.2744T/abstract>`_). In this scenario, jets launched from thin disks are of order :math:`\approx100` times less powerful than those launched from thick disks. On the other hand, some simulations of thin disks have found jet efficiencies similar to thick disk ones (e.g. `Liska et al. 2019 <https://ui.adsabs.harvard.edu/abs/2019MNRAS.487..550L/abstract>`_), which is supported by observations of blazars (`Ghisellini et al. 2014 <https://ui.adsabs.harvard.edu/abs/2014Natur.515..376G/abstract>`_). In this picture, thin jets are approximately as efficient as thick disk ones, which can in our case be implemented as :math:`\eta=0`. The reality is likely to be somwhere in between. Note that the choice of :math:`\eta` likely has a strong impact on the evolution of galaxies and BHs; our default choice is the classical picture in which :math:`\eta=1`.
+
+.. figure:: efficiencies.svg
+    :width: 1200px
+    :align: center
+    :figclass: align-center
+    :alt: Efficiencies
+
+    Feedback efficiencies (jet - blue, radiation - red) for all three accretion disk types. Shaded regions represent likely ranges of efficiencies (where the efficiencies depend on mass and/or accretion rate). The thin disk jet efficiencies were computed assuming the slope of the efficiency vs. aspect ratio relation is :math:`\eta=1`, and the aspect ratios were computed for region b) of the Shakura & Sunyaev solution. Radiative efficiencies in the thick disk were computed assuming the electron heating parameter :math:`\delta=0.2`.
+
+Radiative efficiencies
+----------------------
+
+In the EAGLE and COLIBRE models, all subgrid accretion disks are effectively thin, and the BH is always assumed to be in this regime. In our model, the radiative efficiency (defined in an analagous way to the jet efficiency, but using the luminosity) is no longer fixed at a value of order :math:`10` per cent. Instead, we use spin-dependant formulas that vary with the type of disk. In the thin disk, the radiative efficiency :math:`\epsilon_\mathrm{r,TD}` is related to the binding energy at the innermost stable circular orbit (ISCO) and is given by
+
+.. math::
+    \epsilon_\mathrm{r,TD}(a) = 1-e_\mathrm{ISCO}(a)=1-\sqrt{1-\frac{2}{3r_\mathrm{ISCO}(a)}}.
+    
+Here, :math:`r_\mathrm{ISCO}` is the radius of the ISCO in gravitational radii (see e.g. appendix B of `Fiacconi et al. 2018 <https://ui.adsabs.harvard.edu/abs/2018MNRAS.477.3807F/abstract>`_ for an expression giving the spin dependence). The radiative efficiency of the thin disk grows slowly from its minimum value of :math:`\approx4` per cent for :math:`a=-1` to :math:`\approx5.5` per cent for :math:`a=0`. For positive spins it grows more steeply; it is :math:`10` per cent by :math:`a=0.65`. Beyond that the dependence steepens even further, with values of :math:`20`, :math:`30` and :math:`40` per cent reached at :math:`a=0.95`, :math:`a=0.997` and :math:`a=1`, respectively.
+
+In the thick disk regime, radiative efficiencies are lower by a factor :math:`\approx100` than jet efficiencies. The formulas we use are based on results by `Mahadevan (1997) <https://ui.adsabs.harvard.edu/abs/1997ApJ...477..585M/abstract>`_, who studied cooling processes of electrons (which dominate in the radiation) in the context of the original thick disc solution. They found two different regimes: for :math:`\dot{m}<\dot{m}_\mathrm{crit,visc}`, viscous heating dominates the heating of electrons, whereas for :math:`\dot{m}_\mathrm{crit,visc}<\dot{m}<\dot{m}_\mathrm{crit,ADAF}`, it is dominated by ion-electron heating. Here, :math:`\dot{m}_\mathrm{crit,visc}` is the transitional value between the two thick disc (ADAF) regimes, and :math:`\dot{m}_\mathrm{crit,ADAF}=0.4\alpha^2` is the transitional accretion rate which separates thin and thick discs. The radiative efficiency in the viscous heating regime is given by
+
+.. math::
+    \epsilon_\mathrm{r,ADAF}=0.0002\epsilon_\mathrm{r,TD}\bigg(\frac{\delta_\mathrm{ADAF}}{0.0005}\bigg)\bigg(\frac{1-\beta}{0.5}\bigg)\bigg(\frac{6}{r_\mathrm{ISCO}}\bigg),
+
+while in the ion-heating regime it is given by
+
+.. math::
+    \epsilon_\mathrm{r,ADAF}=0.2\epsilon_\mathrm{r,TD}\bigg(\frac{\dot{m}}{\alpha^2}\bigg)\bigg(\frac{\beta}{0.5}\bigg)\bigg(\frac{6}{r_\mathrm{ISCO}}\bigg).
+    
+Here, :math:`\beta` is the ratio of gas pressure and total pressure (which includes the magnetic pressure). `Yuan & Narayan (2014) <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_ define a somewhat different parameter, :math:`\beta_\mathrm{ADAF}`, as the ratio of gas pressure and magnetic pressure. The two parameters are related by :math:`\beta=\beta_\mathrm{ADAF}/(1+\beta_\mathrm{ADAF})`. :math:`\beta_\mathrm{ADAF}` is not an independent parameter; many simulations have found that :math:`\alpha\beta_\mathrm{ADAF}\approx0.5` (e.g. `Begelman et al. 2021 <https://ui.adsabs.harvard.edu/abs/2022MNRAS.511.2040B/abstract>`_, see also `Yuan & Narayan 2014 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_ for a review), which we adopt. :math:`\delta_\mathrm{ADAF}` represents the fraction of viscous energy transferred to the electrons, and is constrained in theoretical studies between 0.1 and 0.5 (`Yuan & Narayan 2014 <https://ui.adsabs.harvard.edu/abs/2014ARA%26A..52..529Y/abstract>`_, `Sharma et al. 2007 <https://ui.adsabs.harvard.edu/abs/2007ApJ...667..714S/abstract>`_). Observations imply a value close to 0.2 (`Yuan et al. 2003 <https://ui.adsabs.harvard.edu/abs/2003ApJ...598..301Y/abstract>`_, `Liu & Wu 2013 <https://ui.adsabs.harvard.edu/abs/2013ApJ...764...17L/abstract>`_). The critical accretion rate between the two thick disc regimes can be found by ensuring that both formulas presented above yield the same radiative efficiency (at that accretion rate). This gives an accretion rate equal to
+
+.. math::
+    \dot{m}_\mathrm{crit,visc}=0.0002\bigg(\frac{\delta_\mathrm{ADAF}}{0.0005}\bigg)\bigg(\frac{1-\beta}{\beta}\bigg)\alpha^2.
+    
+For slim disks we take the radiative efficiency based on GRMHD simulations of super-Eddington accretion (for various BH spins) performed by `Sadowski et al. (2014) <https://ui.adsabs.harvard.edu/abs/2014MNRAS.439..503S/abstract>`_. `Madau et al. (2014) <https://ui.adsabs.harvard.edu/abs/2014ApJ...784L..38M/abstract>`_ found the following fitting function which represents the `Sadowski et al. (2014) <https://ui.adsabs.harvard.edu/abs/2014MNRAS.439..503S/abstract>`_ results:
+
+.. math::
+    \epsilon_\mathrm{r,SD}=\frac{0.1}{\dot{m}}A(a)\bigg( \frac{0.985}{1.6/\dot{m}+B(a)}+\frac{0.015}{1.6/\dot{m}+C(a)}\bigg),
+    
+where the three spin-dependant functions are given by :math:`A(a)=(0.9663-0.9292a)^{−0.5639}`, :math:`B(a)=(4.627-4.445a)^{−0.5524}` and :math:`C(a)=(827.3-718.1a)^{−0.7060}`. The radiative efficiency of slim disks, based on this formula, matches the thin disk radiative efficiency (given at the beginning of the section) at low accretion rates. At high accretion rates (:math:`\dot{m}\gtrapprox1`, but depending on spin), the radiative efficiency drops. These two formulas are used to decide when a disk transitions from thin to slim.
+
+Evolution of the black hole spin magnitude
+------------------------------------------
+
+.. figure:: spec_ang_mom.svg
+    :width: 600px
+    :align: center
+    :figclass: align-center
+    :alt: Angular momenta
+
+    Dimensionless pecific angular momentum of the thin disk at the innermost stable circular orbit (ISCO, solid red line), compared with the specific angular momentum at the inner radius (the event horizon) for advection-dominated flows (the thick and slim disk) for a few values of the viscosity parameter :math:`\alpha`. The dashed red line shows that the latter can be approximated as :math:`45` per cent of the former.
+
+The BH spin (or angular momentum) is, naturally, a vector. However, due to Lense-thirring torques (we discuss these in more detail below), the accretion disk is always either aligned or counteraligned with the rotational axis of the black hole. This means that almost all relevant quantities, such as the efficiencies discussed above, can be expressed as depending only on the magnitude of spin, but also allowing for a negative sign to account for counteraligned disks (retrograde accretion). This is also true for the evolution of the magnitude of spin.
+
+In the absence of jet spindown, the evolution of angular momentum is given simply by :math:`\dot{J}_\mathrm{BH}=L_\mathrm{in}\dot{M}_\mathrm{BH}`, where :math:`L_\mathrm{in}` is the specific angular momentum at the inner radius of the accretion disk. This can be transformed into an equation for spin evolution, yielding
+
+.. math::
+    \frac{\mathrm{d}a}{\mathrm{d}\ln M_\mathrm{BH,0}}=\ell_\mathrm{in}-2a e_\mathrm{in},
+    
+where :math:`\ell_\mathrm{in}` is the specific angular momentum in units where :math:`G` and :math:`c` are equal to unity, and :math:`\mathrm{d}\ln M_\mathrm{BH,0}=\mathrm{d}M_\mathrm{BH,0}/M_\mathrm{BH}` is the logarithmic change in mass, not including losses due to radiation (`Fanidakis et al. 2011 <https://ui.adsabs.harvard.edu/abs/2011MNRAS.410...53F/abstract>`_). The specific binding energy can be related to the radiative efficiency through :math:`e_\mathrm{in}=1-\epsilon_\mathrm{r}` for all three accretion states (for the thick disc, the radiative efficiency is negligible for this application). 
+
+For the thin disc, the inner radius :math:`R_\mathrm{in}` can be taken to be the radius of the ISCO, since orbits should quickly degrade within it. We thus take :math:`\ell_\mathrm{in}` as the specific angular momentum at the ISCO for the thin disc (the expression for which is given in e.g. Appendix B of `Fiacconi et al. 2018 <https://ui.adsabs.harvard.edu/abs/2018MNRAS.477.3807F/abstract>`_). For the thin disk, the spinup function (the equation shown above) is always positive, meaning that the BH will always be spun up to positive values. This means that the BH will be spun down if spin is negative, or spun up to an equilibrium value of :math:`a_\mathrm{eq}=1` if spin is positive. For advection-dominated disks (the thick and slim disk), we assume that :math:`\ell_\mathrm{in}` is :math:`45` per cent of the ISCO value, based on numerical GR calculations by `Popham & Gammie (1998) <https://ui.adsabs.harvard.edu/abs/1998ApJ...504..419P/abstract>`_. We base this assumption on fits of the `Popham & Gammie (1998) <https://ui.adsabs.harvard.edu/abs/1998ApJ...504..419P/abstract>`_ results done by `Benson & Babul (2009) <https://ui.adsabs.harvard.edu/abs/2009MNRAS.397.1302B/abstract>`_. We independently compared these fits to the ISCO values and found :math:`\ell_\mathrm{in}\approx0.45\ell_\mathrm{ISCO}` with no more than :math:`10` per cent error for all values of spin and relevant values of :math:`\alpha=0.1-0.3`.
+
+For the thick and slim disk, these lower specific angular momenta lead to a non-zero equilibrium spin value :math:`a_\mathrm{eq}<1`. If :math:`a>a_\mathrm{eq}`, the BH will be spun down due to frame-dragging and viscosity; the frame-dragging rotationally accelerates any accreting gas (on account of the BH angular momentum), while viscosity carries away some of that angular momentum. Including jets into the model leads to further spindown. The jet spindown term (to be added to the spinup equation above) can be derived as 
+
+.. math::
+    \bigg(\frac{\mathrm{d}a}{\mathrm{d}\ln M_\mathrm{BH,0}}\bigg)_\mathrm{j}=-\epsilon_\mathrm{j}(a)\frac{\sqrt{1-a^2}}{a}\bigg[\Big(\sqrt{1-a^2}+1 \Big)^2+a^2 \bigg]
+    
+(see `Benson & Babul 2009 <https://ui.adsabs.harvard.edu/abs/2009MNRAS.397.1302B/abstract>`_ for a derivation, which we have independently verified). Including jet spindown leads to even lower equilibrium spin values; e.g. for the thick disk this is only :math:`a_\mathrm{eq}\approx0.25`.
+
+.. figure:: spinup.svg
+    :width: 1200px
+    :align: center
+    :figclass: align-center
+    :alt: Spinup/spindown function
+
+    Spinup/spindown function (the rate of black hole spin evolution) as a function of spin for all three accretion disk types. Black lines show evolution with only accretion included, while blue lines show the total including jet spindown. These plots show that the thin disk is always spun up to to :math:`a_\mathrm{eq}=1`, even with jets (due to low jet efficiencies). The advection-dominated disks (thick and slim disk) are spun up to positive equilibrium values :math:`a_\mathrm{eq}<1`, or spun down to such an equilibrium value if :math:`a>a_\mathrm{eq}`. This is due to extraction of rotational energy from the BH by frame dragging and transport of the angular momentum to large distances through viscous forces. Including jet spindown pushes these equilibrium spins to even smaller values.
+
+Evolution of the black hole spin direction
+------------------------------------------
+
+In the previous section we claimed that the evolution of the magnitude of spin depends only on whether accretion is prograde or retrograde. The two remaining questions are: 1) what about its direction, and 2) how to decide whether accretion is prograde or retrograde. We will now address the first of these.
+
+Lense-Thirring torques (`Lense & Thirring 1918 <https://ui.adsabs.harvard.edu/abs/1918PhyZ...19..156L/abstract>`_) arise from additional GR forces that operate near spinning BHs, related to the frame-dragging of spacetime. In isolation, they cause the precession of a parcel of gas as it orbits around the BH. For accretion disks, their effects depend on the type of disk (see `Nixon & King 2016 <https://ui.adsabs.harvard.edu/abs/2016LNP...905...45N/abstract>`_ for a review). Lense-Thirring torques do not have a component in the direction of the BH spin vector, which is why they do not play a role in the evolution of the magnitude of spin.
+
+In all cases, Lense-Thirring torques are effective only within some radius :math:`R_\mathrm{warp}`, which marks the boundary between the outer disk and an inner region, within which the BH can 'communicate' through these torques with the disk. Within this radius, the disk is on average aligned or counteraligned with the BH, whereas outside it, it is aligned with some large-scale angular momentum direction (which we can measure in the simulation) - hence the name warp radius. Given some surface density, one can also define the warp mass :math:`M_\mathrm{warp}` and the warp angular momentum :math:`J_\mathrm{warp}` as the total mass and angular momentum within :math:`R_\mathrm{warp}`, respectively. We will discuss how all of these warp-related quantities are calculated in each of the accretion disks further below, but for now we focus on how these warped disks feature in our model.
+
+In terms of the evolution of the spin direction, the main assumption of our model is as follows (see `King et al. 2005 <https://ui.adsabs.harvard.edu/abs/2005MNRAS.363...49K/abstract>`_ for the original argument, and additional discussions in e.g. `Fanidakis et al. 2011 <https://ui.adsabs.harvard.edu/abs/2011MNRAS.410...53F/abstract>`_, `Fiacconi et al. 2018 <https://ui.adsabs.harvard.edu/abs/2018MNRAS.477.3807F/abstract>`_ and `Griffin et al. 2019a <https://ui.adsabs.harvard.edu/abs/2019MNRAS.487..198G/abstract>`_). All matter that flows through an accretion disk is aligned or counteraligned with the BH spin vector in the accretion process. Due to conservation of angular momentum, the spin vector itself also has to adjust to keep the total angular momentum conserved. In the process of consuming one warp mass :math:`M_\mathrm{warp}`, the direction of the BH spin vector is aligned to match the direction of the total angular momentum of the system comprising the BH and the disk out to the warp radius. The direction of the BH spin vector can then be determined from :math:`\vec{J}_\mathrm{warp}=\vec{J}_\mathrm{BH}+J_\mathrm{warp}\hat{J}_\mathrm{d}`, where :math:`\vec{J}_\mathrm{BH}` is the old BH angular momentum vector, and :math:`\hat{J}_\mathrm{d}` is the direction of the large-scale accretion disk (which we assume matches the direction of the angular momentum of the gas in the BH smoothing kernel).
+
+In practice, the BH will consume parcels of mass that differ from :math:`M_\mathrm{warp}`. We assume that any such parcel of mass :math:`\Delta M` (e.g. the mass to be consumed within a single time step) can be split up onto :math:`n=\Delta M / M_\mathrm{warp}` individual increments of accretion, so the total angular momentum of the system within that time step is :math:`\vec{J}_\mathrm{warp}=\vec{J}_\mathrm{BH}+n J_\mathrm{warp}\hat{J}_\mathrm{d}`, i.e. :math:`n` warp angular momenta are consumed, with an angular momentum of :math:`\Delta \vec{J}=n J_\mathrm{warp}\hat{J}_\mathrm{d}=(J_\mathrm{warp}/M_\mathrm{warp})\Delta M `. This can also be viewed as the BH consuming material with a specific angular momentum of :math:`L_\mathrm{warp}=J_\mathrm{warp}/M_\mathrm{warp}`. Note that this picture is only valid if the BH spin vector does not change much during this process (in both magnitude and direction), which can be ensured with wisely chosen time steps.
+
+Deciding whether accretion is prograde or retrograde
+----------------------------------------------------
+
+We now discuss how to decide whether the sign of spin is positive or negative. In the process of communicating with the inner disk through Lense-Thirring torques, the disk either aligns or counteraligns with the BH spin vector. The condition for which of the two occurs can be derived by assuming that the magnitude of the spin does not change during this alignment (`King et al. 2005 <https://ui.adsabs.harvard.edu/abs/2005MNRAS.363...49K/abstract>`_). Accretion is retrograde if
+
+.. math::
+    \cos \theta<-\frac{J_{\mathrm{warp}}}{2 J_{\mathrm{BH}}},
+    
+where :math:`\cos \theta=\hat{J}_\mathrm{BH}\cdot\hat{J}_\mathrm{d}` is the angle between the initial spin vector and the large-scale angular momentum of the disk. If this condition is not fulfilled, accretion is assumed to be prograde. Note that retrograde accretion is only possible if the angle between the spin vector and the large-scale accretion disk is larger than :math:`90^\circ`, and if the warp angular momentum is comparable to the BH one.
+
+Structure of the warped and precessing accretion disk
+-----------------------------------------------------
+
+As mentioned already, Lense-Thirring torques have different effects depending on the type of accretion disk. In particular, their effects depend on the ratio of the viscosity parameter :math:`\alpha` and the aspect ratio :math:`H/R`. For thin discs (:math:`\alpha\gg H/R`), the disk is exactly warped as in the manner described in the preceeding two sections (`Bardeen & Peterson 1975 <https://ui.adsabs.harvard.edu/abs/1975ApJ...195L..65B/abstract>`_). The radius :math:`R_\mathrm{warp}` which separates the inner and outer accretion disc can be calculated by equating the Lense-Thirring precession time-scale (:math:`t_\mathrm{p}=2\pi/\Omega_\mathrm{p}`, with :math:`\Omega_\mathrm{p}=2GJ_\mathrm{BH}/c^2R^3` the precession rate) and the vertical warp propagation time-scale (:math:`t_\mathrm{warp}=R^2/\nu_2`, with :math:`\nu_2` the kinematic viscosity in the vertical direction) (e.g. `Martin et al. 2007 <https://ui.adsabs.harvard.edu/abs/2007MNRAS.381.1617M/abstract>`_). The vertical kinematic viscosity :math:`\nu_2` can be related to the horizontal one, :math:`\nu_1`, by :math:`\nu_2=\xi\nu_1`, with :math:`\xi` a numerical parameter given by
+
+.. math::
+    \xi=\frac{2}{\alpha^2}\frac{1+7\alpha^2}{4+\alpha^2}
+
+(`Ogilvie 1999 <https://ui.adsabs.harvard.edu/abs/1999MNRAS.304..557O/abstract>`_, see also `Lodato et al. 2010 <https://ui.adsabs.harvard.edu/abs/2010MNRAS.405.1212L/abstract>`_ for a detailed discussion). We use the relation :math:`\dot{M}=3\pi\nu_1 \Sigma` to calculate :math:`\nu_1`, and therefore :math:`\nu_2`. The warp radius will depend on which region of the thin disc we assume, with each having its own expression for :math:`\Sigma`. In region b) of the `Shakura & Sunyaev (1973) <https://ui.adsabs.harvard.edu/abs/1973A%26A....24..337S/abstract>`_ thin disk, the surface density can be expressed as
+
+.. math::
+    \Sigma_\mathrm{TD,b}=6.84 \times 10^{5} \mathrm{~g} \mathrm{~cm}^{-2} \alpha^{-4 / 5} \dot{m}^{3 / 5}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{1 / 8}\left(\frac{R}{R_{\mathrm{S}}}\right)^{-3 / 5},
+    
+while in region c) we have
+
+.. math::
+    \Sigma_\mathrm{TD,c}=3.41 \times 10^{4} \mathrm{~g} \mathrm{~cm}^{-2} \alpha^{-4 / 5} \dot{m}^{7/10}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{1 / 20}\left(\frac{R}{R_{\mathrm{S}}}\right)^{-3 / 4}.
+    
+These relations lead to the following expressions for :math:`R_\mathrm{warp}`:
+
+.. math::
+    R_{\text {warp,TD,b}}=3410 R_{S} a^{5 / 8} \xi^{-5/8}\alpha^{-1 / 2} \dot{m}^{-1 / 4}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{1 / 8}
+    
+(in region b) and
+
+.. math::
+    R_\mathrm{warp,TD,c}=2629R_\mathrm{S}a^{4/7}\xi^{-4/7}\alpha^{-16/35}\dot{m}^{-6/35}\bigg(\frac{M_\mathrm{BH}}{10^8\hspace{0.5mm}\mathrm{M}_\odot}  \bigg)^{4/35},
+    
+(in region c), with :math:`R_\mathrm{S}=2R_\mathrm{G}` the Schwarzschild radius. These warp radii are generally of order :math:`\approx1000R_\mathrm{G}`, which can lead to fairly quick alignment of the thin disk with the large-scale angular momentum direction (quicker than any significant evolution in mass or spin magnitude, illustrating why the inclusion of the effects of Lense-Thirring torques is important).
+
+In the context of thin disks, there is a futher complication. The self-gravity of the disk may become important at large radii (see `Lodato 2007 <https://www.sif.it/riviste/sif/ncr/econtents/2007/030/07/article/0>`_ for a review). The disk will fragment in the region where the Toomre parameter is :math:`Q(R)>1`. We thus assume that the disk extends out to where :math:`Q(R_\mathrm{sg})=1`. The self-gravity radius :math:`R_\mathrm{sg}` can be calculated from this condition and the definition of the Toomre parameter :math:`Q=\Omega c_{\mathrm{s}} /(\pi G \Sigma)`, yielding
+
+.. math::
+    R_{\text {sg,TD,b}}=6460 R_{S} \alpha^{28/51} \dot{m}^{-18/51}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{-49/51}
+    
+in region b) and
+
+.. math::
+    R_\mathrm{sg,TD,c}=2456 R_{S} \alpha^{28/45} \dot{m}^{-22/45}\left(\frac{M_{\mathrm{BH}}}{10^{8} M_{\odot}}\right)^{-52/45}
+    
+in region c). In all our calculations involving :math:`R_\mathrm{warp}` (for deciding the sign of spin and evolving the direction of angular momentum, as described in the preceeding sections), we always take the minimum of :math:`R_\mathrm{warp}` and :math:`R_\mathrm{sg}`. This is because if :math:`R_\mathrm{sg}<R_\mathrm{warp}`, the entire disk of extent :math:`R_\mathrm{sg}` will be warped.
+
+The thick disk does not experience the Bardeen-Peterson effect, i.e. it is never truly aligned nor counter-aligned in its inner regions. Instead, the disk precesses out to several dozen :math:`R_\mathrm{G}`, as seen in simulations (e.g. `Fragile et al. 2007 <https://ui.adsabs.harvard.edu/abs/2007ApJ...668..417F/abstract>`_), and likely observations through quasi-periodic oscillations (QPO; e.g. `Ingram et al. 2012 <https://ui.adsabs.harvard.edu/abs/2012MNRAS.419.2369I/abstract>`_). The slim disk has received much less attention in both simulations and observations (it is both harder to simulate and observe), but its similarity to the thick disk in its geometric aspects likely means that it precesses in a similar manner.
+
+The exact behaviour of the thick and slim disk (which we will collectively call the advection-dominated disks) again depends on the ratio of :math:`\alpha` and :math:`H/R`. Unfortunately, the advection-dominated disks both satisfy :math:`\alpha\approx H/R`, and in this regime, the effects of Lense-Thirring torques are not well understood from a theoretical perspective. However, if :math:`\alpha\ll H/R` (the so-called bending-wave regime), Lense-Thirring torques are known to cause precession of the entire inner disk as a solid body, as seen in observations and simulations. For simplicity, we will thus assume this to be the case for advection-dominated disks.
+
+`Lubow et al. (2002) <https://ui.adsabs.harvard.edu/abs/2002MNRAS.337..706L/abstract>`_ studied the bending-wave regime. In the inner regions, the disk precesses around the spin axis, while in the outer regions, it is aligned with the large-scale angular momentum of the disk. Based on their results the transition radius between the precessing and non-precessing regions of the disk given by
+
+.. math::
+    R_\mathrm{warp,adv}=R_\mathrm{G}\bigg(\frac{384a}{25(H/R)^2}\bigg)^{2/5}.
+    
+In our model, we assume that the inner regions of the disks are on average aligned or counteraligned with the spin vector (one can think of this as averaging over the precession, which has periods of :math:`\approx`days, over long enough time scales). For simplicity, we  also refer to the radii within which this is true as the warp radii. For both of the advection-dominated disks, these radii are only of order several :math:`R_\mathrm{G}`. Note that similar values are found if one assumes that the Bardeen-Peterson effect operates in these disks. While there are some uncertainties in the assumptions we have made, we point out that using any of these values is much more physically motivated than using thin disk equations (the warp radii of order thousands of :math:`R_\mathrm{G}`), which is what is often done (e.g. `Griffin et al. 2019a <https://ui.adsabs.harvard.edu/abs/2019MNRAS.487..198G/abstract>`_, `Dubois et al. 2012 <https://ui.adsabs.harvard.edu/abs/2014MNRAS.440.1590D/abstract>`_).
+
+In order to determine the sign of spin and evolve the angular momentum direction, expressions for the warp mass :math:`M_\mathrm{warp}` and warp angular momentum :math:`J_\mathrm{warp}` are also needed. We calculate this using surface integrals as
+
+.. math::
+    M_\mathrm{warp}(R_\mathrm{warp})=2\pi\int_0^{R_\mathrm{warp}}\Sigma(R)R\mathrm{d}R,
+    
+and
+    
+.. math::
+    J_\mathrm{warp}(R_\mathrm{warp})=2\pi\int_0^{R_\mathrm{warp}}L(R)\Sigma(R)R\mathrm{d}R,
+    
+respectively. Here, :math:`L(R)` is the specific angular momentum. In the case of the thin disk, we assume Keplerian orbits, i.e. :math:`L(R)=\sqrt{M_\mathrm{BH}G R}`. For the advection-dominated disks, we assume that they are smaller by a numerical factor :math:`\Omega_0`, which is given in the self-similar solutions for the thick (`Narayan & Yi 1995b <https://ui.adsabs.harvard.edu/abs/1995ApJ...452..710N/abstract>`_) and slim disk (`Wang & Zhou 1999 <https://ui.adsabs.harvard.edu/abs/1999ApJ...516..420W/abstract>`_), seperately. The surface densities in both of these accretion disks are given by the same formula in the self-similar solutions, which is
+
+.. math::
+    \Sigma_\mathrm{adv}=\frac{\dot{M}}{2\pi R\vert v_\mathrm{r} \vert},
+
+where :math:`v_\mathrm{r}=-\alpha v_0 v_\mathrm{K}` is the radial velocity. Here, :math:`v_\mathrm{K}=\sqrt{M_\mathrm{BH}G/R}` is the Keplerian velocity, and :math:`v_0` is another numerical coefficient that differs between the two solutions. In the thick disk, the numerical coefficients are given by :math:`v_0=3/(5+2\varepsilon)` and :math:`\Omega_0=\sqrt{2\varepsilon/(5+2\varepsilon)}`, where :math:`\varepsilon=(5/3-\gamma)/(\gamma-1)`. The adiabatic index depends on how magnetized the disk is. In particular, it depends on the gas-to-total pressure ratio as :math:`\gamma = (8-3\beta)/(6-3\beta)`, and :math:`\beta` itself depends on :math:`\alpha` (see discussion above on radiative efficiency in the thin disk). :math:`v_0` varies weakly with :math:`\alpha`; for :math:`\alpha=0.05`, it is :math:`0.56`, whereas for :math:`\alpha=0.3`, it evaluates to 0.5. :math:`\Omega_0` depends on :math:`\alpha` somewhat more strongly; we obtain :math:`0.27` and :math:`0.41` for the same values of :math:`\alpha`. The latter value agrees well with the ratio of actual to Keplerian (ISCO) orbital velocity at the event horizon, which is :math:`0.45`. For the slim disc, :math:`v_0=\Omega_0=1/\sqrt{\gamma}`, with :math:`\gamma=5`.
+
+Black hole mergers
+------------------
+
+In the process of merging, BHs interact in a very complicated manner. Their final spin is not trivial to predict, and it can depend on a very large parameter space (including the mass ratio of the black holes and the relative orientation and magnitude of the spins). Orbital angular momentum plays a role in the merger as well. We use the fitting function found by `Rezzolla et al. (2007) <https://journals.aps.org/prd/abstract/10.1103/PhysRevD.78.044002>`_, whose results have been found to be very accurate in newer and more sophisticated studies that sweep the huge parameter space of possible merger configurations. The only flaw in these formulas is that they do not include the effects of gravitational radiation. However, the effects of this radiation is confined to a :math:`\approx10\%` level, and only if either of the spin vectors is aligned or counteraligned with the direction of the orbital angular momentum (if it is not, the fits are even more accurate).
+
+The final spin, according to `Rezzolla et al. (2007) <https://journals.aps.org/prd/abstract/10.1103/PhysRevD.78.044002>`_ can be calculated as
+
+.. math::
+    \mathbf{a}_\mathrm{fin} = \frac{1}{(1+q)^2}(\mathbf{a}_1+\mathbf{a}_2q^2+\mathbf{l}q),
+
+where :math:`q=M_2/M_1` is the mass ratio (such that :math:`M_2<M_1`), :math:`\mathbf{a}_1` and :math:`\mathbf{a}_2` are the spin vectors, and :math:`\mathbf{l}` is a vector whose direction is the same as that of the orbital angular momentum :math:`\mathbf{L}` (in the centre-of-mass frame), while its magnitude is given by
+
+.. math::
+    |\mathbf{l}|=\frac{s_{4}}{\left(1+q^{2}\right)^{2}}\left(\left|\mathbf{a}_{1}\right|^{2}+|\mathbf{a}|_{1}^{2} q^{4}+2\left|\mathbf{a}_{1} \| \mathbf{a}_{2}\right| q^{2} \cos \phi\right)+ \\
+    \left(\frac{s_{5} \mu+t_{0}+2}{1+q^{2}}\right)\left(\left|\mathbf{a}_{1}\right| \cos \theta+\left|\mathbf{a}_{2}\right| q^{2} \cos \xi\right)+ \\
+    2 \sqrt{3}+t_{2} \mu+t_{3} \mu^{2}.
+
+Here, :math:`\mu=q/(1+q)^2` is the symmetric mass ratio, and :math:`s_4 = -0.129`, :math:`s_5 = -0.384`, :math:`t_0 = -2.686`, :math:`t_2 = -3.454`, :math:`t_3 = 2.353`. The three cosines depend on the angles between the different vectors which play a role in the merger: :math:`\cos \phi=\hat{\mathbf{a}_{1}} \cdot \hat{\mathbf{a}_{\mathbf{2}}}`, :math:`\cos \theta=\hat{\mathbf{a}_{1}} \cdot \hat{\mathbf{l}}`, :math:`\cos \xi=\hat{\mathbf{a}_{2}} \cdot \hat{\mathbf{l}}`.
diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg
deleted file mode 100644
index 398c2bd88331cff072cd82590d4691f0c48bc148..0000000000000000000000000000000000000000
--- a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_EOS.svg
+++ /dev/null
@@ -1,2353 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
-  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Created with matplotlib (https://matplotlib.org/) -->
-<svg height="226.8pt" version="1.1" viewBox="0 0 226.8 226.8" width="226.8pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <defs>
-  <style type="text/css">
-*{stroke-linecap:butt;stroke-linejoin:round;}
-  </style>
- </defs>
- <g id="figure_1">
-  <g id="patch_1">
-   <path d="M 0 226.8 
-L 226.8 226.8 
-L 226.8 0 
-L 0 0 
-z
-" style="fill:#ffffff;"/>
-  </g>
-  <g id="axes_1">
-   <g id="patch_2">
-    <path d="M 34.02 197.316 
-L 224.532 197.316 
-L 224.532 2.268 
-L 34.02 2.268 
-z
-" style="fill:#ffffff;"/>
-   </g>
-   <g id="PathCollection_1">
-    <defs>
-     <path d="M 0 1 
-C 0.265203 1 0.51958 0.894634 0.707107 0.707107 
-C 0.894634 0.51958 1 0.265203 1 0 
-C 1 -0.265203 0.894634 -0.51958 0.707107 -0.707107 
-C 0.51958 -0.894634 0.265203 -1 0 -1 
-C -0.265203 -1 -0.51958 -0.894634 -0.707107 -0.707107 
-C -0.894634 -0.51958 -1 -0.265203 -1 0 
-C -1 0.265203 -0.894634 0.51958 -0.707107 0.707107 
-C -0.51958 0.894634 -0.265203 1 0 1 
-z
-" id="m85090810e3" style="stroke:#000000;"/>
-    </defs>
-    <g clip-path="url(#p70b35b528f)">
-     <use style="stroke:#000000;" x="146.991516" xlink:href="#m85090810e3" y="70.434351"/>
-    </g>
-   </g>
-   <g id="matplotlib.axis_1">
-    <g id="xtick_1">
-     <g id="line2d_1">
-      <defs>
-       <path d="M 0 0 
-L 0 3.5 
-" id="m00a3d680a8" style="stroke:#000000;stroke-width:0.8;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="60.395152" xlink:href="#m00a3d680a8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_1">
-      <!-- $\mathdefault{10^{-6}}$ -->
-      <defs>
-       <path d="M 8.90625 57.09375 
-L 8.90625 60.203125 
-Q 20.90625 60.203125 27.09375 66.59375 
-Q 28.796875 66.59375 29.09375 66.1875 
-Q 29.40625 65.796875 29.40625 64 
-L 29.40625 7.90625 
-Q 29.40625 4.90625 30.84375 4 
-Q 32.296875 3.09375 38.703125 3.09375 
-L 41.90625 3.09375 
-L 41.90625 0 
-Q 38.40625 0.296875 25.703125 0.296875 
-Q 13 0.296875 9.5 0 
-L 9.5 3.09375 
-L 12.703125 3.09375 
-Q 19 3.09375 20.5 4 
-Q 22 4.90625 22 7.90625 
-L 22 59.703125 
-Q 16.796875 57.09375 8.90625 57.09375 
-z
-" id="CMUSerif-Roman-49"/>
-       <path d="M 3.90625 32 
-Q 3.90625 46.703125 7.59375 54.703125 
-Q 12.796875 66.59375 25 66.59375 
-Q 27.59375 66.59375 30.296875 65.890625 
-Q 33 65.203125 36.453125 62.5 
-Q 39.90625 59.796875 42 55.40625 
-Q 46 46.90625 46 32 
-Q 46 17.40625 42.296875 9.40625 
-Q 36.90625 -2.203125 24.90625 -2.203125 
-Q 20.40625 -2.203125 15.84375 0.09375 
-Q 11.296875 2.40625 8.40625 7.90625 
-Q 3.90625 16.203125 3.90625 32 
-z
-M 12.203125 33.203125 
-Q 12.203125 18.09375 13.296875 12.09375 
-Q 14.5 5.59375 17.84375 2.796875 
-Q 21.203125 0 24.90625 0 
-Q 28.90625 0 32.25 3 
-Q 35.59375 6 36.59375 12.5 
-Q 37.703125 18.90625 37.703125 33.203125 
-Q 37.703125 47.09375 36.703125 52.703125 
-Q 35.40625 59.203125 31.90625 61.796875 
-Q 28.40625 64.40625 24.90625 64.40625 
-Q 23.59375 64.40625 22.1875 64 
-Q 20.796875 63.59375 18.796875 62.5 
-Q 16.796875 61.40625 15.25 58.59375 
-Q 13.703125 55.796875 13 51.59375 
-Q 12.203125 46.203125 12.203125 33.203125 
-z
-" id="CMUSerif-Roman-48"/>
-       <path d="M 1 18.59375 
-L 1 24.5 
-L 27.59375 24.5 
-L 27.59375 18.59375 
-z
-" id="CMUSerif-Roman-45"/>
-       <path d="M 4.203125 31.59375 
-Q 4.203125 47.296875 12.203125 56.9375 
-Q 20.203125 66.59375 30.5 66.59375 
-Q 36.5 66.59375 39.84375 63.546875 
-Q 43.203125 60.5 43.203125 55.796875 
-Q 43.203125 53.203125 41.703125 52.09375 
-Q 40.203125 51 38.59375 51 
-Q 36.796875 51 35.390625 52.203125 
-Q 34 53.40625 34 55.59375 
-Q 34 60.09375 39.5 60.09375 
-Q 36.90625 64.09375 30.703125 64.09375 
-Q 28.796875 64.09375 26.84375 63.546875 
-Q 24.90625 63 22.34375 61.140625 
-Q 19.796875 59.296875 17.84375 56.34375 
-Q 15.90625 53.40625 14.546875 47.90625 
-Q 13.203125 42.40625 13.203125 35.203125 
-L 13.203125 32.796875 
-Q 17.296875 42.703125 25.6875 42.703125 
-Q 34.09375 42.703125 39.890625 36.296875 
-Q 45.703125 29.90625 45.703125 20.40625 
-Q 45.703125 10.703125 39.640625 4.25 
-Q 33.59375 -2.203125 25.09375 -2.203125 
-Q 21.296875 -2.203125 17.84375 -0.59375 
-Q 14.40625 1 11.203125 4.59375 
-Q 8 8.203125 6.09375 15.140625 
-Q 4.203125 22.09375 4.203125 31.59375 
-z
-M 13.40625 22.59375 
-Q 13.40625 12.796875 15.203125 8.09375 
-Q 15.5 7.296875 16.140625 6.25 
-Q 16.796875 5.203125 17.9375 3.796875 
-Q 19.09375 2.40625 21 1.5 
-Q 22.90625 0.59375 25.09375 0.59375 
-Q 31.796875 0.59375 35 7.09375 
-Q 36.703125 10.703125 36.703125 20.5 
-Q 36.703125 30.5 34.90625 34.203125 
-Q 31.796875 40.40625 25.59375 40.40625 
-Q 21.40625 40.40625 18.5 37.5 
-Q 15.59375 34.59375 14.5 30.75 
-Q 13.40625 26.90625 13.40625 22.59375 
-z
-" id="CMUSerif-Roman-54"/>
-      </defs>
-      <g transform="translate(52.295152 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-54"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_2">
-     <g id="line2d_2">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="95.033698" xlink:href="#m00a3d680a8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_2">
-      <!-- $\mathdefault{10^{-4}}$ -->
-      <defs>
-       <path d="M 2.796875 16.5 
-L 2.796875 19.59375 
-L 33.5 66.5 
-Q 34.296875 67.703125 35.5 67.703125 
-Q 36.59375 67.703125 36.84375 67.25 
-Q 37.09375 66.796875 37.09375 65.09375 
-L 37.09375 19.59375 
-L 47.09375 19.59375 
-L 47.09375 16.5 
-L 37.09375 16.5 
-L 37.09375 7.796875 
-Q 37.09375 4.90625 38.296875 4 
-Q 39.5 3.09375 44.703125 3.09375 
-L 46.796875 3.09375 
-L 46.796875 0 
-Q 42.703125 0.296875 33.203125 0.296875 
-Q 23.796875 0.296875 19.703125 0 
-L 19.703125 3.09375 
-L 21.796875 3.09375 
-Q 27 3.09375 28.203125 4 
-Q 29.40625 4.90625 29.40625 7.796875 
-L 29.40625 16.5 
-z
-M 5.59375 19.59375 
-L 30 19.59375 
-L 30 56.90625 
-z
-" id="CMUSerif-Roman-52"/>
-      </defs>
-      <g transform="translate(86.933698 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-52"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_3">
-     <g id="line2d_3">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="129.672243" xlink:href="#m00a3d680a8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_3">
-      <!-- $\mathdefault{10^{-2}}$ -->
-      <defs>
-       <path d="M 5 0 
-Q 5 1.796875 5.140625 2.34375 
-Q 5.296875 2.90625 6.09375 3.703125 
-L 25.296875 25.09375 
-Q 35.796875 36.90625 35.796875 47.203125 
-Q 35.796875 53.90625 32.296875 58.703125 
-Q 28.796875 63.5 22.40625 63.5 
-Q 18 63.5 14.296875 60.796875 
-Q 10.59375 58.09375 8.90625 53.296875 
-Q 9.203125 53.40625 10.203125 53.40625 
-Q 12.703125 53.40625 14.09375 51.84375 
-Q 15.5 50.296875 15.5 48.203125 
-Q 15.5 45.5 13.75 44.203125 
-Q 12 42.90625 10.296875 42.90625 
-Q 9.59375 42.90625 8.6875 43.046875 
-Q 7.796875 43.203125 6.390625 44.59375 
-Q 5 46 5 48.5 
-Q 5 55.5 10.296875 61.046875 
-Q 15.59375 66.59375 23.703125 66.59375 
-Q 32.90625 66.59375 38.90625 61.140625 
-Q 44.90625 55.703125 44.90625 47.203125 
-Q 44.90625 44.203125 44 41.5 
-Q 43.09375 38.796875 41.890625 36.6875 
-Q 40.703125 34.59375 37.5 31.25 
-Q 34.296875 27.90625 31.6875 25.5 
-Q 29.09375 23.09375 23.296875 18 
-L 12.703125 7.703125 
-L 30.703125 7.703125 
-Q 39.5 7.703125 40.203125 8.5 
-Q 41.203125 9.90625 42.40625 17.40625 
-L 44.90625 17.40625 
-L 42.09375 0 
-z
-" id="CMUSerif-Roman-50"/>
-      </defs>
-      <g transform="translate(121.572243 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_4">
-     <g id="line2d_4">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="164.310789" xlink:href="#m00a3d680a8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_4">
-      <!-- $\mathdefault{10^{0}}$ -->
-      <g transform="translate(157.410789 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-48"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_5">
-     <g id="line2d_5">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="198.949334" xlink:href="#m00a3d680a8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_5">
-      <!-- $\mathdefault{10^{2}}$ -->
-      <g transform="translate(192.049334 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/>
-      </g>
-     </g>
-    </g>
-    <g id="text_6">
-     <!-- Hydrogen number density $n_{\rm H}$ [cm$^{-3}$] -->
-     <defs>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-L 5.703125 3.09375 
-Q 11.09375 3.09375 12.34375 4 
-Q 13.59375 4.90625 13.59375 7.796875 
-L 13.59375 60.5 
-Q 13.59375 63.40625 12.34375 64.296875 
-Q 11.09375 65.203125 5.703125 65.203125 
-L 3.296875 65.203125 
-L 3.296875 68.296875 
-Q 6.796875 68 18.09375 68 
-Q 29.296875 68 32.796875 68.296875 
-L 32.796875 65.203125 
-L 30.40625 65.203125 
-Q 25 65.203125 23.75 64.296875 
-Q 22.5 63.40625 22.5 60.5 
-L 22.5 37.09375 
-L 52.40625 37.09375 
-L 52.40625 60.5 
-Q 52.40625 63.40625 51.15625 64.296875 
-Q 49.90625 65.203125 44.5 65.203125 
-L 42.09375 65.203125 
-L 42.09375 68.296875 
-Q 45.59375 68 56.90625 68 
-Q 68.09375 68 71.59375 68.296875 
-L 71.59375 65.203125 
-L 69.203125 65.203125 
-Q 63.796875 65.203125 62.546875 64.296875 
-Q 61.296875 63.40625 61.296875 60.5 
-L 61.296875 7.796875 
-Q 61.296875 4.90625 62.546875 4 
-Q 63.796875 3.09375 69.203125 3.09375 
-L 71.59375 3.09375 
-L 71.59375 0 
-Q 68.09375 0.296875 56.796875 0.296875 
-Q 45.59375 0.296875 42.09375 0 
-L 42.09375 3.09375 
-L 44.5 3.09375 
-Q 49.90625 3.09375 51.15625 4 
-Q 52.40625 4.90625 52.40625 7.796875 
-L 52.40625 34 
-L 22.5 34 
-L 22.5 7.796875 
-Q 22.5 4.90625 23.75 4 
-Q 25 3.09375 30.40625 3.09375 
-L 32.796875 3.09375 
-L 32.796875 0 
-Q 29.296875 0.296875 18 0.296875 
-Q 6.796875 0.296875 3.296875 0 
-z
-" id="CMUSerif-Roman-72"/>
-      <path d="M 1.90625 -12.40625 
-Q 1.90625 -10.296875 3.15625 -9.1875 
-Q 4.40625 -8.09375 6.09375 -8.09375 
-Q 7.90625 -8.09375 9.09375 -9.25 
-Q 10.296875 -10.40625 10.296875 -12.296875 
-Q 10.296875 -16 6.40625 -16.5 
-Q 8.296875 -18.296875 11.09375 -18.296875 
-Q 14.09375 -18.296875 16.5 -16.09375 
-Q 18.90625 -13.90625 19.953125 -11.796875 
-Q 21 -9.703125 22.5 -5.90625 
-Q 23.90625 -2.90625 25 0 
-L 10 36.5 
-Q 9 38.90625 7.5 39.453125 
-Q 6 40 1.90625 40 
-L 1.90625 43.09375 
-Q 6.40625 42.796875 11.59375 42.796875 
-Q 14.703125 42.796875 22.5 43.09375 
-L 22.5 40 
-Q 16.90625 40 16.90625 37.40625 
-Q 16.90625 37.09375 17.5 35.59375 
-L 28.59375 8.703125 
-L 38.703125 33.296875 
-Q 39.296875 34.703125 39.296875 35.703125 
-Q 39.296875 39.796875 34.59375 40 
-L 34.59375 43.09375 
-Q 41.203125 42.796875 43.296875 42.796875 
-Q 47.40625 42.796875 50.796875 43.09375 
-L 50.796875 40 
-Q 44.09375 40 41.5 33.59375 
-L 23.90625 -9.09375 
-Q 19.09375 -20.5 11.09375 -20.5 
-Q 7.296875 -20.5 4.59375 -18.140625 
-Q 1.90625 -15.796875 1.90625 -12.40625 
-z
-" id="CMUSerif-Roman-121"/>
-      <path d="M 3.40625 21.5 
-Q 3.40625 31 10.046875 37.59375 
-Q 16.703125 44.203125 25.703125 44.203125 
-Q 33.296875 44.203125 38.296875 38 
-L 38.296875 59.59375 
-Q 38.296875 63.296875 37 64.25 
-Q 35.703125 65.203125 30.5 65.203125 
-L 30.5 68.296875 
-L 44.90625 69.40625 
-L 44.90625 8.703125 
-Q 44.90625 5 46.203125 4.046875 
-Q 47.5 3.09375 52.703125 3.09375 
-L 52.703125 0 
-L 38 -1.09375 
-L 38 5.5 
-Q 32.796875 -1.09375 24.59375 -1.09375 
-Q 16 -1.09375 9.703125 5.5 
-Q 3.40625 12.09375 3.40625 21.5 
-z
-M 11.703125 21.40625 
-Q 11.703125 12.09375 14.59375 7.5 
-Q 18.59375 1.09375 25.09375 1.09375 
-Q 32.5 1.09375 36.90625 8.09375 
-Q 38 9.796875 38 11.796875 
-L 38 32.296875 
-Q 38 34.296875 36.90625 36 
-Q 32.796875 42 26.09375 42 
-Q 19.09375 42 14.796875 35.59375 
-Q 11.703125 30.796875 11.703125 21.40625 
-z
-" id="CMUSerif-Roman-100"/>
-      <path d="M 2.796875 0 
-L 2.796875 3.09375 
-Q 8.09375 3.09375 9.34375 3.75 
-Q 10.59375 4.40625 10.59375 7.59375 
-L 10.59375 34.40625 
-Q 10.59375 38.09375 9.296875 39.046875 
-Q 8 40 2.796875 40 
-L 2.796875 43.09375 
-L 16.703125 44.203125 
-L 16.703125 33.203125 
-Q 18.09375 37.5 21.09375 40.84375 
-Q 24.09375 44.203125 29 44.203125 
-Q 32.203125 44.203125 34.296875 42.390625 
-Q 36.40625 40.59375 36.40625 38.09375 
-Q 36.40625 35.90625 35.046875 34.796875 
-Q 33.703125 33.703125 32.09375 33.703125 
-Q 30.296875 33.703125 29.046875 34.84375 
-Q 27.796875 36 27.796875 38 
-Q 27.796875 39.203125 28.34375 40.140625 
-Q 28.90625 41.09375 29.34375 41.4375 
-Q 29.796875 41.796875 30.09375 41.90625 
-Q 29.90625 42 29 42 
-Q 23.5 42 20.34375 36.5 
-Q 17.203125 31 17.203125 23.203125 
-L 17.203125 7.796875 
-Q 17.203125 4.90625 18.390625 4 
-Q 19.59375 3.09375 24.796875 3.09375 
-L 26.90625 3.09375 
-L 26.90625 0 
-Q 22.90625 0.296875 14.203125 0.296875 
-Q 13 0.296875 11.09375 0.25 
-Q 9.203125 0.203125 6.703125 0.09375 
-Q 4.203125 0 2.796875 0 
-z
-" id="CMUSerif-Roman-114"/>
-      <path d="M 16 -1.09375 
-Q 2.796875 11.90625 2.796875 21.40625 
-Q 2.796875 30.90625 9.25 37.84375 
-Q 15.703125 44.796875 25 44.796875 
-Q 34.09375 44.796875 40.59375 37.890625 
-Q 47.09375 31 47.09375 21.40625 
-Q 47.09375 12 40.546875 5.453125 
-Q 34 -1.09375 24.90625 -1.09375 
-Q 16 -1.09375 2.796875 11.90625 
-z
-M 11.09375 22.203125 
-Q 11.09375 12.5 13.59375 8.09375 
-Q 17.5 1.40625 25 1.40625 
-Q 28.703125 1.40625 31.796875 3.40625 
-Q 34.90625 5.40625 36.59375 8.796875 
-Q 38.796875 13.203125 38.796875 22.203125 
-Q 38.796875 31.796875 36.203125 36.09375 
-Q 32.296875 42.59375 24.90625 42.59375 
-Q 21.703125 42.59375 18.546875 40.890625 
-Q 15.40625 39.203125 13.5 35.90625 
-Q 11.09375 31.5 11.09375 22.203125 
-z
-" id="CMUSerif-Roman-111"/>
-      <path d="M 2.796875 -7.90625 
-Q 2.796875 -4.703125 5.25 -1.890625 
-Q 7.703125 0.90625 12 2.09375 
-Q 7.59375 4.90625 7.59375 11 
-Q 7.59375 15.703125 10.703125 19.296875 
-Q 6 23.203125 6 29.59375 
-Q 6 35.5 10.703125 39.84375 
-Q 15.40625 44.203125 22.203125 44.203125 
-Q 28.203125 44.203125 32.796875 40.59375 
-Q 37.59375 45.296875 43.40625 45.296875 
-Q 46 45.296875 47.25 43.6875 
-Q 48.5 42.09375 48.5 40.40625 
-Q 48.5 38.90625 47.546875 38.15625 
-Q 46.59375 37.40625 45.59375 37.40625 
-Q 44.40625 37.40625 43.546875 38.203125 
-Q 42.703125 39 42.703125 40.296875 
-Q 42.703125 42.40625 44.296875 43 
-Q 44 43.09375 43.296875 43.09375 
-Q 38.40625 43.09375 34.296875 39.203125 
-Q 38.40625 35.40625 38.40625 29.5 
-Q 38.40625 23.59375 33.703125 19.25 
-Q 29 14.90625 22.203125 14.90625 
-Q 16.59375 14.90625 12.296875 18 
-Q 10.59375 16 10.59375 13.296875 
-Q 10.59375 10.796875 12.09375 8.84375 
-Q 13.59375 6.90625 15.90625 6.59375 
-Q 16.59375 6.5 23.40625 6.5 
-Q 27.40625 6.5 29.59375 6.390625 
-Q 31.796875 6.296875 34.9375 5.640625 
-Q 38.09375 5 40.59375 3.703125 
-Q 47.09375 0.09375 47.09375 -7.703125 
-Q 47.09375 -13.40625 40.546875 -17 
-Q 34 -20.59375 24.90625 -20.59375 
-Q 15.703125 -20.59375 9.25 -16.9375 
-Q 2.796875 -13.296875 2.796875 -7.90625 
-z
-M 8 -7.90625 
-Q 8 -12 12.84375 -15.140625 
-Q 17.703125 -18.296875 25 -18.296875 
-Q 32.203125 -18.296875 37.046875 -15.1875 
-Q 41.90625 -12.09375 41.90625 -7.90625 
-Q 41.90625 -4.90625 40.203125 -3 
-Q 38.5 -1.09375 35 -0.34375 
-Q 31.5 0.40625 29.046875 0.546875 
-Q 26.59375 0.703125 22.09375 0.703125 
-L 16.203125 0.703125 
-Q 12.796875 0.5 10.390625 -2 
-Q 8 -4.5 8 -7.90625 
-z
-M 13.5 29.5 
-Q 13.5 17.203125 22.203125 17.203125 
-Q 26.59375 17.203125 29.296875 21.203125 
-Q 30.90625 23.90625 30.90625 29.59375 
-Q 30.90625 41.90625 22.203125 41.90625 
-Q 17.796875 41.90625 15.09375 37.90625 
-Q 13.5 35.203125 13.5 29.5 
-z
-" id="CMUSerif-Roman-103"/>
-      <path d="M 2.796875 22 
-Q 2.796875 31.40625 8.84375 38.09375 
-Q 14.90625 44.796875 23.59375 44.796875 
-Q 32.40625 44.796875 36.953125 39.09375 
-Q 41.5 33.40625 41.5 25.203125 
-Q 41.5 23.703125 41.09375 23.390625 
-Q 40.703125 23.09375 39 23.09375 
-L 11.09375 23.09375 
-Q 11.09375 12.90625 14.09375 8.09375 
-Q 18.296875 1.40625 25.40625 1.40625 
-Q 26.40625 1.40625 27.546875 1.59375 
-Q 28.703125 1.796875 31.09375 2.640625 
-Q 33.5 3.5 35.59375 5.796875 
-Q 37.703125 8.09375 38.90625 11.703125 
-Q 39.203125 13.09375 40.203125 13.09375 
-Q 41.5 13.09375 41.5 11.90625 
-Q 41.5 11 40.546875 9.046875 
-Q 39.59375 7.09375 37.796875 4.75 
-Q 36 2.40625 32.5 0.65625 
-Q 29 -1.09375 24.796875 -1.09375 
-Q 16 -1.09375 9.390625 5.546875 
-Q 2.796875 12.203125 2.796875 22 
-z
-M 11.203125 25.203125 
-L 34.90625 25.203125 
-Q 34.90625 27.296875 34.546875 29.640625 
-Q 34.203125 32 33.140625 35.25 
-Q 32.09375 38.5 29.640625 40.546875 
-Q 27.203125 42.59375 23.59375 42.59375 
-Q 22 42.59375 20.25 41.890625 
-Q 18.5 41.203125 16.390625 39.546875 
-Q 14.296875 37.90625 12.84375 34.15625 
-Q 11.40625 30.40625 11.203125 25.203125 
-z
-" id="CMUSerif-Roman-101"/>
-      <path d="M 3.203125 0 
-L 3.203125 3.09375 
-Q 8.5 3.09375 9.75 3.75 
-Q 11 4.40625 11 7.59375 
-L 11 34.40625 
-Q 11 38.09375 9.703125 39.046875 
-Q 8.40625 40 3.203125 40 
-L 3.203125 43.09375 
-L 17.296875 44.203125 
-L 17.296875 33.703125 
-Q 22 44.203125 32.09375 44.203125 
-Q 39.59375 44.203125 42.59375 40.5 
-Q 44.796875 38 45.25 35.203125 
-Q 45.703125 32.40625 45.703125 25.203125 
-L 45.703125 6.09375 
-Q 45.796875 4 47.390625 3.546875 
-Q 49 3.09375 53.5 3.09375 
-L 53.5 0 
-Q 43.296875 0.296875 42.296875 0.296875 
-Q 41.5 0.296875 31 0 
-L 31 3.09375 
-Q 36.296875 3.09375 37.546875 3.75 
-Q 38.796875 4.40625 38.796875 7.59375 
-L 38.796875 30.90625 
-Q 38.796875 36 37.25 39 
-Q 35.703125 42 31.40625 42 
-Q 26.203125 42 22.046875 37.640625 
-Q 17.90625 33.296875 17.90625 26 
-L 17.90625 7.59375 
-Q 17.90625 4.40625 19.15625 3.75 
-Q 20.40625 3.09375 25.703125 3.09375 
-L 25.703125 0 
-Q 15.5 0.296875 14.5 0.296875 
-Q 13.703125 0.296875 3.203125 0 
-z
-" id="CMUSerif-Roman-110"/>
-      <path id="CMUSerif-Roman-32"/>
-      <path d="M 3.203125 40 
-L 3.203125 43.09375 
-L 17.90625 44.203125 
-L 17.90625 11 
-Q 17.90625 8.59375 18.09375 7.1875 
-Q 18.296875 5.796875 19.09375 4.1875 
-Q 19.90625 2.59375 21.796875 1.84375 
-Q 23.703125 1.09375 26.703125 1.09375 
-Q 32.09375 1.09375 35.4375 5.546875 
-Q 38.796875 10 38.796875 16.59375 
-L 38.796875 34.40625 
-Q 38.796875 38.09375 37.5 39.046875 
-Q 36.203125 40 31 40 
-L 31 43.09375 
-L 45.703125 44.203125 
-L 45.703125 8.703125 
-Q 45.703125 5 47 4.046875 
-Q 48.296875 3.09375 53.5 3.09375 
-L 53.5 0 
-L 39.09375 -1.09375 
-L 39.09375 7.90625 
-Q 34.90625 -1.09375 26.203125 -1.09375 
-Q 21.796875 -1.09375 18.796875 0 
-Q 15.796875 1.09375 14.296875 2.5 
-Q 12.796875 3.90625 12 6.59375 
-Q 11.203125 9.296875 11.09375 10.9375 
-Q 11 12.59375 11 15.796875 
-L 11 30.796875 
-Q 11 37.59375 10 38.796875 
-Q 9 40 3.203125 40 
-z
-" id="CMUSerif-Roman-117"/>
-      <path d="M 3.203125 0 
-L 3.203125 3.09375 
-Q 8.5 3.09375 9.75 3.75 
-Q 11 4.40625 11 7.59375 
-L 11 34.40625 
-Q 11 38.09375 9.703125 39.046875 
-Q 8.40625 40 3.203125 40 
-L 3.203125 43.09375 
-L 17.296875 44.203125 
-L 17.296875 33.703125 
-Q 22 44.203125 32.09375 44.203125 
-Q 43.796875 44.203125 45.40625 34.40625 
-Q 47.09375 38.203125 50.6875 41.203125 
-Q 54.296875 44.203125 59.90625 44.203125 
-Q 67.40625 44.203125 70.40625 40.5 
-Q 72.59375 38 73.046875 35.203125 
-Q 73.5 32.40625 73.5 25.203125 
-L 73.5 6.09375 
-Q 73.59375 4 75.1875 3.546875 
-Q 76.796875 3.09375 81.296875 3.09375 
-L 81.296875 0 
-Q 71.09375 0.296875 70.09375 0.296875 
-Q 69.296875 0.296875 58.796875 0 
-L 58.796875 3.09375 
-Q 64.09375 3.09375 65.34375 3.75 
-Q 66.59375 4.40625 66.59375 7.59375 
-L 66.59375 30.90625 
-Q 66.59375 36 65.046875 39 
-Q 63.5 42 59.203125 42 
-Q 54 42 49.84375 37.640625 
-Q 45.703125 33.296875 45.703125 26 
-L 45.703125 7.59375 
-Q 45.703125 4.40625 46.953125 3.75 
-Q 48.203125 3.09375 53.5 3.09375 
-L 53.5 0 
-Q 43.296875 0.296875 42.296875 0.296875 
-Q 41.5 0.296875 31 0 
-L 31 3.09375 
-Q 36.296875 3.09375 37.546875 3.75 
-Q 38.796875 4.40625 38.796875 7.59375 
-L 38.796875 30.90625 
-Q 38.796875 36 37.25 39 
-Q 35.703125 42 31.40625 42 
-Q 26.203125 42 22.046875 37.640625 
-Q 17.90625 33.296875 17.90625 26 
-L 17.90625 7.59375 
-Q 17.90625 4.40625 19.15625 3.75 
-Q 20.40625 3.09375 25.703125 3.09375 
-L 25.703125 0 
-Q 15.5 0.296875 14.5 0.296875 
-Q 13.703125 0.296875 3.203125 0 
-z
-" id="CMUSerif-Roman-109"/>
-      <path d="M 2.796875 65.203125 
-L 2.796875 68.296875 
-L 17.203125 69.40625 
-L 17.203125 37.703125 
-Q 23 44.203125 30.90625 44.203125 
-Q 39.5 44.203125 45.796875 37.59375 
-Q 52.09375 31 52.09375 21.59375 
-Q 52.09375 12.09375 45.5 5.5 
-Q 38.90625 -1.09375 29.796875 -1.09375 
-Q 21.5 -1.09375 16.703125 6.203125 
-Q 13.203125 0.09375 13.09375 0 
-L 10.59375 0 
-L 10.59375 59.59375 
-Q 10.59375 63.296875 9.296875 64.25 
-Q 8 65.203125 2.796875 65.203125 
-z
-M 17.5 11.40625 
-Q 17.5 9.296875 18.90625 7.203125 
-Q 22.90625 1.09375 29.40625 1.09375 
-Q 36.40625 1.09375 40.703125 7.5 
-Q 43.796875 12.296875 43.796875 21.703125 
-Q 43.796875 31 40.90625 35.59375 
-Q 36.90625 42 30.40625 42 
-Q 23.09375 42 18.59375 35.59375 
-Q 17.5 34 17.5 32 
-z
-" id="CMUSerif-Roman-98"/>
-      <path d="M 3.296875 1.296875 
-L 3.296875 14.5 
-Q 3.296875 15.59375 3.34375 16 
-Q 3.40625 16.40625 3.703125 16.703125 
-Q 4 17 4.59375 17 
-Q 5.296875 17 5.546875 16.703125 
-Q 5.796875 16.40625 6 15.296875 
-Q 7.5 8.40625 10.75 4.75 
-Q 14 1.09375 19.90625 1.09375 
-Q 25.5 1.09375 28.34375 3.59375 
-Q 31.203125 6.09375 31.203125 10.203125 
-Q 31.203125 17.5 20.796875 19.40625 
-Q 14.796875 20.59375 12.296875 21.390625 
-Q 9.796875 22.203125 7.59375 24 
-Q 3.296875 27.5 3.296875 32.5 
-Q 3.296875 37.5 7.09375 41.140625 
-Q 10.90625 44.796875 19.296875 44.796875 
-Q 24.90625 44.796875 28.703125 42 
-Q 29.796875 42.90625 30.40625 43.59375 
-Q 31.703125 44.796875 32.40625 44.796875 
-Q 33.203125 44.796875 33.34375 44.296875 
-Q 33.5 43.796875 33.5 42.40625 
-L 33.5 32.296875 
-Q 33.5 31.203125 33.453125 30.796875 
-Q 33.40625 30.40625 33.09375 30.15625 
-Q 32.796875 29.90625 32.203125 29.90625 
-Q 31.09375 29.90625 31 30.796875 
-Q 30.203125 42.90625 19.296875 42.90625 
-Q 13.40625 42.90625 10.75 40.65625 
-Q 8.09375 38.40625 8.09375 35.296875 
-Q 8.09375 33.59375 8.890625 32.296875 
-Q 9.703125 31 10.75 30.25 
-Q 11.796875 29.5 13.75 28.796875 
-Q 15.703125 28.09375 16.890625 27.84375 
-Q 18.09375 27.59375 20.40625 27.09375 
-Q 28.40625 25.59375 31.796875 22.296875 
-Q 36 18.09375 36 12.796875 
-Q 36 6.90625 32 2.90625 
-Q 28 -1.09375 19.90625 -1.09375 
-Q 13.40625 -1.09375 8.90625 3.203125 
-Q 8.296875 2.59375 7.84375 2.09375 
-Q 7.40625 1.59375 7.25 1.390625 
-Q 7.09375 1.203125 7.046875 1.09375 
-Q 7 1 6.90625 0.90625 
-Q 4.90625 -1.09375 4.40625 -1.09375 
-Q 3.59375 -1.09375 3.4375 -0.59375 
-Q 3.296875 -0.09375 3.296875 1.296875 
-z
-" id="CMUSerif-Roman-115"/>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-Q 8.59375 3.09375 9.84375 3.75 
-Q 11.09375 4.40625 11.09375 7.59375 
-L 11.09375 34.5 
-Q 11.09375 38.203125 9.84375 39.09375 
-Q 8.59375 40 3.703125 40 
-L 3.703125 43.09375 
-L 17.703125 44.203125 
-L 17.703125 7.5 
-Q 17.703125 4.5 18.75 3.796875 
-Q 19.796875 3.09375 24.703125 3.09375 
-L 24.703125 0 
-Q 14.5 0.296875 14.296875 0.296875 
-Q 12.90625 0.296875 3.296875 0 
-z
-M 7.5 61.59375 
-Q 7.5 63.59375 9.046875 65.25 
-Q 10.59375 66.90625 12.796875 66.90625 
-Q 15 66.90625 16.546875 65.40625 
-Q 18.09375 63.90625 18.09375 61.59375 
-Q 18.09375 59.296875 16.546875 57.796875 
-Q 15 56.296875 12.796875 56.296875 
-Q 10.5 56.296875 9 57.890625 
-Q 7.5 59.5 7.5 61.59375 
-z
-" id="CMUSerif-Roman-105"/>
-      <path d="M 1.90625 40 
-L 1.90625 42.203125 
-Q 6.5 42.40625 9.546875 45.65625 
-Q 12.59375 48.90625 13.640625 52.90625 
-Q 14.703125 56.90625 14.796875 61.5 
-L 17.296875 61.5 
-L 17.296875 43.09375 
-L 31.59375 43.09375 
-L 31.59375 40 
-L 17.296875 40 
-L 17.296875 12.203125 
-Q 17.296875 1.40625 24 1.40625 
-Q 26.90625 1.40625 28.796875 4.34375 
-Q 30.703125 7.296875 30.703125 12.59375 
-L 30.703125 18.09375 
-L 33.203125 18.09375 
-L 33.203125 12.40625 
-Q 33.203125 7 30.703125 2.953125 
-Q 28.203125 -1.09375 23.296875 -1.09375 
-Q 21.5 -1.09375 19.703125 -0.640625 
-Q 17.90625 -0.203125 15.59375 1 
-Q 13.296875 2.203125 11.84375 5.140625 
-Q 10.40625 8.09375 10.40625 12.40625 
-L 10.40625 40 
-z
-" id="CMUSerif-Roman-116"/>
-      <path d="M 7.71875 1.703125 
-Q 7.71875 2.296875 7.8125 2.59375 
-L 15.28125 32.421875 
-Q 16.015625 35.203125 16.015625 37.3125 
-Q 16.015625 41.609375 13.09375 41.609375 
-Q 9.96875 41.609375 8.453125 37.859375 
-Q 6.9375 34.125 5.515625 28.421875 
-Q 5.515625 28.125 5.21875 27.953125 
-Q 4.9375 27.78125 4.6875 27.78125 
-L 3.515625 27.78125 
-Q 3.171875 27.78125 2.921875 28.140625 
-Q 2.6875 28.515625 2.6875 28.8125 
-Q 3.765625 33.15625 4.765625 36.171875 
-Q 5.765625 39.203125 7.890625 41.6875 
-Q 10.015625 44.1875 13.1875 44.1875 
-Q 16.9375 44.1875 19.8125 41.8125 
-Q 22.703125 39.453125 22.703125 35.796875 
-Q 25.6875 39.703125 29.6875 41.9375 
-Q 33.6875 44.1875 38.1875 44.1875 
-Q 41.75 44.1875 44.328125 42.96875 
-Q 46.921875 41.75 48.359375 39.28125 
-Q 49.8125 36.8125 49.8125 33.40625 
-Q 49.8125 29.296875 47.96875 23.484375 
-Q 46.140625 17.671875 43.40625 10.5 
-Q 42 7.234375 42 4.5 
-Q 42 1.515625 44.28125 1.515625 
-Q 48.1875 1.515625 50.796875 5.703125 
-Q 53.421875 9.90625 54.5 14.703125 
-Q 54.6875 15.28125 55.328125 15.28125 
-L 56.5 15.28125 
-Q 56.890625 15.28125 57.15625 15.03125 
-Q 57.421875 14.796875 57.421875 14.40625 
-Q 57.421875 14.3125 57.328125 14.109375 
-Q 55.953125 8.453125 52.5625 3.65625 
-Q 49.171875 -1.125 44.09375 -1.125 
-Q 40.578125 -1.125 38.078125 1.296875 
-Q 35.59375 3.71875 35.59375 7.171875 
-Q 35.59375 9.03125 36.375 11.078125 
-Q 37.640625 14.359375 39.28125 18.890625 
-Q 40.921875 23.4375 41.96875 27.578125 
-Q 43.015625 31.734375 43.015625 34.90625 
-Q 43.015625 37.703125 41.859375 39.65625 
-Q 40.71875 41.609375 37.984375 41.609375 
-Q 34.328125 41.609375 31.25 39.984375 
-Q 28.171875 38.375 25.875 35.71875 
-Q 23.578125 33.0625 21.6875 29.390625 
-L 14.890625 2.203125 
-Q 14.546875 0.828125 13.34375 -0.140625 
-Q 12.15625 -1.125 10.6875 -1.125 
-Q 9.46875 -1.125 8.59375 -0.34375 
-Q 7.71875 0.4375 7.71875 1.703125 
-z
-" id="Cmmi10-110"/>
-      <path d="M 3.078125 0 
-L 3.078125 3.515625 
-Q 13.375 3.515625 13.375 6.6875 
-L 13.375 61.625 
-Q 13.375 64.796875 3.078125 64.796875 
-L 3.078125 68.3125 
-L 33.015625 68.3125 
-L 33.015625 64.796875 
-Q 22.703125 64.796875 22.703125 61.625 
-L 22.703125 37.3125 
-L 52.203125 37.3125 
-L 52.203125 61.625 
-Q 52.203125 64.796875 41.890625 64.796875 
-L 41.890625 68.3125 
-L 71.78125 68.3125 
-L 71.78125 64.796875 
-Q 61.53125 64.796875 61.53125 61.625 
-L 61.53125 6.6875 
-Q 61.53125 3.515625 71.78125 3.515625 
-L 71.78125 0 
-L 41.890625 0 
-L 41.890625 3.515625 
-Q 52.203125 3.515625 52.203125 6.6875 
-L 52.203125 33.796875 
-L 22.703125 33.796875 
-L 22.703125 6.6875 
-Q 22.703125 3.515625 33.015625 3.515625 
-L 33.015625 0 
-z
-" id="Cmr10-72"/>
-      <path d="M 10.40625 -25 
-L 10.40625 75 
-L 25.5 75 
-L 25.5 72.703125 
-L 17.09375 72.703125 
-L 17.09375 -22.703125 
-L 25.5 -22.703125 
-L 25.5 -25 
-z
-" id="CMUSerif-Roman-91"/>
-      <path d="M 16.09375 -1.09375 
-Q 3.40625 12.09375 3.40625 21.59375 
-Q 3.40625 31.09375 9.65625 37.9375 
-Q 15.90625 44.796875 25.09375 44.796875 
-Q 31.203125 44.796875 35.796875 41.890625 
-Q 40.40625 39 40.40625 34.09375 
-Q 40.40625 31.90625 39.09375 30.65625 
-Q 37.796875 29.40625 35.796875 29.40625 
-Q 33.703125 29.40625 32.453125 30.703125 
-Q 31.203125 32 31.203125 34 
-Q 31.203125 34.90625 31.5 35.75 
-Q 31.796875 36.59375 32.890625 37.546875 
-Q 34 38.5 35.90625 38.59375 
-Q 32.296875 42.296875 25.203125 42.296875 
-Q 20.09375 42.296875 15.890625 37.5 
-Q 11.703125 32.703125 11.703125 21.796875 
-Q 11.703125 16.09375 13.09375 11.890625 
-Q 14.5 7.703125 16.796875 5.546875 
-Q 19.09375 3.40625 21.34375 2.40625 
-Q 23.59375 1.40625 25.796875 1.40625 
-Q 35.59375 1.40625 38.90625 11.90625 
-Q 39.203125 12.90625 40.203125 12.90625 
-Q 41.5 12.90625 41.5 11.90625 
-Q 41.5 11.40625 41.09375 10.15625 
-Q 40.703125 8.90625 39.5 6.90625 
-Q 38.296875 4.90625 36.546875 3.15625 
-Q 34.796875 1.40625 31.75 0.15625 
-Q 28.703125 -1.09375 24.90625 -1.09375 
-Q 16.09375 -1.09375 3.40625 12.09375 
-z
-" id="CMUSerif-Roman-99"/>
-      <path d="M 10.203125 23 
-Q 9.375 23 8.828125 23.625 
-Q 8.296875 24.265625 8.296875 25 
-Q 8.296875 25.734375 8.828125 26.359375 
-Q 9.375 27 10.203125 27 
-L 67.578125 27 
-Q 68.359375 27 68.875 26.359375 
-Q 69.390625 25.734375 69.390625 25 
-Q 69.390625 24.265625 68.875 23.625 
-Q 68.359375 23 67.578125 23 
-z
-" id="Cmsy10-161"/>
-      <path d="M 9.515625 7.71875 
-Q 11.859375 4.296875 15.8125 2.640625 
-Q 19.78125 0.984375 24.3125 0.984375 
-Q 30.125 0.984375 32.5625 5.9375 
-Q 35.015625 10.890625 35.015625 17.1875 
-Q 35.015625 20.015625 34.5 22.84375 
-Q 33.984375 25.6875 32.765625 28.125 
-Q 31.546875 30.5625 29.421875 32.03125 
-Q 27.296875 33.5 24.21875 33.5 
-L 17.578125 33.5 
-Q 16.703125 33.5 16.703125 34.421875 
-L 16.703125 35.296875 
-Q 16.703125 36.078125 17.578125 36.078125 
-L 23.09375 36.53125 
-Q 26.609375 36.53125 28.921875 39.15625 
-Q 31.25 41.796875 32.328125 45.578125 
-Q 33.40625 49.359375 33.40625 52.78125 
-Q 33.40625 57.5625 31.15625 60.640625 
-Q 28.90625 63.71875 24.3125 63.71875 
-Q 20.515625 63.71875 17.046875 62.28125 
-Q 13.578125 60.84375 11.53125 57.90625 
-Q 11.71875 57.953125 11.859375 57.984375 
-Q 12.015625 58.015625 12.203125 58.015625 
-Q 14.453125 58.015625 15.96875 56.453125 
-Q 17.484375 54.890625 17.484375 52.6875 
-Q 17.484375 50.53125 15.96875 48.96875 
-Q 14.453125 47.40625 12.203125 47.40625 
-Q 10.015625 47.40625 8.453125 48.96875 
-Q 6.890625 50.53125 6.890625 52.6875 
-Q 6.890625 56.984375 9.46875 60.15625 
-Q 12.0625 63.328125 16.140625 64.96875 
-Q 20.21875 66.609375 24.3125 66.609375 
-Q 27.34375 66.609375 30.703125 65.703125 
-Q 34.078125 64.796875 36.8125 63.109375 
-Q 39.546875 61.421875 41.28125 58.78125 
-Q 43.015625 56.15625 43.015625 52.78125 
-Q 43.015625 48.578125 41.140625 45.015625 
-Q 39.265625 41.453125 35.984375 38.859375 
-Q 32.71875 36.28125 28.8125 35.015625 
-Q 33.15625 34.1875 37.0625 31.734375 
-Q 40.96875 29.296875 43.328125 25.484375 
-Q 45.703125 21.6875 45.703125 17.28125 
-Q 45.703125 11.765625 42.671875 7.296875 
-Q 39.65625 2.828125 34.71875 0.3125 
-Q 29.78125 -2.203125 24.3125 -2.203125 
-Q 19.625 -2.203125 14.90625 -0.40625 
-Q 10.203125 1.375 7.203125 4.9375 
-Q 4.203125 8.5 4.203125 13.484375 
-Q 4.203125 15.96875 5.859375 17.625 
-Q 7.515625 19.28125 10.015625 19.28125 
-Q 11.625 19.28125 12.96875 18.53125 
-Q 14.3125 17.78125 15.0625 16.40625 
-Q 15.828125 15.046875 15.828125 13.484375 
-Q 15.828125 11.03125 14.109375 9.375 
-Q 12.40625 7.71875 10.015625 7.71875 
-z
-" id="Cmr10-51"/>
-      <path d="M 2.09375 -22.703125 
-L 10.5 -22.703125 
-L 10.5 72.703125 
-L 2.09375 72.703125 
-L 2.09375 75 
-L 17.203125 75 
-L 17.203125 -25 
-L 2.09375 -25 
-z
-" id="CMUSerif-Roman-93"/>
-     </defs>
-     <g transform="translate(48.976 221.69725)scale(0.1 -0.1)">
-      <use transform="translate(0 0.109375)" xlink:href="#CMUSerif-Roman-72"/>
-      <use transform="translate(74.999985 0.109375)" xlink:href="#CMUSerif-Roman-121"/>
-      <use transform="translate(127.699982 0.109375)" xlink:href="#CMUSerif-Roman-100"/>
-      <use transform="translate(183.199966 0.109375)" xlink:href="#CMUSerif-Roman-114"/>
-      <use transform="translate(222.299957 0.109375)" xlink:href="#CMUSerif-Roman-111"/>
-      <use transform="translate(272.299942 0.109375)" xlink:href="#CMUSerif-Roman-103"/>
-      <use transform="translate(322.299927 0.109375)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(366.699921 0.109375)" xlink:href="#CMUSerif-Roman-110"/>
-      <use transform="translate(422.199905 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(455.499893 0.109375)" xlink:href="#CMUSerif-Roman-110"/>
-      <use transform="translate(510.999878 0.109375)" xlink:href="#CMUSerif-Roman-117"/>
-      <use transform="translate(566.499863 0.109375)" xlink:href="#CMUSerif-Roman-109"/>
-      <use transform="translate(649.79985 0.109375)" xlink:href="#CMUSerif-Roman-98"/>
-      <use transform="translate(705.299835 0.109375)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(749.699829 0.109375)" xlink:href="#CMUSerif-Roman-114"/>
-      <use transform="translate(788.79982 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(822.099808 0.109375)" xlink:href="#CMUSerif-Roman-100"/>
-      <use transform="translate(877.599792 0.109375)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(921.999786 0.109375)" xlink:href="#CMUSerif-Roman-110"/>
-      <use transform="translate(977.499771 0.109375)" xlink:href="#CMUSerif-Roman-115"/>
-      <use transform="translate(1016.899765 0.109375)" xlink:href="#CMUSerif-Roman-105"/>
-      <use transform="translate(1044.599762 0.109375)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(1083.39975 0.109375)" xlink:href="#CMUSerif-Roman-121"/>
-      <use transform="translate(1136.099747 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(1169.399734 0.109375)" xlink:href="#Cmmi10-110"/>
-      <use transform="translate(1229.4095 -16.896875)scale(0.7)" xlink:href="#Cmr10-72"/>
-      <use transform="translate(1288.286844 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(1321.586832 0.109375)" xlink:href="#CMUSerif-Roman-91"/>
-      <use transform="translate(1349.386819 0.109375)" xlink:href="#CMUSerif-Roman-99"/>
-      <use transform="translate(1393.786813 0.109375)" xlink:href="#CMUSerif-Roman-109"/>
-      <use transform="translate(1481.550942 38.373438)scale(0.7)" xlink:href="#Cmsy10-161"/>
-      <use transform="translate(1535.930825 38.373438)scale(0.7)" xlink:href="#Cmr10-51"/>
-      <use transform="translate(1577.308168 0.109375)" xlink:href="#CMUSerif-Roman-93"/>
-     </g>
-    </g>
-   </g>
-   <g id="matplotlib.axis_2">
-    <g id="ytick_1">
-     <g id="line2d_6">
-      <defs>
-       <path d="M 0 0 
-L -3.5 0 
-" id="mbe9d5df88a" style="stroke:#000000;stroke-width:0.8;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="163.232825"/>
-      </g>
-     </g>
-     <g id="text_7">
-      <!-- $\mathdefault{10^{2}}$ -->
-      <g transform="translate(13.22 166.703137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_2">
-     <g id="line2d_7">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="114.470825"/>
-      </g>
-     </g>
-     <g id="text_8">
-      <!-- $\mathdefault{10^{3}}$ -->
-      <defs>
-       <path d="M 4.203125 13.5 
-Q 4.203125 16.5 5.890625 17.890625 
-Q 7.59375 19.296875 9.796875 19.296875 
-Q 12.09375 19.296875 13.75 17.796875 
-Q 15.40625 16.296875 15.40625 13.703125 
-Q 15.40625 10.90625 13.453125 9.34375 
-Q 11.5 7.796875 8.796875 8.203125 
-Q 11.203125 4.203125 15.59375 2.390625 
-Q 20 0.59375 24.09375 0.59375 
-Q 28.40625 0.59375 31.90625 4.296875 
-Q 35.40625 8 35.40625 17.09375 
-Q 35.40625 24.796875 32.40625 29.25 
-Q 29.40625 33.703125 23.5 33.703125 
-L 19.09375 33.703125 
-Q 17.59375 33.703125 17.140625 33.84375 
-Q 16.703125 34 16.703125 34.796875 
-Q 16.703125 35.796875 18.203125 36 
-Q 19.703125 36 22.09375 36.296875 
-Q 27.90625 36.5 31 41.5 
-Q 33.796875 46.203125 33.796875 52.90625 
-Q 33.796875 59 30.890625 61.546875 
-Q 28 64.09375 24.203125 64.09375 
-Q 20.703125 64.09375 16.84375 62.640625 
-Q 13 61.203125 10.90625 57.90625 
-Q 17.09375 57.90625 17.09375 52.90625 
-Q 17.09375 50.703125 15.6875 49.25 
-Q 14.296875 47.796875 12 47.796875 
-Q 9.796875 47.796875 8.34375 49.1875 
-Q 6.90625 50.59375 6.90625 53 
-Q 6.90625 58.703125 12 62.640625 
-Q 17.09375 66.59375 24.59375 66.59375 
-Q 32 66.59375 37.5 62.6875 
-Q 43 58.796875 43 52.796875 
-Q 43 46.90625 39.09375 42.046875 
-Q 35.203125 37.203125 29 35.203125 
-Q 36.59375 33.703125 41.140625 28.546875 
-Q 45.703125 23.40625 45.703125 17.09375 
-Q 45.703125 9.296875 39.546875 3.546875 
-Q 33.40625 -2.203125 24.40625 -2.203125 
-Q 16.09375 -2.203125 10.140625 2.296875 
-Q 4.203125 6.796875 4.203125 13.5 
-z
-" id="CMUSerif-Roman-51"/>
-      </defs>
-      <g transform="translate(13.22 117.941137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-51"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_3">
-     <g id="line2d_8">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="65.708825"/>
-      </g>
-     </g>
-     <g id="text_9">
-      <!-- $\mathdefault{10^{4}}$ -->
-      <g transform="translate(13.22 69.179137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-52"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_4">
-     <g id="line2d_9">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mbe9d5df88a" y="16.946825"/>
-      </g>
-     </g>
-     <g id="text_10">
-      <!-- $\mathdefault{10^{5}}$ -->
-      <defs>
-       <path d="M 5 16.09375 
-Q 5 19.09375 6.59375 20.25 
-Q 8.203125 21.40625 9.90625 21.40625 
-Q 12.203125 21.40625 13.546875 19.953125 
-Q 14.90625 18.5 14.90625 16.5 
-Q 14.90625 14.5 13.546875 13.046875 
-Q 12.203125 11.59375 9.90625 11.59375 
-Q 8.796875 11.59375 8.203125 11.796875 
-Q 9.5 7.203125 13.546875 3.890625 
-Q 17.59375 0.59375 22.90625 0.59375 
-Q 29.59375 0.59375 33.59375 7.09375 
-Q 36 11.296875 36 20.796875 
-Q 36 29.203125 34.203125 33.40625 
-Q 31.40625 39.796875 25.703125 39.796875 
-Q 17.59375 39.796875 12.796875 32.796875 
-Q 12.203125 31.90625 11.5 31.90625 
-Q 10.5 31.90625 10.296875 32.453125 
-Q 10.09375 33 10.09375 34.5 
-L 10.09375 64.09375 
-Q 10.09375 66.5 11.09375 66.5 
-Q 11.5 66.5 12.296875 66.203125 
-Q 18.59375 63.40625 25.59375 63.40625 
-Q 32.796875 63.40625 39.203125 66.296875 
-Q 39.703125 66.59375 40 66.59375 
-Q 41 66.59375 41 65.5 
-Q 41 65.09375 40.203125 63.9375 
-Q 39.40625 62.796875 37.703125 61.296875 
-Q 36 59.796875 33.796875 58.390625 
-Q 31.59375 57 28.390625 56.046875 
-Q 25.203125 55.09375 21.703125 55.09375 
-Q 17.5 55.09375 13.203125 56.40625 
-L 13.203125 36.90625 
-Q 18.40625 42 25.90625 42 
-Q 33.90625 42 39.40625 35.546875 
-Q 44.90625 29.09375 44.90625 20.09375 
-Q 44.90625 10.703125 38.40625 4.25 
-Q 31.90625 -2.203125 23.09375 -2.203125 
-Q 15.09375 -2.203125 10.046875 3.5 
-Q 5 9.203125 5 16.09375 
-z
-" id="CMUSerif-Roman-53"/>
-      </defs>
-      <g transform="translate(13.22 20.417137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-53"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_5">
-     <g id="line2d_10">
-      <defs>
-       <path d="M 0 0 
-L -2 0 
-" id="m1f4f509585" style="stroke:#000000;stroke-width:0.6;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="197.316"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_6">
-     <g id="line2d_11">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="188.729438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_7">
-     <g id="line2d_12">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="182.637175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_8">
-     <g id="line2d_13">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="177.911649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_9">
-     <g id="line2d_14">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="174.050613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_10">
-     <g id="line2d_15">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="170.786154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_11">
-     <g id="line2d_16">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="167.958351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_12">
-     <g id="line2d_17">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="165.464051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_13">
-     <g id="line2d_18">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="148.554"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_14">
-     <g id="line2d_19">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="139.967438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_15">
-     <g id="line2d_20">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="133.875175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_16">
-     <g id="line2d_21">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="129.149649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_17">
-     <g id="line2d_22">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="125.288613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_18">
-     <g id="line2d_23">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="122.024154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_19">
-     <g id="line2d_24">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="119.196351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_20">
-     <g id="line2d_25">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="116.702051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_21">
-     <g id="line2d_26">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="99.792"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_22">
-     <g id="line2d_27">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="91.205438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_23">
-     <g id="line2d_28">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="85.113175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_24">
-     <g id="line2d_29">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="80.387649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_25">
-     <g id="line2d_30">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="76.526613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_26">
-     <g id="line2d_31">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="73.262154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_27">
-     <g id="line2d_32">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="70.434351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_28">
-     <g id="line2d_33">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="67.940051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_29">
-     <g id="line2d_34">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="51.03"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_30">
-     <g id="line2d_35">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="42.443438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_31">
-     <g id="line2d_36">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="36.351175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_32">
-     <g id="line2d_37">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="31.625649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_33">
-     <g id="line2d_38">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="27.764613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_34">
-     <g id="line2d_39">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="24.500154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_35">
-     <g id="line2d_40">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="21.672351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_36">
-     <g id="line2d_41">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="19.178051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_37">
-     <g id="line2d_42">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m1f4f509585" y="2.268"/>
-      </g>
-     </g>
-    </g>
-    <g id="text_11">
-     <!-- Temperature $T$ [K] -->
-     <defs>
-      <path d="M 3.59375 45.203125 
-L 5.5 67.703125 
-L 66.59375 67.703125 
-L 68.5 45.203125 
-L 66 45.203125 
-Q 65.59375 49.703125 65.25 52.296875 
-Q 64.90625 54.90625 64.046875 57.34375 
-Q 63.203125 59.796875 62.140625 61 
-Q 61.09375 62.203125 59.09375 63.140625 
-Q 57.09375 64.09375 54.5 64.34375 
-Q 51.90625 64.59375 48 64.59375 
-Q 43.40625 64.59375 42.296875 64.40625 
-Q 41.09375 64.09375 40.796875 63.25 
-Q 40.5 62.40625 40.5 60.59375 
-L 40.5 7.90625 
-Q 40.5 5.90625 40.890625 5.09375 
-Q 41.296875 4.296875 43.59375 3.6875 
-Q 45.90625 3.09375 51 3.09375 
-L 55 3.09375 
-L 55 0 
-Q 50.90625 0.296875 36 0.296875 
-Q 21.203125 0.296875 17.09375 0 
-L 17.09375 3.09375 
-L 21.09375 3.09375 
-Q 26.203125 3.09375 28.5 3.6875 
-Q 30.796875 4.296875 31.1875 5.09375 
-Q 31.59375 5.90625 31.59375 7.90625 
-L 31.59375 60.59375 
-Q 31.59375 62 31.5 62.59375 
-Q 31.40625 63.203125 30.953125 63.703125 
-Q 30.5 64.203125 29.5 64.390625 
-Q 28.5 64.59375 24.09375 64.59375 
-Q 20.203125 64.59375 17.59375 64.34375 
-Q 15 64.09375 13 63.140625 
-Q 11 62.203125 9.953125 61 
-Q 8.90625 59.796875 8.046875 57.34375 
-Q 7.203125 54.90625 6.84375 52.296875 
-Q 6.5 49.703125 6.09375 45.203125 
-z
-" id="CMUSerif-Roman-84"/>
-      <path d="M 2.796875 -16.296875 
-Q 8.09375 -16.296875 9.34375 -15.640625 
-Q 10.59375 -15 10.59375 -11.796875 
-L 10.59375 35 
-Q 10.59375 38.296875 9.34375 39.140625 
-Q 8.09375 40 2.796875 40 
-L 2.796875 43.09375 
-L 17.203125 44.203125 
-L 17.203125 37.59375 
-Q 23.203125 44.203125 31.203125 44.203125 
-Q 39.703125 44.203125 45.890625 37.59375 
-Q 52.09375 31 52.09375 21.59375 
-Q 52.09375 12.09375 45.5 5.5 
-Q 38.90625 -1.09375 29.796875 -1.09375 
-Q 24.796875 -1.09375 21.4375 1.453125 
-Q 18.09375 4 17.5 5.90625 
-L 17.5 5 
-L 17.5 -11.796875 
-Q 17.5 -15 18.75 -15.640625 
-Q 20 -16.296875 25.296875 -16.296875 
-L 25.296875 -19.40625 
-Q 14.796875 -19.09375 14 -19.09375 
-Q 13 -19.09375 2.796875 -19.40625 
-z
-M 17.5 11.40625 
-Q 17.5 9.90625 17.703125 9.34375 
-Q 17.90625 8.796875 18.90625 7.203125 
-Q 22.90625 1.09375 29.40625 1.09375 
-Q 35.09375 1.09375 39.4375 6.9375 
-Q 43.796875 12.796875 43.796875 21.59375 
-Q 43.796875 30 39.84375 35.84375 
-Q 35.90625 41.703125 30.40625 41.703125 
-Q 26.5 41.703125 23.09375 39.59375 
-Q 19.703125 37.5 17.5 33.703125 
-z
-" id="CMUSerif-Roman-112"/>
-      <path d="M 4.203125 9.5 
-Q 4.203125 18 14.203125 22.5 
-Q 20.203125 25.40625 32.59375 26.09375 
-L 32.59375 29.796875 
-Q 32.59375 36 29.34375 39.296875 
-Q 26.09375 42.59375 22 42.59375 
-Q 14.703125 42.59375 11.203125 38 
-Q 14.203125 37.90625 15.25 36.40625 
-Q 16.296875 34.90625 16.296875 33.40625 
-Q 16.296875 31.40625 15.046875 30.09375 
-Q 13.796875 28.796875 11.703125 28.796875 
-Q 9.703125 28.796875 8.390625 30.046875 
-Q 7.09375 31.296875 7.09375 33.5 
-Q 7.09375 38.40625 11.5 41.59375 
-Q 15.90625 44.796875 22.203125 44.796875 
-Q 30.40625 44.796875 35.90625 39.296875 
-Q 37.59375 37.59375 38.4375 35.390625 
-Q 39.296875 33.203125 39.390625 31.75 
-Q 39.5 30.296875 39.5 27.5 
-L 39.5 7.5 
-Q 39.5 6.90625 39.703125 5.953125 
-Q 39.90625 5 40.796875 3.75 
-Q 41.703125 2.5 43.203125 2.5 
-Q 46.796875 2.5 46.796875 8.90625 
-L 46.796875 14.5 
-L 49.296875 14.5 
-L 49.296875 8.90625 
-Q 49.296875 3.59375 46.5 1.5 
-Q 43.703125 -0.59375 41.09375 -0.59375 
-Q 37.796875 -0.59375 35.6875 1.84375 
-Q 33.59375 4.296875 33.296875 7.59375 
-Q 31.796875 3.796875 28.34375 1.34375 
-Q 24.90625 -1.09375 20.203125 -1.09375 
-Q 16.59375 -1.09375 13.1875 -0.1875 
-Q 9.796875 0.703125 7 3.203125 
-Q 4.203125 5.703125 4.203125 9.5 
-z
-M 11.90625 9.59375 
-Q 11.90625 5.90625 14.546875 3.5 
-Q 17.203125 1.09375 20.90625 1.09375 
-Q 25.09375 1.09375 28.84375 4.34375 
-Q 32.59375 7.59375 32.59375 14 
-L 32.59375 24 
-Q 21.5 23.59375 16.703125 19.1875 
-Q 11.90625 14.796875 11.90625 9.59375 
-z
-" id="CMUSerif-Roman-97"/>
-      <path d="M 4.59375 1.3125 
-Q 4.640625 1.5625 4.8125 2.1875 
-Q 4.984375 2.828125 5.25 3.171875 
-Q 5.515625 3.515625 6 3.515625 
-Q 14.59375 3.515625 17.390625 4 
-Q 20.0625 4.6875 20.609375 6.890625 
-L 34.28125 61.8125 
-Q 34.71875 63.03125 34.71875 64.015625 
-Q 34.71875 64.796875 31.203125 64.796875 
-L 25.390625 64.796875 
-Q 18.703125 64.796875 15.0625 62.734375 
-Q 11.421875 60.6875 9.71875 57.3125 
-Q 8.015625 53.953125 5.328125 46.296875 
-Q 4.984375 45.40625 4.296875 45.40625 
-L 3.421875 45.40625 
-Q 2.390625 45.40625 2.390625 46.6875 
-L 9.515625 67.390625 
-Q 9.71875 68.3125 10.5 68.3125 
-L 69.578125 68.3125 
-Q 70.609375 68.3125 70.609375 67 
-L 67.28125 46.296875 
-Q 67.28125 46 66.9375 45.703125 
-Q 66.609375 45.40625 66.3125 45.40625 
-L 65.375 45.40625 
-Q 64.40625 45.40625 64.40625 46.6875 
-Q 65.484375 53.765625 65.484375 56.6875 
-Q 65.484375 60.203125 64.015625 62 
-Q 62.546875 63.8125 60.203125 64.296875 
-Q 57.859375 64.796875 54.109375 64.796875 
-L 48.1875 64.796875 
-Q 45.515625 64.796875 44.578125 64.296875 
-Q 43.65625 63.8125 43.015625 61.375 
-L 29.296875 6.5 
-Q 29.25 6.296875 29.21875 6.09375 
-Q 29.203125 5.90625 29.109375 5.609375 
-Q 29.109375 4.34375 30.609375 4 
-Q 33.203125 3.515625 41.703125 3.515625 
-Q 42.671875 3.515625 42.671875 2.203125 
-Q 42.328125 0.78125 42.125 0.390625 
-Q 41.9375 0 41.015625 0 
-L 5.609375 0 
-Q 4.59375 0 4.59375 1.3125 
-z
-" id="Cmmi10-84"/>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-L 5.703125 3.09375 
-Q 11.09375 3.09375 12.34375 4 
-Q 13.59375 4.90625 13.59375 7.796875 
-L 13.59375 60.5 
-Q 13.59375 63.40625 12.34375 64.296875 
-Q 11.09375 65.203125 5.703125 65.203125 
-L 3.296875 65.203125 
-L 3.296875 68.296875 
-Q 6.796875 68 18.09375 68 
-Q 29.296875 68 32.796875 68.296875 
-L 32.796875 65.203125 
-L 30.40625 65.203125 
-Q 25 65.203125 23.75 64.296875 
-Q 22.5 63.40625 22.5 60.5 
-L 22.5 28.703125 
-L 53.796875 58.59375 
-Q 55.59375 60.203125 55.59375 61.90625 
-Q 55.59375 62.5 55.34375 63.140625 
-Q 55.09375 63.796875 53.9375 64.5 
-Q 52.796875 65.203125 51 65.203125 
-L 51 68.296875 
-Q 54.40625 68 63.703125 68 
-Q 69.59375 68 72.203125 68.296875 
-L 72.203125 65.203125 
-Q 63.90625 65.09375 58 59.203125 
-L 40 41.90625 
-L 63.09375 7.90625 
-Q 65.203125 4.796875 67.25 3.9375 
-Q 69.296875 3.09375 73.59375 3.09375 
-L 73.59375 0 
-Q 67.296875 0.296875 62.09375 0.296875 
-Q 51.296875 0.296875 47.796875 0 
-L 47.796875 3.09375 
-Q 53.703125 3.09375 53.703125 6.09375 
-Q 53.703125 7.203125 52.203125 9.5 
-L 34.09375 36.296875 
-L 22.5 25.296875 
-L 22.5 7.796875 
-Q 22.5 4.90625 23.75 4 
-Q 25 3.09375 30.40625 3.09375 
-L 32.796875 3.09375 
-L 32.796875 0 
-Q 29.296875 0.296875 18 0.296875 
-Q 6.796875 0.296875 3.296875 0 
-z
-" id="CMUSerif-Roman-75"/>
-     </defs>
-     <g transform="translate(8.72 141.042)rotate(-90)scale(0.1 -0.1)">
-      <use xlink:href="#CMUSerif-Roman-84"/>
-      <use transform="translate(72.199982 0)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(116.599976 0)" xlink:href="#CMUSerif-Roman-109"/>
-      <use transform="translate(199.899963 0)" xlink:href="#CMUSerif-Roman-112"/>
-      <use transform="translate(255.399948 0)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(299.799942 0)" xlink:href="#CMUSerif-Roman-114"/>
-      <use transform="translate(338.899933 0)" xlink:href="#CMUSerif-Roman-97"/>
-      <use transform="translate(388.899918 0)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(427.699905 0)" xlink:href="#CMUSerif-Roman-117"/>
-      <use transform="translate(483.19989 0)" xlink:href="#CMUSerif-Roman-114"/>
-      <use transform="translate(522.299881 0)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(566.699875 0)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(599.999863 0)" xlink:href="#Cmmi10-84"/>
-      <use transform="translate(658.3983 0)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(691.698288 0)" xlink:href="#CMUSerif-Roman-91"/>
-      <use transform="translate(719.498276 0)" xlink:href="#CMUSerif-Roman-75"/>
-      <use transform="translate(797.198257 0)" xlink:href="#CMUSerif-Roman-93"/>
-     </g>
-    </g>
-   </g>
-   <g id="line2d_43">
-    <path clip-path="url(#p70b35b528f)" d="M -1 209.323214 
-L 223.107611 -1 
-L 223.107611 -1 
-" style="fill:none;stroke:#000000;stroke-linecap:square;"/>
-   </g>
-   <g id="line2d_44">
-    <path clip-path="url(#p70b35b528f)" d="M -1 70.434351 
-L 146.991516 70.434351 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_45">
-    <path clip-path="url(#p70b35b528f)" d="M 146.991516 197.316 
-L 146.991516 70.434351 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_46">
-    <path clip-path="url(#p70b35b528f)" d="M 146.991516 51.03 
-L 181.630061 18.522 
-" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="patch_3">
-    <path d="M 34.02 197.316 
-L 34.02 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_4">
-    <path d="M 224.532 197.316 
-L 224.532 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_5">
-    <path d="M 34.02 197.316 
-L 224.532 197.316 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_6">
-    <path d="M 34.02 2.268 
-L 224.532 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="text_12">
-    <!-- $n_{\rm H}$^EOS_gamma_effective -->
-    <defs>
-     <path d="M 34.421875 72.90625 
-L 56.6875 45.703125 
-L 48 45.703125 
-L 30.078125 64.984375 
-L 12.203125 45.703125 
-L 3.515625 45.703125 
-L 25.78125 72.90625 
-z
-" id="DejaVuSansMono-94"/>
-     <path d="M 9.625 72.90625 
-L 52.875 72.90625 
-L 52.875 64.59375 
-L 19.484375 64.59375 
-L 19.484375 43.015625 
-L 51.421875 43.015625 
-L 51.421875 34.71875 
-L 19.484375 34.71875 
-L 19.484375 8.296875 
-L 53.8125 8.296875 
-L 53.8125 0 
-L 9.625 0 
-z
-" id="DejaVuSansMono-69"/>
-     <path d="M 44.1875 36.375 
-Q 44.1875 52.4375 40.890625 59.328125 
-Q 37.59375 66.21875 30.078125 66.21875 
-Q 22.609375 66.21875 19.3125 59.328125 
-Q 16.015625 52.4375 16.015625 36.375 
-Q 16.015625 20.359375 19.3125 13.46875 
-Q 22.609375 6.59375 30.078125 6.59375 
-Q 37.59375 6.59375 40.890625 13.453125 
-Q 44.1875 20.3125 44.1875 36.375 
-z
-M 54.5 36.375 
-Q 54.5 17.328125 48.46875 7.953125 
-Q 42.4375 -1.421875 30.078125 -1.421875 
-Q 17.71875 -1.421875 11.71875 7.90625 
-Q 5.71875 17.234375 5.71875 36.375 
-Q 5.71875 55.46875 11.75 64.84375 
-Q 17.78125 74.21875 30.078125 74.21875 
-Q 42.4375 74.21875 48.46875 64.84375 
-Q 54.5 55.46875 54.5 36.375 
-z
-" id="DejaVuSansMono-79"/>
-     <path d="M 49.421875 70.40625 
-L 49.421875 60.40625 
-Q 44.921875 63.28125 40.40625 64.75 
-Q 35.890625 66.21875 31.296875 66.21875 
-Q 24.3125 66.21875 20.265625 62.96875 
-Q 16.21875 59.71875 16.21875 54.203125 
-Q 16.21875 49.359375 18.875 46.8125 
-Q 21.53125 44.28125 28.8125 42.578125 
-L 33.984375 41.40625 
-Q 44.234375 39.015625 48.921875 33.890625 
-Q 53.609375 28.765625 53.609375 19.921875 
-Q 53.609375 9.515625 47.15625 4.046875 
-Q 40.71875 -1.421875 28.421875 -1.421875 
-Q 23.296875 -1.421875 18.109375 -0.3125 
-Q 12.9375 0.78125 7.71875 2.984375 
-L 7.71875 13.484375 
-Q 13.328125 9.90625 18.328125 8.25 
-Q 23.34375 6.59375 28.421875 6.59375 
-Q 35.890625 6.59375 40.03125 9.9375 
-Q 44.1875 13.28125 44.1875 19.28125 
-Q 44.1875 24.75 41.328125 27.625 
-Q 38.484375 30.515625 31.390625 32.078125 
-L 26.125 33.296875 
-Q 15.96875 35.59375 11.375 40.234375 
-Q 6.78125 44.875 6.78125 52.6875 
-Q 6.78125 62.453125 13.34375 68.328125 
-Q 19.921875 74.21875 30.8125 74.21875 
-Q 35.015625 74.21875 39.640625 73.265625 
-Q 44.28125 72.3125 49.421875 70.40625 
-z
-" id="DejaVuSansMono-83"/>
-     <path d="M 60.203125 -19.671875 
-L 60.203125 -23.578125 
-L 0 -23.578125 
-L 0 -19.671875 
-z
-" id="DejaVuSansMono-95"/>
-     <path d="M 41.890625 27.78125 
-Q 41.890625 37.890625 38.59375 43.140625 
-Q 35.296875 48.390625 29 48.390625 
-Q 22.40625 48.390625 18.9375 43.140625 
-Q 15.484375 37.890625 15.484375 27.78125 
-Q 15.484375 17.671875 18.96875 12.375 
-Q 22.46875 7.078125 29.109375 7.078125 
-Q 35.296875 7.078125 38.59375 12.390625 
-Q 41.890625 17.71875 41.890625 27.78125 
-z
-M 50.875 3.515625 
-Q 50.875 -8.796875 45.0625 -15.140625 
-Q 39.265625 -21.484375 27.984375 -21.484375 
-Q 24.265625 -21.484375 20.203125 -20.796875 
-Q 16.15625 -20.125 12.109375 -18.796875 
-L 12.109375 -9.90625 
-Q 16.890625 -12.15625 20.796875 -13.234375 
-Q 24.703125 -14.3125 27.984375 -14.3125 
-Q 35.25 -14.3125 38.5625 -10.34375 
-Q 41.890625 -6.390625 41.890625 2.203125 
-L 41.890625 2.59375 
-L 41.890625 8.6875 
-Q 39.75 4.109375 36.03125 1.859375 
-Q 32.328125 -0.390625 27 -0.390625 
-Q 17.4375 -0.390625 11.71875 7.265625 
-Q 6 14.9375 6 27.78125 
-Q 6 40.671875 11.71875 48.328125 
-Q 17.4375 56 27 56 
-Q 32.28125 56 35.9375 53.90625 
-Q 39.59375 51.8125 41.890625 47.40625 
-L 41.890625 54.5 
-L 50.875 54.5 
-z
-" id="DejaVuSansMono-103"/>
-     <path d="M 34.28125 27.484375 
-L 31.296875 27.484375 
-Q 23.4375 27.484375 19.453125 24.71875 
-Q 15.484375 21.96875 15.484375 16.5 
-Q 15.484375 11.578125 18.453125 8.84375 
-Q 21.4375 6.109375 26.703125 6.109375 
-Q 34.125 6.109375 38.375 11.25 
-Q 42.625 16.40625 42.671875 25.484375 
-L 42.671875 27.484375 
-z
-M 51.703125 31.203125 
-L 51.703125 0 
-L 42.671875 0 
-L 42.671875 8.109375 
-Q 39.796875 3.21875 35.421875 0.890625 
-Q 31.0625 -1.421875 24.8125 -1.421875 
-Q 16.453125 -1.421875 11.46875 3.296875 
-Q 6.5 8.015625 6.5 15.921875 
-Q 6.5 25.046875 12.625 29.78125 
-Q 18.75 34.515625 30.609375 34.515625 
-L 42.671875 34.515625 
-L 42.671875 35.9375 
-Q 42.625 42.484375 39.34375 45.4375 
-Q 36.078125 48.390625 28.90625 48.390625 
-Q 24.3125 48.390625 19.625 47.0625 
-Q 14.9375 45.75 10.5 43.21875 
-L 10.5 52.203125 
-Q 15.484375 54.109375 20.046875 55.046875 
-Q 24.609375 56 28.90625 56 
-Q 35.6875 56 40.5 54 
-Q 45.3125 52 48.296875 48 
-Q 50.140625 45.5625 50.921875 41.96875 
-Q 51.703125 38.375 51.703125 31.203125 
-z
-" id="DejaVuSansMono-97"/>
-     <path d="M 33.015625 49.125 
-Q 34.671875 52.640625 37.234375 54.3125 
-Q 39.796875 56 43.40625 56 
-Q 50 56 52.703125 50.890625 
-Q 55.421875 45.796875 55.421875 31.6875 
-L 55.421875 0 
-L 47.21875 0 
-L 47.21875 31.296875 
-Q 47.21875 42.875 45.921875 45.671875 
-Q 44.625 48.484375 41.21875 48.484375 
-Q 37.3125 48.484375 35.859375 45.484375 
-Q 34.421875 42.484375 34.421875 31.296875 
-L 34.421875 0 
-L 26.21875 0 
-L 26.21875 31.296875 
-Q 26.21875 43.015625 24.828125 45.75 
-Q 23.4375 48.484375 19.828125 48.484375 
-Q 16.265625 48.484375 14.875 45.484375 
-Q 13.484375 42.484375 13.484375 31.296875 
-L 13.484375 0 
-L 5.328125 0 
-L 5.328125 54.6875 
-L 13.484375 54.6875 
-L 13.484375 50 
-Q 15.09375 52.9375 17.5 54.46875 
-Q 19.921875 56 23 56 
-Q 26.703125 56 29.171875 54.296875 
-Q 31.640625 52.59375 33.015625 49.125 
-z
-" id="DejaVuSansMono-109"/>
-     <path d="M 54.296875 29.59375 
-L 54.296875 25.203125 
-L 15.375 25.203125 
-L 15.375 24.90625 
-Q 15.375 15.96875 20.03125 11.078125 
-Q 24.703125 6.203125 33.203125 6.203125 
-Q 37.5 6.203125 42.1875 7.5625 
-Q 46.875 8.9375 52.203125 11.71875 
-L 52.203125 2.78125 
-Q 47.078125 0.6875 42.3125 -0.359375 
-Q 37.546875 -1.421875 33.109375 -1.421875 
-Q 20.359375 -1.421875 13.171875 6.21875 
-Q 6 13.875 6 27.296875 
-Q 6 40.375 13.03125 48.1875 
-Q 20.0625 56 31.78125 56 
-Q 42.234375 56 48.265625 48.921875 
-Q 54.296875 41.84375 54.296875 29.59375 
-z
-M 45.3125 32.234375 
-Q 45.125 40.140625 41.578125 44.265625 
-Q 38.03125 48.390625 31.390625 48.390625 
-Q 24.90625 48.390625 20.703125 44.09375 
-Q 16.5 39.796875 15.71875 32.171875 
-z
-" id="DejaVuSansMono-101"/>
-     <path d="M 51.90625 75.984375 
-L 51.90625 68.5 
-L 41.703125 68.5 
-Q 36.859375 68.5 34.984375 66.515625 
-Q 33.109375 64.546875 33.109375 59.515625 
-L 33.109375 54.6875 
-L 51.90625 54.6875 
-L 51.90625 47.703125 
-L 33.109375 47.703125 
-L 33.109375 0 
-L 24.125 0 
-L 24.125 47.703125 
-L 9.515625 47.703125 
-L 9.515625 54.6875 
-L 24.125 54.6875 
-L 24.125 58.5 
-Q 24.125 67.484375 28.25 71.734375 
-Q 32.375 75.984375 41.109375 75.984375 
-z
-" id="DejaVuSansMono-102"/>
-     <path d="M 51.8125 2.78125 
-Q 48.1875 0.6875 44.359375 -0.359375 
-Q 40.53125 -1.421875 36.53125 -1.421875 
-Q 23.828125 -1.421875 16.671875 6.1875 
-Q 9.515625 13.8125 9.515625 27.296875 
-Q 9.515625 40.765625 16.671875 48.375 
-Q 23.828125 56 36.53125 56 
-Q 40.484375 56 44.234375 54.96875 
-Q 48 53.953125 51.8125 51.8125 
-L 51.8125 42.390625 
-Q 48.25 45.5625 44.65625 46.96875 
-Q 41.0625 48.390625 36.53125 48.390625 
-Q 28.078125 48.390625 23.53125 42.921875 
-Q 19 37.453125 19 27.296875 
-Q 19 17.1875 23.5625 11.6875 
-Q 28.125 6.203125 36.53125 6.203125 
-Q 41.21875 6.203125 44.921875 7.640625 
-Q 48.640625 9.078125 51.8125 12.109375 
-z
-" id="DejaVuSansMono-99"/>
-     <path d="M 29.984375 70.21875 
-L 29.984375 54.6875 
-L 50.390625 54.6875 
-L 50.390625 47.703125 
-L 29.984375 47.703125 
-L 29.984375 18.015625 
-Q 29.984375 11.96875 32.28125 9.5625 
-Q 34.578125 7.171875 40.28125 7.171875 
-L 50.390625 7.171875 
-L 50.390625 0 
-L 39.40625 0 
-Q 29.296875 0 25.140625 4.046875 
-Q 21 8.109375 21 18.015625 
-L 21 47.703125 
-L 6.390625 47.703125 
-L 6.390625 54.6875 
-L 21 54.6875 
-L 21 70.21875 
-z
-" id="DejaVuSansMono-116"/>
-     <path d="M 12.5 54.6875 
-L 35.5 54.6875 
-L 35.5 6.984375 
-L 53.328125 6.984375 
-L 53.328125 0 
-L 8.6875 0 
-L 8.6875 6.984375 
-L 26.515625 6.984375 
-L 26.515625 47.703125 
-L 12.5 47.703125 
-z
-M 26.515625 75.984375 
-L 35.5 75.984375 
-L 35.5 64.59375 
-L 26.515625 64.59375 
-z
-" id="DejaVuSansMono-105"/>
-     <path d="M 4.890625 54.6875 
-L 14.203125 54.6875 
-L 30.078125 8.796875 
-L 46 54.6875 
-L 55.328125 54.6875 
-L 35.890625 0 
-L 24.3125 0 
-z
-" id="DejaVuSansMono-118"/>
-    </defs>
-    <g transform="translate(145.146967 64.529336)rotate(-43)scale(0.065 -0.065)">
-     <use transform="translate(0 0.015625)" xlink:href="#Cmmi10-110"/>
-     <use transform="translate(60.009766 -16.990625)scale(0.7)" xlink:href="#Cmr10-72"/>
-     <use transform="translate(118.887109 0.015625)" xlink:href="#DejaVuSansMono-94"/>
-     <use transform="translate(179.092187 0.015625)" xlink:href="#DejaVuSansMono-69"/>
-     <use transform="translate(239.297266 0.015625)" xlink:href="#DejaVuSansMono-79"/>
-     <use transform="translate(299.502344 0.015625)" xlink:href="#DejaVuSansMono-83"/>
-     <use transform="translate(359.707422 0.015625)" xlink:href="#DejaVuSansMono-95"/>
-     <use transform="translate(419.9125 0.015625)" xlink:href="#DejaVuSansMono-103"/>
-     <use transform="translate(480.117578 0.015625)" xlink:href="#DejaVuSansMono-97"/>
-     <use transform="translate(540.322656 0.015625)" xlink:href="#DejaVuSansMono-109"/>
-     <use transform="translate(600.527734 0.015625)" xlink:href="#DejaVuSansMono-109"/>
-     <use transform="translate(660.732813 0.015625)" xlink:href="#DejaVuSansMono-97"/>
-     <use transform="translate(720.937891 0.015625)" xlink:href="#DejaVuSansMono-95"/>
-     <use transform="translate(781.142969 0.015625)" xlink:href="#DejaVuSansMono-101"/>
-     <use transform="translate(841.348047 0.015625)" xlink:href="#DejaVuSansMono-102"/>
-     <use transform="translate(901.553125 0.015625)" xlink:href="#DejaVuSansMono-102"/>
-     <use transform="translate(961.758203 0.015625)" xlink:href="#DejaVuSansMono-101"/>
-     <use transform="translate(1021.963281 0.015625)" xlink:href="#DejaVuSansMono-99"/>
-     <use transform="translate(1082.168359 0.015625)" xlink:href="#DejaVuSansMono-116"/>
-     <use transform="translate(1142.373437 0.015625)" xlink:href="#DejaVuSansMono-105"/>
-     <use transform="translate(1202.578516 0.015625)" xlink:href="#DejaVuSansMono-118"/>
-     <use transform="translate(1262.783594 0.015625)" xlink:href="#DejaVuSansMono-101"/>
-    </g>
-   </g>
-   <g id="text_13">
-    <!-- EOS_density_norm_H_p_cm3 -->
-    <defs>
-     <path d="M 41.890625 47.703125 
-L 41.890625 75.984375 
-L 50.875 75.984375 
-L 50.875 0 
-L 41.890625 0 
-L 41.890625 6.890625 
-Q 39.65625 2.828125 35.90625 0.703125 
-Q 32.171875 -1.421875 27.296875 -1.421875 
-Q 17.390625 -1.421875 11.6875 6.265625 
-Q 6 13.96875 6 27.484375 
-Q 6 40.828125 11.71875 48.40625 
-Q 17.4375 56 27.296875 56 
-Q 32.234375 56 35.984375 53.875 
-Q 39.75 51.765625 41.890625 47.703125 
-z
-M 15.484375 27.296875 
-Q 15.484375 16.84375 18.796875 11.515625 
-Q 22.125 6.203125 28.609375 6.203125 
-Q 35.109375 6.203125 38.5 11.5625 
-Q 41.890625 16.9375 41.890625 27.296875 
-Q 41.890625 37.703125 38.5 43.046875 
-Q 35.109375 48.390625 28.609375 48.390625 
-Q 22.125 48.390625 18.796875 43.0625 
-Q 15.484375 37.75 15.484375 27.296875 
-z
-" id="DejaVuSansMono-100"/>
-     <path d="M 51.3125 33.890625 
-L 51.3125 0 
-L 42.28125 0 
-L 42.28125 33.890625 
-Q 42.28125 41.265625 39.6875 44.71875 
-Q 37.109375 48.1875 31.59375 48.1875 
-Q 25.296875 48.1875 21.890625 43.71875 
-Q 18.5 39.265625 18.5 30.90625 
-L 18.5 0 
-L 9.515625 0 
-L 9.515625 54.6875 
-L 18.5 54.6875 
-L 18.5 46.484375 
-Q 20.90625 51.171875 25 53.578125 
-Q 29.109375 56 34.71875 56 
-Q 43.0625 56 47.1875 50.5 
-Q 51.3125 45.015625 51.3125 33.890625 
-z
-" id="DejaVuSansMono-110"/>
-     <path d="M 47.515625 52.78125 
-L 47.515625 44 
-Q 43.65625 46.234375 39.75 47.359375 
-Q 35.84375 48.484375 31.78125 48.484375 
-Q 25.6875 48.484375 22.671875 46.5 
-Q 19.671875 44.53125 19.671875 40.484375 
-Q 19.671875 36.8125 21.921875 35 
-Q 24.171875 33.203125 33.109375 31.5 
-L 36.71875 30.8125 
-Q 43.40625 29.546875 46.84375 25.734375 
-Q 50.296875 21.921875 50.296875 15.828125 
-Q 50.296875 7.71875 44.53125 3.140625 
-Q 38.765625 -1.421875 28.515625 -1.421875 
-Q 24.46875 -1.421875 20.015625 -0.5625 
-Q 15.578125 0.296875 10.40625 2 
-L 10.40625 11.28125 
-Q 15.4375 8.6875 20.015625 7.390625 
-Q 24.609375 6.109375 28.71875 6.109375 
-Q 34.671875 6.109375 37.9375 8.515625 
-Q 41.21875 10.9375 41.21875 15.28125 
-Q 41.21875 21.53125 29.25 23.921875 
-L 28.859375 24.03125 
-L 25.484375 24.703125 
-Q 17.71875 26.21875 14.15625 29.8125 
-Q 10.59375 33.40625 10.59375 39.59375 
-Q 10.59375 47.46875 15.90625 51.734375 
-Q 21.234375 56 31.109375 56 
-Q 35.5 56 39.546875 55.1875 
-Q 43.609375 54.390625 47.515625 52.78125 
-z
-" id="DejaVuSansMono-115"/>
-     <path d="M 41.890625 17.578125 
-Q 39.65625 11.859375 36.1875 2.546875 
-Q 31.34375 -10.359375 29.6875 -13.1875 
-Q 27.4375 -17 24.0625 -18.890625 
-Q 20.703125 -20.796875 16.21875 -20.796875 
-L 8.984375 -20.796875 
-L 8.984375 -13.28125 
-L 14.3125 -13.28125 
-Q 18.265625 -13.28125 20.5 -10.984375 
-Q 22.75 -8.6875 26.21875 0.875 
-L 5.078125 54.6875 
-L 14.59375 54.6875 
-L 30.8125 11.921875 
-L 46.78125 54.6875 
-L 56.296875 54.6875 
-z
-" id="DejaVuSansMono-121"/>
-     <path d="M 30.078125 48.390625 
-Q 23.25 48.390625 19.734375 43.0625 
-Q 16.21875 37.75 16.21875 27.296875 
-Q 16.21875 16.890625 19.734375 11.546875 
-Q 23.25 6.203125 30.078125 6.203125 
-Q 36.96875 6.203125 40.484375 11.546875 
-Q 44 16.890625 44 27.296875 
-Q 44 37.75 40.484375 43.0625 
-Q 36.96875 48.390625 30.078125 48.390625 
-z
-M 30.078125 56 
-Q 41.453125 56 47.484375 48.625 
-Q 53.515625 41.265625 53.515625 27.296875 
-Q 53.515625 13.28125 47.5 5.921875 
-Q 41.5 -1.421875 30.078125 -1.421875 
-Q 18.703125 -1.421875 12.6875 5.921875 
-Q 6.6875 13.28125 6.6875 27.296875 
-Q 6.6875 41.265625 12.6875 48.625 
-Q 18.703125 56 30.078125 56 
-z
-" id="DejaVuSansMono-111"/>
-     <path d="M 56.390625 43.40625 
-Q 53.515625 45.65625 50.53125 46.671875 
-Q 47.5625 47.703125 44 47.703125 
-Q 35.59375 47.703125 31.140625 42.421875 
-Q 26.703125 37.15625 26.703125 27.203125 
-L 26.703125 0 
-L 17.671875 0 
-L 17.671875 54.6875 
-L 26.703125 54.6875 
-L 26.703125 44 
-Q 28.953125 49.8125 33.609375 52.90625 
-Q 38.28125 56 44.671875 56 
-Q 48 56 50.875 55.171875 
-Q 53.765625 54.34375 56.390625 52.59375 
-z
-" id="DejaVuSansMono-114"/>
-     <path d="M 6.6875 72.90625 
-L 16.609375 72.90625 
-L 16.609375 43.015625 
-L 43.609375 43.015625 
-L 43.609375 72.90625 
-L 53.515625 72.90625 
-L 53.515625 0 
-L 43.609375 0 
-L 43.609375 34.71875 
-L 16.609375 34.71875 
-L 16.609375 0 
-L 6.6875 0 
-z
-" id="DejaVuSansMono-72"/>
-     <path d="M 18.3125 6.890625 
-L 18.3125 -20.796875 
-L 9.28125 -20.796875 
-L 9.28125 54.6875 
-L 18.3125 54.6875 
-L 18.3125 47.703125 
-Q 20.5625 51.765625 24.296875 53.875 
-Q 28.03125 56 32.90625 56 
-Q 42.828125 56 48.46875 48.328125 
-Q 54.109375 40.671875 54.109375 27.09375 
-Q 54.109375 13.765625 48.4375 6.171875 
-Q 42.78125 -1.421875 32.90625 -1.421875 
-Q 27.9375 -1.421875 24.1875 0.703125 
-Q 20.453125 2.828125 18.3125 6.890625 
-z
-M 44.671875 27.296875 
-Q 44.671875 37.75 41.375 43.0625 
-Q 38.09375 48.390625 31.59375 48.390625 
-Q 25.046875 48.390625 21.671875 43.046875 
-Q 18.3125 37.703125 18.3125 27.296875 
-Q 18.3125 16.9375 21.671875 11.5625 
-Q 25.046875 6.203125 31.59375 6.203125 
-Q 38.09375 6.203125 41.375 11.515625 
-Q 44.671875 16.84375 44.671875 27.296875 
-z
-" id="DejaVuSansMono-112"/>
-     <path d="M 37.890625 39.015625 
-Q 45.0625 37.109375 48.875 32.25 
-Q 52.6875 27.390625 52.6875 20.125 
-Q 52.6875 10.0625 45.921875 4.3125 
-Q 39.15625 -1.421875 27.203125 -1.421875 
-Q 22.171875 -1.421875 16.9375 -0.484375 
-Q 11.71875 0.4375 6.6875 2.203125 
-L 6.6875 12.015625 
-Q 11.671875 9.421875 16.5 8.15625 
-Q 21.34375 6.890625 26.125 6.890625 
-Q 34.234375 6.890625 38.578125 10.546875 
-Q 42.921875 14.203125 42.921875 21.09375 
-Q 42.921875 27.4375 38.578125 31.171875 
-Q 34.234375 34.90625 26.8125 34.90625 
-L 19.28125 34.90625 
-L 19.28125 43.015625 
-L 26.8125 43.015625 
-Q 33.59375 43.015625 37.40625 45.984375 
-Q 41.21875 48.96875 41.21875 54.296875 
-Q 41.21875 59.90625 37.671875 62.90625 
-Q 34.125 65.921875 27.59375 65.921875 
-Q 23.25 65.921875 18.609375 64.9375 
-Q 13.96875 63.96875 8.890625 62.015625 
-L 8.890625 71.09375 
-Q 14.796875 72.65625 19.40625 73.4375 
-Q 24.03125 74.21875 27.59375 74.21875 
-Q 38.234375 74.21875 44.609375 68.875 
-Q 50.984375 63.53125 50.984375 54.6875 
-Q 50.984375 48.6875 47.625 44.671875 
-Q 44.28125 40.671875 37.890625 39.015625 
-z
-" id="DejaVuSansMono-51"/>
-    </defs>
-    <g transform="translate(144.955236 192.590474)rotate(-90)scale(0.07 -0.07)">
-     <use xlink:href="#DejaVuSansMono-69"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-79"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-83"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-95"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-100"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-101"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-110"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-115"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-105"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-116"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-121"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-95"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-110"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-111"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-114"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-109"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-72"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-112"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-99"/>
-     <use x="1324.511719" xlink:href="#DejaVuSansMono-109"/>
-     <use x="1384.716797" xlink:href="#DejaVuSansMono-51"/>
-    </g>
-   </g>
-   <g id="text_14">
-    <!-- EOS_temperature_norm_K -->
-    <defs>
-     <path d="M 9.515625 20.703125 
-L 9.515625 54.59375 
-L 18.5 54.59375 
-L 18.5 20.703125 
-Q 18.5 13.328125 21.109375 9.859375 
-Q 23.734375 6.390625 29.203125 6.390625 
-Q 35.546875 6.390625 38.90625 10.859375 
-Q 42.28125 15.328125 42.28125 23.6875 
-L 42.28125 54.59375 
-L 51.3125 54.59375 
-L 51.3125 0 
-L 42.28125 0 
-L 42.28125 8.203125 
-Q 39.890625 3.46875 35.765625 1.015625 
-Q 31.640625 -1.421875 26.125 -1.421875 
-Q 17.71875 -1.421875 13.609375 4.078125 
-Q 9.515625 9.578125 9.515625 20.703125 
-z
-" id="DejaVuSansMono-117"/>
-     <path d="M 6.6875 72.90625 
-L 16.609375 72.90625 
-L 16.609375 40.484375 
-L 47.40625 72.90625 
-L 58.984375 72.90625 
-L 30.609375 43.109375 
-L 59.8125 0 
-L 47.90625 0 
-L 24.125 36.53125 
-L 16.609375 28.515625 
-L 16.609375 0 
-L 6.6875 0 
-z
-" id="DejaVuSansMono-75"/>
-    </defs>
-    <g transform="translate(37.862259 67.750649)scale(0.07 -0.07)">
-     <use xlink:href="#DejaVuSansMono-69"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-79"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-83"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-95"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-116"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-101"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-109"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-112"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-101"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-114"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-97"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-116"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-117"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-114"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-101"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-95"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-110"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-111"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-114"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-109"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-75"/>
-    </g>
-   </g>
-  </g>
- </g>
- <defs>
-  <clipPath id="p70b35b528f">
-   <rect height="195.048" width="190.512" x="34.02" y="2.268"/>
-  </clipPath>
- </defs>
-</svg>
diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_Z_dep.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_Z_dep.svg
deleted file mode 100644
index 5314e6c83b0e394cc1262eb59fbfaf763151923a..0000000000000000000000000000000000000000
--- a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_SF_Z_dep.svg
+++ /dev/null
@@ -1,2649 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
-  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Created with matplotlib (https://matplotlib.org/) -->
-<svg height="226.8pt" version="1.1" viewBox="0 0 226.8 226.8" width="226.8pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <defs>
-  <style type="text/css">
-*{stroke-linecap:butt;stroke-linejoin:round;}
-  </style>
- </defs>
- <g id="figure_1">
-  <g id="patch_1">
-   <path d="M 0 226.8 
-L 226.8 226.8 
-L 226.8 0 
-L 0 0 
-z
-" style="fill:#ffffff;"/>
-  </g>
-  <g id="axes_1">
-   <g id="patch_2">
-    <path d="M 34.02 197.316 
-L 224.532 197.316 
-L 224.532 2.268 
-L 34.02 2.268 
-z
-" style="fill:#ffffff;"/>
-   </g>
-   <g id="PathCollection_1">
-    <defs>
-     <path d="M 0 1 
-C 0.265203 1 0.51958 0.894634 0.707107 0.707107 
-C 0.894634 0.51958 1 0.265203 1 0 
-C 1 -0.265203 0.894634 -0.51958 0.707107 -0.707107 
-C 0.51958 -0.894634 0.265203 -1 0 -1 
-C -0.265203 -1 -0.51958 -0.894634 -0.707107 -0.707107 
-C -0.894634 -0.51958 -1 -0.265203 -1 0 
-C -1 0.265203 -0.894634 0.51958 -0.707107 0.707107 
-C -0.51958 0.894634 -0.265203 1 0 1 
-z
-" id="mfeca7ee311" style="stroke:#000000;"/>
-    </defs>
-    <g clip-path="url(#p23ab05f3c8)">
-     <use style="stroke:#000000;" x="151.076832" xlink:href="#mfeca7ee311" y="99.792"/>
-    </g>
-   </g>
-   <g id="matplotlib.axis_1">
-    <g id="xtick_1">
-     <g id="line2d_1">
-      <defs>
-       <path d="M 0 0 
-L 0 3.5 
-" id="mf7651b78e9" style="stroke:#000000;stroke-width:0.8;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mf7651b78e9" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_1">
-      <!-- $\mathdefault{10^{-7}}$ -->
-      <defs>
-       <path d="M 8.90625 57.09375 
-L 8.90625 60.203125 
-Q 20.90625 60.203125 27.09375 66.59375 
-Q 28.796875 66.59375 29.09375 66.1875 
-Q 29.40625 65.796875 29.40625 64 
-L 29.40625 7.90625 
-Q 29.40625 4.90625 30.84375 4 
-Q 32.296875 3.09375 38.703125 3.09375 
-L 41.90625 3.09375 
-L 41.90625 0 
-Q 38.40625 0.296875 25.703125 0.296875 
-Q 13 0.296875 9.5 0 
-L 9.5 3.09375 
-L 12.703125 3.09375 
-Q 19 3.09375 20.5 4 
-Q 22 4.90625 22 7.90625 
-L 22 59.703125 
-Q 16.796875 57.09375 8.90625 57.09375 
-z
-" id="CMUSerif-Roman-49"/>
-       <path d="M 3.90625 32 
-Q 3.90625 46.703125 7.59375 54.703125 
-Q 12.796875 66.59375 25 66.59375 
-Q 27.59375 66.59375 30.296875 65.890625 
-Q 33 65.203125 36.453125 62.5 
-Q 39.90625 59.796875 42 55.40625 
-Q 46 46.90625 46 32 
-Q 46 17.40625 42.296875 9.40625 
-Q 36.90625 -2.203125 24.90625 -2.203125 
-Q 20.40625 -2.203125 15.84375 0.09375 
-Q 11.296875 2.40625 8.40625 7.90625 
-Q 3.90625 16.203125 3.90625 32 
-z
-M 12.203125 33.203125 
-Q 12.203125 18.09375 13.296875 12.09375 
-Q 14.5 5.59375 17.84375 2.796875 
-Q 21.203125 0 24.90625 0 
-Q 28.90625 0 32.25 3 
-Q 35.59375 6 36.59375 12.5 
-Q 37.703125 18.90625 37.703125 33.203125 
-Q 37.703125 47.09375 36.703125 52.703125 
-Q 35.40625 59.203125 31.90625 61.796875 
-Q 28.40625 64.40625 24.90625 64.40625 
-Q 23.59375 64.40625 22.1875 64 
-Q 20.796875 63.59375 18.796875 62.5 
-Q 16.796875 61.40625 15.25 58.59375 
-Q 13.703125 55.796875 13 51.59375 
-Q 12.203125 46.203125 12.203125 33.203125 
-z
-" id="CMUSerif-Roman-48"/>
-       <path d="M 1 18.59375 
-L 1 24.5 
-L 27.59375 24.5 
-L 27.59375 18.59375 
-z
-" id="CMUSerif-Roman-45"/>
-       <path d="M 5.59375 47 
-L 8.90625 67.59375 
-L 11.40625 67.59375 
-Q 11.703125 66.40625 12.203125 65.84375 
-Q 12.703125 65.296875 15.546875 64.84375 
-Q 18.40625 64.40625 24.203125 64.40625 
-L 48.5 64.40625 
-Q 48.5 62.09375 47.59375 60.90625 
-L 34.09375 41.90625 
-Q 29.796875 35.90625 27.90625 24.796875 
-Q 26.796875 17.40625 26.796875 8.40625 
-L 26.796875 3.296875 
-Q 26.796875 -2.203125 22.1875 -2.203125 
-Q 17.59375 -2.203125 17.59375 3.296875 
-Q 17.59375 22.40625 29.90625 40.90625 
-Q 31.203125 42.796875 36.046875 49.59375 
-Q 40.90625 56.40625 41.09375 56.703125 
-L 20.40625 56.703125 
-Q 11.296875 56.703125 10.59375 56.09375 
-Q 9.59375 55.203125 8.09375 47 
-z
-" id="CMUSerif-Roman-55"/>
-      </defs>
-      <g transform="translate(25.92 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.51875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.51875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.684375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.684375)scale(0.7)" xlink:href="#CMUSerif-Roman-55"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_2">
-     <g id="line2d_2">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="88.452" xlink:href="#mf7651b78e9" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_2">
-      <!-- $\mathdefault{10^{-5}}$ -->
-      <defs>
-       <path d="M 5 16.09375 
-Q 5 19.09375 6.59375 20.25 
-Q 8.203125 21.40625 9.90625 21.40625 
-Q 12.203125 21.40625 13.546875 19.953125 
-Q 14.90625 18.5 14.90625 16.5 
-Q 14.90625 14.5 13.546875 13.046875 
-Q 12.203125 11.59375 9.90625 11.59375 
-Q 8.796875 11.59375 8.203125 11.796875 
-Q 9.5 7.203125 13.546875 3.890625 
-Q 17.59375 0.59375 22.90625 0.59375 
-Q 29.59375 0.59375 33.59375 7.09375 
-Q 36 11.296875 36 20.796875 
-Q 36 29.203125 34.203125 33.40625 
-Q 31.40625 39.796875 25.703125 39.796875 
-Q 17.59375 39.796875 12.796875 32.796875 
-Q 12.203125 31.90625 11.5 31.90625 
-Q 10.5 31.90625 10.296875 32.453125 
-Q 10.09375 33 10.09375 34.5 
-L 10.09375 64.09375 
-Q 10.09375 66.5 11.09375 66.5 
-Q 11.5 66.5 12.296875 66.203125 
-Q 18.59375 63.40625 25.59375 63.40625 
-Q 32.796875 63.40625 39.203125 66.296875 
-Q 39.703125 66.59375 40 66.59375 
-Q 41 66.59375 41 65.5 
-Q 41 65.09375 40.203125 63.9375 
-Q 39.40625 62.796875 37.703125 61.296875 
-Q 36 59.796875 33.796875 58.390625 
-Q 31.59375 57 28.390625 56.046875 
-Q 25.203125 55.09375 21.703125 55.09375 
-Q 17.5 55.09375 13.203125 56.40625 
-L 13.203125 36.90625 
-Q 18.40625 42 25.90625 42 
-Q 33.90625 42 39.40625 35.546875 
-Q 44.90625 29.09375 44.90625 20.09375 
-Q 44.90625 10.703125 38.40625 4.25 
-Q 31.90625 -2.203125 23.09375 -2.203125 
-Q 15.09375 -2.203125 10.046875 3.5 
-Q 5 9.203125 5 16.09375 
-z
-" id="CMUSerif-Roman-53"/>
-      </defs>
-      <g transform="translate(80.352 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-53"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_3">
-     <g id="line2d_3">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="142.884" xlink:href="#mf7651b78e9" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_3">
-      <!-- $\mathdefault{10^{-3}}$ -->
-      <defs>
-       <path d="M 4.203125 13.5 
-Q 4.203125 16.5 5.890625 17.890625 
-Q 7.59375 19.296875 9.796875 19.296875 
-Q 12.09375 19.296875 13.75 17.796875 
-Q 15.40625 16.296875 15.40625 13.703125 
-Q 15.40625 10.90625 13.453125 9.34375 
-Q 11.5 7.796875 8.796875 8.203125 
-Q 11.203125 4.203125 15.59375 2.390625 
-Q 20 0.59375 24.09375 0.59375 
-Q 28.40625 0.59375 31.90625 4.296875 
-Q 35.40625 8 35.40625 17.09375 
-Q 35.40625 24.796875 32.40625 29.25 
-Q 29.40625 33.703125 23.5 33.703125 
-L 19.09375 33.703125 
-Q 17.59375 33.703125 17.140625 33.84375 
-Q 16.703125 34 16.703125 34.796875 
-Q 16.703125 35.796875 18.203125 36 
-Q 19.703125 36 22.09375 36.296875 
-Q 27.90625 36.5 31 41.5 
-Q 33.796875 46.203125 33.796875 52.90625 
-Q 33.796875 59 30.890625 61.546875 
-Q 28 64.09375 24.203125 64.09375 
-Q 20.703125 64.09375 16.84375 62.640625 
-Q 13 61.203125 10.90625 57.90625 
-Q 17.09375 57.90625 17.09375 52.90625 
-Q 17.09375 50.703125 15.6875 49.25 
-Q 14.296875 47.796875 12 47.796875 
-Q 9.796875 47.796875 8.34375 49.1875 
-Q 6.90625 50.59375 6.90625 53 
-Q 6.90625 58.703125 12 62.640625 
-Q 17.09375 66.59375 24.59375 66.59375 
-Q 32 66.59375 37.5 62.6875 
-Q 43 58.796875 43 52.796875 
-Q 43 46.90625 39.09375 42.046875 
-Q 35.203125 37.203125 29 35.203125 
-Q 36.59375 33.703125 41.140625 28.546875 
-Q 45.703125 23.40625 45.703125 17.09375 
-Q 45.703125 9.296875 39.546875 3.546875 
-Q 33.40625 -2.203125 24.40625 -2.203125 
-Q 16.09375 -2.203125 10.140625 2.296875 
-Q 4.203125 6.796875 4.203125 13.5 
-z
-" id="CMUSerif-Roman-51"/>
-      </defs>
-      <g transform="translate(134.784 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-51"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_4">
-     <g id="line2d_4">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="197.316" xlink:href="#mf7651b78e9" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_4">
-      <!-- $\mathdefault{10^{-1}}$ -->
-      <g transform="translate(189.216 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-49"/>
-      </g>
-     </g>
-    </g>
-    <g id="text_5">
-     <!-- Metallicity (metal mass fraction) $Z$ [-] -->
-     <defs>
-      <path d="M 3.703125 0 
-L 3.703125 3.09375 
-Q 7.296875 3.09375 9.59375 3.796875 
-Q 11.90625 4.5 12.75 5.84375 
-Q 13.59375 7.203125 13.796875 8.09375 
-Q 14 9 14 10.5 
-L 14 60.5 
-Q 14 63.40625 12.75 64.296875 
-Q 11.5 65.203125 6.09375 65.203125 
-L 3.703125 65.203125 
-L 3.703125 68.296875 
-L 20.59375 68.296875 
-Q 22.40625 68.296875 22.90625 68 
-Q 23.40625 67.703125 24.09375 66.09375 
-L 45.796875 10.09375 
-L 67.703125 66.40625 
-Q 68.203125 67.796875 68.640625 68.046875 
-Q 69.09375 68.296875 71 68.296875 
-L 87.90625 68.296875 
-L 87.90625 65.203125 
-L 85.5 65.203125 
-Q 80.09375 65.203125 78.84375 64.296875 
-Q 77.59375 63.40625 77.59375 60.5 
-L 77.59375 7.796875 
-Q 77.59375 4.90625 78.84375 4 
-Q 80.09375 3.09375 85.5 3.09375 
-L 87.90625 3.09375 
-L 87.90625 0 
-Q 84.203125 0.296875 73.59375 0.296875 
-Q 62.90625 0.296875 59.203125 0 
-L 59.203125 3.09375 
-L 61.59375 3.09375 
-Q 67 3.09375 68.25 4 
-Q 69.5 4.90625 69.5 7.796875 
-L 69.5 65.203125 
-L 69.40625 65.203125 
-L 44.796875 1.90625 
-Q 44.09375 0 43 0 
-Q 41.796875 0 41 2.203125 
-L 16.90625 64.40625 
-L 16.796875 64.40625 
-L 16.796875 10.5 
-Q 16.796875 9 17 8.09375 
-Q 17.203125 7.203125 18.046875 5.84375 
-Q 18.90625 4.5 21.203125 3.796875 
-Q 23.5 3.09375 27.09375 3.09375 
-L 27.09375 0 
-Q 16.59375 0.296875 15.390625 0.296875 
-Q 14.203125 0.296875 3.703125 0 
-z
-" id="CMUSerif-Roman-77"/>
-      <path d="M 2.796875 22 
-Q 2.796875 31.40625 8.84375 38.09375 
-Q 14.90625 44.796875 23.59375 44.796875 
-Q 32.40625 44.796875 36.953125 39.09375 
-Q 41.5 33.40625 41.5 25.203125 
-Q 41.5 23.703125 41.09375 23.390625 
-Q 40.703125 23.09375 39 23.09375 
-L 11.09375 23.09375 
-Q 11.09375 12.90625 14.09375 8.09375 
-Q 18.296875 1.40625 25.40625 1.40625 
-Q 26.40625 1.40625 27.546875 1.59375 
-Q 28.703125 1.796875 31.09375 2.640625 
-Q 33.5 3.5 35.59375 5.796875 
-Q 37.703125 8.09375 38.90625 11.703125 
-Q 39.203125 13.09375 40.203125 13.09375 
-Q 41.5 13.09375 41.5 11.90625 
-Q 41.5 11 40.546875 9.046875 
-Q 39.59375 7.09375 37.796875 4.75 
-Q 36 2.40625 32.5 0.65625 
-Q 29 -1.09375 24.796875 -1.09375 
-Q 16 -1.09375 9.390625 5.546875 
-Q 2.796875 12.203125 2.796875 22 
-z
-M 11.203125 25.203125 
-L 34.90625 25.203125 
-Q 34.90625 27.296875 34.546875 29.640625 
-Q 34.203125 32 33.140625 35.25 
-Q 32.09375 38.5 29.640625 40.546875 
-Q 27.203125 42.59375 23.59375 42.59375 
-Q 22 42.59375 20.25 41.890625 
-Q 18.5 41.203125 16.390625 39.546875 
-Q 14.296875 37.90625 12.84375 34.15625 
-Q 11.40625 30.40625 11.203125 25.203125 
-z
-" id="CMUSerif-Roman-101"/>
-      <path d="M 1.90625 40 
-L 1.90625 42.203125 
-Q 6.5 42.40625 9.546875 45.65625 
-Q 12.59375 48.90625 13.640625 52.90625 
-Q 14.703125 56.90625 14.796875 61.5 
-L 17.296875 61.5 
-L 17.296875 43.09375 
-L 31.59375 43.09375 
-L 31.59375 40 
-L 17.296875 40 
-L 17.296875 12.203125 
-Q 17.296875 1.40625 24 1.40625 
-Q 26.90625 1.40625 28.796875 4.34375 
-Q 30.703125 7.296875 30.703125 12.59375 
-L 30.703125 18.09375 
-L 33.203125 18.09375 
-L 33.203125 12.40625 
-Q 33.203125 7 30.703125 2.953125 
-Q 28.203125 -1.09375 23.296875 -1.09375 
-Q 21.5 -1.09375 19.703125 -0.640625 
-Q 17.90625 -0.203125 15.59375 1 
-Q 13.296875 2.203125 11.84375 5.140625 
-Q 10.40625 8.09375 10.40625 12.40625 
-L 10.40625 40 
-z
-" id="CMUSerif-Roman-116"/>
-      <path d="M 4.203125 9.5 
-Q 4.203125 18 14.203125 22.5 
-Q 20.203125 25.40625 32.59375 26.09375 
-L 32.59375 29.796875 
-Q 32.59375 36 29.34375 39.296875 
-Q 26.09375 42.59375 22 42.59375 
-Q 14.703125 42.59375 11.203125 38 
-Q 14.203125 37.90625 15.25 36.40625 
-Q 16.296875 34.90625 16.296875 33.40625 
-Q 16.296875 31.40625 15.046875 30.09375 
-Q 13.796875 28.796875 11.703125 28.796875 
-Q 9.703125 28.796875 8.390625 30.046875 
-Q 7.09375 31.296875 7.09375 33.5 
-Q 7.09375 38.40625 11.5 41.59375 
-Q 15.90625 44.796875 22.203125 44.796875 
-Q 30.40625 44.796875 35.90625 39.296875 
-Q 37.59375 37.59375 38.4375 35.390625 
-Q 39.296875 33.203125 39.390625 31.75 
-Q 39.5 30.296875 39.5 27.5 
-L 39.5 7.5 
-Q 39.5 6.90625 39.703125 5.953125 
-Q 39.90625 5 40.796875 3.75 
-Q 41.703125 2.5 43.203125 2.5 
-Q 46.796875 2.5 46.796875 8.90625 
-L 46.796875 14.5 
-L 49.296875 14.5 
-L 49.296875 8.90625 
-Q 49.296875 3.59375 46.5 1.5 
-Q 43.703125 -0.59375 41.09375 -0.59375 
-Q 37.796875 -0.59375 35.6875 1.84375 
-Q 33.59375 4.296875 33.296875 7.59375 
-Q 31.796875 3.796875 28.34375 1.34375 
-Q 24.90625 -1.09375 20.203125 -1.09375 
-Q 16.59375 -1.09375 13.1875 -0.1875 
-Q 9.796875 0.703125 7 3.203125 
-Q 4.203125 5.703125 4.203125 9.5 
-z
-M 11.90625 9.59375 
-Q 11.90625 5.90625 14.546875 3.5 
-Q 17.203125 1.09375 20.90625 1.09375 
-Q 25.09375 1.09375 28.84375 4.34375 
-Q 32.59375 7.59375 32.59375 14 
-L 32.59375 24 
-Q 21.5 23.59375 16.703125 19.1875 
-Q 11.90625 14.796875 11.90625 9.59375 
-z
-" id="CMUSerif-Roman-97"/>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-Q 8.59375 3.09375 9.84375 3.75 
-Q 11.09375 4.40625 11.09375 7.59375 
-L 11.09375 59.59375 
-Q 11.09375 63.296875 9.796875 64.25 
-Q 8.5 65.203125 3.296875 65.203125 
-L 3.296875 68.296875 
-L 17.703125 69.40625 
-L 17.703125 7.59375 
-Q 17.703125 4.40625 18.953125 3.75 
-Q 20.203125 3.09375 25.5 3.09375 
-L 25.5 0 
-Q 24.296875 0 21.75 0.09375 
-Q 19.203125 0.203125 17.34375 0.25 
-Q 15.5 0.296875 14.40625 0.296875 
-Q 13.203125 0.296875 3.296875 0 
-z
-" id="CMUSerif-Roman-108"/>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-Q 8.59375 3.09375 9.84375 3.75 
-Q 11.09375 4.40625 11.09375 7.59375 
-L 11.09375 34.5 
-Q 11.09375 38.203125 9.84375 39.09375 
-Q 8.59375 40 3.703125 40 
-L 3.703125 43.09375 
-L 17.703125 44.203125 
-L 17.703125 7.5 
-Q 17.703125 4.5 18.75 3.796875 
-Q 19.796875 3.09375 24.703125 3.09375 
-L 24.703125 0 
-Q 14.5 0.296875 14.296875 0.296875 
-Q 12.90625 0.296875 3.296875 0 
-z
-M 7.5 61.59375 
-Q 7.5 63.59375 9.046875 65.25 
-Q 10.59375 66.90625 12.796875 66.90625 
-Q 15 66.90625 16.546875 65.40625 
-Q 18.09375 63.90625 18.09375 61.59375 
-Q 18.09375 59.296875 16.546875 57.796875 
-Q 15 56.296875 12.796875 56.296875 
-Q 10.5 56.296875 9 57.890625 
-Q 7.5 59.5 7.5 61.59375 
-z
-" id="CMUSerif-Roman-105"/>
-      <path d="M 16.09375 -1.09375 
-Q 3.40625 12.09375 3.40625 21.59375 
-Q 3.40625 31.09375 9.65625 37.9375 
-Q 15.90625 44.796875 25.09375 44.796875 
-Q 31.203125 44.796875 35.796875 41.890625 
-Q 40.40625 39 40.40625 34.09375 
-Q 40.40625 31.90625 39.09375 30.65625 
-Q 37.796875 29.40625 35.796875 29.40625 
-Q 33.703125 29.40625 32.453125 30.703125 
-Q 31.203125 32 31.203125 34 
-Q 31.203125 34.90625 31.5 35.75 
-Q 31.796875 36.59375 32.890625 37.546875 
-Q 34 38.5 35.90625 38.59375 
-Q 32.296875 42.296875 25.203125 42.296875 
-Q 20.09375 42.296875 15.890625 37.5 
-Q 11.703125 32.703125 11.703125 21.796875 
-Q 11.703125 16.09375 13.09375 11.890625 
-Q 14.5 7.703125 16.796875 5.546875 
-Q 19.09375 3.40625 21.34375 2.40625 
-Q 23.59375 1.40625 25.796875 1.40625 
-Q 35.59375 1.40625 38.90625 11.90625 
-Q 39.203125 12.90625 40.203125 12.90625 
-Q 41.5 12.90625 41.5 11.90625 
-Q 41.5 11.40625 41.09375 10.15625 
-Q 40.703125 8.90625 39.5 6.90625 
-Q 38.296875 4.90625 36.546875 3.15625 
-Q 34.796875 1.40625 31.75 0.15625 
-Q 28.703125 -1.09375 24.90625 -1.09375 
-Q 16.09375 -1.09375 3.40625 12.09375 
-z
-" id="CMUSerif-Roman-99"/>
-      <path d="M 1.90625 -12.40625 
-Q 1.90625 -10.296875 3.15625 -9.1875 
-Q 4.40625 -8.09375 6.09375 -8.09375 
-Q 7.90625 -8.09375 9.09375 -9.25 
-Q 10.296875 -10.40625 10.296875 -12.296875 
-Q 10.296875 -16 6.40625 -16.5 
-Q 8.296875 -18.296875 11.09375 -18.296875 
-Q 14.09375 -18.296875 16.5 -16.09375 
-Q 18.90625 -13.90625 19.953125 -11.796875 
-Q 21 -9.703125 22.5 -5.90625 
-Q 23.90625 -2.90625 25 0 
-L 10 36.5 
-Q 9 38.90625 7.5 39.453125 
-Q 6 40 1.90625 40 
-L 1.90625 43.09375 
-Q 6.40625 42.796875 11.59375 42.796875 
-Q 14.703125 42.796875 22.5 43.09375 
-L 22.5 40 
-Q 16.90625 40 16.90625 37.40625 
-Q 16.90625 37.09375 17.5 35.59375 
-L 28.59375 8.703125 
-L 38.703125 33.296875 
-Q 39.296875 34.703125 39.296875 35.703125 
-Q 39.296875 39.796875 34.59375 40 
-L 34.59375 43.09375 
-Q 41.203125 42.796875 43.296875 42.796875 
-Q 47.40625 42.796875 50.796875 43.09375 
-L 50.796875 40 
-Q 44.09375 40 41.5 33.59375 
-L 23.90625 -9.09375 
-Q 19.09375 -20.5 11.09375 -20.5 
-Q 7.296875 -20.5 4.59375 -18.140625 
-Q 1.90625 -15.796875 1.90625 -12.40625 
-z
-" id="CMUSerif-Roman-121"/>
-      <path id="CMUSerif-Roman-32"/>
-      <path d="M 9.90625 25 
-Q 9.90625 41.90625 16.203125 55.5 
-Q 18.90625 61.203125 22.65625 66 
-Q 26.40625 70.796875 28.90625 72.890625 
-Q 31.40625 75 32.09375 75 
-Q 33.09375 75 33.09375 74 
-Q 33.09375 73.5 31.796875 72.296875 
-Q 15.703125 55.90625 15.703125 25 
-Q 15.703125 -6 31.40625 -21.796875 
-Q 33.09375 -23.5 33.09375 -24 
-Q 33.09375 -25 32.09375 -25 
-Q 31.40625 -25 29 -23 
-Q 26.59375 -21 22.890625 -16.390625 
-Q 19.203125 -11.796875 16.5 -6.203125 
-Q 9.90625 7.40625 9.90625 25 
-z
-" id="CMUSerif-Roman-40"/>
-      <path d="M 3.203125 0 
-L 3.203125 3.09375 
-Q 8.5 3.09375 9.75 3.75 
-Q 11 4.40625 11 7.59375 
-L 11 34.40625 
-Q 11 38.09375 9.703125 39.046875 
-Q 8.40625 40 3.203125 40 
-L 3.203125 43.09375 
-L 17.296875 44.203125 
-L 17.296875 33.703125 
-Q 22 44.203125 32.09375 44.203125 
-Q 43.796875 44.203125 45.40625 34.40625 
-Q 47.09375 38.203125 50.6875 41.203125 
-Q 54.296875 44.203125 59.90625 44.203125 
-Q 67.40625 44.203125 70.40625 40.5 
-Q 72.59375 38 73.046875 35.203125 
-Q 73.5 32.40625 73.5 25.203125 
-L 73.5 6.09375 
-Q 73.59375 4 75.1875 3.546875 
-Q 76.796875 3.09375 81.296875 3.09375 
-L 81.296875 0 
-Q 71.09375 0.296875 70.09375 0.296875 
-Q 69.296875 0.296875 58.796875 0 
-L 58.796875 3.09375 
-Q 64.09375 3.09375 65.34375 3.75 
-Q 66.59375 4.40625 66.59375 7.59375 
-L 66.59375 30.90625 
-Q 66.59375 36 65.046875 39 
-Q 63.5 42 59.203125 42 
-Q 54 42 49.84375 37.640625 
-Q 45.703125 33.296875 45.703125 26 
-L 45.703125 7.59375 
-Q 45.703125 4.40625 46.953125 3.75 
-Q 48.203125 3.09375 53.5 3.09375 
-L 53.5 0 
-Q 43.296875 0.296875 42.296875 0.296875 
-Q 41.5 0.296875 31 0 
-L 31 3.09375 
-Q 36.296875 3.09375 37.546875 3.75 
-Q 38.796875 4.40625 38.796875 7.59375 
-L 38.796875 30.90625 
-Q 38.796875 36 37.25 39 
-Q 35.703125 42 31.40625 42 
-Q 26.203125 42 22.046875 37.640625 
-Q 17.90625 33.296875 17.90625 26 
-L 17.90625 7.59375 
-Q 17.90625 4.40625 19.15625 3.75 
-Q 20.40625 3.09375 25.703125 3.09375 
-L 25.703125 0 
-Q 15.5 0.296875 14.5 0.296875 
-Q 13.703125 0.296875 3.203125 0 
-z
-" id="CMUSerif-Roman-109"/>
-      <path d="M 3.296875 1.296875 
-L 3.296875 14.5 
-Q 3.296875 15.59375 3.34375 16 
-Q 3.40625 16.40625 3.703125 16.703125 
-Q 4 17 4.59375 17 
-Q 5.296875 17 5.546875 16.703125 
-Q 5.796875 16.40625 6 15.296875 
-Q 7.5 8.40625 10.75 4.75 
-Q 14 1.09375 19.90625 1.09375 
-Q 25.5 1.09375 28.34375 3.59375 
-Q 31.203125 6.09375 31.203125 10.203125 
-Q 31.203125 17.5 20.796875 19.40625 
-Q 14.796875 20.59375 12.296875 21.390625 
-Q 9.796875 22.203125 7.59375 24 
-Q 3.296875 27.5 3.296875 32.5 
-Q 3.296875 37.5 7.09375 41.140625 
-Q 10.90625 44.796875 19.296875 44.796875 
-Q 24.90625 44.796875 28.703125 42 
-Q 29.796875 42.90625 30.40625 43.59375 
-Q 31.703125 44.796875 32.40625 44.796875 
-Q 33.203125 44.796875 33.34375 44.296875 
-Q 33.5 43.796875 33.5 42.40625 
-L 33.5 32.296875 
-Q 33.5 31.203125 33.453125 30.796875 
-Q 33.40625 30.40625 33.09375 30.15625 
-Q 32.796875 29.90625 32.203125 29.90625 
-Q 31.09375 29.90625 31 30.796875 
-Q 30.203125 42.90625 19.296875 42.90625 
-Q 13.40625 42.90625 10.75 40.65625 
-Q 8.09375 38.40625 8.09375 35.296875 
-Q 8.09375 33.59375 8.890625 32.296875 
-Q 9.703125 31 10.75 30.25 
-Q 11.796875 29.5 13.75 28.796875 
-Q 15.703125 28.09375 16.890625 27.84375 
-Q 18.09375 27.59375 20.40625 27.09375 
-Q 28.40625 25.59375 31.796875 22.296875 
-Q 36 18.09375 36 12.796875 
-Q 36 6.90625 32 2.90625 
-Q 28 -1.09375 19.90625 -1.09375 
-Q 13.40625 -1.09375 8.90625 3.203125 
-Q 8.296875 2.59375 7.84375 2.09375 
-Q 7.40625 1.59375 7.25 1.390625 
-Q 7.09375 1.203125 7.046875 1.09375 
-Q 7 1 6.90625 0.90625 
-Q 4.90625 -1.09375 4.40625 -1.09375 
-Q 3.59375 -1.09375 3.4375 -0.59375 
-Q 3.296875 -0.09375 3.296875 1.296875 
-z
-" id="CMUSerif-Roman-115"/>
-      <path d="M 3.296875 40 
-L 3.296875 43.09375 
-L 11.203125 43.09375 
-L 11.203125 54.59375 
-Q 11.203125 62 16 66.25 
-Q 20.796875 70.5 26.703125 70.5 
-Q 30.59375 70.5 33.140625 68.390625 
-Q 35.703125 66.296875 35.703125 63.5 
-Q 35.703125 61.59375 34.546875 60.34375 
-Q 33.40625 59.09375 31.296875 59.09375 
-Q 29.296875 59.09375 28.140625 60.34375 
-Q 27 61.59375 27 63.40625 
-Q 27 66.59375 30 67.59375 
-Q 28.5 68.296875 26.703125 68.296875 
-Q 23.09375 68.296875 20.296875 64.59375 
-Q 17.5 60.90625 17.5 54.703125 
-L 17.5 43.09375 
-L 29.203125 43.09375 
-L 29.203125 40 
-L 17.796875 40 
-L 17.796875 7.796875 
-Q 17.796875 4.90625 19 4 
-Q 20.203125 3.09375 25.40625 3.09375 
-L 27.5 3.09375 
-L 27.5 0 
-Q 23.5 0.296875 14.796875 0.296875 
-Q 13.59375 0.296875 11.6875 0.25 
-Q 9.796875 0.203125 7.296875 0.09375 
-Q 4.796875 0 3.40625 0 
-L 3.40625 3.09375 
-Q 8.703125 3.09375 9.953125 3.75 
-Q 11.203125 4.40625 11.203125 7.59375 
-L 11.203125 40 
-z
-" id="CMUSerif-Roman-102"/>
-      <path d="M 2.796875 0 
-L 2.796875 3.09375 
-Q 8.09375 3.09375 9.34375 3.75 
-Q 10.59375 4.40625 10.59375 7.59375 
-L 10.59375 34.40625 
-Q 10.59375 38.09375 9.296875 39.046875 
-Q 8 40 2.796875 40 
-L 2.796875 43.09375 
-L 16.703125 44.203125 
-L 16.703125 33.203125 
-Q 18.09375 37.5 21.09375 40.84375 
-Q 24.09375 44.203125 29 44.203125 
-Q 32.203125 44.203125 34.296875 42.390625 
-Q 36.40625 40.59375 36.40625 38.09375 
-Q 36.40625 35.90625 35.046875 34.796875 
-Q 33.703125 33.703125 32.09375 33.703125 
-Q 30.296875 33.703125 29.046875 34.84375 
-Q 27.796875 36 27.796875 38 
-Q 27.796875 39.203125 28.34375 40.140625 
-Q 28.90625 41.09375 29.34375 41.4375 
-Q 29.796875 41.796875 30.09375 41.90625 
-Q 29.90625 42 29 42 
-Q 23.5 42 20.34375 36.5 
-Q 17.203125 31 17.203125 23.203125 
-L 17.203125 7.796875 
-Q 17.203125 4.90625 18.390625 4 
-Q 19.59375 3.09375 24.796875 3.09375 
-L 26.90625 3.09375 
-L 26.90625 0 
-Q 22.90625 0.296875 14.203125 0.296875 
-Q 13 0.296875 11.09375 0.25 
-Q 9.203125 0.203125 6.703125 0.09375 
-Q 4.203125 0 2.796875 0 
-z
-" id="CMUSerif-Roman-114"/>
-      <path d="M 16 -1.09375 
-Q 2.796875 11.90625 2.796875 21.40625 
-Q 2.796875 30.90625 9.25 37.84375 
-Q 15.703125 44.796875 25 44.796875 
-Q 34.09375 44.796875 40.59375 37.890625 
-Q 47.09375 31 47.09375 21.40625 
-Q 47.09375 12 40.546875 5.453125 
-Q 34 -1.09375 24.90625 -1.09375 
-Q 16 -1.09375 2.796875 11.90625 
-z
-M 11.09375 22.203125 
-Q 11.09375 12.5 13.59375 8.09375 
-Q 17.5 1.40625 25 1.40625 
-Q 28.703125 1.40625 31.796875 3.40625 
-Q 34.90625 5.40625 36.59375 8.796875 
-Q 38.796875 13.203125 38.796875 22.203125 
-Q 38.796875 31.796875 36.203125 36.09375 
-Q 32.296875 42.59375 24.90625 42.59375 
-Q 21.703125 42.59375 18.546875 40.890625 
-Q 15.40625 39.203125 13.5 35.90625 
-Q 11.09375 31.5 11.09375 22.203125 
-z
-" id="CMUSerif-Roman-111"/>
-      <path d="M 3.203125 0 
-L 3.203125 3.09375 
-Q 8.5 3.09375 9.75 3.75 
-Q 11 4.40625 11 7.59375 
-L 11 34.40625 
-Q 11 38.09375 9.703125 39.046875 
-Q 8.40625 40 3.203125 40 
-L 3.203125 43.09375 
-L 17.296875 44.203125 
-L 17.296875 33.703125 
-Q 22 44.203125 32.09375 44.203125 
-Q 39.59375 44.203125 42.59375 40.5 
-Q 44.796875 38 45.25 35.203125 
-Q 45.703125 32.40625 45.703125 25.203125 
-L 45.703125 6.09375 
-Q 45.796875 4 47.390625 3.546875 
-Q 49 3.09375 53.5 3.09375 
-L 53.5 0 
-Q 43.296875 0.296875 42.296875 0.296875 
-Q 41.5 0.296875 31 0 
-L 31 3.09375 
-Q 36.296875 3.09375 37.546875 3.75 
-Q 38.796875 4.40625 38.796875 7.59375 
-L 38.796875 30.90625 
-Q 38.796875 36 37.25 39 
-Q 35.703125 42 31.40625 42 
-Q 26.203125 42 22.046875 37.640625 
-Q 17.90625 33.296875 17.90625 26 
-L 17.90625 7.59375 
-Q 17.90625 4.40625 19.15625 3.75 
-Q 20.40625 3.09375 25.703125 3.09375 
-L 25.703125 0 
-Q 15.5 0.296875 14.5 0.296875 
-Q 13.703125 0.296875 3.203125 0 
-z
-" id="CMUSerif-Roman-110"/>
-      <path d="M 5.703125 -24 
-Q 5.703125 -23.5 7 -22.296875 
-Q 23.09375 -5.90625 23.09375 25 
-Q 23.09375 56 7.59375 71.703125 
-Q 5.703125 73.5 5.703125 74 
-Q 5.703125 75 6.703125 75 
-Q 7.40625 75 9.796875 73 
-Q 12.203125 71 15.890625 66.390625 
-Q 19.59375 61.796875 22.296875 56.203125 
-Q 28.90625 42.59375 28.90625 25 
-Q 28.90625 8.09375 22.59375 -5.5 
-Q 19.90625 -11.203125 16.15625 -16 
-Q 12.40625 -20.796875 9.90625 -22.890625 
-Q 7.40625 -25 6.703125 -25 
-Q 5.703125 -25 5.703125 -24 
-z
-" id="CMUSerif-Roman-41"/>
-      <path d="M 5.8125 0.78125 
-Q 5.8125 2.25 6.5 3.078125 
-L 61.53125 64.796875 
-L 46.78125 64.796875 
-Q 39.453125 64.796875 34.640625 63.0625 
-Q 29.828125 61.328125 26.671875 57.296875 
-Q 23.53125 53.265625 21.390625 46.296875 
-Q 21.1875 45.40625 20.40625 45.40625 
-L 19.484375 45.40625 
-Q 18.5 45.40625 18.5 46.6875 
-L 24.8125 67.390625 
-Q 25 68.3125 25.78125 68.3125 
-L 71.390625 68.3125 
-Q 72.21875 68.3125 72.21875 67.484375 
-Q 72.21875 66.015625 71.578125 65.375 
-L 16.796875 3.8125 
-L 31.984375 3.8125 
-Q 40.828125 3.8125 45.875 5.984375 
-Q 50.921875 8.15625 53.875 12.84375 
-Q 56.84375 17.53125 59.625 26.21875 
-Q 59.71875 26.5625 59.984375 26.828125 
-Q 60.25 27.09375 60.59375 27.09375 
-L 61.53125 27.09375 
-Q 62.5 27.09375 62.5 25.78125 
-L 54.59375 0.875 
-Q 54.15625 0 53.609375 0 
-L 6.6875 0 
-Q 5.8125 0 5.8125 0.78125 
-z
-" id="Cmmi10-90"/>
-      <path d="M 10.40625 -25 
-L 10.40625 75 
-L 25.5 75 
-L 25.5 72.703125 
-L 17.09375 72.703125 
-L 17.09375 -22.703125 
-L 25.5 -22.703125 
-L 25.5 -25 
-z
-" id="CMUSerif-Roman-91"/>
-      <path d="M 2.09375 -22.703125 
-L 10.5 -22.703125 
-L 10.5 72.703125 
-L 2.09375 72.703125 
-L 2.09375 75 
-L 17.203125 75 
-L 17.203125 -25 
-L 2.09375 -25 
-z
-" id="CMUSerif-Roman-93"/>
-     </defs>
-     <g transform="translate(45.976 222.69725)scale(0.1 -0.1)">
-      <use xlink:href="#CMUSerif-Roman-77"/>
-      <use transform="translate(91.599991 0)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(135.999985 0)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(174.799973 0)" xlink:href="#CMUSerif-Roman-97"/>
-      <use transform="translate(224.799957 0)" xlink:href="#CMUSerif-Roman-108"/>
-      <use transform="translate(252.499954 0)" xlink:href="#CMUSerif-Roman-108"/>
-      <use transform="translate(280.199951 0)" xlink:href="#CMUSerif-Roman-105"/>
-      <use transform="translate(307.899948 0)" xlink:href="#CMUSerif-Roman-99"/>
-      <use transform="translate(352.299942 0)" xlink:href="#CMUSerif-Roman-105"/>
-      <use transform="translate(379.999939 0)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(418.799927 0)" xlink:href="#CMUSerif-Roman-121"/>
-      <use transform="translate(471.499924 0)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(504.799911 0)" xlink:href="#CMUSerif-Roman-40"/>
-      <use transform="translate(543.599899 0)" xlink:href="#CMUSerif-Roman-109"/>
-      <use transform="translate(626.899887 0)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(671.299881 0)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(710.099869 0)" xlink:href="#CMUSerif-Roman-97"/>
-      <use transform="translate(760.099854 0)" xlink:href="#CMUSerif-Roman-108"/>
-      <use transform="translate(787.79985 0)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(821.099838 0)" xlink:href="#CMUSerif-Roman-109"/>
-      <use transform="translate(904.399826 0)" xlink:href="#CMUSerif-Roman-97"/>
-      <use transform="translate(954.399811 0)" xlink:href="#CMUSerif-Roman-115"/>
-      <use transform="translate(993.799805 0)" xlink:href="#CMUSerif-Roman-115"/>
-      <use transform="translate(1033.199799 0)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(1066.499786 0)" xlink:href="#CMUSerif-Roman-102"/>
-      <use transform="translate(1096.999771 0)" xlink:href="#CMUSerif-Roman-114"/>
-      <use transform="translate(1136.099762 0)" xlink:href="#CMUSerif-Roman-97"/>
-      <use transform="translate(1186.099747 0)" xlink:href="#CMUSerif-Roman-99"/>
-      <use transform="translate(1230.499741 0)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(1269.299728 0)" xlink:href="#CMUSerif-Roman-105"/>
-      <use transform="translate(1296.999725 0)" xlink:href="#CMUSerif-Roman-111"/>
-      <use transform="translate(1346.99971 0)" xlink:href="#CMUSerif-Roman-110"/>
-      <use transform="translate(1402.499695 0)" xlink:href="#CMUSerif-Roman-41"/>
-      <use transform="translate(1441.299683 0)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(1474.59967 0)" xlink:href="#Cmmi10-90"/>
-      <use transform="translate(1542.812561 0)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(1576.112549 0)" xlink:href="#CMUSerif-Roman-91"/>
-      <use transform="translate(1603.912537 0)" xlink:href="#CMUSerif-Roman-45"/>
-      <use transform="translate(1637.212524 0)" xlink:href="#CMUSerif-Roman-93"/>
-     </g>
-    </g>
-   </g>
-   <g id="matplotlib.axis_2">
-    <g id="ytick_1">
-     <g id="line2d_5">
-      <defs>
-       <path d="M 0 0 
-L -3.5 0 
-" id="m7bcf80c517" style="stroke:#000000;stroke-width:0.8;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="172.059569"/>
-      </g>
-     </g>
-     <g id="text_6">
-      <!-- $\mathdefault{10^{-3}}$ -->
-      <g transform="translate(10.82 175.529881)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-51"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_2">
-     <g id="line2d_6">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="135.925784"/>
-      </g>
-     </g>
-     <g id="text_7">
-      <!-- $\mathdefault{10^{-2}}$ -->
-      <defs>
-       <path d="M 5 0 
-Q 5 1.796875 5.140625 2.34375 
-Q 5.296875 2.90625 6.09375 3.703125 
-L 25.296875 25.09375 
-Q 35.796875 36.90625 35.796875 47.203125 
-Q 35.796875 53.90625 32.296875 58.703125 
-Q 28.796875 63.5 22.40625 63.5 
-Q 18 63.5 14.296875 60.796875 
-Q 10.59375 58.09375 8.90625 53.296875 
-Q 9.203125 53.40625 10.203125 53.40625 
-Q 12.703125 53.40625 14.09375 51.84375 
-Q 15.5 50.296875 15.5 48.203125 
-Q 15.5 45.5 13.75 44.203125 
-Q 12 42.90625 10.296875 42.90625 
-Q 9.59375 42.90625 8.6875 43.046875 
-Q 7.796875 43.203125 6.390625 44.59375 
-Q 5 46 5 48.5 
-Q 5 55.5 10.296875 61.046875 
-Q 15.59375 66.59375 23.703125 66.59375 
-Q 32.90625 66.59375 38.90625 61.140625 
-Q 44.90625 55.703125 44.90625 47.203125 
-Q 44.90625 44.203125 44 41.5 
-Q 43.09375 38.796875 41.890625 36.6875 
-Q 40.703125 34.59375 37.5 31.25 
-Q 34.296875 27.90625 31.6875 25.5 
-Q 29.09375 23.09375 23.296875 18 
-L 12.703125 7.703125 
-L 30.703125 7.703125 
-Q 39.5 7.703125 40.203125 8.5 
-Q 41.203125 9.90625 42.40625 17.40625 
-L 44.90625 17.40625 
-L 42.09375 0 
-z
-" id="CMUSerif-Roman-50"/>
-      </defs>
-      <g transform="translate(10.82 139.396097)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-50"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_3">
-     <g id="line2d_7">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="99.792"/>
-      </g>
-     </g>
-     <g id="text_8">
-      <!-- $\mathdefault{10^{-1}}$ -->
-      <g transform="translate(10.82 103.262312)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-45"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-49"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_4">
-     <g id="line2d_8">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="63.658216"/>
-      </g>
-     </g>
-     <g id="text_9">
-      <!-- $\mathdefault{10^{0}}$ -->
-      <g transform="translate(13.22 67.128528)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-48"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_5">
-     <g id="line2d_9">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#m7bcf80c517" y="27.524431"/>
-      </g>
-     </g>
-     <g id="text_10">
-      <!-- $\mathdefault{10^{1}}$ -->
-      <g transform="translate(13.22 30.994744)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-49"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-48"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-49"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_6">
-     <g id="line2d_10">
-      <defs>
-       <path d="M 0 0 
-L -2 0 
-" id="mb9ef9ffb1f" style="stroke:#000000;stroke-width:0.6;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="197.316"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_7">
-     <g id="line2d_11">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="190.953156"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_8">
-     <g id="line2d_12">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="186.438647"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_9">
-     <g id="line2d_13">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="182.936922"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_10">
-     <g id="line2d_14">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="180.075803"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_11">
-     <g id="line2d_15">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="177.656763"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_12">
-     <g id="line2d_16">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="175.561294"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_13">
-     <g id="line2d_17">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="173.71296"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_14">
-     <g id="line2d_18">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="161.182216"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_15">
-     <g id="line2d_19">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="154.819372"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_16">
-     <g id="line2d_20">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="150.304863"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_17">
-     <g id="line2d_21">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="146.803137"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_18">
-     <g id="line2d_22">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="143.942019"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_19">
-     <g id="line2d_23">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="141.522978"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_20">
-     <g id="line2d_24">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="139.42751"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_21">
-     <g id="line2d_25">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="137.579176"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_22">
-     <g id="line2d_26">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="125.048431"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_23">
-     <g id="line2d_27">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="118.685588"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_24">
-     <g id="line2d_28">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="114.171078"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_25">
-     <g id="line2d_29">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="110.669353"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_26">
-     <g id="line2d_30">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="107.808235"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_27">
-     <g id="line2d_31">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="105.389194"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_28">
-     <g id="line2d_32">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="103.293726"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_29">
-     <g id="line2d_33">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="101.445391"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_30">
-     <g id="line2d_34">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="88.914647"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_31">
-     <g id="line2d_35">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="82.551803"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_32">
-     <g id="line2d_36">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="78.037294"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_33">
-     <g id="line2d_37">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="74.535569"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_34">
-     <g id="line2d_38">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="71.674451"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_35">
-     <g id="line2d_39">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="69.25541"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_36">
-     <g id="line2d_40">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="67.159941"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_37">
-     <g id="line2d_41">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="65.311607"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_38">
-     <g id="line2d_42">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="52.780863"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_39">
-     <g id="line2d_43">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="46.418019"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_40">
-     <g id="line2d_44">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="41.90351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_41">
-     <g id="line2d_45">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="38.401784"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_42">
-     <g id="line2d_46">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="35.540666"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_43">
-     <g id="line2d_47">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="33.121625"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_44">
-     <g id="line2d_48">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="31.026157"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_45">
-     <g id="line2d_49">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="29.177823"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_46">
-     <g id="line2d_50">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="16.647078"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_47">
-     <g id="line2d_51">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="10.284235"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_48">
-     <g id="line2d_52">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="5.769726"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_49">
-     <g id="line2d_53">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#mb9ef9ffb1f" y="2.268"/>
-      </g>
-     </g>
-    </g>
-    <g id="text_11">
-     <!-- SF threshold number density $n_{\rm H, thresh}$ [cm$^{-3}$] -->
-     <defs>
-      <path d="M 5.59375 0.203125 
-L 5.59375 20.203125 
-Q 5.59375 21.296875 5.640625 21.6875 
-Q 5.703125 22.09375 6 22.390625 
-Q 6.296875 22.703125 6.90625 22.703125 
-Q 8 22.703125 8.09375 21.703125 
-Q 8.40625 11.796875 14.296875 6.296875 
-Q 20.09375 0.90625 30.09375 0.90625 
-Q 36.09375 0.90625 39.796875 5.203125 
-Q 43.5 9.5 43.5 15.203125 
-Q 43.5 20.59375 40.40625 24.40625 
-Q 39.09375 26.09375 37.4375 27.1875 
-Q 35.796875 28.296875 34.796875 28.640625 
-Q 33.796875 29 31.90625 29.5 
-Q 19.5 32.5 18.703125 32.796875 
-Q 12.796875 34.796875 9.1875 39.84375 
-Q 5.59375 44.90625 5.59375 51.09375 
-Q 5.59375 59.09375 11.25 64.796875 
-Q 16.90625 70.5 25.09375 70.5 
-Q 29.203125 70.5 32.640625 69.203125 
-Q 36.09375 67.90625 37.5 66.75 
-Q 38.90625 65.59375 41.09375 63.5 
-L 44.59375 69.203125 
-Q 45.40625 70.5 46.09375 70.5 
-Q 46.90625 70.5 47.046875 70.046875 
-Q 47.203125 69.59375 47.203125 68.09375 
-L 47.203125 48 
-Q 47.203125 46.90625 47.140625 46.5 
-Q 47.09375 46.09375 46.796875 45.84375 
-Q 46.5 45.59375 45.90625 45.59375 
-Q 44.90625 45.59375 44.703125 46.796875 
-Q 41.90625 67.703125 25.203125 67.703125 
-Q 19.5 67.703125 15.75 63.84375 
-Q 12 60 12 54.59375 
-Q 12 50.203125 14.75 46.59375 
-Q 17.5 43 22.09375 41.90625 
-L 34.90625 38.796875 
-Q 41.296875 37.296875 45.59375 31.640625 
-Q 49.90625 26 49.90625 18.59375 
-Q 49.90625 10.09375 44.34375 3.9375 
-Q 38.796875 -2.203125 30.203125 -2.203125 
-Q 24 -2.203125 19.203125 -0.140625 
-Q 14.40625 1.90625 11.796875 4.796875 
-Q 9.796875 1.5 8.5 -0.40625 
-L 8.203125 -0.90625 
-Q 7.40625 -2.203125 6.703125 -2.203125 
-Q 5.90625 -2.203125 5.75 -1.703125 
-Q 5.59375 -1.203125 5.59375 0.203125 
-z
-" id="CMUSerif-Roman-83"/>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-L 5.703125 3.09375 
-Q 11.09375 3.09375 12.34375 4 
-Q 13.59375 4.90625 13.59375 7.796875 
-L 13.59375 60.203125 
-Q 13.59375 63.09375 12.34375 64 
-Q 11.09375 64.90625 5.703125 64.90625 
-L 3.296875 64.90625 
-L 3.296875 68 
-L 58.203125 68 
-L 61 45.5 
-L 58.5 45.5 
-Q 57.796875 51.203125 56.890625 54.453125 
-Q 56 57.703125 53.953125 60.296875 
-Q 51.90625 62.90625 48.453125 63.90625 
-Q 45 64.90625 39.40625 64.90625 
-L 27.40625 64.90625 
-Q 24 64.90625 23.25 64.203125 
-Q 22.5 63.5 22.5 60.90625 
-L 22.5 35.59375 
-L 31.09375 35.59375 
-Q 37.90625 35.59375 39.796875 38.046875 
-Q 41.703125 40.5 41.703125 47.296875 
-L 44.203125 47.296875 
-L 44.203125 20.796875 
-L 41.703125 20.796875 
-Q 41.703125 27.703125 39.796875 30.09375 
-Q 37.90625 32.5 31.09375 32.5 
-L 22.5 32.5 
-L 22.5 7.90625 
-Q 22.5 5.796875 22.84375 5 
-Q 23.203125 4.203125 25.296875 3.640625 
-Q 27.40625 3.09375 32 3.09375 
-L 35.296875 3.09375 
-L 35.296875 0 
-Q 31.703125 0.296875 18.5 0.296875 
-Q 6.796875 0.296875 3.296875 0 
-z
-" id="CMUSerif-Roman-70"/>
-      <path d="M 3.203125 0 
-L 3.203125 3.09375 
-Q 8.5 3.09375 9.75 3.75 
-Q 11 4.40625 11 7.59375 
-L 11 59.59375 
-Q 11 63.296875 9.703125 64.25 
-Q 8.40625 65.203125 3.203125 65.203125 
-L 3.203125 68.296875 
-L 17.59375 69.40625 
-L 17.59375 34.59375 
-L 17.703125 34.59375 
-Q 19.296875 38.203125 22.890625 41.203125 
-Q 26.5 44.203125 32.09375 44.203125 
-Q 39.59375 44.203125 42.59375 40.5 
-Q 44.796875 38 45.25 35.203125 
-Q 45.703125 32.40625 45.703125 25.203125 
-L 45.703125 6.09375 
-Q 45.796875 4 47.390625 3.546875 
-Q 49 3.09375 53.5 3.09375 
-L 53.5 0 
-Q 43.296875 0.296875 42.296875 0.296875 
-Q 41.5 0.296875 31 0 
-L 31 3.09375 
-Q 36.296875 3.09375 37.546875 3.75 
-Q 38.796875 4.40625 38.796875 7.59375 
-L 38.796875 30.90625 
-Q 38.796875 36 37.25 39 
-Q 35.703125 42 31.40625 42 
-Q 26.203125 42 22.046875 37.640625 
-Q 17.90625 33.296875 17.90625 26 
-L 17.90625 7.59375 
-Q 17.90625 4.40625 19.15625 3.75 
-Q 20.40625 3.09375 25.703125 3.09375 
-L 25.703125 0 
-Q 15.5 0.296875 14.5 0.296875 
-Q 13.703125 0.296875 3.203125 0 
-z
-" id="CMUSerif-Roman-104"/>
-      <path d="M 3.40625 21.5 
-Q 3.40625 31 10.046875 37.59375 
-Q 16.703125 44.203125 25.703125 44.203125 
-Q 33.296875 44.203125 38.296875 38 
-L 38.296875 59.59375 
-Q 38.296875 63.296875 37 64.25 
-Q 35.703125 65.203125 30.5 65.203125 
-L 30.5 68.296875 
-L 44.90625 69.40625 
-L 44.90625 8.703125 
-Q 44.90625 5 46.203125 4.046875 
-Q 47.5 3.09375 52.703125 3.09375 
-L 52.703125 0 
-L 38 -1.09375 
-L 38 5.5 
-Q 32.796875 -1.09375 24.59375 -1.09375 
-Q 16 -1.09375 9.703125 5.5 
-Q 3.40625 12.09375 3.40625 21.5 
-z
-M 11.703125 21.40625 
-Q 11.703125 12.09375 14.59375 7.5 
-Q 18.59375 1.09375 25.09375 1.09375 
-Q 32.5 1.09375 36.90625 8.09375 
-Q 38 9.796875 38 11.796875 
-L 38 32.296875 
-Q 38 34.296875 36.90625 36 
-Q 32.796875 42 26.09375 42 
-Q 19.09375 42 14.796875 35.59375 
-Q 11.703125 30.796875 11.703125 21.40625 
-z
-" id="CMUSerif-Roman-100"/>
-      <path d="M 3.203125 40 
-L 3.203125 43.09375 
-L 17.90625 44.203125 
-L 17.90625 11 
-Q 17.90625 8.59375 18.09375 7.1875 
-Q 18.296875 5.796875 19.09375 4.1875 
-Q 19.90625 2.59375 21.796875 1.84375 
-Q 23.703125 1.09375 26.703125 1.09375 
-Q 32.09375 1.09375 35.4375 5.546875 
-Q 38.796875 10 38.796875 16.59375 
-L 38.796875 34.40625 
-Q 38.796875 38.09375 37.5 39.046875 
-Q 36.203125 40 31 40 
-L 31 43.09375 
-L 45.703125 44.203125 
-L 45.703125 8.703125 
-Q 45.703125 5 47 4.046875 
-Q 48.296875 3.09375 53.5 3.09375 
-L 53.5 0 
-L 39.09375 -1.09375 
-L 39.09375 7.90625 
-Q 34.90625 -1.09375 26.203125 -1.09375 
-Q 21.796875 -1.09375 18.796875 0 
-Q 15.796875 1.09375 14.296875 2.5 
-Q 12.796875 3.90625 12 6.59375 
-Q 11.203125 9.296875 11.09375 10.9375 
-Q 11 12.59375 11 15.796875 
-L 11 30.796875 
-Q 11 37.59375 10 38.796875 
-Q 9 40 3.203125 40 
-z
-" id="CMUSerif-Roman-117"/>
-      <path d="M 2.796875 65.203125 
-L 2.796875 68.296875 
-L 17.203125 69.40625 
-L 17.203125 37.703125 
-Q 23 44.203125 30.90625 44.203125 
-Q 39.5 44.203125 45.796875 37.59375 
-Q 52.09375 31 52.09375 21.59375 
-Q 52.09375 12.09375 45.5 5.5 
-Q 38.90625 -1.09375 29.796875 -1.09375 
-Q 21.5 -1.09375 16.703125 6.203125 
-Q 13.203125 0.09375 13.09375 0 
-L 10.59375 0 
-L 10.59375 59.59375 
-Q 10.59375 63.296875 9.296875 64.25 
-Q 8 65.203125 2.796875 65.203125 
-z
-M 17.5 11.40625 
-Q 17.5 9.296875 18.90625 7.203125 
-Q 22.90625 1.09375 29.40625 1.09375 
-Q 36.40625 1.09375 40.703125 7.5 
-Q 43.796875 12.296875 43.796875 21.703125 
-Q 43.796875 31 40.90625 35.59375 
-Q 36.90625 42 30.40625 42 
-Q 23.09375 42 18.59375 35.59375 
-Q 17.5 34 17.5 32 
-z
-" id="CMUSerif-Roman-98"/>
-      <path d="M 7.71875 1.703125 
-Q 7.71875 2.296875 7.8125 2.59375 
-L 15.28125 32.421875 
-Q 16.015625 35.203125 16.015625 37.3125 
-Q 16.015625 41.609375 13.09375 41.609375 
-Q 9.96875 41.609375 8.453125 37.859375 
-Q 6.9375 34.125 5.515625 28.421875 
-Q 5.515625 28.125 5.21875 27.953125 
-Q 4.9375 27.78125 4.6875 27.78125 
-L 3.515625 27.78125 
-Q 3.171875 27.78125 2.921875 28.140625 
-Q 2.6875 28.515625 2.6875 28.8125 
-Q 3.765625 33.15625 4.765625 36.171875 
-Q 5.765625 39.203125 7.890625 41.6875 
-Q 10.015625 44.1875 13.1875 44.1875 
-Q 16.9375 44.1875 19.8125 41.8125 
-Q 22.703125 39.453125 22.703125 35.796875 
-Q 25.6875 39.703125 29.6875 41.9375 
-Q 33.6875 44.1875 38.1875 44.1875 
-Q 41.75 44.1875 44.328125 42.96875 
-Q 46.921875 41.75 48.359375 39.28125 
-Q 49.8125 36.8125 49.8125 33.40625 
-Q 49.8125 29.296875 47.96875 23.484375 
-Q 46.140625 17.671875 43.40625 10.5 
-Q 42 7.234375 42 4.5 
-Q 42 1.515625 44.28125 1.515625 
-Q 48.1875 1.515625 50.796875 5.703125 
-Q 53.421875 9.90625 54.5 14.703125 
-Q 54.6875 15.28125 55.328125 15.28125 
-L 56.5 15.28125 
-Q 56.890625 15.28125 57.15625 15.03125 
-Q 57.421875 14.796875 57.421875 14.40625 
-Q 57.421875 14.3125 57.328125 14.109375 
-Q 55.953125 8.453125 52.5625 3.65625 
-Q 49.171875 -1.125 44.09375 -1.125 
-Q 40.578125 -1.125 38.078125 1.296875 
-Q 35.59375 3.71875 35.59375 7.171875 
-Q 35.59375 9.03125 36.375 11.078125 
-Q 37.640625 14.359375 39.28125 18.890625 
-Q 40.921875 23.4375 41.96875 27.578125 
-Q 43.015625 31.734375 43.015625 34.90625 
-Q 43.015625 37.703125 41.859375 39.65625 
-Q 40.71875 41.609375 37.984375 41.609375 
-Q 34.328125 41.609375 31.25 39.984375 
-Q 28.171875 38.375 25.875 35.71875 
-Q 23.578125 33.0625 21.6875 29.390625 
-L 14.890625 2.203125 
-Q 14.546875 0.828125 13.34375 -0.140625 
-Q 12.15625 -1.125 10.6875 -1.125 
-Q 9.46875 -1.125 8.59375 -0.34375 
-Q 7.71875 0.4375 7.71875 1.703125 
-z
-" id="Cmmi10-110"/>
-      <path d="M 3.078125 0 
-L 3.078125 3.515625 
-Q 13.375 3.515625 13.375 6.6875 
-L 13.375 61.625 
-Q 13.375 64.796875 3.078125 64.796875 
-L 3.078125 68.3125 
-L 33.015625 68.3125 
-L 33.015625 64.796875 
-Q 22.703125 64.796875 22.703125 61.625 
-L 22.703125 37.3125 
-L 52.203125 37.3125 
-L 52.203125 61.625 
-Q 52.203125 64.796875 41.890625 64.796875 
-L 41.890625 68.3125 
-L 71.78125 68.3125 
-L 71.78125 64.796875 
-Q 61.53125 64.796875 61.53125 61.625 
-L 61.53125 6.6875 
-Q 61.53125 3.515625 71.78125 3.515625 
-L 71.78125 0 
-L 41.890625 0 
-L 41.890625 3.515625 
-Q 52.203125 3.515625 52.203125 6.6875 
-L 52.203125 33.796875 
-L 22.703125 33.796875 
-L 22.703125 6.6875 
-Q 22.703125 3.515625 33.015625 3.515625 
-L 33.015625 0 
-z
-" id="Cmr10-72"/>
-      <path d="M 9.90625 -18.015625 
-Q 9.90625 -17.578125 10.296875 -17.1875 
-Q 13.921875 -13.71875 15.921875 -9.171875 
-Q 17.921875 -4.640625 17.921875 0.390625 
-L 17.921875 1.609375 
-Q 16.3125 0 13.921875 0 
-Q 11.625 0 10.015625 1.609375 
-Q 8.40625 3.21875 8.40625 5.515625 
-Q 8.40625 7.859375 10.015625 9.421875 
-Q 11.625 10.984375 13.921875 10.984375 
-Q 17.484375 10.984375 19 7.6875 
-Q 20.515625 4.390625 20.515625 0.390625 
-Q 20.515625 -5.171875 18.28125 -10.171875 
-Q 16.0625 -15.1875 12.015625 -19.1875 
-Q 11.625 -19.390625 11.375 -19.390625 
-Q 10.890625 -19.390625 10.390625 -18.9375 
-Q 9.90625 -18.5 9.90625 -18.015625 
-z
-" id="Cmmi10-59"/>
-      <path d="M 10.203125 12.015625 
-L 10.203125 39.59375 
-L 1.90625 39.59375 
-L 1.90625 42.1875 
-Q 8.453125 42.1875 11.515625 48.28125 
-Q 14.59375 54.390625 14.59375 61.53125 
-L 17.484375 61.53125 
-L 17.484375 43.109375 
-L 31.59375 43.109375 
-L 31.59375 39.59375 
-L 17.484375 39.59375 
-L 17.484375 12.203125 
-Q 17.484375 8.0625 18.875 4.9375 
-Q 20.265625 1.8125 23.875 1.8125 
-Q 27.296875 1.8125 28.8125 5.109375 
-Q 30.328125 8.40625 30.328125 12.203125 
-L 30.328125 18.109375 
-L 33.203125 18.109375 
-L 33.203125 12.015625 
-Q 33.203125 8.890625 32.046875 5.828125 
-Q 30.90625 2.78125 28.65625 0.828125 
-Q 26.421875 -1.125 23.1875 -1.125 
-Q 17.1875 -1.125 13.6875 2.46875 
-Q 10.203125 6.0625 10.203125 12.015625 
-z
-" id="Cmr10-116"/>
-      <path d="M 2.984375 0 
-L 2.984375 3.515625 
-Q 6.390625 3.515625 8.59375 4.046875 
-Q 10.796875 4.59375 10.796875 6.6875 
-L 10.796875 59.1875 
-Q 10.796875 61.859375 9.984375 63.0625 
-Q 9.1875 64.265625 7.671875 64.53125 
-Q 6.15625 64.796875 2.984375 64.796875 
-L 2.984375 68.3125 
-L 17.828125 69.390625 
-L 17.828125 35.015625 
-Q 19.921875 39.15625 23.671875 41.671875 
-Q 27.4375 44.1875 31.984375 44.1875 
-Q 38.921875 44.1875 42.40625 40.859375 
-Q 45.90625 37.546875 45.90625 30.71875 
-L 45.90625 6.6875 
-Q 45.90625 4.59375 48.09375 4.046875 
-Q 50.296875 3.515625 53.71875 3.515625 
-L 53.71875 0 
-L 30.8125 0 
-L 30.8125 3.515625 
-Q 34.234375 3.515625 36.421875 4.046875 
-Q 38.625 4.59375 38.625 6.6875 
-L 38.625 30.421875 
-Q 38.625 35.296875 37.203125 38.453125 
-Q 35.796875 41.609375 31.390625 41.609375 
-Q 25.59375 41.609375 21.84375 36.96875 
-Q 18.109375 32.328125 18.109375 26.421875 
-L 18.109375 6.6875 
-Q 18.109375 4.59375 20.3125 4.046875 
-Q 22.515625 3.515625 25.875 3.515625 
-L 25.875 0 
-z
-" id="Cmr10-104"/>
-      <path d="M 2.59375 0 
-L 2.59375 3.515625 
-Q 6 3.515625 8.203125 4.046875 
-Q 10.40625 4.59375 10.40625 6.6875 
-L 10.40625 33.984375 
-Q 10.40625 36.671875 9.59375 37.859375 
-Q 8.796875 39.0625 7.28125 39.328125 
-Q 5.765625 39.59375 2.59375 39.59375 
-L 2.59375 43.109375 
-L 16.890625 44.1875 
-L 16.890625 34.421875 
-Q 18.5 38.765625 21.484375 41.46875 
-Q 24.46875 44.1875 28.71875 44.1875 
-Q 31.6875 44.1875 34.03125 42.421875 
-Q 36.375 40.671875 36.375 37.796875 
-Q 36.375 35.984375 35.078125 34.640625 
-Q 33.796875 33.296875 31.890625 33.296875 
-Q 30.03125 33.296875 28.703125 34.609375 
-Q 27.390625 35.9375 27.390625 37.796875 
-Q 27.390625 40.484375 29.296875 41.609375 
-L 28.71875 41.609375 
-Q 24.65625 41.609375 22.09375 38.671875 
-Q 19.53125 35.75 18.453125 31.390625 
-Q 17.390625 27.046875 17.390625 23.09375 
-L 17.390625 6.6875 
-Q 17.390625 3.515625 27.09375 3.515625 
-L 27.09375 0 
-z
-" id="Cmr10-114"/>
-      <path d="M 24.90625 -1.125 
-Q 18.796875 -1.125 13.6875 2.078125 
-Q 8.59375 5.28125 5.6875 10.625 
-Q 2.78125 15.96875 2.78125 21.921875 
-Q 2.78125 27.78125 5.4375 33.046875 
-Q 8.109375 38.328125 12.859375 41.578125 
-Q 17.625 44.828125 23.484375 44.828125 
-Q 28.078125 44.828125 31.46875 43.28125 
-Q 34.859375 41.75 37.0625 39.015625 
-Q 39.265625 36.28125 40.375 32.5625 
-Q 41.5 28.859375 41.5 24.421875 
-Q 41.5 23.09375 40.484375 23.09375 
-L 11.53125 23.09375 
-L 11.53125 22.015625 
-Q 11.53125 13.71875 14.875 7.765625 
-Q 18.21875 1.8125 25.78125 1.8125 
-Q 28.859375 1.8125 31.46875 3.171875 
-Q 34.078125 4.546875 36 6.984375 
-Q 37.9375 9.421875 38.625 12.203125 
-Q 38.71875 12.546875 38.984375 12.8125 
-Q 39.265625 13.09375 39.59375 13.09375 
-L 40.484375 13.09375 
-Q 41.5 13.09375 41.5 11.8125 
-Q 40.09375 6.15625 35.40625 2.515625 
-Q 30.71875 -1.125 24.90625 -1.125 
-z
-M 11.625 25.59375 
-L 34.421875 25.59375 
-Q 34.421875 29.34375 33.375 33.203125 
-Q 32.328125 37.0625 29.875 39.625 
-Q 27.4375 42.1875 23.484375 42.1875 
-Q 17.828125 42.1875 14.71875 36.890625 
-Q 11.625 31.59375 11.625 25.59375 
-z
-" id="Cmr10-101"/>
-      <path d="M 3.328125 -0.296875 
-L 3.328125 16.015625 
-Q 3.328125 16.796875 4.203125 16.796875 
-L 5.421875 16.796875 
-Q 6 16.796875 6.203125 16.015625 
-Q 8.984375 1.515625 19.671875 1.515625 
-Q 24.421875 1.515625 27.609375 3.65625 
-Q 30.8125 5.8125 30.8125 10.296875 
-Q 30.8125 13.53125 28.3125 15.796875 
-Q 25.828125 18.0625 22.40625 18.890625 
-L 15.71875 20.21875 
-Q 12.359375 20.953125 9.59375 22.453125 
-Q 6.84375 23.96875 5.078125 26.484375 
-Q 3.328125 29 3.328125 32.328125 
-Q 3.328125 36.71875 5.640625 39.515625 
-Q 7.953125 42.328125 11.65625 43.578125 
-Q 15.375 44.828125 19.671875 44.828125 
-Q 24.8125 44.828125 28.609375 42.09375 
-L 31.5 44.578125 
-Q 31.5 44.828125 31.984375 44.828125 
-L 32.71875 44.828125 
-Q 33.015625 44.828125 33.25 44.546875 
-Q 33.5 44.28125 33.5 44 
-L 33.5 30.90625 
-Q 33.5 29.984375 32.71875 29.984375 
-L 31.5 29.984375 
-Q 30.609375 29.984375 30.609375 30.90625 
-Q 30.609375 36.140625 27.703125 39.3125 
-Q 24.8125 42.484375 19.578125 42.484375 
-Q 15.09375 42.484375 11.796875 40.8125 
-Q 8.5 39.15625 8.5 35.109375 
-Q 8.5 32.328125 10.859375 30.546875 
-Q 13.234375 28.765625 16.40625 27.984375 
-L 23.1875 26.703125 
-Q 26.609375 25.921875 29.5625 24.0625 
-Q 32.515625 22.21875 34.25 19.375 
-Q 35.984375 16.546875 35.984375 12.984375 
-Q 35.984375 9.375 34.734375 6.703125 
-Q 33.5 4.046875 31.265625 2.28125 
-Q 29.046875 0.53125 26.015625 -0.296875 
-Q 23 -1.125 19.671875 -1.125 
-Q 13.421875 -1.125 8.984375 3.078125 
-L 5.328125 -0.875 
-Q 5.328125 -1.125 4.78125 -1.125 
-L 4.203125 -1.125 
-Q 3.328125 -1.125 3.328125 -0.296875 
-z
-" id="Cmr10-115"/>
-      <path d="M 10.203125 23 
-Q 9.375 23 8.828125 23.625 
-Q 8.296875 24.265625 8.296875 25 
-Q 8.296875 25.734375 8.828125 26.359375 
-Q 9.375 27 10.203125 27 
-L 67.578125 27 
-Q 68.359375 27 68.875 26.359375 
-Q 69.390625 25.734375 69.390625 25 
-Q 69.390625 24.265625 68.875 23.625 
-Q 68.359375 23 67.578125 23 
-z
-" id="Cmsy10-161"/>
-      <path d="M 9.515625 7.71875 
-Q 11.859375 4.296875 15.8125 2.640625 
-Q 19.78125 0.984375 24.3125 0.984375 
-Q 30.125 0.984375 32.5625 5.9375 
-Q 35.015625 10.890625 35.015625 17.1875 
-Q 35.015625 20.015625 34.5 22.84375 
-Q 33.984375 25.6875 32.765625 28.125 
-Q 31.546875 30.5625 29.421875 32.03125 
-Q 27.296875 33.5 24.21875 33.5 
-L 17.578125 33.5 
-Q 16.703125 33.5 16.703125 34.421875 
-L 16.703125 35.296875 
-Q 16.703125 36.078125 17.578125 36.078125 
-L 23.09375 36.53125 
-Q 26.609375 36.53125 28.921875 39.15625 
-Q 31.25 41.796875 32.328125 45.578125 
-Q 33.40625 49.359375 33.40625 52.78125 
-Q 33.40625 57.5625 31.15625 60.640625 
-Q 28.90625 63.71875 24.3125 63.71875 
-Q 20.515625 63.71875 17.046875 62.28125 
-Q 13.578125 60.84375 11.53125 57.90625 
-Q 11.71875 57.953125 11.859375 57.984375 
-Q 12.015625 58.015625 12.203125 58.015625 
-Q 14.453125 58.015625 15.96875 56.453125 
-Q 17.484375 54.890625 17.484375 52.6875 
-Q 17.484375 50.53125 15.96875 48.96875 
-Q 14.453125 47.40625 12.203125 47.40625 
-Q 10.015625 47.40625 8.453125 48.96875 
-Q 6.890625 50.53125 6.890625 52.6875 
-Q 6.890625 56.984375 9.46875 60.15625 
-Q 12.0625 63.328125 16.140625 64.96875 
-Q 20.21875 66.609375 24.3125 66.609375 
-Q 27.34375 66.609375 30.703125 65.703125 
-Q 34.078125 64.796875 36.8125 63.109375 
-Q 39.546875 61.421875 41.28125 58.78125 
-Q 43.015625 56.15625 43.015625 52.78125 
-Q 43.015625 48.578125 41.140625 45.015625 
-Q 39.265625 41.453125 35.984375 38.859375 
-Q 32.71875 36.28125 28.8125 35.015625 
-Q 33.15625 34.1875 37.0625 31.734375 
-Q 40.96875 29.296875 43.328125 25.484375 
-Q 45.703125 21.6875 45.703125 17.28125 
-Q 45.703125 11.765625 42.671875 7.296875 
-Q 39.65625 2.828125 34.71875 0.3125 
-Q 29.78125 -2.203125 24.3125 -2.203125 
-Q 19.625 -2.203125 14.90625 -0.40625 
-Q 10.203125 1.375 7.203125 4.9375 
-Q 4.203125 8.5 4.203125 13.484375 
-Q 4.203125 15.96875 5.859375 17.625 
-Q 7.515625 19.28125 10.015625 19.28125 
-Q 11.625 19.28125 12.96875 18.53125 
-Q 14.3125 17.78125 15.0625 16.40625 
-Q 15.828125 15.046875 15.828125 13.484375 
-Q 15.828125 11.03125 14.109375 9.375 
-Q 12.40625 7.71875 10.015625 7.71875 
-z
-" id="Cmr10-51"/>
-     </defs>
-     <g transform="translate(8.72 198.042)rotate(-90)scale(0.1 -0.1)">
-      <use transform="translate(0 0.109375)" xlink:href="#CMUSerif-Roman-83"/>
-      <use transform="translate(55.499985 0.109375)" xlink:href="#CMUSerif-Roman-70"/>
-      <use transform="translate(120.699982 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(153.999969 0.109375)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(192.799957 0.109375)" xlink:href="#CMUSerif-Roman-104"/>
-      <use transform="translate(248.299942 0.109375)" xlink:href="#CMUSerif-Roman-114"/>
-      <use transform="translate(287.399933 0.109375)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(331.799927 0.109375)" xlink:href="#CMUSerif-Roman-115"/>
-      <use transform="translate(371.199921 0.109375)" xlink:href="#CMUSerif-Roman-104"/>
-      <use transform="translate(426.699905 0.109375)" xlink:href="#CMUSerif-Roman-111"/>
-      <use transform="translate(476.69989 0.109375)" xlink:href="#CMUSerif-Roman-108"/>
-      <use transform="translate(504.399887 0.109375)" xlink:href="#CMUSerif-Roman-100"/>
-      <use transform="translate(559.899872 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(593.19986 0.109375)" xlink:href="#CMUSerif-Roman-110"/>
-      <use transform="translate(648.699844 0.109375)" xlink:href="#CMUSerif-Roman-117"/>
-      <use transform="translate(704.199829 0.109375)" xlink:href="#CMUSerif-Roman-109"/>
-      <use transform="translate(787.499817 0.109375)" xlink:href="#CMUSerif-Roman-98"/>
-      <use transform="translate(842.999802 0.109375)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(887.399796 0.109375)" xlink:href="#CMUSerif-Roman-114"/>
-      <use transform="translate(926.499786 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(959.799774 0.109375)" xlink:href="#CMUSerif-Roman-100"/>
-      <use transform="translate(1015.299759 0.109375)" xlink:href="#CMUSerif-Roman-101"/>
-      <use transform="translate(1059.699753 0.109375)" xlink:href="#CMUSerif-Roman-110"/>
-      <use transform="translate(1115.199738 0.109375)" xlink:href="#CMUSerif-Roman-115"/>
-      <use transform="translate(1154.599731 0.109375)" xlink:href="#CMUSerif-Roman-105"/>
-      <use transform="translate(1182.299728 0.109375)" xlink:href="#CMUSerif-Roman-116"/>
-      <use transform="translate(1221.099716 0.109375)" xlink:href="#CMUSerif-Roman-121"/>
-      <use transform="translate(1273.799713 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(1307.099701 0.109375)" xlink:href="#Cmmi10-110"/>
-      <use transform="translate(1367.109467 -16.896875)scale(0.7)" xlink:href="#Cmr10-72"/>
-      <use transform="translate(1419.609467 -16.896875)scale(0.7)" xlink:href="#Cmmi10-59"/>
-      <use transform="translate(1450.651459 -16.896875)scale(0.7)" xlink:href="#Cmr10-116"/>
-      <use transform="translate(1477.82431 -16.896875)scale(0.7)" xlink:href="#Cmr10-104"/>
-      <use transform="translate(1516.686615 -16.896875)scale(0.7)" xlink:href="#Cmr10-114"/>
-      <use transform="translate(1544.064545 -16.896875)scale(0.7)" xlink:href="#Cmr10-101"/>
-      <use transform="translate(1575.133881 -16.896875)scale(0.7)" xlink:href="#Cmr10-115"/>
-      <use transform="translate(1602.716888 -16.896875)scale(0.7)" xlink:href="#Cmr10-104"/>
-      <use transform="translate(1647.956537 0.109375)" xlink:href="#CMUSerif-Roman-32"/>
-      <use transform="translate(1681.256525 0.109375)" xlink:href="#CMUSerif-Roman-91"/>
-      <use transform="translate(1709.056512 0.109375)" xlink:href="#CMUSerif-Roman-99"/>
-      <use transform="translate(1753.456506 0.109375)" xlink:href="#CMUSerif-Roman-109"/>
-      <use transform="translate(1841.220635 38.373438)scale(0.7)" xlink:href="#Cmsy10-161"/>
-      <use transform="translate(1895.600518 38.373438)scale(0.7)" xlink:href="#Cmr10-51"/>
-      <use transform="translate(1936.977861 0.109375)" xlink:href="#CMUSerif-Roman-93"/>
-     </g>
-    </g>
-   </g>
-   <g id="line2d_54">
-    <path clip-path="url(#p23ab05f3c8)" d="M 6.804 27.524431 
-L 66.139784 27.620407 
-L 227.8 164.984202 
-L 227.8 164.984202 
-" style="fill:none;stroke:#000000;stroke-linecap:square;"/>
-   </g>
-   <g id="line2d_55">
-    <path clip-path="url(#p23ab05f3c8)" d="M 128.653332 63.658216 
-L 183.085332 109.90946 
-" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_56">
-    <path clip-path="url(#p23ab05f3c8)" d="M -1 27.524431 
-L 227.8 27.524431 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_57">
-    <path clip-path="url(#p23ab05f3c8)" d="M 151.076832 227.8 
-L 151.076832 99.792 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_58">
-    <path clip-path="url(#p23ab05f3c8)" d="M -1 99.792 
-L 151.076832 99.792 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="patch_3">
-    <path d="M 34.02 197.316 
-L 34.02 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_4">
-    <path d="M 224.532 197.316 
-L 224.532 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_5">
-    <path d="M 34.02 197.316 
-L 224.532 197.316 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_6">
-    <path d="M 34.02 2.268 
-L 224.532 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_7">
-    <path d="M 174.027021 190.953156 
-Q 174.027021 184.004135 174.027021 177.055114 
-L 172.577021 177.055114 
-Q 173.327021 174.557342 174.077021 172.059569 
-Q 174.827021 174.557342 175.577021 177.055114 
-L 174.127021 177.055114 
-Q 174.127021 184.004135 174.127021 190.953156 
-L 174.027021 190.953156 
-z
-" style="stroke:#000000;stroke-linecap:round;"/>
-   </g>
-   <g id="text_12">
-    <!-- ${Z_\odot}$ -->
-    <defs>
-     <path d="M 38.921875 -8.296875 
-Q 32.078125 -8.296875 26.0625 -5.703125 
-Q 20.0625 -3.125 15.40625 1.484375 
-Q 10.75 6.109375 8.171875 12.1875 
-Q 5.609375 18.265625 5.609375 25 
-Q 5.609375 31.84375 8.140625 37.84375 
-Q 10.6875 43.84375 15.375 48.53125 
-Q 20.0625 53.21875 26.0625 55.75 
-Q 32.078125 58.296875 38.921875 58.296875 
-Q 45.703125 58.296875 51.75 55.703125 
-Q 57.8125 53.125 62.40625 48.5 
-Q 67 43.890625 69.5625 37.828125 
-Q 72.125 31.78125 72.125 25 
-Q 72.125 18.3125 69.53125 12.1875 
-Q 66.9375 6.0625 62.375 1.484375 
-Q 57.8125 -3.078125 51.75 -5.6875 
-Q 45.703125 -8.296875 38.921875 -8.296875 
-z
-M 38.921875 -5.421875 
-Q 44.96875 -5.421875 50.53125 -3.046875 
-Q 56.109375 -0.6875 60.296875 3.515625 
-Q 64.5 7.71875 66.84375 13.25 
-Q 69.1875 18.796875 69.1875 25 
-Q 69.1875 31.203125 66.8125 36.796875 
-Q 64.453125 42.390625 60.34375 46.515625 
-Q 56.25 50.640625 50.703125 53.03125 
-Q 45.171875 55.421875 38.921875 55.421875 
-Q 32.671875 55.421875 27.09375 53.03125 
-Q 21.53125 50.640625 17.421875 46.53125 
-Q 13.328125 42.4375 10.90625 36.71875 
-Q 8.5 31 8.5 25 
-Q 8.5 19 10.859375 13.359375 
-Q 13.234375 7.71875 17.453125 3.515625 
-Q 21.6875 -0.6875 27.265625 -3.046875 
-Q 32.859375 -5.421875 38.921875 -5.421875 
-z
-M 38.921875 17.828125 
-Q 36.03125 17.828125 33.859375 19.953125 
-Q 31.6875 22.078125 31.6875 25 
-Q 31.6875 27 32.65625 28.609375 
-Q 33.640625 30.21875 35.296875 31.1875 
-Q 36.96875 32.171875 38.921875 32.171875 
-Q 40.765625 32.171875 42.40625 31.1875 
-Q 44.046875 30.21875 45.015625 28.609375 
-Q 46 27 46 25 
-Q 46 22.078125 43.875 19.953125 
-Q 41.75 17.828125 38.921875 17.828125 
-z
-" id="Cmsy10-175"/>
-    </defs>
-    <g transform="translate(175.655329 188.534116)scale(0.09 -0.09)">
-     <use transform="translate(0 0.6875)" xlink:href="#Cmmi10-90"/>
-     <use transform="translate(80.503906 -16.31875)scale(0.7)" xlink:href="#Cmsy10-175"/>
-    </g>
-   </g>
-   <g id="text_13">
-    <!-- Z^threshold_slope -->
-    <defs>
-     <path d="M 8.6875 72.90625 
-L 56 72.90625 
-L 56 65.375 
-L 17.921875 8.296875 
-L 57.078125 8.296875 
-L 57.078125 0 
-L 7.625 0 
-L 7.625 7.515625 
-L 44.671875 64.59375 
-L 8.6875 64.59375 
-z
-" id="DejaVuSansMono-90"/>
-     <path d="M 34.421875 72.90625 
-L 56.6875 45.703125 
-L 48 45.703125 
-L 30.078125 64.984375 
-L 12.203125 45.703125 
-L 3.515625 45.703125 
-L 25.78125 72.90625 
-z
-" id="DejaVuSansMono-94"/>
-     <path d="M 29.984375 70.21875 
-L 29.984375 54.6875 
-L 50.390625 54.6875 
-L 50.390625 47.703125 
-L 29.984375 47.703125 
-L 29.984375 18.015625 
-Q 29.984375 11.96875 32.28125 9.5625 
-Q 34.578125 7.171875 40.28125 7.171875 
-L 50.390625 7.171875 
-L 50.390625 0 
-L 39.40625 0 
-Q 29.296875 0 25.140625 4.046875 
-Q 21 8.109375 21 18.015625 
-L 21 47.703125 
-L 6.390625 47.703125 
-L 6.390625 54.6875 
-L 21 54.6875 
-L 21 70.21875 
-z
-" id="DejaVuSansMono-116"/>
-     <path d="M 51.3125 33.890625 
-L 51.3125 0 
-L 42.28125 0 
-L 42.28125 33.890625 
-Q 42.28125 41.265625 39.6875 44.71875 
-Q 37.109375 48.1875 31.59375 48.1875 
-Q 25.296875 48.1875 21.890625 43.71875 
-Q 18.5 39.265625 18.5 30.90625 
-L 18.5 0 
-L 9.515625 0 
-L 9.515625 75.984375 
-L 18.5 75.984375 
-L 18.5 46.484375 
-Q 20.90625 51.171875 25 53.578125 
-Q 29.109375 56 34.71875 56 
-Q 43.0625 56 47.1875 50.5 
-Q 51.3125 45.015625 51.3125 33.890625 
-z
-" id="DejaVuSansMono-104"/>
-     <path d="M 56.390625 43.40625 
-Q 53.515625 45.65625 50.53125 46.671875 
-Q 47.5625 47.703125 44 47.703125 
-Q 35.59375 47.703125 31.140625 42.421875 
-Q 26.703125 37.15625 26.703125 27.203125 
-L 26.703125 0 
-L 17.671875 0 
-L 17.671875 54.6875 
-L 26.703125 54.6875 
-L 26.703125 44 
-Q 28.953125 49.8125 33.609375 52.90625 
-Q 38.28125 56 44.671875 56 
-Q 48 56 50.875 55.171875 
-Q 53.765625 54.34375 56.390625 52.59375 
-z
-" id="DejaVuSansMono-114"/>
-     <path d="M 54.296875 29.59375 
-L 54.296875 25.203125 
-L 15.375 25.203125 
-L 15.375 24.90625 
-Q 15.375 15.96875 20.03125 11.078125 
-Q 24.703125 6.203125 33.203125 6.203125 
-Q 37.5 6.203125 42.1875 7.5625 
-Q 46.875 8.9375 52.203125 11.71875 
-L 52.203125 2.78125 
-Q 47.078125 0.6875 42.3125 -0.359375 
-Q 37.546875 -1.421875 33.109375 -1.421875 
-Q 20.359375 -1.421875 13.171875 6.21875 
-Q 6 13.875 6 27.296875 
-Q 6 40.375 13.03125 48.1875 
-Q 20.0625 56 31.78125 56 
-Q 42.234375 56 48.265625 48.921875 
-Q 54.296875 41.84375 54.296875 29.59375 
-z
-M 45.3125 32.234375 
-Q 45.125 40.140625 41.578125 44.265625 
-Q 38.03125 48.390625 31.390625 48.390625 
-Q 24.90625 48.390625 20.703125 44.09375 
-Q 16.5 39.796875 15.71875 32.171875 
-z
-" id="DejaVuSansMono-101"/>
-     <path d="M 47.515625 52.78125 
-L 47.515625 44 
-Q 43.65625 46.234375 39.75 47.359375 
-Q 35.84375 48.484375 31.78125 48.484375 
-Q 25.6875 48.484375 22.671875 46.5 
-Q 19.671875 44.53125 19.671875 40.484375 
-Q 19.671875 36.8125 21.921875 35 
-Q 24.171875 33.203125 33.109375 31.5 
-L 36.71875 30.8125 
-Q 43.40625 29.546875 46.84375 25.734375 
-Q 50.296875 21.921875 50.296875 15.828125 
-Q 50.296875 7.71875 44.53125 3.140625 
-Q 38.765625 -1.421875 28.515625 -1.421875 
-Q 24.46875 -1.421875 20.015625 -0.5625 
-Q 15.578125 0.296875 10.40625 2 
-L 10.40625 11.28125 
-Q 15.4375 8.6875 20.015625 7.390625 
-Q 24.609375 6.109375 28.71875 6.109375 
-Q 34.671875 6.109375 37.9375 8.515625 
-Q 41.21875 10.9375 41.21875 15.28125 
-Q 41.21875 21.53125 29.25 23.921875 
-L 28.859375 24.03125 
-L 25.484375 24.703125 
-Q 17.71875 26.21875 14.15625 29.8125 
-Q 10.59375 33.40625 10.59375 39.59375 
-Q 10.59375 47.46875 15.90625 51.734375 
-Q 21.234375 56 31.109375 56 
-Q 35.5 56 39.546875 55.1875 
-Q 43.609375 54.390625 47.515625 52.78125 
-z
-" id="DejaVuSansMono-115"/>
-     <path d="M 30.078125 48.390625 
-Q 23.25 48.390625 19.734375 43.0625 
-Q 16.21875 37.75 16.21875 27.296875 
-Q 16.21875 16.890625 19.734375 11.546875 
-Q 23.25 6.203125 30.078125 6.203125 
-Q 36.96875 6.203125 40.484375 11.546875 
-Q 44 16.890625 44 27.296875 
-Q 44 37.75 40.484375 43.0625 
-Q 36.96875 48.390625 30.078125 48.390625 
-z
-M 30.078125 56 
-Q 41.453125 56 47.484375 48.625 
-Q 53.515625 41.265625 53.515625 27.296875 
-Q 53.515625 13.28125 47.5 5.921875 
-Q 41.5 -1.421875 30.078125 -1.421875 
-Q 18.703125 -1.421875 12.6875 5.921875 
-Q 6.6875 13.28125 6.6875 27.296875 
-Q 6.6875 41.265625 12.6875 48.625 
-Q 18.703125 56 30.078125 56 
-z
-" id="DejaVuSansMono-111"/>
-     <path d="M 31.203125 19.828125 
-Q 31.203125 13.765625 33.421875 10.6875 
-Q 35.640625 7.625 39.984375 7.625 
-L 50.484375 7.625 
-L 50.484375 0 
-L 39.109375 0 
-Q 31.0625 0 26.640625 5.171875 
-Q 22.21875 10.359375 22.21875 19.828125 
-L 22.21875 69.484375 
-L 7.8125 69.484375 
-L 7.8125 76.515625 
-L 31.203125 76.515625 
-z
-" id="DejaVuSansMono-108"/>
-     <path d="M 41.890625 47.703125 
-L 41.890625 75.984375 
-L 50.875 75.984375 
-L 50.875 0 
-L 41.890625 0 
-L 41.890625 6.890625 
-Q 39.65625 2.828125 35.90625 0.703125 
-Q 32.171875 -1.421875 27.296875 -1.421875 
-Q 17.390625 -1.421875 11.6875 6.265625 
-Q 6 13.96875 6 27.484375 
-Q 6 40.828125 11.71875 48.40625 
-Q 17.4375 56 27.296875 56 
-Q 32.234375 56 35.984375 53.875 
-Q 39.75 51.765625 41.890625 47.703125 
-z
-M 15.484375 27.296875 
-Q 15.484375 16.84375 18.796875 11.515625 
-Q 22.125 6.203125 28.609375 6.203125 
-Q 35.109375 6.203125 38.5 11.5625 
-Q 41.890625 16.9375 41.890625 27.296875 
-Q 41.890625 37.703125 38.5 43.046875 
-Q 35.109375 48.390625 28.609375 48.390625 
-Q 22.125 48.390625 18.796875 43.0625 
-Q 15.484375 37.75 15.484375 27.296875 
-z
-" id="DejaVuSansMono-100"/>
-     <path d="M 60.203125 -19.671875 
-L 60.203125 -23.578125 
-L 0 -23.578125 
-L 0 -19.671875 
-z
-" id="DejaVuSansMono-95"/>
-     <path d="M 18.3125 6.890625 
-L 18.3125 -20.796875 
-L 9.28125 -20.796875 
-L 9.28125 54.6875 
-L 18.3125 54.6875 
-L 18.3125 47.703125 
-Q 20.5625 51.765625 24.296875 53.875 
-Q 28.03125 56 32.90625 56 
-Q 42.828125 56 48.46875 48.328125 
-Q 54.109375 40.671875 54.109375 27.09375 
-Q 54.109375 13.765625 48.4375 6.171875 
-Q 42.78125 -1.421875 32.90625 -1.421875 
-Q 27.9375 -1.421875 24.1875 0.703125 
-Q 20.453125 2.828125 18.3125 6.890625 
-z
-M 44.671875 27.296875 
-Q 44.671875 37.75 41.375 43.0625 
-Q 38.09375 48.390625 31.59375 48.390625 
-Q 25.046875 48.390625 21.671875 43.046875 
-Q 18.3125 37.703125 18.3125 27.296875 
-Q 18.3125 16.9375 21.671875 11.5625 
-Q 25.046875 6.203125 31.59375 6.203125 
-Q 38.09375 6.203125 41.375 11.515625 
-Q 44.671875 16.84375 44.671875 27.296875 
-z
-" id="DejaVuSansMono-112"/>
-    </defs>
-    <g transform="translate(129.714233 61.930385)rotate(-320)scale(0.07 -0.07)">
-     <use xlink:href="#DejaVuSansMono-90"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-94"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-116"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-104"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-114"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-101"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-115"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-104"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-111"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-108"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-100"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-95"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-115"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-108"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-111"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-112"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-101"/>
-    </g>
-   </g>
-   <g id="text_14">
-    <!-- threshold_max_density_H_p_cm3 -->
-    <defs>
-     <path d="M 33.015625 49.125 
-Q 34.671875 52.640625 37.234375 54.3125 
-Q 39.796875 56 43.40625 56 
-Q 50 56 52.703125 50.890625 
-Q 55.421875 45.796875 55.421875 31.6875 
-L 55.421875 0 
-L 47.21875 0 
-L 47.21875 31.296875 
-Q 47.21875 42.875 45.921875 45.671875 
-Q 44.625 48.484375 41.21875 48.484375 
-Q 37.3125 48.484375 35.859375 45.484375 
-Q 34.421875 42.484375 34.421875 31.296875 
-L 34.421875 0 
-L 26.21875 0 
-L 26.21875 31.296875 
-Q 26.21875 43.015625 24.828125 45.75 
-Q 23.4375 48.484375 19.828125 48.484375 
-Q 16.265625 48.484375 14.875 45.484375 
-Q 13.484375 42.484375 13.484375 31.296875 
-L 13.484375 0 
-L 5.328125 0 
-L 5.328125 54.6875 
-L 13.484375 54.6875 
-L 13.484375 50 
-Q 15.09375 52.9375 17.5 54.46875 
-Q 19.921875 56 23 56 
-Q 26.703125 56 29.171875 54.296875 
-Q 31.640625 52.59375 33.015625 49.125 
-z
-" id="DejaVuSansMono-109"/>
-     <path d="M 34.28125 27.484375 
-L 31.296875 27.484375 
-Q 23.4375 27.484375 19.453125 24.71875 
-Q 15.484375 21.96875 15.484375 16.5 
-Q 15.484375 11.578125 18.453125 8.84375 
-Q 21.4375 6.109375 26.703125 6.109375 
-Q 34.125 6.109375 38.375 11.25 
-Q 42.625 16.40625 42.671875 25.484375 
-L 42.671875 27.484375 
-z
-M 51.703125 31.203125 
-L 51.703125 0 
-L 42.671875 0 
-L 42.671875 8.109375 
-Q 39.796875 3.21875 35.421875 0.890625 
-Q 31.0625 -1.421875 24.8125 -1.421875 
-Q 16.453125 -1.421875 11.46875 3.296875 
-Q 6.5 8.015625 6.5 15.921875 
-Q 6.5 25.046875 12.625 29.78125 
-Q 18.75 34.515625 30.609375 34.515625 
-L 42.671875 34.515625 
-L 42.671875 35.9375 
-Q 42.625 42.484375 39.34375 45.4375 
-Q 36.078125 48.390625 28.90625 48.390625 
-Q 24.3125 48.390625 19.625 47.0625 
-Q 14.9375 45.75 10.5 43.21875 
-L 10.5 52.203125 
-Q 15.484375 54.109375 20.046875 55.046875 
-Q 24.609375 56 28.90625 56 
-Q 35.6875 56 40.5 54 
-Q 45.3125 52 48.296875 48 
-Q 50.140625 45.5625 50.921875 41.96875 
-Q 51.703125 38.375 51.703125 31.203125 
-z
-" id="DejaVuSansMono-97"/>
-     <path d="M 54.59375 54.6875 
-L 35.015625 28.515625 
-L 56.5 0 
-L 46.09375 0 
-L 30.078125 21.921875 
-L 14.109375 0 
-L 3.71875 0 
-L 25.203125 28.515625 
-L 5.609375 54.6875 
-L 15.578125 54.6875 
-L 30.078125 34.90625 
-L 44.484375 54.6875 
-z
-" id="DejaVuSansMono-120"/>
-     <path d="M 51.3125 33.890625 
-L 51.3125 0 
-L 42.28125 0 
-L 42.28125 33.890625 
-Q 42.28125 41.265625 39.6875 44.71875 
-Q 37.109375 48.1875 31.59375 48.1875 
-Q 25.296875 48.1875 21.890625 43.71875 
-Q 18.5 39.265625 18.5 30.90625 
-L 18.5 0 
-L 9.515625 0 
-L 9.515625 54.6875 
-L 18.5 54.6875 
-L 18.5 46.484375 
-Q 20.90625 51.171875 25 53.578125 
-Q 29.109375 56 34.71875 56 
-Q 43.0625 56 47.1875 50.5 
-Q 51.3125 45.015625 51.3125 33.890625 
-z
-" id="DejaVuSansMono-110"/>
-     <path d="M 12.5 54.6875 
-L 35.5 54.6875 
-L 35.5 6.984375 
-L 53.328125 6.984375 
-L 53.328125 0 
-L 8.6875 0 
-L 8.6875 6.984375 
-L 26.515625 6.984375 
-L 26.515625 47.703125 
-L 12.5 47.703125 
-z
-M 26.515625 75.984375 
-L 35.5 75.984375 
-L 35.5 64.59375 
-L 26.515625 64.59375 
-z
-" id="DejaVuSansMono-105"/>
-     <path d="M 41.890625 17.578125 
-Q 39.65625 11.859375 36.1875 2.546875 
-Q 31.34375 -10.359375 29.6875 -13.1875 
-Q 27.4375 -17 24.0625 -18.890625 
-Q 20.703125 -20.796875 16.21875 -20.796875 
-L 8.984375 -20.796875 
-L 8.984375 -13.28125 
-L 14.3125 -13.28125 
-Q 18.265625 -13.28125 20.5 -10.984375 
-Q 22.75 -8.6875 26.21875 0.875 
-L 5.078125 54.6875 
-L 14.59375 54.6875 
-L 30.8125 11.921875 
-L 46.78125 54.6875 
-L 56.296875 54.6875 
-z
-" id="DejaVuSansMono-121"/>
-     <path d="M 6.6875 72.90625 
-L 16.609375 72.90625 
-L 16.609375 43.015625 
-L 43.609375 43.015625 
-L 43.609375 72.90625 
-L 53.515625 72.90625 
-L 53.515625 0 
-L 43.609375 0 
-L 43.609375 34.71875 
-L 16.609375 34.71875 
-L 16.609375 0 
-L 6.6875 0 
-z
-" id="DejaVuSansMono-72"/>
-     <path d="M 51.8125 2.78125 
-Q 48.1875 0.6875 44.359375 -0.359375 
-Q 40.53125 -1.421875 36.53125 -1.421875 
-Q 23.828125 -1.421875 16.671875 6.1875 
-Q 9.515625 13.8125 9.515625 27.296875 
-Q 9.515625 40.765625 16.671875 48.375 
-Q 23.828125 56 36.53125 56 
-Q 40.484375 56 44.234375 54.96875 
-Q 48 53.953125 51.8125 51.8125 
-L 51.8125 42.390625 
-Q 48.25 45.5625 44.65625 46.96875 
-Q 41.0625 48.390625 36.53125 48.390625 
-Q 28.078125 48.390625 23.53125 42.921875 
-Q 19 37.453125 19 27.296875 
-Q 19 17.1875 23.5625 11.6875 
-Q 28.125 6.203125 36.53125 6.203125 
-Q 41.21875 6.203125 44.921875 7.640625 
-Q 48.640625 9.078125 51.8125 12.109375 
-z
-" id="DejaVuSansMono-99"/>
-     <path d="M 37.890625 39.015625 
-Q 45.0625 37.109375 48.875 32.25 
-Q 52.6875 27.390625 52.6875 20.125 
-Q 52.6875 10.0625 45.921875 4.3125 
-Q 39.15625 -1.421875 27.203125 -1.421875 
-Q 22.171875 -1.421875 16.9375 -0.484375 
-Q 11.71875 0.4375 6.6875 2.203125 
-L 6.6875 12.015625 
-Q 11.671875 9.421875 16.5 8.15625 
-Q 21.34375 6.890625 26.125 6.890625 
-Q 34.234375 6.890625 38.578125 10.546875 
-Q 42.921875 14.203125 42.921875 21.09375 
-Q 42.921875 27.4375 38.578125 31.171875 
-Q 34.234375 34.90625 26.8125 34.90625 
-L 19.28125 34.90625 
-L 19.28125 43.015625 
-L 26.8125 43.015625 
-Q 33.59375 43.015625 37.40625 45.984375 
-Q 41.21875 48.96875 41.21875 54.296875 
-Q 41.21875 59.90625 37.671875 62.90625 
-Q 34.125 65.921875 27.59375 65.921875 
-Q 23.25 65.921875 18.609375 64.9375 
-Q 13.96875 63.96875 8.890625 62.015625 
-L 8.890625 71.09375 
-Q 14.796875 72.65625 19.40625 73.4375 
-Q 24.03125 74.21875 27.59375 74.21875 
-Q 38.234375 74.21875 44.609375 68.875 
-Q 50.984375 63.53125 50.984375 54.6875 
-Q 50.984375 48.6875 47.625 44.671875 
-Q 44.28125 40.671875 37.890625 39.015625 
-z
-" id="DejaVuSansMono-51"/>
-    </defs>
-    <g transform="translate(101.437332 24.663313)scale(0.07 -0.07)">
-     <use xlink:href="#DejaVuSansMono-116"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-104"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-114"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-101"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-115"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-104"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-111"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-108"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-100"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-95"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-109"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-97"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-120"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-95"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-100"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-101"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-110"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-115"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-105"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-116"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-121"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1324.511719" xlink:href="#DejaVuSansMono-72"/>
-     <use x="1384.716797" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1444.921875" xlink:href="#DejaVuSansMono-112"/>
-     <use x="1505.126953" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1565.332031" xlink:href="#DejaVuSansMono-99"/>
-     <use x="1625.537109" xlink:href="#DejaVuSansMono-109"/>
-     <use x="1685.742188" xlink:href="#DejaVuSansMono-51"/>
-    </g>
-   </g>
-   <g id="text_15">
-    <!-- threshold_norm_H_p_cm3 -->
-    <g transform="translate(47.005332 96.930882)scale(0.07 -0.07)">
-     <use xlink:href="#DejaVuSansMono-116"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-104"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-114"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-101"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-115"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-104"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-111"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-108"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-100"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-95"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-110"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-111"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-114"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-109"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-95"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-72"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-112"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-95"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-99"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-109"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-51"/>
-    </g>
-   </g>
-   <g id="text_16">
-    <!-- threshold_Z0 -->
-    <defs>
-     <path d="M 23.578125 36.625 
-Q 23.578125 39.3125 25.453125 41.265625 
-Q 27.34375 43.21875 29.984375 43.21875 
-Q 32.71875 43.21875 34.671875 41.265625 
-Q 36.625 39.3125 36.625 36.625 
-Q 36.625 33.890625 34.6875 31.984375 
-Q 32.765625 30.078125 29.984375 30.078125 
-Q 27.25 30.078125 25.40625 31.9375 
-Q 23.578125 33.796875 23.578125 36.625 
-z
-M 30.078125 66.40625 
-Q 23.1875 66.40625 19.796875 58.984375 
-Q 16.40625 51.5625 16.40625 36.375 
-Q 16.40625 21.234375 19.796875 13.8125 
-Q 23.1875 6.390625 30.078125 6.390625 
-Q 37.015625 6.390625 40.40625 13.8125 
-Q 43.796875 21.234375 43.796875 36.375 
-Q 43.796875 51.5625 40.40625 58.984375 
-Q 37.015625 66.40625 30.078125 66.40625 
-z
-M 30.078125 74.21875 
-Q 41.75 74.21875 47.734375 64.640625 
-Q 53.71875 55.078125 53.71875 36.375 
-Q 53.71875 17.71875 47.734375 8.140625 
-Q 41.75 -1.421875 30.078125 -1.421875 
-Q 18.40625 -1.421875 12.453125 8.140625 
-Q 6.5 17.71875 6.5 36.375 
-Q 6.5 55.078125 12.453125 64.640625 
-Q 18.40625 74.21875 30.078125 74.21875 
-z
-" id="DejaVuSansMono-48"/>
-    </defs>
-    <g transform="translate(148.181028 186.438647)rotate(-90)scale(0.07 -0.07)">
-     <use xlink:href="#DejaVuSansMono-116"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-104"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-114"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-101"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-115"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-104"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-111"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-108"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-100"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-95"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-90"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-48"/>
-    </g>
-   </g>
-  </g>
- </g>
- <defs>
-  <clipPath id="p23ab05f3c8">
-   <rect height="195.048" width="190.512" x="34.02" y="2.268"/>
-  </clipPath>
- </defs>
-</svg>
diff --git a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg b/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg
deleted file mode 100644
index a5ee0fe738d924018986b00a80bdec2e2311afd4..0000000000000000000000000000000000000000
--- a/doc/RTD/source/SubgridModels/EAGLE/EAGLE_entropy_floor.svg
+++ /dev/null
@@ -1,2540 +0,0 @@
-<?xml version="1.0" encoding="utf-8" standalone="no"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
-  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<!-- Created with matplotlib (http://matplotlib.org/) -->
-<svg height="226pt" version="1.1" viewBox="0 0 226 226" width="226pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <defs>
-  <style type="text/css">
-*{stroke-linecap:butt;stroke-linejoin:round;}
-  </style>
- </defs>
- <g id="figure_1">
-  <g id="patch_1">
-   <path d="M 0 226.8 
-L 226.8 226.8 
-L 226.8 0 
-L 0 0 
-z
-" style="fill:#ffffff;"/>
-  </g>
-  <g id="axes_1">
-   <g id="patch_2">
-    <path d="M 34.02 197.316 
-L 224.532 197.316 
-L 224.532 2.268 
-L 34.02 2.268 
-z
-" style="fill:#ffffff;"/>
-   </g>
-   <g id="PolyCollection_1">
-    <defs>
-     <path d="M 77.714425 -156.365649 
-L 77.714425 -14.805175 
-L 250.907152 -14.805175 
-L 250.907152 -156.365649 
-L 250.907152 -156.365649 
-L 77.714425 -156.365649 
-z
-" id="m97efab0c60" style="stroke:#e6e6e6;"/>
-    </defs>
-    <g clip-path="url(#p5e3fe66b1e)">
-     <use style="fill:#e6e6e6;stroke:#e6e6e6;" x="0" xlink:href="#m97efab0c60" y="226.8"/>
-    </g>
-   </g>
-   <g id="PolyCollection_2">
-    <path clip-path="url(#p5e3fe66b1e)" d="M 146.991516 49022.756825 
-L 146.991516 85.113175 
-L 250.907152 -12.410825 
-L 250.907152 49022.756825 
-L 250.907152 49022.756825 
-L 146.991516 49022.756825 
-z
-" style="fill:#e6e6e6;stroke:#e6e6e6;"/>
-   </g>
-   <g id="PathCollection_1">
-    <defs>
-     <path d="M 0 1 
-C 0.265203 1 0.51958 0.894634 0.707107 0.707107 
-C 0.894634 0.51958 1 0.265203 1 0 
-C 1 -0.265203 0.894634 -0.51958 0.707107 -0.707107 
-C 0.51958 -0.894634 0.265203 -1 0 -1 
-C -0.265203 -1 -0.51958 -0.894634 -0.707107 -0.707107 
-C -0.894634 -0.51958 -1 -0.265203 -1 0 
-C -1 0.265203 -0.894634 0.51958 -0.707107 0.707107 
-C -0.51958 0.894634 -0.265203 1 0 1 
-z
-" id="me4af860824" style="stroke:#000000;"/>
-    </defs>
-    <g clip-path="url(#p5e3fe66b1e)">
-     <use style="stroke:#000000;" x="77.714425" xlink:href="#me4af860824" y="70.434351"/>
-    </g>
-   </g>
-   <g id="PathCollection_2">
-    <g clip-path="url(#p5e3fe66b1e)">
-     <use style="stroke:#000000;" x="146.991516" xlink:href="#me4af860824" y="85.113175"/>
-    </g>
-   </g>
-   <g id="matplotlib.axis_1">
-    <g id="xtick_1">
-     <g id="line2d_1">
-      <defs>
-       <path d="M 0 0 
-L 0 3.5 
-" id="m680b56acf8" style="stroke:#000000;stroke-width:0.8;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="60.395152" xlink:href="#m680b56acf8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_1">
-      <!-- $\mathdefault{10^{-6}}$ -->
-      <defs>
-       <path d="M 8.90625 57.09375 
-L 8.90625 60.203125 
-Q 20.90625 60.203125 27.09375 66.59375 
-Q 28.796875 66.59375 29.09375 66.1875 
-Q 29.40625 65.796875 29.40625 64 
-L 29.40625 7.90625 
-Q 29.40625 4.90625 30.84375 4 
-Q 32.296875 3.09375 38.703125 3.09375 
-L 41.90625 3.09375 
-L 41.90625 0 
-Q 38.40625 0.296875 25.703125 0.296875 
-Q 13 0.296875 9.5 0 
-L 9.5 3.09375 
-L 12.703125 3.09375 
-Q 19 3.09375 20.5 4 
-Q 22 4.90625 22 7.90625 
-L 22 59.703125 
-Q 16.796875 57.09375 8.90625 57.09375 
-z
-" id="CMUSerif-Roman-31"/>
-       <path d="M 3.90625 32 
-Q 3.90625 46.703125 7.59375 54.703125 
-Q 12.796875 66.59375 25 66.59375 
-Q 27.59375 66.59375 30.296875 65.890625 
-Q 33 65.203125 36.453125 62.5 
-Q 39.90625 59.796875 42 55.40625 
-Q 46 46.90625 46 32 
-Q 46 17.40625 42.296875 9.40625 
-Q 36.90625 -2.203125 24.90625 -2.203125 
-Q 20.40625 -2.203125 15.84375 0.09375 
-Q 11.296875 2.40625 8.40625 7.90625 
-Q 3.90625 16.203125 3.90625 32 
-z
-M 12.203125 33.203125 
-Q 12.203125 18.09375 13.296875 12.09375 
-Q 14.5 5.59375 17.84375 2.796875 
-Q 21.203125 0 24.90625 0 
-Q 28.90625 0 32.25 3 
-Q 35.59375 6 36.59375 12.5 
-Q 37.703125 18.90625 37.703125 33.203125 
-Q 37.703125 47.09375 36.703125 52.703125 
-Q 35.40625 59.203125 31.90625 61.796875 
-Q 28.40625 64.40625 24.90625 64.40625 
-Q 23.59375 64.40625 22.1875 64 
-Q 20.796875 63.59375 18.796875 62.5 
-Q 16.796875 61.40625 15.25 58.59375 
-Q 13.703125 55.796875 13 51.59375 
-Q 12.203125 46.203125 12.203125 33.203125 
-z
-" id="CMUSerif-Roman-30"/>
-       <path d="M 1 18.59375 
-L 1 24.5 
-L 27.59375 24.5 
-L 27.59375 18.59375 
-z
-" id="CMUSerif-Roman-2d"/>
-       <path d="M 4.203125 31.59375 
-Q 4.203125 47.296875 12.203125 56.9375 
-Q 20.203125 66.59375 30.5 66.59375 
-Q 36.5 66.59375 39.84375 63.546875 
-Q 43.203125 60.5 43.203125 55.796875 
-Q 43.203125 53.203125 41.703125 52.09375 
-Q 40.203125 51 38.59375 51 
-Q 36.796875 51 35.390625 52.203125 
-Q 34 53.40625 34 55.59375 
-Q 34 60.09375 39.5 60.09375 
-Q 36.90625 64.09375 30.703125 64.09375 
-Q 28.796875 64.09375 26.84375 63.546875 
-Q 24.90625 63 22.34375 61.140625 
-Q 19.796875 59.296875 17.84375 56.34375 
-Q 15.90625 53.40625 14.546875 47.90625 
-Q 13.203125 42.40625 13.203125 35.203125 
-L 13.203125 32.796875 
-Q 17.296875 42.703125 25.6875 42.703125 
-Q 34.09375 42.703125 39.890625 36.296875 
-Q 45.703125 29.90625 45.703125 20.40625 
-Q 45.703125 10.703125 39.640625 4.25 
-Q 33.59375 -2.203125 25.09375 -2.203125 
-Q 21.296875 -2.203125 17.84375 -0.59375 
-Q 14.40625 1 11.203125 4.59375 
-Q 8 8.203125 6.09375 15.140625 
-Q 4.203125 22.09375 4.203125 31.59375 
-z
-M 13.40625 22.59375 
-Q 13.40625 12.796875 15.203125 8.09375 
-Q 15.5 7.296875 16.140625 6.25 
-Q 16.796875 5.203125 17.9375 3.796875 
-Q 19.09375 2.40625 21 1.5 
-Q 22.90625 0.59375 25.09375 0.59375 
-Q 31.796875 0.59375 35 7.09375 
-Q 36.703125 10.703125 36.703125 20.5 
-Q 36.703125 30.5 34.90625 34.203125 
-Q 31.796875 40.40625 25.59375 40.40625 
-Q 21.40625 40.40625 18.5 37.5 
-Q 15.59375 34.59375 14.5 30.75 
-Q 13.40625 26.90625 13.40625 22.59375 
-z
-" id="CMUSerif-Roman-36"/>
-      </defs>
-      <g transform="translate(52.295152 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-2d"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-36"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_2">
-     <g id="line2d_2">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="95.033698" xlink:href="#m680b56acf8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_2">
-      <!-- $\mathdefault{10^{-4}}$ -->
-      <defs>
-       <path d="M 2.796875 16.5 
-L 2.796875 19.59375 
-L 33.5 66.5 
-Q 34.296875 67.703125 35.5 67.703125 
-Q 36.59375 67.703125 36.84375 67.25 
-Q 37.09375 66.796875 37.09375 65.09375 
-L 37.09375 19.59375 
-L 47.09375 19.59375 
-L 47.09375 16.5 
-L 37.09375 16.5 
-L 37.09375 7.796875 
-Q 37.09375 4.90625 38.296875 4 
-Q 39.5 3.09375 44.703125 3.09375 
-L 46.796875 3.09375 
-L 46.796875 0 
-Q 42.703125 0.296875 33.203125 0.296875 
-Q 23.796875 0.296875 19.703125 0 
-L 19.703125 3.09375 
-L 21.796875 3.09375 
-Q 27 3.09375 28.203125 4 
-Q 29.40625 4.90625 29.40625 7.796875 
-L 29.40625 16.5 
-z
-M 5.59375 19.59375 
-L 30 19.59375 
-L 30 56.90625 
-z
-" id="CMUSerif-Roman-34"/>
-      </defs>
-      <g transform="translate(86.933698 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-2d"/>
-       <use transform="translate(124.064102 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-34"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_3">
-     <g id="line2d_3">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="129.672243" xlink:href="#m680b56acf8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_3">
-      <!-- $\mathdefault{10^{-2}}$ -->
-      <defs>
-       <path d="M 5 0 
-Q 5 1.796875 5.140625 2.34375 
-Q 5.296875 2.90625 6.09375 3.703125 
-L 25.296875 25.09375 
-Q 35.796875 36.90625 35.796875 47.203125 
-Q 35.796875 53.90625 32.296875 58.703125 
-Q 28.796875 63.5 22.40625 63.5 
-Q 18 63.5 14.296875 60.796875 
-Q 10.59375 58.09375 8.90625 53.296875 
-Q 9.203125 53.40625 10.203125 53.40625 
-Q 12.703125 53.40625 14.09375 51.84375 
-Q 15.5 50.296875 15.5 48.203125 
-Q 15.5 45.5 13.75 44.203125 
-Q 12 42.90625 10.296875 42.90625 
-Q 9.59375 42.90625 8.6875 43.046875 
-Q 7.796875 43.203125 6.390625 44.59375 
-Q 5 46 5 48.5 
-Q 5 55.5 10.296875 61.046875 
-Q 15.59375 66.59375 23.703125 66.59375 
-Q 32.90625 66.59375 38.90625 61.140625 
-Q 44.90625 55.703125 44.90625 47.203125 
-Q 44.90625 44.203125 44 41.5 
-Q 43.09375 38.796875 41.890625 36.6875 
-Q 40.703125 34.59375 37.5 31.25 
-Q 34.296875 27.90625 31.6875 25.5 
-Q 29.09375 23.09375 23.296875 18 
-L 12.703125 7.703125 
-L 30.703125 7.703125 
-Q 39.5 7.703125 40.203125 8.5 
-Q 41.203125 9.90625 42.40625 17.40625 
-L 44.90625 17.40625 
-L 42.09375 0 
-z
-" id="CMUSerif-Roman-32"/>
-      </defs>
-      <g transform="translate(121.572243 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-2d"/>
-       <use transform="translate(124.064102 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-32"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_4">
-     <g id="line2d_4">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="164.310789" xlink:href="#m680b56acf8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_4">
-      <!-- $\mathdefault{10^{0}}$ -->
-      <g transform="translate(157.410789 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-30"/>
-      </g>
-     </g>
-    </g>
-    <g id="xtick_5">
-     <g id="line2d_5">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="198.949334" xlink:href="#m680b56acf8" y="197.316"/>
-      </g>
-     </g>
-     <g id="text_5">
-      <!-- $\mathdefault{10^{2}}$ -->
-      <g transform="translate(192.049334 211.256625)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-32"/>
-      </g>
-     </g>
-    </g>
-    <g id="text_6">
-     <!-- Hydrogen number density $n_{\rm H}$ [cm$^{-3}$] -->
-     <defs>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-L 5.703125 3.09375 
-Q 11.09375 3.09375 12.34375 4 
-Q 13.59375 4.90625 13.59375 7.796875 
-L 13.59375 60.5 
-Q 13.59375 63.40625 12.34375 64.296875 
-Q 11.09375 65.203125 5.703125 65.203125 
-L 3.296875 65.203125 
-L 3.296875 68.296875 
-Q 6.796875 68 18.09375 68 
-Q 29.296875 68 32.796875 68.296875 
-L 32.796875 65.203125 
-L 30.40625 65.203125 
-Q 25 65.203125 23.75 64.296875 
-Q 22.5 63.40625 22.5 60.5 
-L 22.5 37.09375 
-L 52.40625 37.09375 
-L 52.40625 60.5 
-Q 52.40625 63.40625 51.15625 64.296875 
-Q 49.90625 65.203125 44.5 65.203125 
-L 42.09375 65.203125 
-L 42.09375 68.296875 
-Q 45.59375 68 56.90625 68 
-Q 68.09375 68 71.59375 68.296875 
-L 71.59375 65.203125 
-L 69.203125 65.203125 
-Q 63.796875 65.203125 62.546875 64.296875 
-Q 61.296875 63.40625 61.296875 60.5 
-L 61.296875 7.796875 
-Q 61.296875 4.90625 62.546875 4 
-Q 63.796875 3.09375 69.203125 3.09375 
-L 71.59375 3.09375 
-L 71.59375 0 
-Q 68.09375 0.296875 56.796875 0.296875 
-Q 45.59375 0.296875 42.09375 0 
-L 42.09375 3.09375 
-L 44.5 3.09375 
-Q 49.90625 3.09375 51.15625 4 
-Q 52.40625 4.90625 52.40625 7.796875 
-L 52.40625 34 
-L 22.5 34 
-L 22.5 7.796875 
-Q 22.5 4.90625 23.75 4 
-Q 25 3.09375 30.40625 3.09375 
-L 32.796875 3.09375 
-L 32.796875 0 
-Q 29.296875 0.296875 18 0.296875 
-Q 6.796875 0.296875 3.296875 0 
-z
-" id="CMUSerif-Roman-48"/>
-      <path d="M 1.90625 -12.40625 
-Q 1.90625 -10.296875 3.15625 -9.1875 
-Q 4.40625 -8.09375 6.09375 -8.09375 
-Q 7.90625 -8.09375 9.09375 -9.25 
-Q 10.296875 -10.40625 10.296875 -12.296875 
-Q 10.296875 -16 6.40625 -16.5 
-Q 8.296875 -18.296875 11.09375 -18.296875 
-Q 14.09375 -18.296875 16.5 -16.09375 
-Q 18.90625 -13.90625 19.953125 -11.796875 
-Q 21 -9.703125 22.5 -5.90625 
-Q 23.90625 -2.90625 25 0 
-L 10 36.5 
-Q 9 38.90625 7.5 39.453125 
-Q 6 40 1.90625 40 
-L 1.90625 43.09375 
-Q 6.40625 42.796875 11.59375 42.796875 
-Q 14.703125 42.796875 22.5 43.09375 
-L 22.5 40 
-Q 16.90625 40 16.90625 37.40625 
-Q 16.90625 37.09375 17.5 35.59375 
-L 28.59375 8.703125 
-L 38.703125 33.296875 
-Q 39.296875 34.703125 39.296875 35.703125 
-Q 39.296875 39.796875 34.59375 40 
-L 34.59375 43.09375 
-Q 41.203125 42.796875 43.296875 42.796875 
-Q 47.40625 42.796875 50.796875 43.09375 
-L 50.796875 40 
-Q 44.09375 40 41.5 33.59375 
-L 23.90625 -9.09375 
-Q 19.09375 -20.5 11.09375 -20.5 
-Q 7.296875 -20.5 4.59375 -18.140625 
-Q 1.90625 -15.796875 1.90625 -12.40625 
-z
-" id="CMUSerif-Roman-79"/>
-      <path d="M 3.40625 21.5 
-Q 3.40625 31 10.046875 37.59375 
-Q 16.703125 44.203125 25.703125 44.203125 
-Q 33.296875 44.203125 38.296875 38 
-L 38.296875 59.59375 
-Q 38.296875 63.296875 37 64.25 
-Q 35.703125 65.203125 30.5 65.203125 
-L 30.5 68.296875 
-L 44.90625 69.40625 
-L 44.90625 8.703125 
-Q 44.90625 5 46.203125 4.046875 
-Q 47.5 3.09375 52.703125 3.09375 
-L 52.703125 0 
-L 38 -1.09375 
-L 38 5.5 
-Q 32.796875 -1.09375 24.59375 -1.09375 
-Q 16 -1.09375 9.703125 5.5 
-Q 3.40625 12.09375 3.40625 21.5 
-z
-M 11.703125 21.40625 
-Q 11.703125 12.09375 14.59375 7.5 
-Q 18.59375 1.09375 25.09375 1.09375 
-Q 32.5 1.09375 36.90625 8.09375 
-Q 38 9.796875 38 11.796875 
-L 38 32.296875 
-Q 38 34.296875 36.90625 36 
-Q 32.796875 42 26.09375 42 
-Q 19.09375 42 14.796875 35.59375 
-Q 11.703125 30.796875 11.703125 21.40625 
-z
-" id="CMUSerif-Roman-64"/>
-      <path d="M 2.796875 0 
-L 2.796875 3.09375 
-Q 8.09375 3.09375 9.34375 3.75 
-Q 10.59375 4.40625 10.59375 7.59375 
-L 10.59375 34.40625 
-Q 10.59375 38.09375 9.296875 39.046875 
-Q 8 40 2.796875 40 
-L 2.796875 43.09375 
-L 16.703125 44.203125 
-L 16.703125 33.203125 
-Q 18.09375 37.5 21.09375 40.84375 
-Q 24.09375 44.203125 29 44.203125 
-Q 32.203125 44.203125 34.296875 42.390625 
-Q 36.40625 40.59375 36.40625 38.09375 
-Q 36.40625 35.90625 35.046875 34.796875 
-Q 33.703125 33.703125 32.09375 33.703125 
-Q 30.296875 33.703125 29.046875 34.84375 
-Q 27.796875 36 27.796875 38 
-Q 27.796875 39.203125 28.34375 40.140625 
-Q 28.90625 41.09375 29.34375 41.4375 
-Q 29.796875 41.796875 30.09375 41.90625 
-Q 29.90625 42 29 42 
-Q 23.5 42 20.34375 36.5 
-Q 17.203125 31 17.203125 23.203125 
-L 17.203125 7.796875 
-Q 17.203125 4.90625 18.390625 4 
-Q 19.59375 3.09375 24.796875 3.09375 
-L 26.90625 3.09375 
-L 26.90625 0 
-Q 22.90625 0.296875 14.203125 0.296875 
-Q 13 0.296875 11.09375 0.25 
-Q 9.203125 0.203125 6.703125 0.09375 
-Q 4.203125 0 2.796875 0 
-z
-" id="CMUSerif-Roman-72"/>
-      <path d="M 16 -1.09375 
-Q 2.796875 11.90625 2.796875 21.40625 
-Q 2.796875 30.90625 9.25 37.84375 
-Q 15.703125 44.796875 25 44.796875 
-Q 34.09375 44.796875 40.59375 37.890625 
-Q 47.09375 31 47.09375 21.40625 
-Q 47.09375 12 40.546875 5.453125 
-Q 34 -1.09375 24.90625 -1.09375 
-Q 16 -1.09375 2.796875 11.90625 
-z
-M 11.09375 22.203125 
-Q 11.09375 12.5 13.59375 8.09375 
-Q 17.5 1.40625 25 1.40625 
-Q 28.703125 1.40625 31.796875 3.40625 
-Q 34.90625 5.40625 36.59375 8.796875 
-Q 38.796875 13.203125 38.796875 22.203125 
-Q 38.796875 31.796875 36.203125 36.09375 
-Q 32.296875 42.59375 24.90625 42.59375 
-Q 21.703125 42.59375 18.546875 40.890625 
-Q 15.40625 39.203125 13.5 35.90625 
-Q 11.09375 31.5 11.09375 22.203125 
-z
-" id="CMUSerif-Roman-6f"/>
-      <path d="M 2.796875 -7.90625 
-Q 2.796875 -4.703125 5.25 -1.890625 
-Q 7.703125 0.90625 12 2.09375 
-Q 7.59375 4.90625 7.59375 11 
-Q 7.59375 15.703125 10.703125 19.296875 
-Q 6 23.203125 6 29.59375 
-Q 6 35.5 10.703125 39.84375 
-Q 15.40625 44.203125 22.203125 44.203125 
-Q 28.203125 44.203125 32.796875 40.59375 
-Q 37.59375 45.296875 43.40625 45.296875 
-Q 46 45.296875 47.25 43.6875 
-Q 48.5 42.09375 48.5 40.40625 
-Q 48.5 38.90625 47.546875 38.15625 
-Q 46.59375 37.40625 45.59375 37.40625 
-Q 44.40625 37.40625 43.546875 38.203125 
-Q 42.703125 39 42.703125 40.296875 
-Q 42.703125 42.40625 44.296875 43 
-Q 44 43.09375 43.296875 43.09375 
-Q 38.40625 43.09375 34.296875 39.203125 
-Q 38.40625 35.40625 38.40625 29.5 
-Q 38.40625 23.59375 33.703125 19.25 
-Q 29 14.90625 22.203125 14.90625 
-Q 16.59375 14.90625 12.296875 18 
-Q 10.59375 16 10.59375 13.296875 
-Q 10.59375 10.796875 12.09375 8.84375 
-Q 13.59375 6.90625 15.90625 6.59375 
-Q 16.59375 6.5 23.40625 6.5 
-Q 27.40625 6.5 29.59375 6.390625 
-Q 31.796875 6.296875 34.9375 5.640625 
-Q 38.09375 5 40.59375 3.703125 
-Q 47.09375 0.09375 47.09375 -7.703125 
-Q 47.09375 -13.40625 40.546875 -17 
-Q 34 -20.59375 24.90625 -20.59375 
-Q 15.703125 -20.59375 9.25 -16.9375 
-Q 2.796875 -13.296875 2.796875 -7.90625 
-z
-M 8 -7.90625 
-Q 8 -12 12.84375 -15.140625 
-Q 17.703125 -18.296875 25 -18.296875 
-Q 32.203125 -18.296875 37.046875 -15.1875 
-Q 41.90625 -12.09375 41.90625 -7.90625 
-Q 41.90625 -4.90625 40.203125 -3 
-Q 38.5 -1.09375 35 -0.34375 
-Q 31.5 0.40625 29.046875 0.546875 
-Q 26.59375 0.703125 22.09375 0.703125 
-L 16.203125 0.703125 
-Q 12.796875 0.5 10.390625 -2 
-Q 8 -4.5 8 -7.90625 
-z
-M 13.5 29.5 
-Q 13.5 17.203125 22.203125 17.203125 
-Q 26.59375 17.203125 29.296875 21.203125 
-Q 30.90625 23.90625 30.90625 29.59375 
-Q 30.90625 41.90625 22.203125 41.90625 
-Q 17.796875 41.90625 15.09375 37.90625 
-Q 13.5 35.203125 13.5 29.5 
-z
-" id="CMUSerif-Roman-67"/>
-      <path d="M 2.796875 22 
-Q 2.796875 31.40625 8.84375 38.09375 
-Q 14.90625 44.796875 23.59375 44.796875 
-Q 32.40625 44.796875 36.953125 39.09375 
-Q 41.5 33.40625 41.5 25.203125 
-Q 41.5 23.703125 41.09375 23.390625 
-Q 40.703125 23.09375 39 23.09375 
-L 11.09375 23.09375 
-Q 11.09375 12.90625 14.09375 8.09375 
-Q 18.296875 1.40625 25.40625 1.40625 
-Q 26.40625 1.40625 27.546875 1.59375 
-Q 28.703125 1.796875 31.09375 2.640625 
-Q 33.5 3.5 35.59375 5.796875 
-Q 37.703125 8.09375 38.90625 11.703125 
-Q 39.203125 13.09375 40.203125 13.09375 
-Q 41.5 13.09375 41.5 11.90625 
-Q 41.5 11 40.546875 9.046875 
-Q 39.59375 7.09375 37.796875 4.75 
-Q 36 2.40625 32.5 0.65625 
-Q 29 -1.09375 24.796875 -1.09375 
-Q 16 -1.09375 9.390625 5.546875 
-Q 2.796875 12.203125 2.796875 22 
-z
-M 11.203125 25.203125 
-L 34.90625 25.203125 
-Q 34.90625 27.296875 34.546875 29.640625 
-Q 34.203125 32 33.140625 35.25 
-Q 32.09375 38.5 29.640625 40.546875 
-Q 27.203125 42.59375 23.59375 42.59375 
-Q 22 42.59375 20.25 41.890625 
-Q 18.5 41.203125 16.390625 39.546875 
-Q 14.296875 37.90625 12.84375 34.15625 
-Q 11.40625 30.40625 11.203125 25.203125 
-z
-" id="CMUSerif-Roman-65"/>
-      <path d="M 3.203125 0 
-L 3.203125 3.09375 
-Q 8.5 3.09375 9.75 3.75 
-Q 11 4.40625 11 7.59375 
-L 11 34.40625 
-Q 11 38.09375 9.703125 39.046875 
-Q 8.40625 40 3.203125 40 
-L 3.203125 43.09375 
-L 17.296875 44.203125 
-L 17.296875 33.703125 
-Q 22 44.203125 32.09375 44.203125 
-Q 39.59375 44.203125 42.59375 40.5 
-Q 44.796875 38 45.25 35.203125 
-Q 45.703125 32.40625 45.703125 25.203125 
-L 45.703125 6.09375 
-Q 45.796875 4 47.390625 3.546875 
-Q 49 3.09375 53.5 3.09375 
-L 53.5 0 
-Q 43.296875 0.296875 42.296875 0.296875 
-Q 41.5 0.296875 31 0 
-L 31 3.09375 
-Q 36.296875 3.09375 37.546875 3.75 
-Q 38.796875 4.40625 38.796875 7.59375 
-L 38.796875 30.90625 
-Q 38.796875 36 37.25 39 
-Q 35.703125 42 31.40625 42 
-Q 26.203125 42 22.046875 37.640625 
-Q 17.90625 33.296875 17.90625 26 
-L 17.90625 7.59375 
-Q 17.90625 4.40625 19.15625 3.75 
-Q 20.40625 3.09375 25.703125 3.09375 
-L 25.703125 0 
-Q 15.5 0.296875 14.5 0.296875 
-Q 13.703125 0.296875 3.203125 0 
-z
-" id="CMUSerif-Roman-6e"/>
-      <path id="CMUSerif-Roman-20"/>
-      <path d="M 3.203125 40 
-L 3.203125 43.09375 
-L 17.90625 44.203125 
-L 17.90625 11 
-Q 17.90625 8.59375 18.09375 7.1875 
-Q 18.296875 5.796875 19.09375 4.1875 
-Q 19.90625 2.59375 21.796875 1.84375 
-Q 23.703125 1.09375 26.703125 1.09375 
-Q 32.09375 1.09375 35.4375 5.546875 
-Q 38.796875 10 38.796875 16.59375 
-L 38.796875 34.40625 
-Q 38.796875 38.09375 37.5 39.046875 
-Q 36.203125 40 31 40 
-L 31 43.09375 
-L 45.703125 44.203125 
-L 45.703125 8.703125 
-Q 45.703125 5 47 4.046875 
-Q 48.296875 3.09375 53.5 3.09375 
-L 53.5 0 
-L 39.09375 -1.09375 
-L 39.09375 7.90625 
-Q 34.90625 -1.09375 26.203125 -1.09375 
-Q 21.796875 -1.09375 18.796875 0 
-Q 15.796875 1.09375 14.296875 2.5 
-Q 12.796875 3.90625 12 6.59375 
-Q 11.203125 9.296875 11.09375 10.9375 
-Q 11 12.59375 11 15.796875 
-L 11 30.796875 
-Q 11 37.59375 10 38.796875 
-Q 9 40 3.203125 40 
-z
-" id="CMUSerif-Roman-75"/>
-      <path d="M 3.203125 0 
-L 3.203125 3.09375 
-Q 8.5 3.09375 9.75 3.75 
-Q 11 4.40625 11 7.59375 
-L 11 34.40625 
-Q 11 38.09375 9.703125 39.046875 
-Q 8.40625 40 3.203125 40 
-L 3.203125 43.09375 
-L 17.296875 44.203125 
-L 17.296875 33.703125 
-Q 22 44.203125 32.09375 44.203125 
-Q 43.796875 44.203125 45.40625 34.40625 
-Q 47.09375 38.203125 50.6875 41.203125 
-Q 54.296875 44.203125 59.90625 44.203125 
-Q 67.40625 44.203125 70.40625 40.5 
-Q 72.59375 38 73.046875 35.203125 
-Q 73.5 32.40625 73.5 25.203125 
-L 73.5 6.09375 
-Q 73.59375 4 75.1875 3.546875 
-Q 76.796875 3.09375 81.296875 3.09375 
-L 81.296875 0 
-Q 71.09375 0.296875 70.09375 0.296875 
-Q 69.296875 0.296875 58.796875 0 
-L 58.796875 3.09375 
-Q 64.09375 3.09375 65.34375 3.75 
-Q 66.59375 4.40625 66.59375 7.59375 
-L 66.59375 30.90625 
-Q 66.59375 36 65.046875 39 
-Q 63.5 42 59.203125 42 
-Q 54 42 49.84375 37.640625 
-Q 45.703125 33.296875 45.703125 26 
-L 45.703125 7.59375 
-Q 45.703125 4.40625 46.953125 3.75 
-Q 48.203125 3.09375 53.5 3.09375 
-L 53.5 0 
-Q 43.296875 0.296875 42.296875 0.296875 
-Q 41.5 0.296875 31 0 
-L 31 3.09375 
-Q 36.296875 3.09375 37.546875 3.75 
-Q 38.796875 4.40625 38.796875 7.59375 
-L 38.796875 30.90625 
-Q 38.796875 36 37.25 39 
-Q 35.703125 42 31.40625 42 
-Q 26.203125 42 22.046875 37.640625 
-Q 17.90625 33.296875 17.90625 26 
-L 17.90625 7.59375 
-Q 17.90625 4.40625 19.15625 3.75 
-Q 20.40625 3.09375 25.703125 3.09375 
-L 25.703125 0 
-Q 15.5 0.296875 14.5 0.296875 
-Q 13.703125 0.296875 3.203125 0 
-z
-" id="CMUSerif-Roman-6d"/>
-      <path d="M 2.796875 65.203125 
-L 2.796875 68.296875 
-L 17.203125 69.40625 
-L 17.203125 37.703125 
-Q 23 44.203125 30.90625 44.203125 
-Q 39.5 44.203125 45.796875 37.59375 
-Q 52.09375 31 52.09375 21.59375 
-Q 52.09375 12.09375 45.5 5.5 
-Q 38.90625 -1.09375 29.796875 -1.09375 
-Q 21.5 -1.09375 16.703125 6.203125 
-Q 13.203125 0.09375 13.09375 0 
-L 10.59375 0 
-L 10.59375 59.59375 
-Q 10.59375 63.296875 9.296875 64.25 
-Q 8 65.203125 2.796875 65.203125 
-z
-M 17.5 11.40625 
-Q 17.5 9.296875 18.90625 7.203125 
-Q 22.90625 1.09375 29.40625 1.09375 
-Q 36.40625 1.09375 40.703125 7.5 
-Q 43.796875 12.296875 43.796875 21.703125 
-Q 43.796875 31 40.90625 35.59375 
-Q 36.90625 42 30.40625 42 
-Q 23.09375 42 18.59375 35.59375 
-Q 17.5 34 17.5 32 
-z
-" id="CMUSerif-Roman-62"/>
-      <path d="M 3.296875 1.296875 
-L 3.296875 14.5 
-Q 3.296875 15.59375 3.34375 16 
-Q 3.40625 16.40625 3.703125 16.703125 
-Q 4 17 4.59375 17 
-Q 5.296875 17 5.546875 16.703125 
-Q 5.796875 16.40625 6 15.296875 
-Q 7.5 8.40625 10.75 4.75 
-Q 14 1.09375 19.90625 1.09375 
-Q 25.5 1.09375 28.34375 3.59375 
-Q 31.203125 6.09375 31.203125 10.203125 
-Q 31.203125 17.5 20.796875 19.40625 
-Q 14.796875 20.59375 12.296875 21.390625 
-Q 9.796875 22.203125 7.59375 24 
-Q 3.296875 27.5 3.296875 32.5 
-Q 3.296875 37.5 7.09375 41.140625 
-Q 10.90625 44.796875 19.296875 44.796875 
-Q 24.90625 44.796875 28.703125 42 
-Q 29.796875 42.90625 30.40625 43.59375 
-Q 31.703125 44.796875 32.40625 44.796875 
-Q 33.203125 44.796875 33.34375 44.296875 
-Q 33.5 43.796875 33.5 42.40625 
-L 33.5 32.296875 
-Q 33.5 31.203125 33.453125 30.796875 
-Q 33.40625 30.40625 33.09375 30.15625 
-Q 32.796875 29.90625 32.203125 29.90625 
-Q 31.09375 29.90625 31 30.796875 
-Q 30.203125 42.90625 19.296875 42.90625 
-Q 13.40625 42.90625 10.75 40.65625 
-Q 8.09375 38.40625 8.09375 35.296875 
-Q 8.09375 33.59375 8.890625 32.296875 
-Q 9.703125 31 10.75 30.25 
-Q 11.796875 29.5 13.75 28.796875 
-Q 15.703125 28.09375 16.890625 27.84375 
-Q 18.09375 27.59375 20.40625 27.09375 
-Q 28.40625 25.59375 31.796875 22.296875 
-Q 36 18.09375 36 12.796875 
-Q 36 6.90625 32 2.90625 
-Q 28 -1.09375 19.90625 -1.09375 
-Q 13.40625 -1.09375 8.90625 3.203125 
-Q 8.296875 2.59375 7.84375 2.09375 
-Q 7.40625 1.59375 7.25 1.390625 
-Q 7.09375 1.203125 7.046875 1.09375 
-Q 7 1 6.90625 0.90625 
-Q 4.90625 -1.09375 4.40625 -1.09375 
-Q 3.59375 -1.09375 3.4375 -0.59375 
-Q 3.296875 -0.09375 3.296875 1.296875 
-z
-" id="CMUSerif-Roman-73"/>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-Q 8.59375 3.09375 9.84375 3.75 
-Q 11.09375 4.40625 11.09375 7.59375 
-L 11.09375 34.5 
-Q 11.09375 38.203125 9.84375 39.09375 
-Q 8.59375 40 3.703125 40 
-L 3.703125 43.09375 
-L 17.703125 44.203125 
-L 17.703125 7.5 
-Q 17.703125 4.5 18.75 3.796875 
-Q 19.796875 3.09375 24.703125 3.09375 
-L 24.703125 0 
-Q 14.5 0.296875 14.296875 0.296875 
-Q 12.90625 0.296875 3.296875 0 
-z
-M 7.5 61.59375 
-Q 7.5 63.59375 9.046875 65.25 
-Q 10.59375 66.90625 12.796875 66.90625 
-Q 15 66.90625 16.546875 65.40625 
-Q 18.09375 63.90625 18.09375 61.59375 
-Q 18.09375 59.296875 16.546875 57.796875 
-Q 15 56.296875 12.796875 56.296875 
-Q 10.5 56.296875 9 57.890625 
-Q 7.5 59.5 7.5 61.59375 
-z
-" id="CMUSerif-Roman-69"/>
-      <path d="M 1.90625 40 
-L 1.90625 42.203125 
-Q 6.5 42.40625 9.546875 45.65625 
-Q 12.59375 48.90625 13.640625 52.90625 
-Q 14.703125 56.90625 14.796875 61.5 
-L 17.296875 61.5 
-L 17.296875 43.09375 
-L 31.59375 43.09375 
-L 31.59375 40 
-L 17.296875 40 
-L 17.296875 12.203125 
-Q 17.296875 1.40625 24 1.40625 
-Q 26.90625 1.40625 28.796875 4.34375 
-Q 30.703125 7.296875 30.703125 12.59375 
-L 30.703125 18.09375 
-L 33.203125 18.09375 
-L 33.203125 12.40625 
-Q 33.203125 7 30.703125 2.953125 
-Q 28.203125 -1.09375 23.296875 -1.09375 
-Q 21.5 -1.09375 19.703125 -0.640625 
-Q 17.90625 -0.203125 15.59375 1 
-Q 13.296875 2.203125 11.84375 5.140625 
-Q 10.40625 8.09375 10.40625 12.40625 
-L 10.40625 40 
-z
-" id="CMUSerif-Roman-74"/>
-      <path d="M 7.71875 1.703125 
-Q 7.71875 2.296875 7.8125 2.59375 
-L 15.28125 32.421875 
-Q 16.015625 35.203125 16.015625 37.3125 
-Q 16.015625 41.609375 13.09375 41.609375 
-Q 9.96875 41.609375 8.453125 37.859375 
-Q 6.9375 34.125 5.515625 28.421875 
-Q 5.515625 28.125 5.21875 27.953125 
-Q 4.9375 27.78125 4.6875 27.78125 
-L 3.515625 27.78125 
-Q 3.171875 27.78125 2.921875 28.140625 
-Q 2.6875 28.515625 2.6875 28.8125 
-Q 3.765625 33.15625 4.765625 36.171875 
-Q 5.765625 39.203125 7.890625 41.6875 
-Q 10.015625 44.1875 13.1875 44.1875 
-Q 16.9375 44.1875 19.8125 41.8125 
-Q 22.703125 39.453125 22.703125 35.796875 
-Q 25.6875 39.703125 29.6875 41.9375 
-Q 33.6875 44.1875 38.1875 44.1875 
-Q 41.75 44.1875 44.328125 42.96875 
-Q 46.921875 41.75 48.359375 39.28125 
-Q 49.8125 36.8125 49.8125 33.40625 
-Q 49.8125 29.296875 47.96875 23.484375 
-Q 46.140625 17.671875 43.40625 10.5 
-Q 42 7.234375 42 4.5 
-Q 42 1.515625 44.28125 1.515625 
-Q 48.1875 1.515625 50.796875 5.703125 
-Q 53.421875 9.90625 54.5 14.703125 
-Q 54.6875 15.28125 55.328125 15.28125 
-L 56.5 15.28125 
-Q 56.890625 15.28125 57.15625 15.03125 
-Q 57.421875 14.796875 57.421875 14.40625 
-Q 57.421875 14.3125 57.328125 14.109375 
-Q 55.953125 8.453125 52.5625 3.65625 
-Q 49.171875 -1.125 44.09375 -1.125 
-Q 40.578125 -1.125 38.078125 1.296875 
-Q 35.59375 3.71875 35.59375 7.171875 
-Q 35.59375 9.03125 36.375 11.078125 
-Q 37.640625 14.359375 39.28125 18.890625 
-Q 40.921875 23.4375 41.96875 27.578125 
-Q 43.015625 31.734375 43.015625 34.90625 
-Q 43.015625 37.703125 41.859375 39.65625 
-Q 40.71875 41.609375 37.984375 41.609375 
-Q 34.328125 41.609375 31.25 39.984375 
-Q 28.171875 38.375 25.875 35.71875 
-Q 23.578125 33.0625 21.6875 29.390625 
-L 14.890625 2.203125 
-Q 14.546875 0.828125 13.34375 -0.140625 
-Q 12.15625 -1.125 10.6875 -1.125 
-Q 9.46875 -1.125 8.59375 -0.34375 
-Q 7.71875 0.4375 7.71875 1.703125 
-z
-" id="Cmmi10-6e"/>
-      <path d="M 3.078125 0 
-L 3.078125 3.515625 
-Q 13.375 3.515625 13.375 6.6875 
-L 13.375 61.625 
-Q 13.375 64.796875 3.078125 64.796875 
-L 3.078125 68.3125 
-L 33.015625 68.3125 
-L 33.015625 64.796875 
-Q 22.703125 64.796875 22.703125 61.625 
-L 22.703125 37.3125 
-L 52.203125 37.3125 
-L 52.203125 61.625 
-Q 52.203125 64.796875 41.890625 64.796875 
-L 41.890625 68.3125 
-L 71.78125 68.3125 
-L 71.78125 64.796875 
-Q 61.53125 64.796875 61.53125 61.625 
-L 61.53125 6.6875 
-Q 61.53125 3.515625 71.78125 3.515625 
-L 71.78125 0 
-L 41.890625 0 
-L 41.890625 3.515625 
-Q 52.203125 3.515625 52.203125 6.6875 
-L 52.203125 33.796875 
-L 22.703125 33.796875 
-L 22.703125 6.6875 
-Q 22.703125 3.515625 33.015625 3.515625 
-L 33.015625 0 
-z
-" id="Cmr10-48"/>
-      <path d="M 10.40625 -25 
-L 10.40625 75 
-L 25.5 75 
-L 25.5 72.703125 
-L 17.09375 72.703125 
-L 17.09375 -22.703125 
-L 25.5 -22.703125 
-L 25.5 -25 
-z
-" id="CMUSerif-Roman-5b"/>
-      <path d="M 16.09375 -1.09375 
-Q 3.40625 12.09375 3.40625 21.59375 
-Q 3.40625 31.09375 9.65625 37.9375 
-Q 15.90625 44.796875 25.09375 44.796875 
-Q 31.203125 44.796875 35.796875 41.890625 
-Q 40.40625 39 40.40625 34.09375 
-Q 40.40625 31.90625 39.09375 30.65625 
-Q 37.796875 29.40625 35.796875 29.40625 
-Q 33.703125 29.40625 32.453125 30.703125 
-Q 31.203125 32 31.203125 34 
-Q 31.203125 34.90625 31.5 35.75 
-Q 31.796875 36.59375 32.890625 37.546875 
-Q 34 38.5 35.90625 38.59375 
-Q 32.296875 42.296875 25.203125 42.296875 
-Q 20.09375 42.296875 15.890625 37.5 
-Q 11.703125 32.703125 11.703125 21.796875 
-Q 11.703125 16.09375 13.09375 11.890625 
-Q 14.5 7.703125 16.796875 5.546875 
-Q 19.09375 3.40625 21.34375 2.40625 
-Q 23.59375 1.40625 25.796875 1.40625 
-Q 35.59375 1.40625 38.90625 11.90625 
-Q 39.203125 12.90625 40.203125 12.90625 
-Q 41.5 12.90625 41.5 11.90625 
-Q 41.5 11.40625 41.09375 10.15625 
-Q 40.703125 8.90625 39.5 6.90625 
-Q 38.296875 4.90625 36.546875 3.15625 
-Q 34.796875 1.40625 31.75 0.15625 
-Q 28.703125 -1.09375 24.90625 -1.09375 
-Q 16.09375 -1.09375 3.40625 12.09375 
-z
-" id="CMUSerif-Roman-63"/>
-      <path d="M 10.203125 23 
-Q 9.375 23 8.828125 23.625 
-Q 8.296875 24.265625 8.296875 25 
-Q 8.296875 25.734375 8.828125 26.359375 
-Q 9.375 27 10.203125 27 
-L 67.578125 27 
-Q 68.359375 27 68.875 26.359375 
-Q 69.390625 25.734375 69.390625 25 
-Q 69.390625 24.265625 68.875 23.625 
-Q 68.359375 23 67.578125 23 
-z
-" id="Cmsy10-a1"/>
-      <path d="M 9.515625 7.71875 
-Q 11.859375 4.296875 15.8125 2.640625 
-Q 19.78125 0.984375 24.3125 0.984375 
-Q 30.125 0.984375 32.5625 5.9375 
-Q 35.015625 10.890625 35.015625 17.1875 
-Q 35.015625 20.015625 34.5 22.84375 
-Q 33.984375 25.6875 32.765625 28.125 
-Q 31.546875 30.5625 29.421875 32.03125 
-Q 27.296875 33.5 24.21875 33.5 
-L 17.578125 33.5 
-Q 16.703125 33.5 16.703125 34.421875 
-L 16.703125 35.296875 
-Q 16.703125 36.078125 17.578125 36.078125 
-L 23.09375 36.53125 
-Q 26.609375 36.53125 28.921875 39.15625 
-Q 31.25 41.796875 32.328125 45.578125 
-Q 33.40625 49.359375 33.40625 52.78125 
-Q 33.40625 57.5625 31.15625 60.640625 
-Q 28.90625 63.71875 24.3125 63.71875 
-Q 20.515625 63.71875 17.046875 62.28125 
-Q 13.578125 60.84375 11.53125 57.90625 
-Q 11.71875 57.953125 11.859375 57.984375 
-Q 12.015625 58.015625 12.203125 58.015625 
-Q 14.453125 58.015625 15.96875 56.453125 
-Q 17.484375 54.890625 17.484375 52.6875 
-Q 17.484375 50.53125 15.96875 48.96875 
-Q 14.453125 47.40625 12.203125 47.40625 
-Q 10.015625 47.40625 8.453125 48.96875 
-Q 6.890625 50.53125 6.890625 52.6875 
-Q 6.890625 56.984375 9.46875 60.15625 
-Q 12.0625 63.328125 16.140625 64.96875 
-Q 20.21875 66.609375 24.3125 66.609375 
-Q 27.34375 66.609375 30.703125 65.703125 
-Q 34.078125 64.796875 36.8125 63.109375 
-Q 39.546875 61.421875 41.28125 58.78125 
-Q 43.015625 56.15625 43.015625 52.78125 
-Q 43.015625 48.578125 41.140625 45.015625 
-Q 39.265625 41.453125 35.984375 38.859375 
-Q 32.71875 36.28125 28.8125 35.015625 
-Q 33.15625 34.1875 37.0625 31.734375 
-Q 40.96875 29.296875 43.328125 25.484375 
-Q 45.703125 21.6875 45.703125 17.28125 
-Q 45.703125 11.765625 42.671875 7.296875 
-Q 39.65625 2.828125 34.71875 0.3125 
-Q 29.78125 -2.203125 24.3125 -2.203125 
-Q 19.625 -2.203125 14.90625 -0.40625 
-Q 10.203125 1.375 7.203125 4.9375 
-Q 4.203125 8.5 4.203125 13.484375 
-Q 4.203125 15.96875 5.859375 17.625 
-Q 7.515625 19.28125 10.015625 19.28125 
-Q 11.625 19.28125 12.96875 18.53125 
-Q 14.3125 17.78125 15.0625 16.40625 
-Q 15.828125 15.046875 15.828125 13.484375 
-Q 15.828125 11.03125 14.109375 9.375 
-Q 12.40625 7.71875 10.015625 7.71875 
-z
-" id="Cmr10-33"/>
-      <path d="M 2.09375 -22.703125 
-L 10.5 -22.703125 
-L 10.5 72.703125 
-L 2.09375 72.703125 
-L 2.09375 75 
-L 17.203125 75 
-L 17.203125 -25 
-L 2.09375 -25 
-z
-" id="CMUSerif-Roman-5d"/>
-     </defs>
-     <g transform="translate(48.976 221.69725)scale(0.1 -0.1)">
-      <use transform="translate(0 0.109375)" xlink:href="#CMUSerif-Roman-48"/>
-      <use transform="translate(74.999985 0.109375)" xlink:href="#CMUSerif-Roman-79"/>
-      <use transform="translate(127.699982 0.109375)" xlink:href="#CMUSerif-Roman-64"/>
-      <use transform="translate(183.199966 0.109375)" xlink:href="#CMUSerif-Roman-72"/>
-      <use transform="translate(222.299957 0.109375)" xlink:href="#CMUSerif-Roman-6f"/>
-      <use transform="translate(272.299942 0.109375)" xlink:href="#CMUSerif-Roman-67"/>
-      <use transform="translate(322.299927 0.109375)" xlink:href="#CMUSerif-Roman-65"/>
-      <use transform="translate(366.699921 0.109375)" xlink:href="#CMUSerif-Roman-6e"/>
-      <use transform="translate(422.199905 0.109375)" xlink:href="#CMUSerif-Roman-20"/>
-      <use transform="translate(455.499893 0.109375)" xlink:href="#CMUSerif-Roman-6e"/>
-      <use transform="translate(510.999878 0.109375)" xlink:href="#CMUSerif-Roman-75"/>
-      <use transform="translate(566.499863 0.109375)" xlink:href="#CMUSerif-Roman-6d"/>
-      <use transform="translate(649.79985 0.109375)" xlink:href="#CMUSerif-Roman-62"/>
-      <use transform="translate(705.299835 0.109375)" xlink:href="#CMUSerif-Roman-65"/>
-      <use transform="translate(749.699829 0.109375)" xlink:href="#CMUSerif-Roman-72"/>
-      <use transform="translate(788.79982 0.109375)" xlink:href="#CMUSerif-Roman-20"/>
-      <use transform="translate(822.099808 0.109375)" xlink:href="#CMUSerif-Roman-64"/>
-      <use transform="translate(877.599792 0.109375)" xlink:href="#CMUSerif-Roman-65"/>
-      <use transform="translate(921.999786 0.109375)" xlink:href="#CMUSerif-Roman-6e"/>
-      <use transform="translate(977.499771 0.109375)" xlink:href="#CMUSerif-Roman-73"/>
-      <use transform="translate(1016.899765 0.109375)" xlink:href="#CMUSerif-Roman-69"/>
-      <use transform="translate(1044.599762 0.109375)" xlink:href="#CMUSerif-Roman-74"/>
-      <use transform="translate(1083.39975 0.109375)" xlink:href="#CMUSerif-Roman-79"/>
-      <use transform="translate(1136.099747 0.109375)" xlink:href="#CMUSerif-Roman-20"/>
-      <use transform="translate(1169.399734 0.109375)" xlink:href="#Cmmi10-6e"/>
-      <use transform="translate(1229.4095 -16.896875)scale(0.7)" xlink:href="#Cmr10-48"/>
-      <use transform="translate(1288.286844 0.109375)" xlink:href="#CMUSerif-Roman-20"/>
-      <use transform="translate(1321.586832 0.109375)" xlink:href="#CMUSerif-Roman-5b"/>
-      <use transform="translate(1349.386819 0.109375)" xlink:href="#CMUSerif-Roman-63"/>
-      <use transform="translate(1393.786813 0.109375)" xlink:href="#CMUSerif-Roman-6d"/>
-      <use transform="translate(1481.550942 38.373438)scale(0.7)" xlink:href="#Cmsy10-a1"/>
-      <use transform="translate(1535.930825 38.373438)scale(0.7)" xlink:href="#Cmr10-33"/>
-      <use transform="translate(1577.308168 0.109375)" xlink:href="#CMUSerif-Roman-5d"/>
-     </g>
-    </g>
-   </g>
-   <g id="matplotlib.axis_2">
-    <g id="ytick_1">
-     <g id="line2d_6">
-      <defs>
-       <path d="M 0 0 
-L -3.5 0 
-" id="mefc29cf603" style="stroke:#000000;stroke-width:0.8;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="163.232825"/>
-      </g>
-     </g>
-     <g id="text_7">
-      <!-- $\mathdefault{10^{2}}$ -->
-      <g transform="translate(13.22 166.703137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-32"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_2">
-     <g id="line2d_7">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="114.470825"/>
-      </g>
-     </g>
-     <g id="text_8">
-      <!-- $\mathdefault{10^{3}}$ -->
-      <defs>
-       <path d="M 4.203125 13.5 
-Q 4.203125 16.5 5.890625 17.890625 
-Q 7.59375 19.296875 9.796875 19.296875 
-Q 12.09375 19.296875 13.75 17.796875 
-Q 15.40625 16.296875 15.40625 13.703125 
-Q 15.40625 10.90625 13.453125 9.34375 
-Q 11.5 7.796875 8.796875 8.203125 
-Q 11.203125 4.203125 15.59375 2.390625 
-Q 20 0.59375 24.09375 0.59375 
-Q 28.40625 0.59375 31.90625 4.296875 
-Q 35.40625 8 35.40625 17.09375 
-Q 35.40625 24.796875 32.40625 29.25 
-Q 29.40625 33.703125 23.5 33.703125 
-L 19.09375 33.703125 
-Q 17.59375 33.703125 17.140625 33.84375 
-Q 16.703125 34 16.703125 34.796875 
-Q 16.703125 35.796875 18.203125 36 
-Q 19.703125 36 22.09375 36.296875 
-Q 27.90625 36.5 31 41.5 
-Q 33.796875 46.203125 33.796875 52.90625 
-Q 33.796875 59 30.890625 61.546875 
-Q 28 64.09375 24.203125 64.09375 
-Q 20.703125 64.09375 16.84375 62.640625 
-Q 13 61.203125 10.90625 57.90625 
-Q 17.09375 57.90625 17.09375 52.90625 
-Q 17.09375 50.703125 15.6875 49.25 
-Q 14.296875 47.796875 12 47.796875 
-Q 9.796875 47.796875 8.34375 49.1875 
-Q 6.90625 50.59375 6.90625 53 
-Q 6.90625 58.703125 12 62.640625 
-Q 17.09375 66.59375 24.59375 66.59375 
-Q 32 66.59375 37.5 62.6875 
-Q 43 58.796875 43 52.796875 
-Q 43 46.90625 39.09375 42.046875 
-Q 35.203125 37.203125 29 35.203125 
-Q 36.59375 33.703125 41.140625 28.546875 
-Q 45.703125 23.40625 45.703125 17.09375 
-Q 45.703125 9.296875 39.546875 3.546875 
-Q 33.40625 -2.203125 24.40625 -2.203125 
-Q 16.09375 -2.203125 10.140625 2.296875 
-Q 4.203125 6.796875 4.203125 13.5 
-z
-" id="CMUSerif-Roman-33"/>
-      </defs>
-      <g transform="translate(13.22 117.941137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-33"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_3">
-     <g id="line2d_8">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="65.708825"/>
-      </g>
-     </g>
-     <g id="text_9">
-      <!-- $\mathdefault{10^{4}}$ -->
-      <g transform="translate(13.22 69.179137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.442188)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.442188)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.607813)scale(0.7)" xlink:href="#CMUSerif-Roman-34"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_4">
-     <g id="line2d_9">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.8;" x="34.02" xlink:href="#mefc29cf603" y="16.946825"/>
-      </g>
-     </g>
-     <g id="text_10">
-      <!-- $\mathdefault{10^{5}}$ -->
-      <defs>
-       <path d="M 5 16.09375 
-Q 5 19.09375 6.59375 20.25 
-Q 8.203125 21.40625 9.90625 21.40625 
-Q 12.203125 21.40625 13.546875 19.953125 
-Q 14.90625 18.5 14.90625 16.5 
-Q 14.90625 14.5 13.546875 13.046875 
-Q 12.203125 11.59375 9.90625 11.59375 
-Q 8.796875 11.59375 8.203125 11.796875 
-Q 9.5 7.203125 13.546875 3.890625 
-Q 17.59375 0.59375 22.90625 0.59375 
-Q 29.59375 0.59375 33.59375 7.09375 
-Q 36 11.296875 36 20.796875 
-Q 36 29.203125 34.203125 33.40625 
-Q 31.40625 39.796875 25.703125 39.796875 
-Q 17.59375 39.796875 12.796875 32.796875 
-Q 12.203125 31.90625 11.5 31.90625 
-Q 10.5 31.90625 10.296875 32.453125 
-Q 10.09375 33 10.09375 34.5 
-L 10.09375 64.09375 
-Q 10.09375 66.5 11.09375 66.5 
-Q 11.5 66.5 12.296875 66.203125 
-Q 18.59375 63.40625 25.59375 63.40625 
-Q 32.796875 63.40625 39.203125 66.296875 
-Q 39.703125 66.59375 40 66.59375 
-Q 41 66.59375 41 65.5 
-Q 41 65.09375 40.203125 63.9375 
-Q 39.40625 62.796875 37.703125 61.296875 
-Q 36 59.796875 33.796875 58.390625 
-Q 31.59375 57 28.390625 56.046875 
-Q 25.203125 55.09375 21.703125 55.09375 
-Q 17.5 55.09375 13.203125 56.40625 
-L 13.203125 36.90625 
-Q 18.40625 42 25.90625 42 
-Q 33.90625 42 39.40625 35.546875 
-Q 44.90625 29.09375 44.90625 20.09375 
-Q 44.90625 10.703125 38.40625 4.25 
-Q 31.90625 -2.203125 23.09375 -2.203125 
-Q 15.09375 -2.203125 10.046875 3.5 
-Q 5 9.203125 5 16.09375 
-z
-" id="CMUSerif-Roman-35"/>
-      </defs>
-      <g transform="translate(13.22 20.417137)scale(0.1 -0.1)">
-       <use transform="translate(0 0.21875)" xlink:href="#CMUSerif-Roman-31"/>
-       <use transform="translate(49.999985 0.21875)" xlink:href="#CMUSerif-Roman-30"/>
-       <use transform="translate(100.75411 30.384375)scale(0.7)" xlink:href="#CMUSerif-Roman-35"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_5">
-     <g id="line2d_10">
-      <defs>
-       <path d="M 0 0 
-L -2 0 
-" id="m75a05ee26c" style="stroke:#000000;stroke-width:0.6;"/>
-      </defs>
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="197.316"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_6">
-     <g id="line2d_11">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="188.729438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_7">
-     <g id="line2d_12">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="182.637175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_8">
-     <g id="line2d_13">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="177.911649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_9">
-     <g id="line2d_14">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="174.050613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_10">
-     <g id="line2d_15">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="170.786154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_11">
-     <g id="line2d_16">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="167.958351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_12">
-     <g id="line2d_17">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="165.464051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_13">
-     <g id="line2d_18">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="148.554"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_14">
-     <g id="line2d_19">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="139.967438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_15">
-     <g id="line2d_20">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="133.875175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_16">
-     <g id="line2d_21">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="129.149649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_17">
-     <g id="line2d_22">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="125.288613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_18">
-     <g id="line2d_23">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="122.024154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_19">
-     <g id="line2d_24">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="119.196351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_20">
-     <g id="line2d_25">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="116.702051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_21">
-     <g id="line2d_26">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="99.792"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_22">
-     <g id="line2d_27">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="91.205438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_23">
-     <g id="line2d_28">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="85.113175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_24">
-     <g id="line2d_29">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="80.387649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_25">
-     <g id="line2d_30">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="76.526613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_26">
-     <g id="line2d_31">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="73.262154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_27">
-     <g id="line2d_32">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="70.434351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_28">
-     <g id="line2d_33">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="67.940051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_29">
-     <g id="line2d_34">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="51.03"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_30">
-     <g id="line2d_35">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="42.443438"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_31">
-     <g id="line2d_36">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="36.351175"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_32">
-     <g id="line2d_37">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="31.625649"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_33">
-     <g id="line2d_38">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="27.764613"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_34">
-     <g id="line2d_39">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="24.500154"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_35">
-     <g id="line2d_40">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="21.672351"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_36">
-     <g id="line2d_41">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="19.178051"/>
-      </g>
-     </g>
-    </g>
-    <g id="ytick_37">
-     <g id="line2d_42">
-      <g>
-       <use style="stroke:#000000;stroke-width:0.6;" x="34.02" xlink:href="#m75a05ee26c" y="2.268"/>
-      </g>
-     </g>
-    </g>
-    <g id="text_11">
-     <!-- Temperature $T$ [K] -->
-     <defs>
-      <path d="M 3.59375 45.203125 
-L 5.5 67.703125 
-L 66.59375 67.703125 
-L 68.5 45.203125 
-L 66 45.203125 
-Q 65.59375 49.703125 65.25 52.296875 
-Q 64.90625 54.90625 64.046875 57.34375 
-Q 63.203125 59.796875 62.140625 61 
-Q 61.09375 62.203125 59.09375 63.140625 
-Q 57.09375 64.09375 54.5 64.34375 
-Q 51.90625 64.59375 48 64.59375 
-Q 43.40625 64.59375 42.296875 64.40625 
-Q 41.09375 64.09375 40.796875 63.25 
-Q 40.5 62.40625 40.5 60.59375 
-L 40.5 7.90625 
-Q 40.5 5.90625 40.890625 5.09375 
-Q 41.296875 4.296875 43.59375 3.6875 
-Q 45.90625 3.09375 51 3.09375 
-L 55 3.09375 
-L 55 0 
-Q 50.90625 0.296875 36 0.296875 
-Q 21.203125 0.296875 17.09375 0 
-L 17.09375 3.09375 
-L 21.09375 3.09375 
-Q 26.203125 3.09375 28.5 3.6875 
-Q 30.796875 4.296875 31.1875 5.09375 
-Q 31.59375 5.90625 31.59375 7.90625 
-L 31.59375 60.59375 
-Q 31.59375 62 31.5 62.59375 
-Q 31.40625 63.203125 30.953125 63.703125 
-Q 30.5 64.203125 29.5 64.390625 
-Q 28.5 64.59375 24.09375 64.59375 
-Q 20.203125 64.59375 17.59375 64.34375 
-Q 15 64.09375 13 63.140625 
-Q 11 62.203125 9.953125 61 
-Q 8.90625 59.796875 8.046875 57.34375 
-Q 7.203125 54.90625 6.84375 52.296875 
-Q 6.5 49.703125 6.09375 45.203125 
-z
-" id="CMUSerif-Roman-54"/>
-      <path d="M 2.796875 -16.296875 
-Q 8.09375 -16.296875 9.34375 -15.640625 
-Q 10.59375 -15 10.59375 -11.796875 
-L 10.59375 35 
-Q 10.59375 38.296875 9.34375 39.140625 
-Q 8.09375 40 2.796875 40 
-L 2.796875 43.09375 
-L 17.203125 44.203125 
-L 17.203125 37.59375 
-Q 23.203125 44.203125 31.203125 44.203125 
-Q 39.703125 44.203125 45.890625 37.59375 
-Q 52.09375 31 52.09375 21.59375 
-Q 52.09375 12.09375 45.5 5.5 
-Q 38.90625 -1.09375 29.796875 -1.09375 
-Q 24.796875 -1.09375 21.4375 1.453125 
-Q 18.09375 4 17.5 5.90625 
-L 17.5 5 
-L 17.5 -11.796875 
-Q 17.5 -15 18.75 -15.640625 
-Q 20 -16.296875 25.296875 -16.296875 
-L 25.296875 -19.40625 
-Q 14.796875 -19.09375 14 -19.09375 
-Q 13 -19.09375 2.796875 -19.40625 
-z
-M 17.5 11.40625 
-Q 17.5 9.90625 17.703125 9.34375 
-Q 17.90625 8.796875 18.90625 7.203125 
-Q 22.90625 1.09375 29.40625 1.09375 
-Q 35.09375 1.09375 39.4375 6.9375 
-Q 43.796875 12.796875 43.796875 21.59375 
-Q 43.796875 30 39.84375 35.84375 
-Q 35.90625 41.703125 30.40625 41.703125 
-Q 26.5 41.703125 23.09375 39.59375 
-Q 19.703125 37.5 17.5 33.703125 
-z
-" id="CMUSerif-Roman-70"/>
-      <path d="M 4.203125 9.5 
-Q 4.203125 18 14.203125 22.5 
-Q 20.203125 25.40625 32.59375 26.09375 
-L 32.59375 29.796875 
-Q 32.59375 36 29.34375 39.296875 
-Q 26.09375 42.59375 22 42.59375 
-Q 14.703125 42.59375 11.203125 38 
-Q 14.203125 37.90625 15.25 36.40625 
-Q 16.296875 34.90625 16.296875 33.40625 
-Q 16.296875 31.40625 15.046875 30.09375 
-Q 13.796875 28.796875 11.703125 28.796875 
-Q 9.703125 28.796875 8.390625 30.046875 
-Q 7.09375 31.296875 7.09375 33.5 
-Q 7.09375 38.40625 11.5 41.59375 
-Q 15.90625 44.796875 22.203125 44.796875 
-Q 30.40625 44.796875 35.90625 39.296875 
-Q 37.59375 37.59375 38.4375 35.390625 
-Q 39.296875 33.203125 39.390625 31.75 
-Q 39.5 30.296875 39.5 27.5 
-L 39.5 7.5 
-Q 39.5 6.90625 39.703125 5.953125 
-Q 39.90625 5 40.796875 3.75 
-Q 41.703125 2.5 43.203125 2.5 
-Q 46.796875 2.5 46.796875 8.90625 
-L 46.796875 14.5 
-L 49.296875 14.5 
-L 49.296875 8.90625 
-Q 49.296875 3.59375 46.5 1.5 
-Q 43.703125 -0.59375 41.09375 -0.59375 
-Q 37.796875 -0.59375 35.6875 1.84375 
-Q 33.59375 4.296875 33.296875 7.59375 
-Q 31.796875 3.796875 28.34375 1.34375 
-Q 24.90625 -1.09375 20.203125 -1.09375 
-Q 16.59375 -1.09375 13.1875 -0.1875 
-Q 9.796875 0.703125 7 3.203125 
-Q 4.203125 5.703125 4.203125 9.5 
-z
-M 11.90625 9.59375 
-Q 11.90625 5.90625 14.546875 3.5 
-Q 17.203125 1.09375 20.90625 1.09375 
-Q 25.09375 1.09375 28.84375 4.34375 
-Q 32.59375 7.59375 32.59375 14 
-L 32.59375 24 
-Q 21.5 23.59375 16.703125 19.1875 
-Q 11.90625 14.796875 11.90625 9.59375 
-z
-" id="CMUSerif-Roman-61"/>
-      <path d="M 4.59375 1.3125 
-Q 4.640625 1.5625 4.8125 2.1875 
-Q 4.984375 2.828125 5.25 3.171875 
-Q 5.515625 3.515625 6 3.515625 
-Q 14.59375 3.515625 17.390625 4 
-Q 20.0625 4.6875 20.609375 6.890625 
-L 34.28125 61.8125 
-Q 34.71875 63.03125 34.71875 64.015625 
-Q 34.71875 64.796875 31.203125 64.796875 
-L 25.390625 64.796875 
-Q 18.703125 64.796875 15.0625 62.734375 
-Q 11.421875 60.6875 9.71875 57.3125 
-Q 8.015625 53.953125 5.328125 46.296875 
-Q 4.984375 45.40625 4.296875 45.40625 
-L 3.421875 45.40625 
-Q 2.390625 45.40625 2.390625 46.6875 
-L 9.515625 67.390625 
-Q 9.71875 68.3125 10.5 68.3125 
-L 69.578125 68.3125 
-Q 70.609375 68.3125 70.609375 67 
-L 67.28125 46.296875 
-Q 67.28125 46 66.9375 45.703125 
-Q 66.609375 45.40625 66.3125 45.40625 
-L 65.375 45.40625 
-Q 64.40625 45.40625 64.40625 46.6875 
-Q 65.484375 53.765625 65.484375 56.6875 
-Q 65.484375 60.203125 64.015625 62 
-Q 62.546875 63.8125 60.203125 64.296875 
-Q 57.859375 64.796875 54.109375 64.796875 
-L 48.1875 64.796875 
-Q 45.515625 64.796875 44.578125 64.296875 
-Q 43.65625 63.8125 43.015625 61.375 
-L 29.296875 6.5 
-Q 29.25 6.296875 29.21875 6.09375 
-Q 29.203125 5.90625 29.109375 5.609375 
-Q 29.109375 4.34375 30.609375 4 
-Q 33.203125 3.515625 41.703125 3.515625 
-Q 42.671875 3.515625 42.671875 2.203125 
-Q 42.328125 0.78125 42.125 0.390625 
-Q 41.9375 0 41.015625 0 
-L 5.609375 0 
-Q 4.59375 0 4.59375 1.3125 
-z
-" id="Cmmi10-54"/>
-      <path d="M 3.296875 0 
-L 3.296875 3.09375 
-L 5.703125 3.09375 
-Q 11.09375 3.09375 12.34375 4 
-Q 13.59375 4.90625 13.59375 7.796875 
-L 13.59375 60.5 
-Q 13.59375 63.40625 12.34375 64.296875 
-Q 11.09375 65.203125 5.703125 65.203125 
-L 3.296875 65.203125 
-L 3.296875 68.296875 
-Q 6.796875 68 18.09375 68 
-Q 29.296875 68 32.796875 68.296875 
-L 32.796875 65.203125 
-L 30.40625 65.203125 
-Q 25 65.203125 23.75 64.296875 
-Q 22.5 63.40625 22.5 60.5 
-L 22.5 28.703125 
-L 53.796875 58.59375 
-Q 55.59375 60.203125 55.59375 61.90625 
-Q 55.59375 62.5 55.34375 63.140625 
-Q 55.09375 63.796875 53.9375 64.5 
-Q 52.796875 65.203125 51 65.203125 
-L 51 68.296875 
-Q 54.40625 68 63.703125 68 
-Q 69.59375 68 72.203125 68.296875 
-L 72.203125 65.203125 
-Q 63.90625 65.09375 58 59.203125 
-L 40 41.90625 
-L 63.09375 7.90625 
-Q 65.203125 4.796875 67.25 3.9375 
-Q 69.296875 3.09375 73.59375 3.09375 
-L 73.59375 0 
-Q 67.296875 0.296875 62.09375 0.296875 
-Q 51.296875 0.296875 47.796875 0 
-L 47.796875 3.09375 
-Q 53.703125 3.09375 53.703125 6.09375 
-Q 53.703125 7.203125 52.203125 9.5 
-L 34.09375 36.296875 
-L 22.5 25.296875 
-L 22.5 7.796875 
-Q 22.5 4.90625 23.75 4 
-Q 25 3.09375 30.40625 3.09375 
-L 32.796875 3.09375 
-L 32.796875 0 
-Q 29.296875 0.296875 18 0.296875 
-Q 6.796875 0.296875 3.296875 0 
-z
-" id="CMUSerif-Roman-4b"/>
-     </defs>
-     <g transform="translate(8.72 141.042)rotate(-90)scale(0.1 -0.1)">
-      <use xlink:href="#CMUSerif-Roman-54"/>
-      <use transform="translate(72.199982 0)" xlink:href="#CMUSerif-Roman-65"/>
-      <use transform="translate(116.599976 0)" xlink:href="#CMUSerif-Roman-6d"/>
-      <use transform="translate(199.899963 0)" xlink:href="#CMUSerif-Roman-70"/>
-      <use transform="translate(255.399948 0)" xlink:href="#CMUSerif-Roman-65"/>
-      <use transform="translate(299.799942 0)" xlink:href="#CMUSerif-Roman-72"/>
-      <use transform="translate(338.899933 0)" xlink:href="#CMUSerif-Roman-61"/>
-      <use transform="translate(388.899918 0)" xlink:href="#CMUSerif-Roman-74"/>
-      <use transform="translate(427.699905 0)" xlink:href="#CMUSerif-Roman-75"/>
-      <use transform="translate(483.19989 0)" xlink:href="#CMUSerif-Roman-72"/>
-      <use transform="translate(522.299881 0)" xlink:href="#CMUSerif-Roman-65"/>
-      <use transform="translate(566.699875 0)" xlink:href="#CMUSerif-Roman-20"/>
-      <use transform="translate(599.999863 0)" xlink:href="#Cmmi10-54"/>
-      <use transform="translate(658.3983 0)" xlink:href="#CMUSerif-Roman-20"/>
-      <use transform="translate(691.698288 0)" xlink:href="#CMUSerif-Roman-5b"/>
-      <use transform="translate(719.498276 0)" xlink:href="#CMUSerif-Roman-4b"/>
-      <use transform="translate(797.198257 0)" xlink:href="#CMUSerif-Roman-5d"/>
-     </g>
-    </g>
-   </g>
-   <g id="line2d_43">
-    <path clip-path="url(#p5e3fe66b1e)" d="M 77.714425 70.434351 
-L 227.8 70.434351 
-L 227.8 70.434351 
-" style="fill:none;stroke:#000000;stroke-linecap:square;"/>
-   </g>
-   <g id="line2d_44">
-    <path clip-path="url(#p5e3fe66b1e)" d="M 146.991516 85.113175 
-L 227.8 9.275054 
-L 227.8 9.275054 
-" style="fill:none;stroke:#000000;stroke-linecap:square;"/>
-   </g>
-   <g id="line2d_45">
-    <path clip-path="url(#p5e3fe66b1e)" d="M -1 70.434351 
-L 77.714425 70.434351 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_46">
-    <path clip-path="url(#p5e3fe66b1e)" d="M -1 85.113175 
-L 146.991516 85.113175 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_47">
-    <path clip-path="url(#p5e3fe66b1e)" d="M 77.714425 197.316 
-L 77.714425 70.434351 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_48">
-    <path clip-path="url(#p5e3fe66b1e)" d="M 146.991516 197.316 
-L 146.991516 85.113175 
-" style="fill:none;stroke:#000000;stroke-dasharray:0.6,0.99;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_49">
-    <path clip-path="url(#p5e3fe66b1e)" d="M 68.658545 43.904505 
-L 103.297091 43.904505 
-" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="line2d_50">
-    <path clip-path="url(#p5e3fe66b1e)" d="M 155.254909 57.122263 
-L 189.893455 24.614263 
-" style="fill:none;stroke:#000000;stroke-dasharray:2.22,0.96;stroke-dashoffset:0;stroke-width:0.6;"/>
-   </g>
-   <g id="patch_3">
-    <path d="M 34.02 197.316 
-L 34.02 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_4">
-    <path d="M 224.532 197.316 
-L 224.532 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_5">
-    <path d="M 34.02 197.316 
-L 224.532 197.316 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="patch_6">
-    <path d="M 34.02 2.268 
-L 224.532 2.268 
-" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
-   </g>
-   <g id="text_12">
-    <!-- $n_{\rm H}$^Cool_gamma_effective - 1 -->
-    <defs>
-     <path d="M 34.421875 72.90625 
-L 56.6875 45.703125 
-L 48 45.703125 
-L 30.078125 64.984375 
-L 12.203125 45.703125 
-L 3.515625 45.703125 
-L 25.78125 72.90625 
-z
-" id="DejaVuSansMono-5e"/>
-     <path d="M 52.390625 2.59375 
-Q 48.640625 0.59375 44.671875 -0.40625 
-Q 40.71875 -1.421875 36.28125 -1.421875 
-Q 22.265625 -1.421875 14.515625 8.484375 
-Q 6.78125 18.40625 6.78125 36.375 
-Q 6.78125 54.25 14.5625 64.234375 
-Q 22.359375 74.21875 36.28125 74.21875 
-Q 40.71875 74.21875 44.671875 73.21875 
-Q 48.640625 72.21875 52.390625 70.21875 
-L 52.390625 60.109375 
-Q 48.78125 63.09375 44.625 64.65625 
-Q 40.484375 66.21875 36.28125 66.21875 
-Q 26.65625 66.21875 21.875 58.796875 
-Q 17.09375 51.375 17.09375 36.375 
-Q 17.09375 21.4375 21.875 14.015625 
-Q 26.65625 6.59375 36.28125 6.59375 
-Q 40.578125 6.59375 44.703125 8.15625 
-Q 48.828125 9.71875 52.390625 12.703125 
-z
-" id="DejaVuSansMono-43"/>
-     <path d="M 30.078125 48.390625 
-Q 23.25 48.390625 19.734375 43.0625 
-Q 16.21875 37.75 16.21875 27.296875 
-Q 16.21875 16.890625 19.734375 11.546875 
-Q 23.25 6.203125 30.078125 6.203125 
-Q 36.96875 6.203125 40.484375 11.546875 
-Q 44 16.890625 44 27.296875 
-Q 44 37.75 40.484375 43.0625 
-Q 36.96875 48.390625 30.078125 48.390625 
-z
-M 30.078125 56 
-Q 41.453125 56 47.484375 48.625 
-Q 53.515625 41.265625 53.515625 27.296875 
-Q 53.515625 13.28125 47.5 5.921875 
-Q 41.5 -1.421875 30.078125 -1.421875 
-Q 18.703125 -1.421875 12.6875 5.921875 
-Q 6.6875 13.28125 6.6875 27.296875 
-Q 6.6875 41.265625 12.6875 48.625 
-Q 18.703125 56 30.078125 56 
-z
-" id="DejaVuSansMono-6f"/>
-     <path d="M 31.203125 19.828125 
-Q 31.203125 13.765625 33.421875 10.6875 
-Q 35.640625 7.625 39.984375 7.625 
-L 50.484375 7.625 
-L 50.484375 0 
-L 39.109375 0 
-Q 31.0625 0 26.640625 5.171875 
-Q 22.21875 10.359375 22.21875 19.828125 
-L 22.21875 69.484375 
-L 7.8125 69.484375 
-L 7.8125 76.515625 
-L 31.203125 76.515625 
-z
-" id="DejaVuSansMono-6c"/>
-     <path d="M 60.203125 -19.671875 
-L 60.203125 -23.578125 
-L 0 -23.578125 
-L 0 -19.671875 
-z
-" id="DejaVuSansMono-5f"/>
-     <path d="M 41.890625 27.78125 
-Q 41.890625 37.890625 38.59375 43.140625 
-Q 35.296875 48.390625 29 48.390625 
-Q 22.40625 48.390625 18.9375 43.140625 
-Q 15.484375 37.890625 15.484375 27.78125 
-Q 15.484375 17.671875 18.96875 12.375 
-Q 22.46875 7.078125 29.109375 7.078125 
-Q 35.296875 7.078125 38.59375 12.390625 
-Q 41.890625 17.71875 41.890625 27.78125 
-z
-M 50.875 3.515625 
-Q 50.875 -8.796875 45.0625 -15.140625 
-Q 39.265625 -21.484375 27.984375 -21.484375 
-Q 24.265625 -21.484375 20.203125 -20.796875 
-Q 16.15625 -20.125 12.109375 -18.796875 
-L 12.109375 -9.90625 
-Q 16.890625 -12.15625 20.796875 -13.234375 
-Q 24.703125 -14.3125 27.984375 -14.3125 
-Q 35.25 -14.3125 38.5625 -10.34375 
-Q 41.890625 -6.390625 41.890625 2.203125 
-L 41.890625 2.59375 
-L 41.890625 8.6875 
-Q 39.75 4.109375 36.03125 1.859375 
-Q 32.328125 -0.390625 27 -0.390625 
-Q 17.4375 -0.390625 11.71875 7.265625 
-Q 6 14.9375 6 27.78125 
-Q 6 40.671875 11.71875 48.328125 
-Q 17.4375 56 27 56 
-Q 32.28125 56 35.9375 53.90625 
-Q 39.59375 51.8125 41.890625 47.40625 
-L 41.890625 54.5 
-L 50.875 54.5 
-z
-" id="DejaVuSansMono-67"/>
-     <path d="M 34.28125 27.484375 
-L 31.296875 27.484375 
-Q 23.4375 27.484375 19.453125 24.71875 
-Q 15.484375 21.96875 15.484375 16.5 
-Q 15.484375 11.578125 18.453125 8.84375 
-Q 21.4375 6.109375 26.703125 6.109375 
-Q 34.125 6.109375 38.375 11.25 
-Q 42.625 16.40625 42.671875 25.484375 
-L 42.671875 27.484375 
-z
-M 51.703125 31.203125 
-L 51.703125 0 
-L 42.671875 0 
-L 42.671875 8.109375 
-Q 39.796875 3.21875 35.421875 0.890625 
-Q 31.0625 -1.421875 24.8125 -1.421875 
-Q 16.453125 -1.421875 11.46875 3.296875 
-Q 6.5 8.015625 6.5 15.921875 
-Q 6.5 25.046875 12.625 29.78125 
-Q 18.75 34.515625 30.609375 34.515625 
-L 42.671875 34.515625 
-L 42.671875 35.9375 
-Q 42.625 42.484375 39.34375 45.4375 
-Q 36.078125 48.390625 28.90625 48.390625 
-Q 24.3125 48.390625 19.625 47.0625 
-Q 14.9375 45.75 10.5 43.21875 
-L 10.5 52.203125 
-Q 15.484375 54.109375 20.046875 55.046875 
-Q 24.609375 56 28.90625 56 
-Q 35.6875 56 40.5 54 
-Q 45.3125 52 48.296875 48 
-Q 50.140625 45.5625 50.921875 41.96875 
-Q 51.703125 38.375 51.703125 31.203125 
-z
-" id="DejaVuSansMono-61"/>
-     <path d="M 33.015625 49.125 
-Q 34.671875 52.640625 37.234375 54.3125 
-Q 39.796875 56 43.40625 56 
-Q 50 56 52.703125 50.890625 
-Q 55.421875 45.796875 55.421875 31.6875 
-L 55.421875 0 
-L 47.21875 0 
-L 47.21875 31.296875 
-Q 47.21875 42.875 45.921875 45.671875 
-Q 44.625 48.484375 41.21875 48.484375 
-Q 37.3125 48.484375 35.859375 45.484375 
-Q 34.421875 42.484375 34.421875 31.296875 
-L 34.421875 0 
-L 26.21875 0 
-L 26.21875 31.296875 
-Q 26.21875 43.015625 24.828125 45.75 
-Q 23.4375 48.484375 19.828125 48.484375 
-Q 16.265625 48.484375 14.875 45.484375 
-Q 13.484375 42.484375 13.484375 31.296875 
-L 13.484375 0 
-L 5.328125 0 
-L 5.328125 54.6875 
-L 13.484375 54.6875 
-L 13.484375 50 
-Q 15.09375 52.9375 17.5 54.46875 
-Q 19.921875 56 23 56 
-Q 26.703125 56 29.171875 54.296875 
-Q 31.640625 52.59375 33.015625 49.125 
-z
-" id="DejaVuSansMono-6d"/>
-     <path d="M 54.296875 29.59375 
-L 54.296875 25.203125 
-L 15.375 25.203125 
-L 15.375 24.90625 
-Q 15.375 15.96875 20.03125 11.078125 
-Q 24.703125 6.203125 33.203125 6.203125 
-Q 37.5 6.203125 42.1875 7.5625 
-Q 46.875 8.9375 52.203125 11.71875 
-L 52.203125 2.78125 
-Q 47.078125 0.6875 42.3125 -0.359375 
-Q 37.546875 -1.421875 33.109375 -1.421875 
-Q 20.359375 -1.421875 13.171875 6.21875 
-Q 6 13.875 6 27.296875 
-Q 6 40.375 13.03125 48.1875 
-Q 20.0625 56 31.78125 56 
-Q 42.234375 56 48.265625 48.921875 
-Q 54.296875 41.84375 54.296875 29.59375 
-z
-M 45.3125 32.234375 
-Q 45.125 40.140625 41.578125 44.265625 
-Q 38.03125 48.390625 31.390625 48.390625 
-Q 24.90625 48.390625 20.703125 44.09375 
-Q 16.5 39.796875 15.71875 32.171875 
-z
-" id="DejaVuSansMono-65"/>
-     <path d="M 51.90625 75.984375 
-L 51.90625 68.5 
-L 41.703125 68.5 
-Q 36.859375 68.5 34.984375 66.515625 
-Q 33.109375 64.546875 33.109375 59.515625 
-L 33.109375 54.6875 
-L 51.90625 54.6875 
-L 51.90625 47.703125 
-L 33.109375 47.703125 
-L 33.109375 0 
-L 24.125 0 
-L 24.125 47.703125 
-L 9.515625 47.703125 
-L 9.515625 54.6875 
-L 24.125 54.6875 
-L 24.125 58.5 
-Q 24.125 67.484375 28.25 71.734375 
-Q 32.375 75.984375 41.109375 75.984375 
-z
-" id="DejaVuSansMono-66"/>
-     <path d="M 51.8125 2.78125 
-Q 48.1875 0.6875 44.359375 -0.359375 
-Q 40.53125 -1.421875 36.53125 -1.421875 
-Q 23.828125 -1.421875 16.671875 6.1875 
-Q 9.515625 13.8125 9.515625 27.296875 
-Q 9.515625 40.765625 16.671875 48.375 
-Q 23.828125 56 36.53125 56 
-Q 40.484375 56 44.234375 54.96875 
-Q 48 53.953125 51.8125 51.8125 
-L 51.8125 42.390625 
-Q 48.25 45.5625 44.65625 46.96875 
-Q 41.0625 48.390625 36.53125 48.390625 
-Q 28.078125 48.390625 23.53125 42.921875 
-Q 19 37.453125 19 27.296875 
-Q 19 17.1875 23.5625 11.6875 
-Q 28.125 6.203125 36.53125 6.203125 
-Q 41.21875 6.203125 44.921875 7.640625 
-Q 48.640625 9.078125 51.8125 12.109375 
-z
-" id="DejaVuSansMono-63"/>
-     <path d="M 29.984375 70.21875 
-L 29.984375 54.6875 
-L 50.390625 54.6875 
-L 50.390625 47.703125 
-L 29.984375 47.703125 
-L 29.984375 18.015625 
-Q 29.984375 11.96875 32.28125 9.5625 
-Q 34.578125 7.171875 40.28125 7.171875 
-L 50.390625 7.171875 
-L 50.390625 0 
-L 39.40625 0 
-Q 29.296875 0 25.140625 4.046875 
-Q 21 8.109375 21 18.015625 
-L 21 47.703125 
-L 6.390625 47.703125 
-L 6.390625 54.6875 
-L 21 54.6875 
-L 21 70.21875 
-z
-" id="DejaVuSansMono-74"/>
-     <path d="M 12.5 54.6875 
-L 35.5 54.6875 
-L 35.5 6.984375 
-L 53.328125 6.984375 
-L 53.328125 0 
-L 8.6875 0 
-L 8.6875 6.984375 
-L 26.515625 6.984375 
-L 26.515625 47.703125 
-L 12.5 47.703125 
-z
-M 26.515625 75.984375 
-L 35.5 75.984375 
-L 35.5 64.59375 
-L 26.515625 64.59375 
-z
-" id="DejaVuSansMono-69"/>
-     <path d="M 4.890625 54.6875 
-L 14.203125 54.6875 
-L 30.078125 8.796875 
-L 46 54.6875 
-L 55.328125 54.6875 
-L 35.890625 0 
-L 24.3125 0 
-z
-" id="DejaVuSansMono-76"/>
-     <path id="DejaVuSansMono-20"/>
-     <path d="M 17.390625 31.390625 
-L 42.828125 31.390625 
-L 42.828125 23.390625 
-L 17.390625 23.390625 
-z
-" id="DejaVuSansMono-2d"/>
-     <path d="M 13.1875 8.296875 
-L 28.515625 8.296875 
-L 28.515625 64.015625 
-L 12.015625 60.296875 
-L 12.015625 69.28125 
-L 28.421875 72.90625 
-L 38.28125 72.90625 
-L 38.28125 8.296875 
-L 53.421875 8.296875 
-L 53.421875 0 
-L 13.1875 0 
-z
-" id="DejaVuSansMono-31"/>
-    </defs>
-    <g transform="translate(60.395152 52.770701)scale(0.055 -0.055)">
-     <use transform="translate(0 0.484375)" xlink:href="#Cmmi10-6e"/>
-     <use transform="translate(60.009766 -16.521875)scale(0.7)" xlink:href="#Cmr10-48"/>
-     <use transform="translate(118.887109 0.484375)" xlink:href="#DejaVuSansMono-5e"/>
-     <use transform="translate(179.092187 0.484375)" xlink:href="#DejaVuSansMono-43"/>
-     <use transform="translate(239.297266 0.484375)" xlink:href="#DejaVuSansMono-6f"/>
-     <use transform="translate(299.502344 0.484375)" xlink:href="#DejaVuSansMono-6f"/>
-     <use transform="translate(359.707422 0.484375)" xlink:href="#DejaVuSansMono-6c"/>
-     <use transform="translate(419.9125 0.484375)" xlink:href="#DejaVuSansMono-5f"/>
-     <use transform="translate(480.117578 0.484375)" xlink:href="#DejaVuSansMono-67"/>
-     <use transform="translate(540.322656 0.484375)" xlink:href="#DejaVuSansMono-61"/>
-     <use transform="translate(600.527734 0.484375)" xlink:href="#DejaVuSansMono-6d"/>
-     <use transform="translate(660.732813 0.484375)" xlink:href="#DejaVuSansMono-6d"/>
-     <use transform="translate(720.937891 0.484375)" xlink:href="#DejaVuSansMono-61"/>
-     <use transform="translate(781.142969 0.484375)" xlink:href="#DejaVuSansMono-5f"/>
-     <use transform="translate(841.348047 0.484375)" xlink:href="#DejaVuSansMono-65"/>
-     <use transform="translate(901.553125 0.484375)" xlink:href="#DejaVuSansMono-66"/>
-     <use transform="translate(961.758203 0.484375)" xlink:href="#DejaVuSansMono-66"/>
-     <use transform="translate(1021.963281 0.484375)" xlink:href="#DejaVuSansMono-65"/>
-     <use transform="translate(1082.168359 0.484375)" xlink:href="#DejaVuSansMono-63"/>
-     <use transform="translate(1142.373437 0.484375)" xlink:href="#DejaVuSansMono-74"/>
-     <use transform="translate(1202.578516 0.484375)" xlink:href="#DejaVuSansMono-69"/>
-     <use transform="translate(1262.783594 0.484375)" xlink:href="#DejaVuSansMono-76"/>
-     <use transform="translate(1322.988672 0.484375)" xlink:href="#DejaVuSansMono-65"/>
-     <use transform="translate(1383.19375 0.484375)" xlink:href="#DejaVuSansMono-20"/>
-     <use transform="translate(1443.398828 0.484375)" xlink:href="#DejaVuSansMono-2d"/>
-     <use transform="translate(1503.603906 0.484375)" xlink:href="#DejaVuSansMono-20"/>
-     <use transform="translate(1563.808984 0.484375)" xlink:href="#DejaVuSansMono-31"/>
-    </g>
-   </g>
-   <g id="text_13">
-    <!-- $n_{\rm H}$^Jeans_gamma_effective - 1 -->
-    <defs>
-     <path d="M 5.328125 2.984375 
-L 5.328125 14.5 
-Q 9.765625 10.546875 14.5 8.5625 
-Q 19.234375 6.59375 24.3125 6.59375 
-Q 31.296875 6.59375 34.046875 10.234375 
-Q 36.8125 13.875 36.8125 23.78125 
-L 36.8125 64.59375 
-L 18.21875 64.59375 
-L 18.21875 72.90625 
-L 46.6875 72.90625 
-L 46.6875 23.78125 
-Q 46.6875 10.015625 41.53125 4.296875 
-Q 36.375 -1.421875 24.3125 -1.421875 
-Q 19.625 -1.421875 14.984375 -0.34375 
-Q 10.359375 0.734375 5.328125 2.984375 
-z
-" id="DejaVuSansMono-4a"/>
-     <path d="M 51.3125 33.890625 
-L 51.3125 0 
-L 42.28125 0 
-L 42.28125 33.890625 
-Q 42.28125 41.265625 39.6875 44.71875 
-Q 37.109375 48.1875 31.59375 48.1875 
-Q 25.296875 48.1875 21.890625 43.71875 
-Q 18.5 39.265625 18.5 30.90625 
-L 18.5 0 
-L 9.515625 0 
-L 9.515625 54.6875 
-L 18.5 54.6875 
-L 18.5 46.484375 
-Q 20.90625 51.171875 25 53.578125 
-Q 29.109375 56 34.71875 56 
-Q 43.0625 56 47.1875 50.5 
-Q 51.3125 45.015625 51.3125 33.890625 
-z
-" id="DejaVuSansMono-6e"/>
-     <path d="M 47.515625 52.78125 
-L 47.515625 44 
-Q 43.65625 46.234375 39.75 47.359375 
-Q 35.84375 48.484375 31.78125 48.484375 
-Q 25.6875 48.484375 22.671875 46.5 
-Q 19.671875 44.53125 19.671875 40.484375 
-Q 19.671875 36.8125 21.921875 35 
-Q 24.171875 33.203125 33.109375 31.5 
-L 36.71875 30.8125 
-Q 43.40625 29.546875 46.84375 25.734375 
-Q 50.296875 21.921875 50.296875 15.828125 
-Q 50.296875 7.71875 44.53125 3.140625 
-Q 38.765625 -1.421875 28.515625 -1.421875 
-Q 24.46875 -1.421875 20.015625 -0.5625 
-Q 15.578125 0.296875 10.40625 2 
-L 10.40625 11.28125 
-Q 15.4375 8.6875 20.015625 7.390625 
-Q 24.609375 6.109375 28.71875 6.109375 
-Q 34.671875 6.109375 37.9375 8.515625 
-Q 41.21875 10.9375 41.21875 15.28125 
-Q 41.21875 21.53125 29.25 23.921875 
-L 28.859375 24.03125 
-L 25.484375 24.703125 
-Q 17.71875 26.21875 14.15625 29.8125 
-Q 10.59375 33.40625 10.59375 39.59375 
-Q 10.59375 47.46875 15.90625 51.734375 
-Q 21.234375 56 31.109375 56 
-Q 35.5 56 39.546875 55.1875 
-Q 43.609375 54.390625 47.515625 52.78125 
-z
-" id="DejaVuSansMono-73"/>
-    </defs>
-    <g transform="translate(151.213631 69.615498)rotate(-43)scale(0.055 -0.055)">
-     <use transform="translate(0 0.015625)" xlink:href="#Cmmi10-6e"/>
-     <use transform="translate(60.009766 -16.990625)scale(0.7)" xlink:href="#Cmr10-48"/>
-     <use transform="translate(118.887109 0.015625)" xlink:href="#DejaVuSansMono-5e"/>
-     <use transform="translate(179.092187 0.015625)" xlink:href="#DejaVuSansMono-4a"/>
-     <use transform="translate(239.297266 0.015625)" xlink:href="#DejaVuSansMono-65"/>
-     <use transform="translate(299.502344 0.015625)" xlink:href="#DejaVuSansMono-61"/>
-     <use transform="translate(359.707422 0.015625)" xlink:href="#DejaVuSansMono-6e"/>
-     <use transform="translate(419.9125 0.015625)" xlink:href="#DejaVuSansMono-73"/>
-     <use transform="translate(480.117578 0.015625)" xlink:href="#DejaVuSansMono-5f"/>
-     <use transform="translate(540.322656 0.015625)" xlink:href="#DejaVuSansMono-67"/>
-     <use transform="translate(600.527734 0.015625)" xlink:href="#DejaVuSansMono-61"/>
-     <use transform="translate(660.732813 0.015625)" xlink:href="#DejaVuSansMono-6d"/>
-     <use transform="translate(720.937891 0.015625)" xlink:href="#DejaVuSansMono-6d"/>
-     <use transform="translate(781.142969 0.015625)" xlink:href="#DejaVuSansMono-61"/>
-     <use transform="translate(841.348047 0.015625)" xlink:href="#DejaVuSansMono-5f"/>
-     <use transform="translate(901.553125 0.015625)" xlink:href="#DejaVuSansMono-65"/>
-     <use transform="translate(961.758203 0.015625)" xlink:href="#DejaVuSansMono-66"/>
-     <use transform="translate(1021.963281 0.015625)" xlink:href="#DejaVuSansMono-66"/>
-     <use transform="translate(1082.168359 0.015625)" xlink:href="#DejaVuSansMono-65"/>
-     <use transform="translate(1142.373437 0.015625)" xlink:href="#DejaVuSansMono-63"/>
-     <use transform="translate(1202.578516 0.015625)" xlink:href="#DejaVuSansMono-74"/>
-     <use transform="translate(1262.783594 0.015625)" xlink:href="#DejaVuSansMono-69"/>
-     <use transform="translate(1322.988672 0.015625)" xlink:href="#DejaVuSansMono-76"/>
-     <use transform="translate(1383.19375 0.015625)" xlink:href="#DejaVuSansMono-65"/>
-     <use transform="translate(1443.398828 0.015625)" xlink:href="#DejaVuSansMono-20"/>
-     <use transform="translate(1503.603906 0.015625)" xlink:href="#DejaVuSansMono-2d"/>
-     <use transform="translate(1563.808984 0.015625)" xlink:href="#DejaVuSansMono-20"/>
-     <use transform="translate(1624.014062 0.015625)" xlink:href="#DejaVuSansMono-31"/>
-    </g>
-   </g>
-   <g id="text_14">
-    <!-- Cool_density_threshold_H_p_cm3 -->
-    <defs>
-     <path d="M 41.890625 47.703125 
-L 41.890625 75.984375 
-L 50.875 75.984375 
-L 50.875 0 
-L 41.890625 0 
-L 41.890625 6.890625 
-Q 39.65625 2.828125 35.90625 0.703125 
-Q 32.171875 -1.421875 27.296875 -1.421875 
-Q 17.390625 -1.421875 11.6875 6.265625 
-Q 6 13.96875 6 27.484375 
-Q 6 40.828125 11.71875 48.40625 
-Q 17.4375 56 27.296875 56 
-Q 32.234375 56 35.984375 53.875 
-Q 39.75 51.765625 41.890625 47.703125 
-z
-M 15.484375 27.296875 
-Q 15.484375 16.84375 18.796875 11.515625 
-Q 22.125 6.203125 28.609375 6.203125 
-Q 35.109375 6.203125 38.5 11.5625 
-Q 41.890625 16.9375 41.890625 27.296875 
-Q 41.890625 37.703125 38.5 43.046875 
-Q 35.109375 48.390625 28.609375 48.390625 
-Q 22.125 48.390625 18.796875 43.0625 
-Q 15.484375 37.75 15.484375 27.296875 
-z
-" id="DejaVuSansMono-64"/>
-     <path d="M 41.890625 17.578125 
-Q 39.65625 11.859375 36.1875 2.546875 
-Q 31.34375 -10.359375 29.6875 -13.1875 
-Q 27.4375 -17 24.0625 -18.890625 
-Q 20.703125 -20.796875 16.21875 -20.796875 
-L 8.984375 -20.796875 
-L 8.984375 -13.28125 
-L 14.3125 -13.28125 
-Q 18.265625 -13.28125 20.5 -10.984375 
-Q 22.75 -8.6875 26.21875 0.875 
-L 5.078125 54.6875 
-L 14.59375 54.6875 
-L 30.8125 11.921875 
-L 46.78125 54.6875 
-L 56.296875 54.6875 
-z
-" id="DejaVuSansMono-79"/>
-     <path d="M 51.3125 33.890625 
-L 51.3125 0 
-L 42.28125 0 
-L 42.28125 33.890625 
-Q 42.28125 41.265625 39.6875 44.71875 
-Q 37.109375 48.1875 31.59375 48.1875 
-Q 25.296875 48.1875 21.890625 43.71875 
-Q 18.5 39.265625 18.5 30.90625 
-L 18.5 0 
-L 9.515625 0 
-L 9.515625 75.984375 
-L 18.5 75.984375 
-L 18.5 46.484375 
-Q 20.90625 51.171875 25 53.578125 
-Q 29.109375 56 34.71875 56 
-Q 43.0625 56 47.1875 50.5 
-Q 51.3125 45.015625 51.3125 33.890625 
-z
-" id="DejaVuSansMono-68"/>
-     <path d="M 56.390625 43.40625 
-Q 53.515625 45.65625 50.53125 46.671875 
-Q 47.5625 47.703125 44 47.703125 
-Q 35.59375 47.703125 31.140625 42.421875 
-Q 26.703125 37.15625 26.703125 27.203125 
-L 26.703125 0 
-L 17.671875 0 
-L 17.671875 54.6875 
-L 26.703125 54.6875 
-L 26.703125 44 
-Q 28.953125 49.8125 33.609375 52.90625 
-Q 38.28125 56 44.671875 56 
-Q 48 56 50.875 55.171875 
-Q 53.765625 54.34375 56.390625 52.59375 
-z
-" id="DejaVuSansMono-72"/>
-     <path d="M 6.6875 72.90625 
-L 16.609375 72.90625 
-L 16.609375 43.015625 
-L 43.609375 43.015625 
-L 43.609375 72.90625 
-L 53.515625 72.90625 
-L 53.515625 0 
-L 43.609375 0 
-L 43.609375 34.71875 
-L 16.609375 34.71875 
-L 16.609375 0 
-L 6.6875 0 
-z
-" id="DejaVuSansMono-48"/>
-     <path d="M 18.3125 6.890625 
-L 18.3125 -20.796875 
-L 9.28125 -20.796875 
-L 9.28125 54.6875 
-L 18.3125 54.6875 
-L 18.3125 47.703125 
-Q 20.5625 51.765625 24.296875 53.875 
-Q 28.03125 56 32.90625 56 
-Q 42.828125 56 48.46875 48.328125 
-Q 54.109375 40.671875 54.109375 27.09375 
-Q 54.109375 13.765625 48.4375 6.171875 
-Q 42.78125 -1.421875 32.90625 -1.421875 
-Q 27.9375 -1.421875 24.1875 0.703125 
-Q 20.453125 2.828125 18.3125 6.890625 
-z
-M 44.671875 27.296875 
-Q 44.671875 37.75 41.375 43.0625 
-Q 38.09375 48.390625 31.59375 48.390625 
-Q 25.046875 48.390625 21.671875 43.046875 
-Q 18.3125 37.703125 18.3125 27.296875 
-Q 18.3125 16.9375 21.671875 11.5625 
-Q 25.046875 6.203125 31.59375 6.203125 
-Q 38.09375 6.203125 41.375 11.515625 
-Q 44.671875 16.84375 44.671875 27.296875 
-z
-" id="DejaVuSansMono-70"/>
-     <path d="M 37.890625 39.015625 
-Q 45.0625 37.109375 48.875 32.25 
-Q 52.6875 27.390625 52.6875 20.125 
-Q 52.6875 10.0625 45.921875 4.3125 
-Q 39.15625 -1.421875 27.203125 -1.421875 
-Q 22.171875 -1.421875 16.9375 -0.484375 
-Q 11.71875 0.4375 6.6875 2.203125 
-L 6.6875 12.015625 
-Q 11.671875 9.421875 16.5 8.15625 
-Q 21.34375 6.890625 26.125 6.890625 
-Q 34.234375 6.890625 38.578125 10.546875 
-Q 42.921875 14.203125 42.921875 21.09375 
-Q 42.921875 27.4375 38.578125 31.171875 
-Q 34.234375 34.90625 26.8125 34.90625 
-L 19.28125 34.90625 
-L 19.28125 43.015625 
-L 26.8125 43.015625 
-Q 33.59375 43.015625 37.40625 45.984375 
-Q 41.21875 48.96875 41.21875 54.296875 
-Q 41.21875 59.90625 37.671875 62.90625 
-Q 34.125 65.921875 27.59375 65.921875 
-Q 23.25 65.921875 18.609375 64.9375 
-Q 13.96875 63.96875 8.890625 62.015625 
-L 8.890625 71.09375 
-Q 14.796875 72.65625 19.40625 73.4375 
-Q 24.03125 74.21875 27.59375 74.21875 
-Q 38.234375 74.21875 44.609375 68.875 
-Q 50.984375 63.53125 50.984375 54.6875 
-Q 50.984375 48.6875 47.625 44.671875 
-Q 44.28125 40.671875 37.890625 39.015625 
-z
-" id="DejaVuSansMono-33"/>
-    </defs>
-    <g transform="translate(76.031817 194.356252)rotate(-90)scale(0.055 -0.055)">
-     <use xlink:href="#DejaVuSansMono-43"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-6c"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-64"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-65"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-6e"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-73"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-69"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-74"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-79"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-74"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-68"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-72"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-65"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-73"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-68"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-6c"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-64"/>
-     <use x="1324.511719" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1384.716797" xlink:href="#DejaVuSansMono-48"/>
-     <use x="1444.921875" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1505.126953" xlink:href="#DejaVuSansMono-70"/>
-     <use x="1565.332031" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1625.537109" xlink:href="#DejaVuSansMono-63"/>
-     <use x="1685.742188" xlink:href="#DejaVuSansMono-6d"/>
-     <use x="1745.947266" xlink:href="#DejaVuSansMono-33"/>
-    </g>
-   </g>
-   <g id="text_15">
-    <!-- Jeans_density_threshold_H_p_cm3 -->
-    <g transform="translate(145.308908 194.356252)rotate(-90)scale(0.055 -0.055)">
-     <use xlink:href="#DejaVuSansMono-4a"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-65"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-61"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-6e"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-73"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-64"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-65"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-6e"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-73"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-69"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-74"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-79"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-74"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-68"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-72"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-65"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-73"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-68"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-6c"/>
-     <use x="1324.511719" xlink:href="#DejaVuSansMono-64"/>
-     <use x="1384.716797" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1444.921875" xlink:href="#DejaVuSansMono-48"/>
-     <use x="1505.126953" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1565.332031" xlink:href="#DejaVuSansMono-70"/>
-     <use x="1625.537109" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1685.742188" xlink:href="#DejaVuSansMono-63"/>
-     <use x="1745.947266" xlink:href="#DejaVuSansMono-6d"/>
-     <use x="1806.152344" xlink:href="#DejaVuSansMono-33"/>
-    </g>
-   </g>
-   <g id="text_16">
-    <!-- Cool_temperature_norm_K -->
-    <defs>
-     <path d="M 9.515625 20.703125 
-L 9.515625 54.59375 
-L 18.5 54.59375 
-L 18.5 20.703125 
-Q 18.5 13.328125 21.109375 9.859375 
-Q 23.734375 6.390625 29.203125 6.390625 
-Q 35.546875 6.390625 38.90625 10.859375 
-Q 42.28125 15.328125 42.28125 23.6875 
-L 42.28125 54.59375 
-L 51.3125 54.59375 
-L 51.3125 0 
-L 42.28125 0 
-L 42.28125 8.203125 
-Q 39.890625 3.46875 35.765625 1.015625 
-Q 31.640625 -1.421875 26.125 -1.421875 
-Q 17.71875 -1.421875 13.609375 4.078125 
-Q 9.515625 9.578125 9.515625 20.703125 
-z
-" id="DejaVuSansMono-75"/>
-     <path d="M 6.6875 72.90625 
-L 16.609375 72.90625 
-L 16.609375 40.484375 
-L 47.40625 72.90625 
-L 58.984375 72.90625 
-L 30.609375 43.109375 
-L 59.8125 0 
-L 47.90625 0 
-L 24.125 36.53125 
-L 16.609375 28.515625 
-L 16.609375 0 
-L 6.6875 0 
-z
-" id="DejaVuSansMono-4b"/>
-    </defs>
-    <g transform="translate(37.862259 67.119164)scale(0.055 -0.055)">
-     <use xlink:href="#DejaVuSansMono-43"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-6c"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-74"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-65"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-6d"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-70"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-65"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-72"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-61"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-74"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-75"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-72"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-65"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-6e"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-72"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-6d"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1324.511719" xlink:href="#DejaVuSansMono-4b"/>
-    </g>
-   </g>
-   <g id="text_17">
-    <!-- Jeans_temperature_norm_K -->
-    <g transform="translate(37.862259 81.797988)scale(0.055 -0.055)">
-     <use xlink:href="#DejaVuSansMono-4a"/>
-     <use x="60.205078" xlink:href="#DejaVuSansMono-65"/>
-     <use x="120.410156" xlink:href="#DejaVuSansMono-61"/>
-     <use x="180.615234" xlink:href="#DejaVuSansMono-6e"/>
-     <use x="240.820312" xlink:href="#DejaVuSansMono-73"/>
-     <use x="301.025391" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="361.230469" xlink:href="#DejaVuSansMono-74"/>
-     <use x="421.435547" xlink:href="#DejaVuSansMono-65"/>
-     <use x="481.640625" xlink:href="#DejaVuSansMono-6d"/>
-     <use x="541.845703" xlink:href="#DejaVuSansMono-70"/>
-     <use x="602.050781" xlink:href="#DejaVuSansMono-65"/>
-     <use x="662.255859" xlink:href="#DejaVuSansMono-72"/>
-     <use x="722.460938" xlink:href="#DejaVuSansMono-61"/>
-     <use x="782.666016" xlink:href="#DejaVuSansMono-74"/>
-     <use x="842.871094" xlink:href="#DejaVuSansMono-75"/>
-     <use x="903.076172" xlink:href="#DejaVuSansMono-72"/>
-     <use x="963.28125" xlink:href="#DejaVuSansMono-65"/>
-     <use x="1023.486328" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1083.691406" xlink:href="#DejaVuSansMono-6e"/>
-     <use x="1143.896484" xlink:href="#DejaVuSansMono-6f"/>
-     <use x="1204.101562" xlink:href="#DejaVuSansMono-72"/>
-     <use x="1264.306641" xlink:href="#DejaVuSansMono-6d"/>
-     <use x="1324.511719" xlink:href="#DejaVuSansMono-5f"/>
-     <use x="1384.716797" xlink:href="#DejaVuSansMono-4b"/>
-    </g>
-   </g>
-  </g>
- </g>
- <defs>
-  <clipPath id="p5e3fe66b1e">
-   <rect height="195.048" width="190.512" x="34.02" y="2.268"/>
-  </clipPath>
- </defs>
-</svg>
diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py
index 88ca56d750bf716fea8b4bf72b70c2b598953ac7..e5c954127c0ed88aef8a277a1ea9a4dc1f8bff70 100644
--- a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py
+++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_EOS.py
@@ -1,3 +1,9 @@
+import os
+
+if os.path.exists("EAGLE_SF_EOS.svg"):
+    # do not generate plot again
+    exit()
+
 import matplotlib
 
 matplotlib.use("Agg")
diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py
index 26ae522947ecc9e75f6f287bafed0bb9acb9134a..26cda85bea458731906e80126b160626fbee2edd 100644
--- a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py
+++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_SF_Z_dep.py
@@ -1,3 +1,9 @@
+import os
+
+if os.path.exists("EAGLE_SF_Z_dep.svg"):
+    # do not generate plot again
+    exit()
+
 import matplotlib
 
 matplotlib.use("Agg")
diff --git a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py
index 00de0e382e1e7f04e7508703addea3464edccd43..66d38c2b47cbc6b984cc45f3ce316f3134a706e0 100644
--- a/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py
+++ b/doc/RTD/source/SubgridModels/EAGLE/plot_EAGLE_entropy_floor.py
@@ -1,3 +1,9 @@
+import os
+
+if os.path.exists("EAGLE_entropy_floor.svg"):
+    # do not generate plot again
+    exit()
+
 import matplotlib
 
 matplotlib.use("Agg")
diff --git a/doc/RTD/source/SubgridModels/index.rst b/doc/RTD/source/SubgridModels/index.rst
index 9dba18c3f5595dc3b2f8275c7610c9bb32ce2b43..d835e6baf442ed6988cafbe70c92fe5992f04ca2 100644
--- a/doc/RTD/source/SubgridModels/index.rst
+++ b/doc/RTD/source/SubgridModels/index.rst
@@ -17,3 +17,4 @@ be use as an empty canvas to be copied to create additional models.
    QuickLymanAlpha/index
    GEAR/index
    Basic/index	     
+   AGNSpinJets/index
diff --git a/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst b/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst
index f8163741477974bad0d7a54cafa0e73a8def2ba7..937421eb7e80e136c5d66a0b2706a9d104d94212 100644
--- a/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst
+++ b/doc/RTD/source/Task/adding_your_own_neighbour_loop.rst
@@ -482,7 +482,7 @@ Then we also need a ``runner_doiact_my_suff.c`` file where the functions declare
 ``runner_doiact_my_suff.h`` are defined by including them with ``FUNCTION`` defined::
 
 
-    #include "../config.h"
+    #include <config.h>
     /* other includes too... */
 
     /* Import the new interaction loop functions. */
diff --git a/doc/RTD/source/Task/current_dependencies.rst b/doc/RTD/source/Task/current_dependencies.rst
index 63b1cb62f7a2776e255da1f1c2ce408e09319133..47ff74c6afaab23a87079c9824b53c6a8d5f5479 100644
--- a/doc/RTD/source/Task/current_dependencies.rst
+++ b/doc/RTD/source/Task/current_dependencies.rst
@@ -68,3 +68,6 @@ As the hydrodynamics are described in :ref:`hydro`, we are only showing the grav
     In the second one, the gas particles tagged as "to be swallowed" are effectively swallowed.
     In the third one, the sink particles tagged as "to be swallowed" are effectively swallowed.
     This was done with SWIFT v0.9.0.
+
+For documentation on the radiative transfer tasking system, please refer to its 
+:ref:`own page <rt_task_system>`.
diff --git a/doc/RTD/source/conf.py b/doc/RTD/source/conf.py
index 24e89bdd11a2affec726838bf6e984687cb77e44..c14bfc6263af7bfe75e95f793638846d52b301e4 100644
--- a/doc/RTD/source/conf.py
+++ b/doc/RTD/source/conf.py
@@ -19,7 +19,7 @@
 # -- Project information -----------------------------------------------------
 
 project = "SWIFT: SPH With Inter-dependent Fine-grained Tasking"
-copyright = "2014-2021, SWIFT Collaboration"
+copyright = "2014-2022, SWIFT Collaboration"
 author = "SWIFT Team"
 
 # The short X.Y version
@@ -27,6 +27,20 @@ version = "0.9"
 # The full version, including alpha/beta/rc tags
 release = "0.9.0"
 
+# -- Find additional scripts to run as part of the documentation build -------
+import glob
+import os
+import subprocess
+
+additional_scripts = glob.glob("**/*.py", recursive=True)
+# remove this script
+additional_scripts.remove("conf.py")
+for additional_script in additional_scripts:
+    wdir, script = os.path.split(additional_script)
+    print(f"Running {additional_script}...")
+    status = subprocess.run(f"python3 {script}", shell=True, cwd=wdir)
+    if not status.returncode == 0:
+        raise RuntimeError(f"Could not run script!")
 
 # -- General configuration ---------------------------------------------------
 
diff --git a/examples/Cooling/CoolingBox/plotEnergy.py b/examples/Cooling/CoolingBox/plotEnergy.py
index 29b9a2594798da0a589faf18bbc8dd0295917d0d..64d34c6127244e6ed920cc7f4b44676f034a8904 100644
--- a/examples/Cooling/CoolingBox/plotEnergy.py
+++ b/examples/Cooling/CoolingBox/plotEnergy.py
@@ -1,38 +1,35 @@
-from h5py import File
-import numpy as np
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2022 Matthieu Schaller (schaller@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
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
 import matplotlib
-from glob import glob
 
 matplotlib.use("Agg")
 import matplotlib.pyplot as plt
+import numpy as np
+from glob import glob
+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": (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.0,
-}
-plt.rcParams.update(params)
-
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 # 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 = "./statistics.txt"
 
@@ -40,7 +37,7 @@ stats_filename = "./statistics.txt"
 snap_filename = "coolingBox_0000.hdf5"
 
 # Read the initial state of the gas
-f = File(snap_filename, "r")
+f = h5py.File(snap_filename, "r")
 
 # Read the units parameters from the snapshot
 units = f["InternalCodeUnits"]
@@ -88,7 +85,7 @@ N = len(files)
 temp_snap = np.zeros(N)
 time_snap_cgs = np.zeros(N)
 for i in range(N):
-    snap = File(files[i], "r")
+    snap = h5py.File(files[i], "r")
     u = snap["/PartType0/InternalEnergies"][:] * snap["/PartType0/Masses"][:]
     u = sum(u) / total_mass[0]
     temp_snap[i] = energyUnits(u)
@@ -97,7 +94,7 @@ for i in range(N):
 
 plt.figure()
 
-Myr_in_s = 3.15e13
+Myr_in_s = 1e6 * 365.25 * 24.0 * 60.0 * 60.0
 plt.plot(time / Myr_in_s, total_energy_cgs, "r-", lw=1.6, label="Gas total energy")
 # statistics and snapshots may not be at same timestep and frequency
 plt.plot(time_snap_cgs / Myr_in_s, temp_snap, "rD", ms=3)
@@ -114,4 +111,6 @@ 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.tight_layout()
+
 plt.savefig("energy.png", dpi=200)
diff --git a/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml b/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml
index 1b29e1376e47ad32beacaf9bfb5408b8ff4d3191..1f73ad57b5a7396d96402746d2296784bd4ec551 100644
--- a/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml
+++ b/examples/Cooling/CoolingHaloWithSpin/cooling_halo.yml
@@ -10,13 +10,16 @@ InternalUnitSystem:
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   10.   # The end time of the simulation (in internal units).
-  dt_min:     1e-5  # The minimal time-step size of the simulation (in internal units).
+  dt_min:     1e-8  # The minimal time-step size of the simulation (in internal units).
   dt_max:     1e-1  # The maximal time-step size of the simulation (in internal units).
 
 # Parameters governing the conserved quantities statistics
 Statistics:
   delta_time:          1e-2 # Time between statistics output
-  
+
+Scheduler:
+  tasks_per_cell:      200
+
 # Parameters governing the snapshots
 Snapshots:
   basename:            CoolingHalo  # Common part of the name of output files
@@ -39,8 +42,10 @@ IsothermalPotential:
   vrot:            200.   # Rotation speed of isothermal potential in internal units
   timestep_mult:   0.03   # Controls time step
   epsilon:         1.0    # Softening for the isothermal potential
+  useabspos:       0
+  position:        [0., 0., 0.]
 
 # Cooling parameters
 LambdaCooling:
-  lambda_nH2_cgs:              1e-22 # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
+  lambda_nH2_cgs:              1e-23  # Cooling rate divided by square Hydrogen number density (in cgs units [erg * s^-1 * cm^3])
   cooling_tstep_mult:          0.1    # Dimensionless pre-factor for the time-step condition
diff --git a/examples/Cooling/CoolingHaloWithSpin/density_profile.py b/examples/Cooling/CoolingHaloWithSpin/density_profile.py
index e9db59a6dd9d7e1dc2ef023756b3385663973703..a35f9b563dd287c1eb56eb93031c0ceada74df2a 100644
--- a/examples/Cooling/CoolingHaloWithSpin/density_profile.py
+++ b/examples/Cooling/CoolingHaloWithSpin/density_profile.py
@@ -30,8 +30,8 @@ unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]
 unit_time_cgs = unit_length_cgs / unit_velocity_cgs
 v_c = float(params.attrs["IsothermalPotential:vrot"])
 v_c_cgs = v_c * unit_velocity_cgs
-lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"])
-X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"])
+lambda_cgs = float(params.attrs["LambdaCooling:lambda_nH2_cgs"])
+X_H = float(params.attrs["SPH:H_mass_fraction"])
 header = f["Header"]
 N = header.attrs["NumPart_Total"][0]
 box_centre = np.array(header.attrs["BoxSize"])
diff --git a/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py b/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py
index 581a48482a0c0304a69325505aa9c248468fb12f..495564b2cd35ea8c9feeabfafa4fb08653b86e15 100644
--- a/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py
+++ b/examples/Cooling/CoolingHaloWithSpin/internal_energy_profile.py
@@ -51,8 +51,8 @@ unit_velocity_cgs = float(params.attrs["InternalUnitSystem:UnitVelocity_in_cgs"]
 unit_time_cgs = unit_length_cgs / unit_velocity_cgs
 v_c = float(params.attrs["IsothermalPotential:vrot"])
 v_c_cgs = v_c * unit_velocity_cgs
-lambda_cgs = float(params.attrs["LambdaCooling:lambda_cgs"])
-X_H = float(params.attrs["LambdaCooling:hydrogen_mass_abundance"])
+lambda_cgs = float(params.attrs["LambdaCooling:lambda_nH2_cgs"])
+X_H = float(params.attrs["SPH:H_mass_fraction"])
 header = f["Header"]
 N = header.attrs["NumPart_Total"][0]
 box_centre = np.array(header.attrs["BoxSize"])
@@ -79,7 +79,7 @@ for i in range(n_snaps):
     radius_over_virial_radius = radius_cgs / r_vir_cgs
 
     # get the internal energies
-    u_dset = f["PartType0/InternalEnergy"]
+    u_dset = f["PartType0/InternalEnergies"]
     u = np.array(u_dset)
 
     # make dimensionless
diff --git a/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py b/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py
index acd44f6560297a6dcb83bd349ac0e1481feb3640..7d94a2cc537b9825fb8c713dacc367004658be4c 100644
--- a/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py
+++ b/examples/Cooling/CoolingHaloWithSpin/velocity_profile.py
@@ -116,6 +116,6 @@ for i in range(n_snaps):
         % (snap_time_cgs, N, v_c)
     )
     plt.ylim((0, 2))
-    plot_filename = "./plots/radial_velocity_profile/velocity_profile_%03d.png" % i
+    plot_filename = "./velocity_profile_%03d.png" % i
     plt.savefig(plot_filename, format="png")
     plt.close()
diff --git a/examples/Cooling/CoolingRates/cooling_rates.c b/examples/Cooling/CoolingRates/cooling_rates.c
index 5b2b7d78f407e322dddec38795f9fe29e8d7e141..55528b5abfa9fed828ee5f3c9edddacd0ea3579a 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/Cosmology/ConstantCosmoVolume/plotSolution.py b/examples/Cosmology/ConstantCosmoVolume/plotSolution.py
index 7f153b9ad39b6e877022f39b31481ab37df80993..cc25919e0ff30e32f2996f3f9205ca6d47d90833 100644
--- a/examples/Cosmology/ConstantCosmoVolume/plotSolution.py
+++ b/examples/Cosmology/ConstantCosmoVolume/plotSolution.py
@@ -25,6 +25,7 @@ T_i = 100.0  # Initial temperature of the gas (in K)
 z_c = 1.0  # Redshift of caustic formation (non-linear collapse)
 z_i = 100.0  # Initial redshift
 gas_gamma = 5.0 / 3.0  # Gas adiabatic index
+N_output = 119
 
 # Physical constants needed for internal energy to temperature conversion
 kB_in_SI = 1.38064852e-23
@@ -33,30 +34,11 @@ mH_in_kg = 1.6737236e-27
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.06,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.06,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.21,
-    "figure.subplot.hspace": 0.13,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
+
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 # Read the simulation data
 sim = h5py.File("box_0000.hdf5", "r")
@@ -85,25 +67,25 @@ u_0 *= a ** (-3 * (1 - gas_gamma))
 S_0 = (gas_gamma - 1.0) * u_0 * rho_0 ** (-(gas_gamma - 1.0))
 
 # Mean quantities over time
-z = np.zeros(119)
-a = np.zeros(119)
-S_mean = np.zeros(119)
-S_std = np.zeros(119)
-u_mean = np.zeros(119)
-u_std = np.zeros(119)
-P_mean = np.zeros(119)
-P_std = np.zeros(119)
-rho_mean = np.zeros(119)
-rho_std = np.zeros(119)
-
-vx_mean = np.zeros(119)
-vy_mean = np.zeros(119)
-vz_mean = np.zeros(119)
-vx_std = np.zeros(119)
-vy_std = np.zeros(119)
-vz_std = np.zeros(119)
-
-for i in range(119):
+z = np.zeros(N_output)
+a = np.zeros(N_output)
+S_mean = np.zeros(N_output)
+S_std = np.zeros(N_output)
+u_mean = np.zeros(N_output)
+u_std = np.zeros(N_output)
+P_mean = np.zeros(N_output)
+P_std = np.zeros(N_output)
+rho_mean = np.zeros(N_output)
+rho_std = np.zeros(N_output)
+
+vx_mean = np.zeros(N_output)
+vy_mean = np.zeros(N_output)
+vz_mean = np.zeros(N_output)
+vx_std = np.zeros(N_output)
+vy_std = np.zeros(N_output)
+vz_std = np.zeros(N_output)
+
+for i in range(N_output):
     sim = h5py.File("box_%04d.hdf5" % i, "r")
 
     z[i] = sim["/Cosmology"].attrs["Redshift"][0]
@@ -138,117 +120,177 @@ rho_mean_phys = rho_mean / a ** 3
 u_mean_phys = u_mean / a ** (3 * (gas_gamma - 1.0))
 S_mean_phys = S_mean
 
-# Solution in physical coordinates
-# T_solution = np.ones(T) / a
-
-figure()
+vx_mean_phys = vx_mean / a
+vy_mean_phys = vy_mean / a
+vz_mean_phys = vz_mean / a
+vx_std_phys = vx_std / a
+vy_std_phys = vy_std / a
+vz_std_phys = vz_std / a
+
+# Plot the interesting quantities
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=4,
+    markeredgecolor="none",
+    alpha=0.2,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
 
 # Density evolution --------------------------------
-subplot(231)  # , yscale="log")
-semilogx(a, rho_mean / rho_0, "-", color="r", lw=1)
-semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0)
-semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0)
-semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0)
-text(1e-2, 1.0105, "+1\\%", color="0.6", fontsize=9)
-text(1e-2, 0.9895, "-1\\%", color="0.6", fontsize=9, va="top")
-text(1e-2, 1.015, "$\\rho_0=%.3f$" % rho_0)
-ylim(0.98, 1.02)
-xlim(8e-3, 1.1)
-xlabel("${\\rm Scale-factor}$", labelpad=0.0)
-ylabel("${\\rm Comoving~density}~\\rho / \\rho_0$", labelpad=0.0)
+plt.subplot(231)  # , yscale="log")
+plt.semilogx(a, rho_mean / rho_0, **scatter_props)
+plt.semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0)
+plt.semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0)
+plt.semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0)
+plt.text(1e-2, 1.0105, "+1%", color="0.6", fontsize=9)
+plt.text(1e-2, 0.9895, "-1%", color="0.6", fontsize=9, va="top")
+plt.text(1e-2, 1.015, "$\\rho_0=%.3f$" % rho_0)
+plt.ylim(0.98, 1.02)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0)
+plt.ylabel("${\\rm Comoving~density}~\\rho / \\rho_0$", labelpad=0.0)
 
 # Thermal energy evolution --------------------------------
-subplot(232)  # , yscale="log")
-semilogx(a, u_mean / u_0, "-", color="r", lw=1)
-semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0)
-semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0)
-semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0)
-text(1e-2, 1.0105, "+1\\%", color="0.6", fontsize=9)
-text(1e-2, 0.9895, "-1\\%", color="0.6", fontsize=9, va="top")
-text(1e-2, 1.015, "$u_0=%.3e$" % (u_0))
-ylim(0.98, 1.02)
-xlim(8e-3, 1.1)
-xlabel("${\\rm Scale-factor}$", labelpad=0.0)
-ylabel("${\\rm Comoving~internal~energy}~u / u_0$", labelpad=0.0)
+plt.subplot(232)  # , yscale="log")
+plt.semilogx(a, u_mean / u_0, **scatter_props)
+plt.semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0)
+plt.semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0)
+plt.semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0)
+plt.text(1e-2, 1.0105, "+1%", color="0.6", fontsize=9)
+plt.text(1e-2, 0.9895, "-1%", color="0.6", fontsize=9, va="top")
+plt.text(1e-2, 1.015, "$u_0=%.3e$" % (u_0))
+plt.ylim(0.98, 1.02)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0)
+plt.ylabel("${\\rm Comoving~internal~energy}~u / u_0$", labelpad=0.0)
 
 # Entropy evolution --------------------------------
-subplot(233)  # , yscale="log")
-semilogx(a, S_mean / S_0, "-", color="r", lw=1)
-semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0)
-semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0)
-semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0)
-text(1e-2, 1.0105, "+1\\%", color="0.6", fontsize=9)
-text(1e-2, 0.9895, "-1\\%", color="0.6", fontsize=9, va="top")
-text(1e-2, 1.015, "$A_0=%.3e$" % (S_0))
-ylim(0.98, 1.02)
-xlim(8e-3, 1.1)
-xlabel("${\\rm Scale-factor}$", labelpad=0.0)
-ylabel("${\\rm Comoving~entropy}~A / A_0$", labelpad=0.0)
+plt.subplot(233)  # , yscale="log")
+plt.semilogx(a, S_mean / S_0, **scatter_props)
+plt.semilogx([1e-10, 1e10], np.ones(2), "-", color="0.6", lw=1.0)
+plt.semilogx([1e-10, 1e10], np.ones(2) * 0.99, "--", color="0.6", lw=1.0)
+plt.semilogx([1e-10, 1e10], np.ones(2) * 1.01, "--", color="0.6", lw=1.0)
+plt.text(1e-2, 1.0105, "+1%", color="0.6", fontsize=9)
+plt.text(1e-2, 0.9895, "-1%", color="0.6", fontsize=9, va="top")
+plt.text(1e-2, 1.015, "$A_0=%.3e$" % (S_0))
+plt.ylim(0.98, 1.02)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0)
+plt.ylabel("${\\rm Comoving~entropy}~A / A_0$", labelpad=0.0)
 
 # Peculiar velocity evolution ---------------------
-subplot(234)
-semilogx(a, vx_mean, "-", color="r", lw=1)
-semilogx(a, vy_mean, "-", color="g", lw=1)
-semilogx(a, vz_mean, "-", color="b", lw=1)
-xlabel("${\\rm Scale-factor}$", labelpad=0.0)
-ylabel("${\\rm Peculiar~velocity~mean}$", labelpad=-5.0)
+plt.subplot(234)
+plt.semilogx(a, vx_mean, **scatter_props)
+plt.semilogx(a, vy_mean, **scatter_props)
+plt.semilogx(a, vz_mean, **scatter_props)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0)
+plt.ylabel("${\\rm Peculiar~velocity~mean}$", labelpad=-5.0)
 
 # Peculiar velocity evolution ---------------------
-subplot(235)
-semilogx(a, vx_std, "--", color="r", lw=1)
-semilogx(a, vy_std, "--", color="g", lw=1)
-semilogx(a, vz_std, "--", color="b", lw=1)
-xlabel("${\\rm Scale-factor}$", labelpad=0.0)
-ylabel("${\\rm Peculiar~velocity~std-dev}$", labelpad=0.0)
+plt.subplot(235)
+plt.semilogx(a, vx_std, **scatter_props)
+plt.semilogx(a, vy_std, **scatter_props)
+plt.semilogx(a, vz_std, **scatter_props)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0)
+plt.ylabel("${\\rm Peculiar~velocity~std-dev}$", labelpad=0.0)
 
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
+
+text_fontsize = 5
+
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
+)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
 
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
+plt.tight_layout()
 
-savefig("ConstantBox_comoving.png", dpi=200)
+plt.savefig("ConstantBox_comoving.png", dpi=200)
 
 
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
 
 # Density evolution --------------------------------
-subplot(231)  # , yscale="log")
-loglog(a, rho_mean_phys, "-", color="r", lw=1)
-xlabel("${\\rm Scale-factor}$")
-ylabel("${\\rm Physical~density}$")
+plt.subplot(231)  # , yscale="log")
+plt.loglog(a, rho_mean_phys, **scatter_props)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$")
+plt.ylabel("${\\rm Physical~density}$")
 
 # Thermal energy evolution --------------------------------
-subplot(232)  # , yscale="log")
-loglog(a, u_mean_phys, "-", color="r", lw=1)
-xlabel("${\\rm Scale-factor}$")
-ylabel("${\\rm Physical~internal~energy}$")
+plt.subplot(232)  # , yscale="log")
+plt.loglog(a, u_mean_phys, **scatter_props)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$")
+plt.ylabel("${\\rm Physical~internal~energy}$")
 
 # Entropy evolution --------------------------------
-subplot(233)  # , yscale="log")
-semilogx(a, S_mean_phys, "-", color="r", lw=1)
-xlabel("${\\rm Scale-factor}$")
-ylabel("${\\rm Physical~entropy}$")
+plt.subplot(233)  # , yscale="log")
+plt.semilogx(a, S_mean_phys, **scatter_props)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$")
+plt.ylabel("${\\rm Physical~entropy}$")
+
+# Peculiar velocity evolution ---------------------
+plt.subplot(234)
+plt.semilogx(a, vx_mean_phys, **scatter_props)
+plt.semilogx(a, vy_mean_phys, **scatter_props)
+plt.semilogx(a, vz_mean_phys, **scatter_props)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0)
+plt.ylabel("${\\rm Peculiar~velocity~mean}$", labelpad=-5.0)
+
+# Peculiar velocity evolution ---------------------
+plt.subplot(235)
+plt.semilogx(a, vx_std_phys, **scatter_props)
+plt.semilogx(a, vy_std_phys, **scatter_props)
+plt.semilogx(a, vz_std_phys, **scatter_props)
+plt.xlim(8e-3, 1.1)
+plt.xlabel("${\\rm Scale-factor}$", labelpad=0.0)
+plt.ylabel("${\\rm Peculiar~velocity~std-dev}$", labelpad=0.0)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
-
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-savefig("ConstantBox_physical.png", dpi=200)
+plt.subplot(236, frameon=False)
+
+text_fontsize = 5
+
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
+)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("ConstantBox_physical.png", dpi=200)
diff --git a/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py b/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py
index 2fc0080cab691cf15460b370f8548e57516bf1de..293f93792a2cf4d7d0af86557a15603700df07e5 100644
--- a/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py
+++ b/examples/Cosmology/ZeldovichPancake_3D/plotSolution.py
@@ -32,30 +32,13 @@ mH_in_kg = 1.6737236e-27
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
+import sys
 import os.path
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
@@ -84,7 +67,7 @@ P = sim["/PartType0/Pressures"][:]
 rho = sim["/PartType0/Densities"][:]
 m = sim["/PartType0/Masses"][:]
 try:
-    phi = sim["/PartType0/Potential"][:]
+    phi = sim["/PartType0/Potentials"][:]
 except KeyError:
     # We didn't write the potential, try to go on without
     print("Couldn't find potential in your output file")
@@ -118,12 +101,12 @@ lambda_i = boxSize  # wavelength of the perturbation
 
 
 # Solution taken from Springel 2010. Eqs. 127 - 130
-q = linspace(-0.5 * lambda_i, 0.5 * lambda_i, 256)
-k_i = 2.0 * pi / lambda_i
+q = np.linspace(-0.5 * lambda_i, 0.5 * lambda_i, 256)
+k_i = 2.0 * np.pi / lambda_i
 zfac = (1.0 + z_c) / (1.0 + redshift)
-x_s = q - zfac * sin(k_i * q) / k_i
-rho_s = rho_0 / (1.0 - zfac * cos(k_i * q))
-v_s = -H_0 * (1.0 + z_c) / sqrt(1.0 + redshift) * sin(k_i * q) / k_i
+x_s = q - zfac * np.sin(k_i * q) / k_i
+rho_s = rho_0 / (1.0 - zfac * np.cos(k_i * q))
+v_s = -H_0 * (1.0 + z_c) / np.sqrt(1.0 + redshift) * np.sin(k_i * q) / k_i
 T_s = T_i * (((1.0 + redshift) / (1.0 + z_i)) ** 3.0 * rho_s / rho_0) ** (2.0 / 3.0)
 
 
@@ -136,37 +119,51 @@ unit_mass_in_si = 0.001 * unit_mass_in_cgs
 unit_time_in_si = unit_time_in_cgs
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=4,
+    markeredgecolor="none",
+    alpha=0.2,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
 
 # Velocity profile --------------------------------
-subplot(231)
+plt.subplot(231)
 if np.size(x_g) > 1:
-    plot(x_g, v_g, "s", color="g", alpha=0.8, lw=1.2, ms=4)
-plot(x, v, ".", color="r", ms=4.0)
-plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
-ylabel("${\\rm{Peculiar~velocity}}~v_x$", labelpad=0)
+    plt.plot(x_g, v_g, "s", color="g", alpha=0.8, lw=1.2, ms=4)
+plt.plot(x, v, **scatter_props)
+plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Peculiar~velocity}}~v_x$", labelpad=0)
 
 
 # Density profile --------------------------------
-subplot(232)  # , yscale="log")
+plt.subplot(232)  # , yscale="log")
 if np.size(x_g) > 1:
-    plot(x_g, rho_g / rho_0, "s", color="g", alpha=0.8, lw=1.2, ms=4)
-plot(x, rho / rho_0, ".", color="r", ms=4.0)
-plot(x_s, rho_s / rho_0, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
-ylabel("${\\rm{Density}}~\\rho / \\rho_0$", labelpad=0)
+    plt.plot(x_g, rho_g / rho_0, "s", color="g", alpha=0.8, lw=1.2, ms=4)
+plt.plot(x, rho / rho_0, **scatter_props)
+plt.plot(x_s, rho_s / rho_0, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho / \\rho_0$", labelpad=0)
 
 # Potential profile --------------------------------
-subplot(233)
+plt.subplot(233)
 if np.size(x_g) > 1:
-    plot(x_g, phi_g, "s", color="g", alpha=0.8, lw=1.2, ms=4)
-plot(x, phi, ".", color="r", ms=4.0)
-xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
-ylabel("${\\rm{Potential}}~\\phi$", labelpad=0)
+    plt.plot(x_g, phi_g, "s", color="g", alpha=0.8, lw=1.2, ms=4)
+plt.plot(x, phi, **scatter_props)
+plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Potential}}~\\phi$", labelpad=0)
 
 # Temperature profile -------------------------
-subplot(234)  # , yscale="log")
+plt.subplot(234)  # , yscale="log")
 u *= unit_length_in_si ** 2 / unit_time_in_si ** 2
 u_g *= unit_length_in_si ** 2 / unit_time_in_si ** 2
 u /= a ** (3 * (gas_gamma - 1.0))
@@ -175,30 +172,41 @@ T = (gas_gamma - 1.0) * u * mH_in_kg / k_in_J_K
 T_g = (gas_gamma - 1.0) * u_g * mH_in_kg / k_in_J_K
 print("z = {0:.2f}, T_avg = {1:.2f}".format(redshift, T.mean()))
 if np.size(x_g) > 1:
-    plot(x_g, T_g, "s", color="g", alpha=0.8, lw=1.2, ms=4)
-plot(x, T, ".", color="r", ms=4.0)
-plot(x_s, T_s, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
-ylabel("${\\rm{Temperature}}~T$", labelpad=0)
+    plt.plot(x_g, T_g, "s", color="g", alpha=0.8, lw=1.2, ms=4)
+plt.plot(x, T, **scatter_props)
+plt.plot(x_s, T_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Comoving~position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Temperature}}~T$", labelpad=0)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
+
+text_fontsize = 5
 
-text(-0.49, 0.9, "Zeldovich pancake at z=%.2f " % (redshift), fontsize=10)
-text(
-    -0.49,
+plt.text(
+    -0.45, 0.9, "Zeldovich pancake at z=%.2f " % (redshift), fontsize=text_fontsize
+)
+plt.text(
+    -0.45,
     0.8,
     "adiabatic index $\\gamma=%.2f$, viscosity $\\alpha=%.2f$" % (gas_gamma, alpha),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-savefig("ZeldovichPancake_%.4d.png" % snap, dpi=200)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
+)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("ZeldovichPancake_%.4d.png" % snap, dpi=200)
diff --git a/examples/GravityTests/Hernquist_circularorbit/plotprog.py b/examples/GravityTests/Hernquist_circularorbit/plotprog.py
index a19c66e7f30e0e4012a23a4d38dd23045deea6e2..881c74301ec4a466f6984ee081b7ac6e3d555f5b 100755
--- a/examples/GravityTests/Hernquist_circularorbit/plotprog.py
+++ b/examples/GravityTests/Hernquist_circularorbit/plotprog.py
@@ -22,7 +22,7 @@ import h5py
 import matplotlib.pyplot as plt
 from scipy.integrate import odeint
 
-t = np.linspace(0, 40, 1e5)
+t = np.linspace(0, 40, 100000)
 y0 = [0, 10]
 a = 30.0
 G = 4.300927e-06
diff --git a/examples/HydroTests/EvrardCollapse_3D/plotSolution.py b/examples/HydroTests/EvrardCollapse_3D/plotSolution.py
index 6b89eb4ff04011454ece10c9190a06880ae6b21b..43cb309a6fcf929a4768ed99c0b3593a33723702 100644
--- a/examples/HydroTests/EvrardCollapse_3D/plotSolution.py
+++ b/examples/HydroTests/EvrardCollapse_3D/plotSolution.py
@@ -24,9 +24,11 @@
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 from scipy import stats
 import h5py
+import sys
 
 # Parameters
 gas_gamma = 5.0 / 3.0  # Polytropic index
@@ -37,25 +39,7 @@ v_R = 0.0  # Velocity right state
 P_L = 1.0  # Pressure left state
 P_R = 0.1  # Pressure right state
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
@@ -70,20 +54,20 @@ eta = sim["/HydroScheme"].attrs["Kernel eta"]
 git = sim["Code"].attrs["Git Revision"]
 
 coords = sim["/PartType0/Coordinates"]
-x = sqrt(
+x = np.sqrt(
     (coords[:, 0] - 0.5 * boxSize) ** 2
     + (coords[:, 1] - 0.5 * boxSize) ** 2
     + (coords[:, 2] - 0.5 * boxSize) ** 2
 )
 vels = sim["/PartType0/Velocities"]
-v = sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2 + vels[:, 2] ** 2)
+v = np.sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2 + vels[:, 2] ** 2)
 u = sim["/PartType0/InternalEnergies"][:]
 S = sim["/PartType0/Entropies"][:]
 P = sim["/PartType0/Pressures"][:]
 rho = sim["/PartType0/Densities"][:]
 
 # Bin the data
-x_bin_edge = logspace(-3.0, log10(2.0), 100)
+x_bin_edge = np.logspace(-3.0, np.log10(2.0), 100)
 x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1])
 rho_bin, _, _ = stats.binned_statistic(x, rho, statistic="mean", bins=x_bin_edge)
 v_bin, _, _ = stats.binned_statistic(x, v, statistic="mean", bins=x_bin_edge)
@@ -101,79 +85,117 @@ P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2)
 S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2)
 u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2)
 
-ref = loadtxt("evrardCollapse3D_exact.txt")
+ref = np.loadtxt("evrardCollapse3D_exact.txt")
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=1,
+    markeredgecolor="none",
+    alpha=0.2,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
 
 # Velocity profile --------------------------------
-subplot(231)
-semilogx(x, -v, ".", color="r", ms=0.2)
-semilogx(ref[:, 0], ref[:, 2], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, -v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
-xlim(1.0e-3, 2.0)
-ylim(-1.7, 0.1)
+plt.subplot(231)
+plt.semilogx(x, -v, **scatter_props)
+plt.semilogx(ref[:, 0], ref[:, 2], "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, -v_bin, yerr=v_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
+plt.xlim(1.0e-3, 2.0)
+plt.ylim(-1.7, 0.1)
 
 # Density profile --------------------------------
-subplot(232)
-loglog(x, rho, ".", color="r", ms=0.2)
-loglog(ref[:, 0], ref[:, 1], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
-xlim(1.0e-3, 2.0)
-ylim(1.0e-2, 1.0e4)
+plt.subplot(232)
+plt.loglog(x, rho, **scatter_props)
+plt.loglog(ref[:, 0], ref[:, 1], "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.xlim(1.0e-3, 2.0)
+plt.ylim(1.0e-2, 1.0e4)
 
 # Pressure profile --------------------------------
-subplot(233)
-loglog(x, P, ".", color="r", ms=0.2)
-loglog(ref[:, 0], ref[:, 3], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Pressure}}~P$", labelpad=0)
-xlim(1.0e-3, 2.0)
-ylim(1.0e-4, 1.0e3)
+plt.subplot(233)
+plt.loglog(x, P, **scatter_props)
+plt.loglog(ref[:, 0], ref[:, 3], "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.xlim(1.0e-3, 2.0)
+plt.ylim(1.0e-4, 1.0e3)
 
 # Internal energy profile -------------------------
-subplot(234)
-loglog(x, u, ".", color="r", ms=0.2)
-loglog(ref[:, 0], ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
-xlim(1.0e-3, 2.0)
-ylim(1.0e-2, 2.0)
+plt.subplot(234)
+plt.loglog(x, u, **scatter_props)
+plt.loglog(
+    ref[:, 0],
+    ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0),
+    "--",
+    color=line_color,
+    alpha=0.8,
+    lw=1.2,
+)
+plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.xlim(1.0e-3, 2.0)
+plt.ylim(1.0e-2, 2.0)
 
 # Entropy profile ---------------------------------
-subplot(235)
-semilogx(x, S, ".", color="r", ms=0.2)
-semilogx(ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Entropy}}~S$", labelpad=0)
-xlim(1.0e-3, 2.0)
-ylim(0.0, 0.25)
+plt.subplot(235)
+plt.semilogx(x, S, **scatter_props)
+plt.semilogx(
+    ref[:, 0],
+    ref[:, 3] / ref[:, 1] ** gas_gamma,
+    "--",
+    color=line_color,
+    alpha=0.8,
+    lw=1.2,
+)
+plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+plt.xlim(1.0e-3, 2.0)
+plt.ylim(0.0, 0.25)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
 
-text(
-    -0.49,
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
     "Evrard collapse with $\\gamma=%.3f$ in 3D\nat $t=%.2f$" % (gas_gamma, time),
-    fontsize=10,
+    fontsize=text_fontsize,
+)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$Swift$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-tight_layout()
-savefig("EvrardCollapse.png", dpi=200)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("EvrardCollapse.png", dpi=200)
diff --git a/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py b/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py
index 5f1cea4f115358a7348e404a879210a2128518b9..862ff3ec03655639037287c48b97c40ddbb1f062 100644
--- a/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py
+++ b/examples/HydroTests/InteractingBlastWaves_1D/plotSolution.py
@@ -17,126 +17,139 @@
 #
 ##############################################################################
 
-import numpy as np
 import matplotlib
 
 matplotlib.use("Agg")
-import pylab as pl
-import h5py
+import matplotlib.pyplot as plt
+import numpy as np
 import sys
+import h5py
+
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
-# Parameters
-gamma = 1.4  # Polytropic index
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-pl.rcParams.update(params)
-
-# Read the snapshot index from the command line argument
 snap = int(sys.argv[1])
 
 # Open the file and read the relevant data
 file = h5py.File("interactingBlastWaves_{0:04d}.hdf5".format(snap), "r")
 x = file["/PartType0/Coordinates"][:, 0]
-rho = file["/PartType0/Densities"]
+rho = file["/PartType0/Densities"][:]
 v = file["/PartType0/Velocities"][:, 0]
-u = file["/PartType0/InternalEnergies"]
-S = file["/PartType0/Entropies"]
-P = file["/PartType0/Pressures"]
+u = file["/PartType0/InternalEnergies"][:]
+S = file["/PartType0/Entropies"][:]
+P = file["/PartType0/Pressures"][:]
 time = file["/Header"].attrs["Time"][0]
 
 scheme = file["/HydroScheme"].attrs["Scheme"]
 kernel = file["/HydroScheme"].attrs["Kernel function"]
 neighbours = file["/HydroScheme"].attrs["Kernel target N_ngb"][0]
 eta = file["/HydroScheme"].attrs["Kernel eta"][0]
+gamma = file["/HydroScheme"].attrs["Adiabatic index"]
 git = file["Code"].attrs["Git Revision"]
 
+if gamma != 1.4:
+    print(
+        "Error: SWIFT was run with the wrong adiabatic index. Should have been 1.4",
+        gamma,
+    )
+    exit(1)
+
 ref = np.loadtxt("interactingBlastWaves1D_exact.txt")
 
 # Plot the interesting quantities
-fig, ax = pl.subplots(2, 3)
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=4,
+    markeredgecolor="none",
+    alpha=0.2,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
 
 # Velocity profile
-ax[0][0].plot(x, v, "r.", markersize=4.0)
-ax[0][0].plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, linewidth=1.2)
-ax[0][0].set_xlabel("${\\rm{Position}}~x$", labelpad=0)
-ax[0][0].set_ylabel("${\\rm{Velocity}}~v_x$", labelpad=0)
-ax[0][0].set_xlim(0.0, 1.0)
-ax[0][0].set_ylim(-1.0, 15.0)
+plt.subplot(231)
+plt.plot(x, v, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 2], "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_x$", labelpad=0)
+plt.xlim(0.0, 1.0)
+plt.ylim(-1.0, 15.0)
 
 # Density profile
-ax[0][1].plot(x, rho, "r.", markersize=4.0)
-ax[0][1].plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, linewidth=1.2)
-ax[0][1].set_xlabel("${\\rm{Position}}~x$", labelpad=0)
-ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
-ax[0][1].set_xlim(0.0, 1.0)
+plt.subplot(232)
+plt.plot(x, rho, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 1], "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.xlim(0.0, 1.0)
 
 # Pressure profile
-ax[0][2].plot(x, P, "r.", markersize=4.0)
-ax[0][2].plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, linewidth=1.2)
-ax[0][2].set_xlabel("${\\rm{Position}}~x$", labelpad=0)
-ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad=0)
-ax[0][2].set_xlim(0.0, 1.0)
+plt.subplot(233)
+plt.plot(x, P, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 3], "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.xlim(0.0, 1.0)
 
 # Internal energy profile
-ax[1][0].plot(x, u, "r.", markersize=4.0)
-ax[1][0].plot(
-    ref[:, 0], ref[:, 3] / ref[:, 1] / (gamma - 1.0), "k--", alpha=0.8, linewidth=1.2
+plt.subplot(234)
+plt.plot(x, u, **scatter_props)
+plt.plot(
+    ref[:, 0],
+    ref[:, 3] / ref[:, 1] / (gamma - 1.0),
+    "--",
+    color=line_color,
+    alpha=0.8,
+    lw=1.2,
 )
-ax[1][0].set_xlabel("${\\rm{Position}}~x$", labelpad=0)
-ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
-ax[1][0].set_xlim(0.0, 1.0)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.xlim(0.0, 1.0)
 
 # Entropy profile
-ax[1][1].plot(x, S, "r.", markersize=4.0)
-ax[1][1].plot(
-    ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "k--", alpha=0.8, linewidth=1.2
+plt.subplot(235)
+plt.plot(x, S, **scatter_props)
+plt.plot(
+    ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "--", color=line_color, alpha=0.8, lw=1.2
 )
-ax[1][1].set_xlabel("${\\rm{Position}}~x$", labelpad=0)
-ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0)
-ax[1][1].set_xlim(0.0, 1.0)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+plt.xlim(0.0, 1.0)
 
 # Run information
-ax[1][2].set_frame_on(False)
-ax[1][2].text(
-    -0.49,
+plt.subplot(236, frameon=False)
+
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
     "Interacting blast waves test\nwith $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(
-        gamma, time
+        gamma[0], time
     ),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10)
-ax[1][2].text(-0.49, 0.4, scheme, fontsize=10)
-ax[1][2].text(-0.49, 0.3, kernel, fontsize=10)
-ax[1][2].text(
-    -0.49,
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
     0.2,
-    "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta),
-    fontsize=10,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
 )
-ax[1][2].set_xlim(-0.5, 0.5)
-ax[1][2].set_ylim(0.0, 1.0)
-ax[1][2].set_xticks([])
-ax[1][2].set_yticks([])
+plt.xlim(-0.5, 0.5)
+plt.ylim(0.0, 1.0)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
 
-pl.tight_layout()
-pl.savefig("InteractingBlastWaves.png", dpi=200)
+plt.savefig("InteractingBlastWaves.png", dpi=200)
diff --git a/examples/HydroTests/InteractingBlastWaves_1D/run.sh b/examples/HydroTests/InteractingBlastWaves_1D/run.sh
index 9139835bdbea88a80026f4a264a44bb60638882a..a3f927491ec07680756072beb378cc7903e374da 100755
--- a/examples/HydroTests/InteractingBlastWaves_1D/run.sh
+++ b/examples/HydroTests/InteractingBlastWaves_1D/run.sh
@@ -8,7 +8,7 @@ then
 fi
 
 # Run SWIFT
-../../../swift --hydro --threads=1 interactingBlastWaves.yml 2>&1 | tee output.log
+../../../swift --hydro --threads=1  --limiter interactingBlastWaves.yml 2>&1 | tee output.log
 
 # Get the high resolution reference solution if not present.
 if [ ! -e interactingBlastWaves1D_exact.txt ]
diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh
index 479de1398092dac3fc1275ffd1fc5741ff4c8c61..d134b8be7a52fb7ad6e968d5a98d7be9499461ca 100755
--- a/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh
+++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_2D/run.sh
@@ -1,6 +1,11 @@
 #!/bin/bash
 
- # Generate the initial conditions if they are not present.
+# Generate the initial conditions if they are not present.
+if [ ! -e glassPlane_128.hdf5 ]
+then
+    ./getGlass.sh
+fi
+
 if [ ! -e kelvinHelmholtzGrowthRate.hdf5 ]
 then
     echo "Generating initial conditions for the Kelvin-Helmholtz growth rate " \
diff --git a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh
index 479de1398092dac3fc1275ffd1fc5741ff4c8c61..73f81e3da13e5395d74e9b5509a6dfd0ec31ea3e 100755
--- a/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh
+++ b/examples/HydroTests/KelvinHelmholtzGrowthRate_3D/run.sh
@@ -1,6 +1,11 @@
 #!/bin/bash
 
- # Generate the initial conditions if they are not present.
+# Generate the initial conditions if they are not present.
+if [ ! -e glassCube_64.hdf5 ]
+then
+    ./getGlass.sh
+fi
+
 if [ ! -e kelvinHelmholtzGrowthRate.hdf5 ]
 then
     echo "Generating initial conditions for the Kelvin-Helmholtz growth rate " \
diff --git a/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py b/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py
index 41997c716e29bd6e005c0b2a9b88ba949f058fe7..750dd016328dfdd44296bb400f6dc84a7e58fea5 100644
--- a/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py
+++ b/examples/HydroTests/KelvinHelmholtz_2D/plotSolution.py
@@ -35,30 +35,12 @@ rho2 = 1  # Outskirts density
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
+import sys
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
-
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
@@ -76,111 +58,131 @@ pos = sim["/PartType0/Coordinates"][:, :]
 x = pos[:, 0] - boxSize / 2
 y = pos[:, 1] - boxSize / 2
 vel = sim["/PartType0/Velocities"][:, :]
-v_norm = sqrt(vel[:, 0] ** 2 + vel[:, 1] ** 2)
+v_norm = np.sqrt(vel[:, 0] ** 2 + vel[:, 1] ** 2)
 rho = sim["/PartType0/Densities"][:]
 u = sim["/PartType0/InternalEnergies"][:]
 S = sim["/PartType0/Entropies"][:]
 P = sim["/PartType0/Pressures"][:]
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
 
 
 # Azimuthal velocity profile -----------------------------
-subplot(231)
-scatter(
+plt.subplot(231)
+plt.scatter(
     pos[:, 0],
     pos[:, 1],
     c=vel[:, 0],
     cmap="PuBu",
     edgecolors="face",
-    s=4,
+    s=0.25,
     vmin=-1.0,
     vmax=1.0,
 )
-text(
+plt.text(
     0.97, 0.97, "${\\rm{Velocity~along}}~x$", ha="right", va="top", backgroundcolor="w"
 )
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=0)
-xlim(0, 1)
-ylim(0, 1)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Position}}~y$", labelpad=0)
+plt.xlim(0, 1)
+plt.ylim(0, 1)
 
 # Radial density profile --------------------------------
-subplot(232)
-scatter(
-    pos[:, 0], pos[:, 1], c=rho, cmap="PuBu", edgecolors="face", s=4, vmin=0.8, vmax=2.2
+plt.subplot(232)
+plt.scatter(
+    pos[:, 0],
+    pos[:, 1],
+    c=rho,
+    cmap="PuBu",
+    edgecolors="face",
+    s=0.25,
+    vmin=0.8,
+    vmax=2.2,
 )
-text(0.97, 0.97, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w")
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=0)
-xlim(0, 1)
-ylim(0, 1)
+plt.text(0.97, 0.97, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w")
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Position}}~y$", labelpad=0)
+plt.xlim(0, 1)
+plt.ylim(0, 1)
 
 # Radial pressure profile --------------------------------
-subplot(233)
-scatter(pos[:, 0], pos[:, 1], c=P, cmap="PuBu", edgecolors="face", s=4, vmin=1, vmax=4)
-text(0.97, 0.97, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w")
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=0)
-xlim(0, 1)
-ylim(0, 1)
+plt.subplot(233)
+plt.scatter(
+    pos[:, 0], pos[:, 1], c=P, cmap="PuBu", edgecolors="face", s=0.25, vmin=1, vmax=4
+)
+plt.text(0.97, 0.97, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w")
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Position}}~y$", labelpad=0)
+plt.xlim(0, 1)
+plt.ylim(0, 1)
 
 # Internal energy profile --------------------------------
-subplot(234)
-scatter(
-    pos[:, 0], pos[:, 1], c=u, cmap="PuBu", edgecolors="face", s=4, vmin=1.5, vmax=5.0
+plt.subplot(234)
+plt.scatter(
+    pos[:, 0],
+    pos[:, 1],
+    c=u,
+    cmap="PuBu",
+    edgecolors="face",
+    s=0.25,
+    vmin=1.5,
+    vmax=5.0,
+)
+plt.text(
+    0.97, 0.97, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w"
 )
-text(0.97, 0.97, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w")
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=0)
-xlim(0, 1)
-ylim(0, 1)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Position}}~y$", labelpad=0)
+plt.xlim(0, 1)
+plt.ylim(0, 1)
 
 # Radial entropy profile --------------------------------
-subplot(235)
-scatter(
-    pos[:, 0], pos[:, 1], c=S, cmap="PuBu", edgecolors="face", s=4, vmin=0.5, vmax=3.0
+plt.subplot(235)
+plt.scatter(
+    pos[:, 0],
+    pos[:, 1],
+    c=S,
+    cmap="PuBu",
+    edgecolors="face",
+    s=0.25,
+    vmin=0.5,
+    vmax=3.0,
 )
-text(0.97, 0.97, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w")
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=0)
-xlim(0, 1)
-ylim(0, 1)
-
-# Image --------------------------------------------------
-# subplot(234)
-# scatter(pos[:,0], pos[:,1], c=v_norm, cmap="PuBu", edgecolors='face', s=4, vmin=0, vmax=1)
-# text(0.95, 0.95, "$|v|$", ha="right", va="top")
-# xlim(0,1)
-# ylim(0,1)
-# xlabel("$x$", labelpad=0)
-# ylabel("$y$", labelpad=0)
+plt.text(0.97, 0.97, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w")
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Position}}~y$", labelpad=0)
+plt.xlim(0, 1)
+plt.ylim(0, 1)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
 
-text(-0.49, 0.9, "Kelvin-Helmholtz instability at $t=%.2f$" % (time), fontsize=10)
-text(
-    -0.49,
+plt.text(-0.45, 0.9, "Kelvin-Helmholtz instability at $t=%.2f$" % (time), fontsize=10)
+plt.text(
+    -0.45,
     0.8,
-    "Centre:~~~ $(P, \\rho, v) = (%.3f, %.3f, %.3f)$" % (P1, rho1, v1),
+    "Centre: $(P, \\rho, v) = (%.3f, %.3f, %.3f)$" % (P1, rho1, v1),
     fontsize=10,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.7,
     "Outskirts: $(P, \\rho, v) = (%.3f, %.3f, %.3f)$" % (P2, rho2, v2),
     fontsize=10,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-savefig("KelvinHelmholtz.png", dpi=200)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=10)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=10)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=10)
+plt.text(
+    -0.45, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10
+)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("KelvinHelmholtz.png", dpi=200)
diff --git a/examples/HydroTests/KelvinHelmholtz_2D/run.sh b/examples/HydroTests/KelvinHelmholtz_2D/run.sh
index 583d270a7a0092f606bf782809aae66628862120..95c3076921de9fd373ed8319de34e7b8c69046f0 100755
--- a/examples/HydroTests/KelvinHelmholtz_2D/run.sh
+++ b/examples/HydroTests/KelvinHelmholtz_2D/run.sh
@@ -12,4 +12,5 @@ fi
 
 
 # Plot the solution
+python3 plotSolution.py 100
 python3 makeMovieSwiftsimIO.py
diff --git a/examples/HydroTests/KeplerianRing/plotSolution.py b/examples/HydroTests/KeplerianRing/plotSolution.py
index 239bb639c9488f8f17b76c6ae8679338fd6bf7f9..25c3a846d972e900affda4b14aa2498fabad369c 100644
--- a/examples/HydroTests/KeplerianRing/plotSolution.py
+++ b/examples/HydroTests/KeplerianRing/plotSolution.py
@@ -306,7 +306,7 @@ def plot_extra_info(ax, filename):
     ax.text(-0.49, 0.8, f"Compiler: {compiler_name} {compiler_version}", fontsize=10)
     ax.text(-0.49, 0.7, "Rotations are quoted at $r=1$", fontsize=10)
     ax.plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-    ax.text(-0.49, 0.5, f"$\\textsc{{Swift}}$ {git}", fontsize=10)
+    ax.text(-0.49, 0.5, f"$SWIFT$ {git}", fontsize=10)
     ax.text(-0.49, 0.4, scheme, fontsize=10)
     ax.text(-0.49, 0.3, kernel, fontsize=10)
     ax.text(
diff --git a/examples/HydroTests/Noh_1D/plotSolution.py b/examples/HydroTests/Noh_1D/plotSolution.py
index 3d435c8f7ec3364137f53e0da5fbd44381c358a8..d224b7fe7bd67ac63fa172065c84dcff8a26405c 100644
--- a/examples/HydroTests/Noh_1D/plotSolution.py
+++ b/examples/HydroTests/Noh_1D/plotSolution.py
@@ -29,34 +29,15 @@ v0 = 1
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
+import sys
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
-
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
-
 # Read the simulation data
 sim = h5py.File("noh_%04d.hdf5" % snap, "r")
 boxSize = sim["/Header"].attrs["BoxSize"][0]
@@ -109,77 +90,100 @@ u_s = P_s / (rho_s * (gas_gamma - 1.0))  # internal energy
 s_s = P_s / rho_s ** gas_gamma  # entropic function
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=4,
+    markeredgecolor="none",
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
 
 # Velocity profile --------------------------------
-subplot(231)
-plot(x, v, ".", color="r", ms=4.0)
-plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Velocity}}~v_x$", labelpad=-4)
-xlim(-0.5, 0.5)
-ylim(-1.2, 1.2)
+plt.subplot(231)
+plt.plot(x, v, **scatter_props)
+plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_x$", labelpad=-4)
+plt.xlim(-0.5, 0.5)
+plt.ylim(-1.2, 1.2)
 
 # Density profile --------------------------------
-subplot(232)
-plot(x, rho, ".", color="r", ms=4.0)
-plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
-xlim(-0.5, 0.5)
-ylim(0.95, 4.4)
+plt.subplot(232)
+plt.plot(x, rho, **scatter_props)
+plt.plot(x_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0.95, 4.4)
 
 # Pressure profile --------------------------------
-subplot(233)
-plot(x, P, ".", color="r", ms=4.0)
-plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Pressure}}~P$", labelpad=0)
-xlim(-0.5, 0.5)
-ylim(-0.05, 1.8)
+plt.subplot(233)
+plt.plot(x, P, **scatter_props)
+plt.plot(x_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.xlim(-0.5, 0.5)
+plt.ylim(-0.05, 1.8)
 
 # Internal energy profile -------------------------
-subplot(234)
-plot(x, u, ".", color="r", ms=4.0)
-plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
-xlim(-0.5, 0.5)
-ylim(-0.05, 0.8)
+plt.subplot(234)
+plt.plot(x, u, **scatter_props)
+plt.plot(x_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.xlim(-0.5, 0.5)
+plt.ylim(-0.05, 0.8)
 
 # Entropy profile ---------------------------------
-subplot(235)
-plot(x, S, ".", color="r", ms=4.0)
-plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Entropy}}~S$", labelpad=-9)
-xlim(-0.5, 0.5)
-ylim(-0.05, 0.2)
+plt.subplot(235)
+plt.plot(x, S, **scatter_props)
+plt.plot(x_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("${\\rm{Position}}~x$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=-9)
+plt.xlim(-0.5, 0.5)
+plt.ylim(-0.05, 0.2)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
 
-text(
-    -0.49,
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
     "Noh problem with  $\\gamma=%.3f$ in 1D at $t=%.2f$" % (gas_gamma, time),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.8,
-    "ICs:~~ $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0),
-    fontsize=10,
+    "ICs: $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0),
+    fontsize=text_fontsize,
+)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-
-savefig("Noh.png", dpi=200)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("Noh.png", dpi=200)
diff --git a/examples/HydroTests/Noh_2D/plotSolution.py b/examples/HydroTests/Noh_2D/plotSolution.py
index 1d6c62b26c83e73e56d2a5ff524de2cd51654d77..62e5437c1b82c4862137a0cb738a7eeb22726845 100644
--- a/examples/HydroTests/Noh_2D/plotSolution.py
+++ b/examples/HydroTests/Noh_2D/plotSolution.py
@@ -29,31 +29,13 @@ v0 = 1
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
 from scipy import stats
+import numpy as np
 import h5py
+import sys
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
-
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
@@ -127,82 +109,106 @@ u_s = P_s / (rho_s * (gas_gamma - 1.0))  # internal energy
 s_s = P_s / rho_s ** gas_gamma  # entropic function
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=1,
+    markeredgecolor="none",
+    alpha=0.2,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
 
 # Velocity profile --------------------------------
-subplot(231)
-plot(r, v, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4)
-xlim(0, 0.5)
-ylim(-1.2, 0.4)
+plt.subplot(231)
+plt.plot(r, v, **scatter_props)
+plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, v_bin, yerr=v_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4)
+plt.xlim(0, 0.5)
+plt.ylim(-1.2, 0.4)
 
 # Density profile --------------------------------
-subplot(232)
-plot(r, rho, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
-xlim(0, 0.5)
-ylim(0.95, 19)
+plt.subplot(232)
+plt.plot(r, rho, **scatter_props)
+plt.plot(x_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.xlim(0, 0.5)
+plt.ylim(0.95, 19)
 
 # Pressure profile --------------------------------
-subplot(233)
-plot(r, P, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Pressure}}~P$", labelpad=0)
-xlim(0, 0.5)
-ylim(-0.5, 11)
+plt.subplot(233)
+plt.plot(r, P, **scatter_props)
+plt.plot(x_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, P_bin, yerr=P_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.xlim(0, 0.5)
+plt.ylim(-0.5, 11)
 
 # Internal energy profile -------------------------
-subplot(234)
-plot(r, u, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
-xlim(0, 0.5)
-ylim(-0.05, 0.8)
+plt.subplot(234)
+plt.plot(r, u, **scatter_props)
+plt.plot(x_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, u_bin, yerr=u_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.xlim(0, 0.5)
+plt.ylim(-0.05, 0.8)
 
 # Entropy profile ---------------------------------
-subplot(235)
-plot(r, S, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Entropy}}~S$", labelpad=-9)
-xlim(0, 0.5)
-ylim(-0.05, 0.2)
+plt.subplot(235)
+plt.plot(r, S, **scatter_props)
+plt.plot(x_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, S_bin, yerr=S_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=-9)
+plt.xlim(0, 0.5)
+plt.ylim(-0.05, 0.2)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
+
+text_fontsize = 5
 
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.9,
     "Noh problem with  $\\gamma=%.3f$ in 2D at $t=%.2f$" % (gas_gamma, time),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.8,
-    "ICs:~~ $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0),
-    fontsize=10,
+    "ICs: $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0),
+    fontsize=text_fontsize,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-
-savefig("Noh.png", dpi=200)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
+)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("Noh.png", dpi=200)
diff --git a/examples/HydroTests/Noh_3D/plotSolution.py b/examples/HydroTests/Noh_3D/plotSolution.py
index 31f6b520863b857b409bce2f2d7ebbb57aa05155..6b46ed07802b45abb4f4f0debea813f9b96ee4be 100644
--- a/examples/HydroTests/Noh_3D/plotSolution.py
+++ b/examples/HydroTests/Noh_3D/plotSolution.py
@@ -29,35 +29,16 @@ v0 = 1
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
 from scipy import stats
+import numpy as np
 import h5py
+import sys
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
-
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
-
 # Read the simulation data
 sim = h5py.File("noh_%04d.hdf5" % snap, "r")
 boxSize = sim["/Header"].attrs["BoxSize"][0]
@@ -133,82 +114,106 @@ u_s = P_s / (rho_s * (gas_gamma - 1.0))  # internal energy
 s_s = P_s / rho_s ** gas_gamma  # entropic function
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=1,
+    markeredgecolor="none",
+    alpha=0.2,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
 
 # Velocity profile --------------------------------
-subplot(231)
-plot(r, v, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, v_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4)
-xlim(0, 0.5)
-ylim(-1.2, 0.4)
+plt.subplot(231)
+plt.plot(r, v, **scatter_props)
+plt.plot(x_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, v_bin, yerr=v_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=-4)
+plt.xlim(0, 0.5)
+plt.ylim(-1.2, 0.4)
 
 # Density profile --------------------------------
-subplot(232)
-plot(r, rho, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, rho_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
-xlim(0, 0.5)
-ylim(0.95, 71)
+plt.subplot(232)
+plt.plot(r, rho, **scatter_props)
+plt.plot(x_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.xlim(0, 0.5)
+plt.ylim(0.95, 71)
 
 # Pressure profile --------------------------------
-subplot(233)
-plot(r, P, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, P_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Pressure}}~P$", labelpad=0)
-xlim(0, 0.5)
-ylim(-0.5, 25)
+plt.subplot(233)
+plt.plot(r, P, **scatter_props)
+plt.plot(x_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, P_bin, yerr=P_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.xlim(0, 0.5)
+plt.ylim(-0.5, 25)
 
 # Internal energy profile -------------------------
-subplot(234)
-plot(r, u, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, u_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
-xlim(0, 0.5)
-ylim(-0.05, 0.8)
+plt.subplot(234)
+plt.plot(r, u, **scatter_props)
+plt.plot(x_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, u_bin, yerr=u_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.xlim(0, 0.5)
+plt.ylim(-0.05, 0.8)
 
 # Entropy profile ---------------------------------
-subplot(235)
-plot(r, S, ".", color="r", ms=0.5, alpha=0.2)
-plot(x_s, s_s, "--", color="k", alpha=0.8, lw=1.2)
-errorbar(r_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Entropy}}~S$", labelpad=-9)
-xlim(0, 0.5)
-ylim(-0.05, 0.2)
+plt.subplot(235)
+plt.plot(r, S, **scatter_props)
+plt.plot(x_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(r_bin, S_bin, yerr=S_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=-9)
+plt.xlim(0, 0.5)
+plt.ylim(-0.05, 0.2)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
 
-text(
-    -0.49,
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
     "Noh problem with  $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.8,
-    "ICs:~~ $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0),
-    fontsize=10,
+    "ICs: $(P_0, \\rho_0, v_0) = (%1.2e, %.3f, %.3f)$" % (1e-6, 1.0, -1.0),
+    fontsize=text_fontsize,
+)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-
-savefig("Noh.png", dpi=200)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("Noh.png", dpi=200)
diff --git a/examples/HydroTests/SedovBlast_1D/plotSolution.py b/examples/HydroTests/SedovBlast_1D/plotSolution.py
index 84976689d5e93effaa0d81e1d356848661e58580..93ecd501946b31d17e84376277813f64203abd0c 100644
--- a/examples/HydroTests/SedovBlast_1D/plotSolution.py
+++ b/examples/HydroTests/SedovBlast_1D/plotSolution.py
@@ -35,11 +35,13 @@ gas_gamma = 5.0 / 3.0  # Gas polytropic index
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
+import sys
 
 # Plot parameters
-style.use("../../../tools/stylesheets/mnras.mplstyle")
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
@@ -228,13 +230,13 @@ s_s = P_s / rho_s ** gas_gamma  # entropic function
 
 
 # Plot the interesting quantities
-figure(figsize=(7, 7 / 1.6))
+plt.figure(figsize=(7, 7 / 1.6))
 
 line_color = "C4"
 
 scatter_props = dict(
     marker=".",
-    ms=1,
+    ms=4,
     markeredgecolor="none",
     alpha=1.0,
     zorder=-1,
@@ -243,88 +245,88 @@ scatter_props = dict(
 )
 
 # Velocity profile --------------------------------
-subplot(231)
-plot(r, v_r, **scatter_props)
-plot(r_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2)
-xlabel("Radius $r$")
-ylabel("Radialvelocity $v_r$")
-xlim(0, 1.3 * r_shock)
-ylim(-0.2, 3.8)
+plt.subplot(231)
+plt.plot(r, v_r, **scatter_props)
+plt.plot(r_s, v_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("Radius $r$")
+plt.ylabel("Radialvelocity $v_r$")
+plt.xlim(0, 1.3 * r_shock)
+plt.ylim(-0.2, 3.8)
 
 # Density profile --------------------------------
-subplot(232)
-plot(r, rho, **scatter_props)
-plot(r_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2)
-xlabel("Radius $r$")
-ylabel("Density $\\rho$")
-xlim(0, 1.3 * r_shock)
-ylim(-0.2, 5.2)
+plt.subplot(232)
+plt.plot(r, rho, **scatter_props)
+plt.plot(r_s, rho_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("Radius $r$")
+plt.ylabel("Density $\\rho$")
+plt.xlim(0, 1.3 * r_shock)
+plt.ylim(-0.2, 5.2)
 
 # Pressure profile --------------------------------
-subplot(233)
-plot(r, P, **scatter_props)
-plot(r_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2)
-xlabel("Radius $r$")
-ylabel("Pressure $P$")
-xlim(0, 1.3 * r_shock)
-ylim(-1, 12.5)
+plt.subplot(233)
+plt.plot(r, P, **scatter_props)
+plt.plot(r_s, P_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("Radius $r$")
+plt.ylabel("Pressure $P$")
+plt.xlim(0, 1.3 * r_shock)
+plt.ylim(-1, 12.5)
 
 # Internal energy profile -------------------------
-subplot(234)
-plot(r, u, **scatter_props)
-plot(r_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2)
-xlabel("Radius $r$")
-ylabel("Internal Energy $u$")
-xlim(0, 1.3 * r_shock)
-ylim(-2, 22)
+plt.subplot(234)
+plt.plot(r, u, **scatter_props)
+plt.plot(r_s, u_s, "--", color=line_color, alpha=0.8, lw=1.2)
+plt.xlabel("Radius $r$")
+plt.ylabel("Internal Energy $u$")
+plt.xlim(0, 1.3 * r_shock)
+plt.ylim(-2, 22)
 
 # Entropy profile ---------------------------------
-subplot(235)
-xlabel("Radius $r$")
+plt.subplot(235)
+plt.xlabel("Radius $r$")
 if plot_diffusion or plot_viscosity:
     if plot_diffusion:
-        plot(r, diffusion, **scatter_props)
+        plt.plot(r, diffusion, **scatter_props)
 
     if plot_viscosity:
-        plot(r, viscosity, **scatter_props)
+        plt.plot(r, viscosity, **scatter_props)
 
-    ylabel(r"Rate Coefficient $\alpha$", labelpad=0)
-    legend()
+    plt.ylabel(r"Rate Coefficient $\alpha$", labelpad=0)
+    plt.legend()
 else:
-    plot(r, S, **scatter_props)
-    plot(r_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2)
-    ylabel("Entropy $S$", labelpad=0)
-    ylim(-5, 50)
+    plt.plot(r, S, **scatter_props)
+    plt.plot(r_s, s_s, "--", color=line_color, alpha=0.8, lw=1.2)
+    plt.ylabel("Entropy $S$", labelpad=0)
+    plt.ylim(-5, 50)
 
-xlim(0, 1.3 * r_shock)
+plt.xlim(0, 1.3 * r_shock)
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
 
 text_fontsize = 5
 
-text(
+plt.text(
     -0.45,
     0.9,
     "Sedov blast with  $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time),
     fontsize=text_fontsize,
 )
-text(-0.45, 0.8, "Background $\\rho_0=%.2f$" % (rho_0), fontsize=text_fontsize)
-text(-0.45, 0.7, "Energy injected $E_0=%.2f$" % (E_0), fontsize=text_fontsize)
-plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.45, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize)
-text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
-text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
-text(
+plt.text(-0.45, 0.8, "Background $\\rho_0=%.2f$" % (rho_0), fontsize=text_fontsize)
+plt.text(-0.45, 0.7, "Energy injected $E_0=%.2f$" % (E_0), fontsize=text_fontsize)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "SWIFT %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
     -0.45,
     0.2,
     "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
     fontsize=text_fontsize,
 )
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
 
-tight_layout()
+plt.tight_layout()
 
-savefig("Sedov.png")
+plt.savefig("Sedov.png")
diff --git a/examples/HydroTests/SodShockSpherical_2D/plotSolution.py b/examples/HydroTests/SodShockSpherical_2D/plotSolution.py
index c1a490d0b8a453592abea9815ff3e2bb3aa5f4c9..c9b4f19cd7d5c80ab16ea1f160713f9831b88a6d 100644
--- a/examples/HydroTests/SodShockSpherical_2D/plotSolution.py
+++ b/examples/HydroTests/SodShockSpherical_2D/plotSolution.py
@@ -24,9 +24,11 @@
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
-from scipy import stats
+import matplotlib.pyplot as plt
+import numpy as np
+import scipy.stats as stats
 import h5py
+import sys
 
 # Parameters
 gas_gamma = 5.0 / 3.0  # Polytropic index
@@ -37,26 +39,7 @@ v_R = 0.0  # Velocity right state
 P_L = 1.0  # Pressure left state
 P_R = 0.1  # Pressure right state
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
@@ -71,9 +54,9 @@ eta = sim["/HydroScheme"].attrs["Kernel eta"]
 git = sim["Code"].attrs["Git Revision"]
 
 coords = sim["/PartType0/Coordinates"]
-x = sqrt((coords[:, 0] - 0.5) ** 2 + (coords[:, 1] - 0.5) ** 2)
+x = np.sqrt((coords[:, 0] - 0.5) ** 2 + (coords[:, 1] - 0.5) ** 2)
 vels = sim["/PartType0/Velocities"]
-v = sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2)
+v = np.sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2)
 u = sim["/PartType0/InternalEnergies"][:]
 S = sim["/PartType0/Entropies"][:]
 P = sim["/PartType0/Pressures"][:]
@@ -97,81 +80,113 @@ P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2)
 S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2)
 u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2)
 
-ref = loadtxt("sodShockSpherical2D_exact.txt")
+ref = np.loadtxt("sodShockSpherical2D_exact.txt")
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=1,
+    markeredgecolor="none",
+    alpha=0.5,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
 
 # Velocity profile --------------------------------
-subplot(231)
-plot(x, v, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
+plt.subplot(231)
+plt.plot(x, v, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
 
 # Density profile --------------------------------
-subplot(232)
-plot(x, rho, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.subplot(232)
+plt.plot(x, rho, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
 
 # Pressure profile --------------------------------
-subplot(233)
-plot(x, P, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.subplot(233)
+plt.plot(x, P, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
 
 # Internal energy profile -------------------------
-subplot(234)
-plot(x, u, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.subplot(234)
+plt.plot(x, u, **scatter_props)
+plt.plot(
+    ref[:, 0],
+    ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0),
+    color=line_color,
+    alpha=0.8,
+    lw=1.2,
+)
+plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
 
 # Entropy profile ---------------------------------
-subplot(235)
-plot(x, S, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+plt.subplot(235)
+plt.plot(x, S, **scatter_props)
+plt.plot(
+    ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, color=line_color, alpha=0.8, lw=1.2
+)
+plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
 
-text(
-    -0.49,
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
     "Sod shock with  $\\gamma=%.3f$ in 2D at $t=%.2f$" % (gas_gamma, time),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.8,
     "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.7,
     "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R),
-    fontsize=10,
+    fontsize=text_fontsize,
+)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-
-savefig("SodShock.png", dpi=200)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("SodShock.png", dpi=200)
diff --git a/examples/HydroTests/SodShockSpherical_3D/plotSolution.py b/examples/HydroTests/SodShockSpherical_3D/plotSolution.py
index b6eb53957d0f260897718608f619a84a797a8358..a548f8ed7cbacd5c4bae05164fadc18a6c2cf9d1 100644
--- a/examples/HydroTests/SodShockSpherical_3D/plotSolution.py
+++ b/examples/HydroTests/SodShockSpherical_3D/plotSolution.py
@@ -24,9 +24,11 @@
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
-from scipy import stats
+import matplotlib.pyplot as plt
+import numpy as np
+import scipy.stats as stats
 import h5py
+import sys
 
 # Parameters
 gas_gamma = 5.0 / 3.0  # Polytropic index
@@ -37,26 +39,7 @@ v_R = 0.0  # Velocity right state
 P_L = 1.0  # Pressure left state
 P_R = 0.1  # Pressure right state
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
@@ -71,11 +54,11 @@ eta = sim["/HydroScheme"].attrs["Kernel eta"]
 git = sim["Code"].attrs["Git Revision"]
 
 coords = sim["/PartType0/Coordinates"]
-x = sqrt(
+x = np.sqrt(
     (coords[:, 0] - 0.5) ** 2 + (coords[:, 1] - 0.5) ** 2 + (coords[:, 2] - 0.5) ** 2
 )
 vels = sim["/PartType0/Velocities"]
-v = sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2 + vels[:, 2] ** 2)
+v = np.sqrt(vels[:, 0] ** 2 + vels[:, 1] ** 2 + vels[:, 2] ** 2)
 u = sim["/PartType0/InternalEnergies"][:]
 S = sim["/PartType0/Entropies"][:]
 P = sim["/PartType0/Pressures"][:]
@@ -99,81 +82,113 @@ P_sigma_bin = np.sqrt(P2_bin - P_bin ** 2)
 S_sigma_bin = np.sqrt(S2_bin - S_bin ** 2)
 u_sigma_bin = np.sqrt(u2_bin - u_bin ** 2)
 
-ref = loadtxt("sodShockSpherical3D_exact.txt")
+ref = np.loadtxt("sodShockSpherical3D_exact.txt")
 
 # Plot the interesting quantities
-figure()
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=1,
+    markeredgecolor="none",
+    alpha=0.5,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
 
 # Velocity profile --------------------------------
-subplot(231)
-plot(x, v, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, v_bin, yerr=v_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
+plt.subplot(231)
+plt.plot(x, v, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
 
 # Density profile --------------------------------
-subplot(232)
-plot(x, rho, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.subplot(232)
+plt.plot(x, rho, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
 
 # Pressure profile --------------------------------
-subplot(233)
-plot(x, P, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, P_bin, yerr=P_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.subplot(233)
+plt.plot(x, P, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
 
 # Internal energy profile -------------------------
-subplot(234)
-plot(x, u, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0), "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, u_bin, yerr=u_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.subplot(234)
+plt.plot(x, u, **scatter_props)
+plt.plot(
+    ref[:, 0],
+    ref[:, 3] / ref[:, 1] / (gas_gamma - 1.0),
+    color=line_color,
+    alpha=0.8,
+    lw=1.2,
+)
+plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
 
 # Entropy profile ---------------------------------
-subplot(235)
-plot(x, S, ".", color="r", ms=0.2)
-plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, "k--", alpha=0.8, lw=1.2)
-errorbar(x_bin, S_bin, yerr=S_sigma_bin, fmt=".", ms=8.0, color="b", lw=1.2)
-xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+plt.subplot(235)
+plt.plot(x, S, **scatter_props)
+plt.plot(
+    ref[:, 0], ref[:, 3] / ref[:, 1] ** gas_gamma, color=line_color, alpha=0.8, lw=1.2
+)
+plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0)
 
 # Information -------------------------------------
-subplot(236, frameon=False)
+plt.subplot(236, frameon=False)
 
-text(
-    -0.49,
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
     "Sod shock with  $\\gamma=%.3f$ in 3D at $t=%.2f$" % (gas_gamma, time),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.8,
     "Left:~~ $(P_L, \\rho_L, v_L) = (%.3f, %.3f, %.3f)$" % (P_L, rho_L, v_L),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-text(
-    -0.49,
+plt.text(
+    -0.45,
     0.7,
     "Right: $(P_R, \\rho_R, v_R) = (%.3f, %.3f, %.3f)$" % (P_R, rho_R, v_R),
-    fontsize=10,
+    fontsize=text_fontsize,
+)
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ %s" % git.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
+    0.2,
+    "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta),
+    fontsize=text_fontsize,
 )
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-
-savefig("SodShock.png", dpi=200)
+plt.xlim(-0.5, 0.5)
+plt.ylim(0, 1)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
+
+plt.savefig("SodShock.png", dpi=200)
diff --git a/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py b/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py
deleted file mode 100644
index 897aaebf0891cb6a1e2e821a1dafd79a4c22d9c8..0000000000000000000000000000000000000000
--- a/examples/HydroTests/SquareTest_2D/plotSolutionLegacy.py
+++ /dev/null
@@ -1,191 +0,0 @@
-###############################################################################
-# This file is part of SWIFT.
-# Copyright (c) 2016  Matthieu Schaller (schaller@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
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-##############################################################################
-
-# Computes the analytical solution of the square test
-
-# Parameters
-gas_gamma = 5.0 / 3.0  # Gas adiabatic index
-gamma = 5.0 / 3.0  # Gas adiabatic index
-rho0 = 4  # Gas central density
-rho1 = 1  # Gas outskirt density
-P0 = 2.5  # Gas central pressure
-P1 = 2.5  # Gas central pressure
-vx = 0.0  # Random velocity for all particles
-vy = 0.0
-
-# ---------------------------------------------------------------
-# Don't touch anything after this.
-# ---------------------------------------------------------------
-
-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": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
-
-snap = int(sys.argv[1])
-
-# Read the simulation data
-sim = h5py.File("square_%04d.hdf5" % snap, "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-time = sim["/Header"].attrs["Time"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"]
-kernel = sim["/HydroScheme"].attrs["Kernel function"]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"]
-eta = sim["/HydroScheme"].attrs["Kernel eta"]
-git = sim["Code"].attrs["Git Revision"]
-
-# Analytical soltion
-centre_x = 0.5 + time * vx
-centre_y = 0.5 + time * vy
-
-while centre_x > 1.0:
-    centre_x -= 1.0
-while centre_x < 0.0:
-    centre_x += 1.0
-while centre_y > 1.0:
-    centre_y -= 1.0
-while centre_y < 0.0:
-    centre_y += 1.0
-
-pos = sim["/PartType0/Coordinates"][:, :]
-vel = sim["/PartType0/Velocities"][:, :]
-v_norm = sqrt(vel[:, 0] ** 2 + vel[:, 1] ** 2)
-rho = sim["/PartType0/Densities"][:]
-u = sim["/PartType0/InternalEnergies"][:]
-S = sim["/PartType0/Entropies"][:]
-P = sim["/PartType0/Pressures"][:]
-x = pos[:, 0] - centre_x
-y = pos[:, 1] - centre_y
-
-# Box wrapping
-x[x > 0.5] -= 1.0
-x[x < -0.5] += 1.0
-y[y > 0.5] -= 1.0
-y[y < -0.5] += 1.0
-
-# Azimuthal velocity profile -----------------------------
-subplot(231)
-scatter(x, y, c=v_norm, cmap="PuBu", edgecolors="face", s=4, vmin=-1.0, vmax=1.0)
-text(0.47, 0.47, "${\\rm{Velocity~norm}}$", ha="right", va="top", backgroundcolor="w")
-plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2)
-plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=-7)
-xlim(-0.5, 0.5)
-ylim(-0.5, 0.5)
-
-# Radial density profile --------------------------------
-subplot(232)
-scatter(x, y, c=rho, cmap="PuBu", edgecolors="face", s=4, vmin=0.0, vmax=4.0)
-text(0.47, 0.47, "${\\rm{Density}}$", ha="right", va="top", backgroundcolor="w")
-plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2)
-plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=-7)
-xlim(-0.5, 0.5)
-ylim(-0.5, 0.5)
-
-# Radial pressure profile --------------------------------
-subplot(233)
-scatter(x, y, c=P, cmap="PuBu", edgecolors="face", s=4, vmin=2, vmax=4)
-text(0.47, 0.47, "${\\rm{Pressure}}$", ha="right", va="top", backgroundcolor="w")
-plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2)
-plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=-7)
-xlim(-0.5, 0.5)
-ylim(-0.5, 0.5)
-
-# Internal energy profile --------------------------------
-subplot(234)
-scatter(x, y, c=u, cmap="PuBu", edgecolors="face", s=4, vmin=0.5, vmax=4.0)
-text(0.47, 0.47, "${\\rm{Internal~energy}}$", ha="right", va="top", backgroundcolor="w")
-plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2)
-plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=-7)
-xlim(-0.5, 0.5)
-ylim(-0.5, 0.5)
-
-# Radial entropy profile --------------------------------
-subplot(235)
-scatter(x, y, c=S, cmap="PuBu", edgecolors="face", s=4, vmin=0.0, vmax=3.0)
-text(0.47, 0.47, "${\\rm{Entropy}}$", ha="right", va="top", backgroundcolor="w")
-plot([-0.25, 0.25], [0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, 0.25], [-0.25, -0.25], "--", color="k", alpha=0.8, lw=2)
-plot([0.25, 0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-plot([-0.25, -0.25], [-0.25, 0.25], "--", color="k", alpha=0.8, lw=2)
-xlabel("${\\rm{Position}}~x$", labelpad=0)
-ylabel("${\\rm{Position}}~y$", labelpad=-7)
-xlim(-0.5, 0.5)
-ylim(-0.5, 0.5)
-
-
-# Information -------------------------------------
-subplot(236, frameon=False)
-
-text(
-    -0.49,
-    0.9,
-    "Square test with $\\gamma=%.3f$ at $t=%.2f$" % (gas_gamma, time),
-    fontsize=10,
-)
-text(-0.49, 0.8, "Centre:~~~ $(P, \\rho) = (%.3f, %.3f)$" % (P0, rho0), fontsize=10)
-text(-0.49, 0.7, "Outskirts: $(P, \\rho) = (%.3f, %.3f)$" % (P1, rho1), fontsize=10)
-plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-text(-0.49, 0.5, "$\\textsc{Swift}$ %s" % git, fontsize=10)
-text(-0.49, 0.4, scheme, fontsize=10)
-text(-0.49, 0.3, kernel, fontsize=10)
-text(-0.49, 0.2, "$%.2f$ neighbours ($\\eta=%.3f$)" % (neighbours, eta), fontsize=10)
-xlim(-0.5, 0.5)
-ylim(0, 1)
-xticks([])
-yticks([])
-
-savefig("SquareTest.png", dpi=200)
diff --git a/examples/HydroTests/VacuumSpherical_2D/plotSolution.py b/examples/HydroTests/VacuumSpherical_2D/plotSolution.py
index bf53bc8373338937db2fa4092dcece9e07f528b2..ef6f398ec7105bc946cb375167c93b8cc067c1bc 100644
--- a/examples/HydroTests/VacuumSpherical_2D/plotSolution.py
+++ b/examples/HydroTests/VacuumSpherical_2D/plotSolution.py
@@ -17,14 +17,14 @@
 #
 ##############################################################################
 
-import numpy as np
 import matplotlib
 
 matplotlib.use("Agg")
-import pylab as pl
+import matplotlib.pyplot as plt
+import numpy as np
+from scipy import stats
 import h5py
 import sys
-import scipy.stats as stats
 
 # Parameters
 gamma = 5.0 / 3.0  # Polytropic index
@@ -35,28 +35,8 @@ rhoR = 0.0  # Initial vacuum density
 vR = 0.0  # Initial vacuum velocity
 PR = 0.0  # Initial vacuum pressure
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-pl.rcParams.update(params)
-
-# Read the snapshot index from the command line argument
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
+
 snap = int(sys.argv[1])
 
 # Open the file and read the relevant data
@@ -104,104 +84,115 @@ x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1])
 ref = np.loadtxt("vacuumSpherical2D_exact.txt")
 
 # Plot the interesting quantities
-fig, ax = pl.subplots(2, 3)
+plt.figure(figsize=(7, 7 / 1.6))
+
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=1,
+    markeredgecolor="none",
+    alpha=0.5,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
+)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
 
 # Velocity profile
-ax[0][0].plot(x, v, "r.", markersize=0.2)
-ax[0][0].plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, linewidth=1.2)
-ax[0][0].errorbar(
-    x_bin, v_bin, yerr=v_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
-)
-ax[0][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[0][0].set_ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
-ax[0][0].set_xlim(0.0, 0.4)
-ax[0][0].set_ylim(-0.1, 3.2)
+plt.subplot(231)
+plt.plot(x, v, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
+plt.xlim(0.0, 0.4)
+plt.ylim(-0.1, 3.2)
 
 # Density profile
-ax[0][1].plot(x, rho, "r.", markersize=0.2)
-ax[0][1].plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, linewidth=1.2)
-ax[0][1].errorbar(
-    x_bin,
-    rho_bin,
-    yerr=rho_sigma_bin,
-    fmt=".",
-    markersize=8.0,
-    color="b",
-    linewidth=1.2,
-)
-ax[0][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
-ax[0][1].set_xlim(0.0, 0.4)
+plt.subplot(232)
+plt.plot(x, rho, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.xlim(0.0, 0.4)
 
 # Pressure profile
-ax[0][2].plot(x, P, "r.", markersize=0.2)
-ax[0][2].plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, linewidth=1.2)
-ax[0][2].errorbar(
-    x_bin, P_bin, yerr=P_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
-)
-ax[0][2].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad=0)
-ax[0][2].set_xlim(0.0, 0.4)
+plt.subplot(233)
+plt.plot(x, P, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.xlim(0.0, 0.4)
 
 # Internal energy profile
-ax[1][0].plot(x, u, "r.", markersize=0.2)
-ax[1][0].plot(
-    ref[:, 0], ref[:, 3] / ref[:, 1] / (gamma - 1.0), "k--", alpha=0.8, linewidth=1.2
-)
-ax[1][0].errorbar(
-    x_bin, u_bin, yerr=u_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
+plt.subplot(234)
+plt.plot(x, u, **scatter_props)
+plt.plot(
+    ref[:, 0],
+    ref[:, 3] / ref[:, 1] / (gamma - 1.0),
+    color=line_color,
+    alpha=0.8,
+    lw=1.2,
 )
-ax[1][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
-ax[1][0].set_xlim(0.0, 0.4)
+plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.xlim(0.0, 0.4)
 
 # Entropy profile
-ax[1][1].plot(x, S, "r.", markersize=0.2)
-ax[1][1].plot(
-    ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "k--", alpha=0.8, linewidth=1.2
-)
-ax[1][1].errorbar(
-    x_bin, S_bin, yerr=S_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
-)
-ax[1][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0)
-ax[1][1].set_xlim(0.0, 0.4)
-ax[1][1].set_ylim(0.0, 4.0)
+plt.subplot(235)
+plt.plot(x, S, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+plt.xlim(0.0, 0.4)
+plt.ylim(0.0, 4.0)
 
 # Run information
-ax[1][2].set_frame_on(False)
-ax[1][2].text(
-    -0.49,
+plt.subplot(236, frameon=False)
+
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
-    "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(gamma, time),
-    fontsize=10,
+    "Vacuum test with $\\gamma={0:.3f}$ in 2D at $t = {1:.2f}$".format(gamma, time),
+    fontsize=text_fontsize,
 )
-ax[1][2].text(
-    -0.49,
+plt.text(
+    -0.45,
     0.8,
-    "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL),
-    fontsize=10,
+    "Left: $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL),
+    fontsize=text_fontsize,
 )
-ax[1][2].text(
-    -0.49,
+plt.text(
+    -0.45,
     0.7,
     "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PR, rhoR, vR),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10)
-ax[1][2].text(-0.49, 0.4, scheme, fontsize=10)
-ax[1][2].text(-0.49, 0.3, kernel, fontsize=10)
-ax[1][2].text(
-    -0.49,
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ {0}".format(git.decode("utf-8")), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
     0.2,
     "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-ax[1][2].set_xlim(-0.5, 0.5)
-ax[1][2].set_ylim(0.0, 1.0)
-ax[1][2].set_xticks([])
-ax[1][2].set_yticks([])
+plt.xlim(-0.5, 0.5)
+plt.ylim(0.0, 1.0)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
 
-pl.tight_layout()
-pl.savefig("Vacuum.png", dpi=200)
+plt.savefig("Vacuum.png", dpi=200)
diff --git a/examples/HydroTests/VacuumSpherical_3D/getGlass.sh b/examples/HydroTests/VacuumSpherical_3D/getGlass.sh
old mode 100644
new mode 100755
diff --git a/examples/HydroTests/VacuumSpherical_3D/plotSolution.py b/examples/HydroTests/VacuumSpherical_3D/plotSolution.py
index dc0b491cbc561163796477ac62a460e6805f2442..cb5ab373718cfcd4c3c99717a59d48f8a23501f0 100644
--- a/examples/HydroTests/VacuumSpherical_3D/plotSolution.py
+++ b/examples/HydroTests/VacuumSpherical_3D/plotSolution.py
@@ -17,14 +17,14 @@
 #
 ##############################################################################
 
-import numpy as np
 import matplotlib
 
 matplotlib.use("Agg")
-import pylab as pl
+import matplotlib.pyplot as plt
+import numpy as np
+from scipy import stats
 import h5py
 import sys
-import scipy.stats as stats
 
 # Parameters
 gamma = 5.0 / 3.0  # Polytropic index
@@ -35,28 +35,8 @@ rhoR = 0.0  # Initial vacuum density
 vR = 0.0  # Initial vacuum velocity
 PR = 0.0  # Initial vacuum pressure
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.045,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.05,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-pl.rcParams.update(params)
-
-# Read the snapshot index from the command line argument
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
+
 snap = int(sys.argv[1])
 
 # Open the file and read the relevant data
@@ -105,105 +85,115 @@ x_bin = 0.5 * (x_bin_edge[1:] + x_bin_edge[:-1])
 
 ref = np.loadtxt("vacuumSpherical3D_exact.txt")
 
-# Plot the interesting quantities
-fig, ax = pl.subplots(2, 3)
+plt.figure(figsize=(7, 7 / 1.6))
 
-# Velocity profile
-ax[0][0].plot(x, v, "r.", markersize=0.2)
-ax[0][0].plot(ref[:, 0], ref[:, 2], "k--", alpha=0.8, linewidth=1.2)
-ax[0][0].errorbar(
-    x_bin, v_bin, yerr=v_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
+line_color = "C4"
+binned_color = "C2"
+binned_marker_size = 4
+
+scatter_props = dict(
+    marker=".",
+    ms=1,
+    markeredgecolor="none",
+    alpha=0.5,
+    zorder=-1,
+    rasterized=True,
+    linestyle="none",
 )
-ax[0][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[0][0].set_ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
-ax[0][0].set_xlim(0.0, 0.4)
-ax[0][0].set_ylim(-0.1, 3.2)
+
+errorbar_props = dict(color=binned_color, ms=binned_marker_size, fmt=".", lw=1.2)
+
+# Velocity profile
+plt.subplot(231)
+plt.plot(x, v, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 2], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, v_bin, yerr=v_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Velocity}}~v_r$", labelpad=0)
+plt.xlim(0.0, 0.4)
+plt.ylim(-0.1, 3.2)
 
 # Density profile
-ax[0][1].plot(x, rho, "r.", markersize=0.2)
-ax[0][1].plot(ref[:, 0], ref[:, 1], "k--", alpha=0.8, linewidth=1.2)
-ax[0][1].errorbar(
-    x_bin,
-    rho_bin,
-    yerr=rho_sigma_bin,
-    fmt=".",
-    markersize=8.0,
-    color="b",
-    linewidth=1.2,
-)
-ax[0][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[0][1].set_ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
-ax[0][1].set_xlim(0.0, 0.4)
+plt.subplot(232)
+plt.plot(x, rho, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 1], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, rho_bin, yerr=rho_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Density}}~\\rho$", labelpad=0)
+plt.xlim(0.0, 0.4)
 
 # Pressure profile
-ax[0][2].plot(x, P, "r.", markersize=0.2)
-ax[0][2].plot(ref[:, 0], ref[:, 3], "k--", alpha=0.8, linewidth=1.2)
-ax[0][2].errorbar(
-    x_bin, P_bin, yerr=P_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
-)
-ax[0][2].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[0][2].set_ylabel("${\\rm{Pressure}}~P$", labelpad=0)
-ax[0][2].set_xlim(0.0, 0.4)
+plt.subplot(233)
+plt.plot(x, P, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 3], color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, P_bin, yerr=P_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Pressure}}~P$", labelpad=0)
+plt.xlim(0.0, 0.4)
 
 # Internal energy profile
-ax[1][0].plot(x, u, "r.", markersize=0.2)
-ax[1][0].plot(
-    ref[:, 0], ref[:, 3] / ref[:, 1] / (gamma - 1.0), "k--", alpha=0.8, linewidth=1.2
+plt.subplot(234)
+plt.plot(x, u, **scatter_props)
+plt.plot(
+    ref[:, 0],
+    ref[:, 3] / ref[:, 1] / (gamma - 1.0),
+    color=line_color,
+    alpha=0.8,
+    lw=1.2,
 )
-ax[1][0].errorbar(
-    x_bin, u_bin, yerr=u_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
-)
-ax[1][0].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[1][0].set_ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
-ax[1][0].set_xlim(0.0, 0.4)
+plt.errorbar(x_bin, u_bin, yerr=u_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Internal~Energy}}~u$", labelpad=0)
+plt.xlim(0.0, 0.4)
 
 # Entropy profile
-ax[1][1].plot(x, S, "r.", markersize=0.2)
-ax[1][1].plot(
-    ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, "k--", alpha=0.8, linewidth=1.2
-)
-ax[1][1].errorbar(
-    x_bin, S_bin, yerr=S_sigma_bin, fmt=".", markersize=8.0, color="b", linewidth=1.2
-)
-ax[1][1].set_xlabel("${\\rm{Radius}}~r$", labelpad=0)
-ax[1][1].set_ylabel("${\\rm{Entropy}}~S$", labelpad=0)
-ax[1][1].set_xlim(0.0, 0.4)
-ax[1][1].set_ylim(0.0, 4.0)
+plt.subplot(235)
+plt.plot(x, S, **scatter_props)
+plt.plot(ref[:, 0], ref[:, 3] / ref[:, 1] ** gamma, color=line_color, alpha=0.8, lw=1.2)
+plt.errorbar(x_bin, S_bin, yerr=S_sigma_bin, **errorbar_props)
+plt.xlabel("${\\rm{Radius}}~r$", labelpad=0)
+plt.ylabel("${\\rm{Entropy}}~S$", labelpad=0)
+plt.xlim(0.0, 0.4)
+plt.ylim(0.0, 4.0)
 
 # Run information
-ax[1][2].set_frame_on(False)
-ax[1][2].text(
-    -0.49,
+plt.subplot(236, frameon=False)
+
+text_fontsize = 5
+
+plt.text(
+    -0.45,
     0.9,
-    "Vacuum test with $\\gamma={0:.3f}$ in 1D at $t = {1:.2f}$".format(gamma, time),
-    fontsize=10,
+    "Vacuum test with $\\gamma={0:.3f}$ in 3D at $t = {1:.2f}$".format(gamma, time),
+    fontsize=text_fontsize,
 )
-ax[1][2].text(
-    -0.49,
+plt.text(
+    -0.45,
     0.8,
-    "Left:~~ $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL),
-    fontsize=10,
+    "Left: $(P_L, \\rho_L, v_L) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PL, rhoL, vL),
+    fontsize=text_fontsize,
 )
-ax[1][2].text(
-    -0.49,
+plt.text(
+    -0.45,
     0.7,
     "Right: $(P_R, \\rho_R, v_R) = ({0:.3f}, {1:.3f}, {2:.3f})$".format(PR, rhoR, vR),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-ax[1][2].plot([-0.49, 0.1], [0.62, 0.62], "k-", lw=1)
-ax[1][2].text(-0.49, 0.5, "$\\textsc{{Swift}}$ {0}".format(git), fontsize=10)
-ax[1][2].text(-0.49, 0.4, scheme, fontsize=10)
-ax[1][2].text(-0.49, 0.3, kernel, fontsize=10)
-ax[1][2].text(
-    -0.49,
+plt.plot([-0.45, 0.1], [0.62, 0.62], "k-", lw=1)
+plt.text(-0.45, 0.5, "$SWIFT$ {0}".format(git.decode("utf-8")), fontsize=text_fontsize)
+plt.text(-0.45, 0.4, scheme.decode("utf-8"), fontsize=text_fontsize)
+plt.text(-0.45, 0.3, kernel.decode("utf-8"), fontsize=text_fontsize)
+plt.text(
+    -0.45,
     0.2,
     "${0:.2f}$ neighbours ($\\eta={1:.3f}$)".format(neighbours, eta),
-    fontsize=10,
+    fontsize=text_fontsize,
 )
-ax[1][2].set_xlim(-0.5, 0.5)
-ax[1][2].set_ylim(0.0, 1.0)
-ax[1][2].set_xticks([])
-ax[1][2].set_yticks([])
+plt.xlim(-0.5, 0.5)
+plt.ylim(0.0, 1.0)
+plt.xticks([])
+plt.yticks([])
+
+plt.tight_layout()
 
-pl.tight_layout()
-pl.savefig("Vacuum.png", dpi=200)
+plt.savefig("Vacuum.png", dpi=200)
diff --git a/examples/IdealisedCluster/IdealisedCluster_M15/getIC.sh b/examples/IdealisedCluster/IdealisedCluster_M15/getIC.sh
new file mode 100755
index 0000000000000000000000000000000000000000..ec17bb5aa7f770f17cf56a5b1686048b43cc5a19
--- /dev/null
+++ b/examples/IdealisedCluster/IdealisedCluster_M15/getIC.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# fiducial IC (T_0 = 10^7 K)
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_fid.hdf5
+
+# Different central temperatures (T_0)
+#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_Tmin725.hdf5
+#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_Tmin75.hdf5
+#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_Tmin80.hdf5
+
+# Fiducial IC at different resolutions
+#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_lowres8.hdf5
+#wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/IdealisedCluster/M5_H15_lowres512.hdf5
diff --git a/examples/IdealisedCluster/IdealisedCluster_M15/idealised_cluster_M15.yml b/examples/IdealisedCluster/IdealisedCluster_M15/idealised_cluster_M15.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cb36183ef4e51f57d8b91b5fc26ff44a89de7323
--- /dev/null
+++ b/examples/IdealisedCluster/IdealisedCluster_M15/idealised_cluster_M15.yml
@@ -0,0 +1,231 @@
+# Define some meta-data about the simulation
+MetaData:
+  run_name:   Idealised-Cluster-M15-fid
+
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.98841e43    # 10^10 M_sun in grams
+  UnitLength_in_cgs:   3.08567758e21 # kpc in centimeters
+  UnitVelocity_in_cgs: 1e5           # km/s in centimeters per second
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
+
+# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.)
+TimeIntegration:
+  time_begin:        0.     # The starting time of the simulation (in internal units).
+  time_end:          8.184  # The end time of the simulation 8 Gyr (in internal units).
+  dt_min:            1e-16  # The minimal time-step size of the simulation (in internal units).
+  dt_max:            1e-2   # The maximal time-step size of the simulation (in internal units).
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:   cluster     # Common part of the name of output files
+  time_first: 0.          # (Optional) Time of the first output if non-cosmological time-integration (in internal units)
+  delta_time: 0.01023     # Time difference between consecutive outputs (in internal units) 0.01023 TU = 10 Myr
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:           1.01
+  scale_factor_first:   0.01
+
+# Parameters for the self-gravity scheme
+Gravity:
+  eta:                         0.025     # Constant dimensionless multiplier for time integration.
+  MAC:                         geometric # Use the geometric opening angle condition
+  theta_cr:                    0.7       # Opening angle (Multipole acceptance criterion)
+  use_tree_below_softening:    0
+  max_physical_baryon_softening: 1.2     # Maximal Plummer-equivalent softening length in physical coordinates for baryon particles (in internal units).
+  
+# Parameters for the hydrodynamics scheme
+SPH:
+  resolution_eta:                    1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
+  h_min_ratio:                       0.01     # Minimal smoothing length in units of softening.
+  h_max:                             800.     # Maximal smoothing length in co-moving internal units.
+  CFL_condition:                     0.2      # Courant-Friedrich-Levy condition for time integration.
+  minimal_temperature:               100.0    # (internal units)
+  particle_splitting:                1        # Particle splitting is ON
+  particle_splitting_mass_threshold: 3e-3     # (internal units, i.e. 7e6 Msun ~ 4x initial gas particle mass)
+  H_mass_fraction:       0.756
+
+# Parameters of the stars neighbour search
+Stars:
+  resolution_eta:        1.1642     # Target smoothing length in units of the mean inter-particle separation
+  h_tolerance:           7e-3
+  luminosity_filename:   ./photometry
+  birth_time:           -9.207      # (Optional) Initial birth times of *all* the stars to be used if we are overwriting them. (-1 means the stars remain inactive feedback-wise througout the run).
+  overwrite_birth_time:  1          # (Optional) Do we want to overwrite the birth time of the stars read from the ICs? (default: 0).
+
+Scheduler:
+  max_top_level_cells:   16
+  cell_split_size:       200
+  
+Restarts:
+  onexit:       1
+  delta_hours:  6.0
+  max_run_time: 71.5                 # Three days minus fergie time
+  resubmit_on_exit:   1
+  resubmit_command:   ./resub.sh
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:               M15_fiducial.hdf5  # The file to read
+  periodic:                0                # Are we running with periodic ICs?
+  stars_smoothing_length:  0.5
+
+# NFW potential parameters
+NFWPotential:
+  useabspos:          0             # 0 -> positions based on centre, 1 -> absolute positions 
+  position:           [0.0,0.0,0.0] # Location of centre of the NFW potential with respect to centre of the box (internal units) if useabspos=0 otherwise with respect to the 0,0,0, coordinates.
+  concentration:      5.6           # Concentration of the halo
+  M_200:              10000.0        # Mass of the halo (M_200 in internal units)
+  h:                  0.704         # Critical density (internal units).
+  timestep_mult:      0.01          # Dimensionless pre-factor for the time-step condition, basically determines fraction of orbital time we need to do an integration step
+  bulgefraction:   0.0025           # Bulge mass fraction
+  epsilon: 1.2                      # Softening of the NFW potential
+
+
+# Impose primoridal metallicity
+EAGLEChemistry:
+  init_abundance_metal:     0.004457      # Inital fraction of particle mass in *all* metals 
+  init_abundance_Hydrogen:  0.749796    # Inital fraction of particle mass in Hydrogen
+  init_abundance_Helium:    0.245747    # Inital fraction of particle mass in Helium
+  init_abundance_Carbon:    0.000788    # Inital fraction of particle mass in Carbon
+  init_abundance_Nitrogen:  0.000231    # Inital fraction of particle mass in Nitrogen
+  init_abundance_Oxygen:    0.001911    # Inital fraction of particle mass in Oxygen
+  init_abundance_Neon:      0.000419    # Inital fraction of particle mass in Neon
+  init_abundance_Magnesium: 0.000236    # Inital fraction of particle mass in Magnesium
+  init_abundance_Silicon:   0.000222    # Inital fraction of particle mass in Silicon
+  init_abundance_Iron:      0.000431    # Inital fraction of particle mass in Iron
+
+# COLIBRE cooling parameters
+COLIBRECooling:
+  dir_name:                ./UV_dust1_CR1_G1_shield1.hdf5 # Location of the cooling tables
+  H_reion_z:               7.5               # Redshift of Hydrogen re-ionization (Planck 2018)
+  H_reion_eV_p_H:          2.0
+  He_reion_z_centre:       3.5               # Redshift of the centre of the Helium re-ionization Gaussian
+  He_reion_z_sigma:        0.5               # Spread in redshift of the  Helium re-ionization Gaussian
+  He_reion_eV_p_H:         2.0               # Energy inject by Helium re-ionization in electron-volt per Hydrogen atom
+  delta_logTEOS_subgrid_properties: 0.3      # delta log T above the EOS below which the subgrid properties use Teq assumption
+  rapid_cooling_threshold:          0.333333 # Switch to rapid cooling regime for dt / t_cool above this threshold.
+
+# EAGLE star formation parameters
+EAGLEStarFormation:
+  SF_threshold:                      Subgrid      # Zdep (Schaye 2004) or Subgrid
+  SF_model:                          PressureLaw  # PressureLaw (Schaye et al. 2008) or SchmidtLaw
+  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.
+  min_over_density:                  100.0        # The over-density above which star-formation is allowed.
+  KS_high_density_threshold_H_p_cm3: 1e8          # 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.
+  EOS_entropy_margin_dex:            0.3          # When using Z-based SF threshold, logarithm base 10 of the maximal entropy above the EOS at which stars can form.
+  threshold_norm_H_p_cm3:            0.1          # When using Z-based SF threshold, normalisation of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  threshold_Z0:                      0.002        # When using Z-based SF threshold, reference metallicity (metal mass fraction) for the metal-dependant threshold for star formation.
+  threshold_slope:                   -0.64        # When using Z-based SF threshold, slope of the metal-dependant star formation threshold
+  threshold_max_density_H_p_cm3:     10.0         # When using Z-based SF threshold, maximal density of the metal-dependant density threshold for star formation in Hydrogen atoms per cm^3.
+  threshold_temperature1_K:          1000         # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming.
+  threshold_temperature2_K:          31622        # When using subgrid-based SF threshold, subgrid temperature below which gas is star-forming if also above the density limit.
+  threshold_number_density_H_p_cm3:  10           # When using subgrid-based SF threshold, subgrid number density above which gas is star-forming if also below the second temperature limit.
+  
+# Parameters for the EAGLE "equation of state"
+EAGLEEntropyFloor:
+  Jeans_density_threshold_H_p_cm3: 1e-4      # 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:        800       # 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:        10.        # Temperature of the EAGLE Cool limiter entropy floor at the density threshold expressed in Kelvin. (NOTE: This is below the min T and hence this floor does nothing)
+  Cool_gamma_effective:           1.         # Slope the of the EAGLE Cool limiter entropy floor
+
+# EAGLE feedback model
+EAGLEFeedback:
+  use_SNII_feedback:                    1               # Global switch for SNII thermal (stochastic) feedback.
+  use_SNIa_feedback:                    1               # Global switch for SNIa thermal (continuous) feedback.
+  use_AGB_enrichment:                   1               # Global switch for enrichement from AGB stars.
+  use_SNII_enrichment:                  1               # Global switch for enrichement from SNII stars.
+  use_SNIa_enrichment:                  1               # Global switch for enrichement from SNIa stars.
+  filename:                             ./yieldtables/  # Path to the directory containing the EAGLE yield tables.
+  IMF_min_mass_Msun:                    0.1             # Minimal stellar mass considered for the Chabrier IMF in solar masses.
+  IMF_max_mass_Msun:                  100.0             # Maximal stellar mass considered for the Chabrier IMF in solar masses.
+  SNII_min_mass_Msun:                   8.0             # Minimal mass considered for SNII stars in solar masses.
+  SNII_max_mass_Msun:                 100.0             # Maximal mass considered for SNII stars in solar masses.
+  SNII_feedback_model:                  MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity
+  SNII_sampled_delay:                   1               # Sample the SNII lifetimes to do feedback.
+  SNII_delta_T_K:                       3.16228e7       # Change in temperature to apply to the gas particle in a SNII thermal feedback event in Kelvin.
+  SNII_energy_erg:                      1.0e51          # Energy of one SNII explosion in ergs.
+  SNII_energy_fraction_function:        Independent     # Type of functional form to use for scaling the energy fraction with density and metallicity ('EAGLE', 'Separable', or 'Independent').
+  SNII_energy_fraction_min:             0.5             # Minimal fraction of energy applied in a SNII feedback event.
+  SNII_energy_fraction_max:             1.0             # Maximal fraction of energy applied in a SNII feedback event.
+  SNII_energy_fraction_delta_E_n:       6.0             # Maximal energy increase due to high density (only used if SNII_energy_fraction_function is 'Independent').
+  SNII_energy_fraction_Z_0:             0.0012663729    # Pivot point for the metallicity dependance of the SNII energy fraction (metal mass fraction).
+  SNII_energy_fraction_n_0_H_p_cm3:     1.4588          # Pivot point for the birth density dependance of the SNII energy fraction in cm^-3.
+  SNII_energy_fraction_n_Z:             0.8686          # Power-law for the metallicity dependance of the SNII energy fraction.
+  SNII_energy_fraction_n_n:             0.8686          # Power-law for the birth density dependance of the SNII energy fraction.
+  SNII_energy_fraction_use_birth_density: 0             # Are we using the density at birth to compute f_E or at feedback time?
+  SNII_energy_fraction_use_birth_metallicity: 0         # Are we using the metallicity at birth to compuote f_E or at feedback time?
+  SNIa_DTD:                             Exponential     # Functional form of the SNIa delay time distribution.
+  SNIa_DTD_delay_Gyr:                   0.04            # Stellar age after which SNIa start in Gyr (40 Myr corresponds to stars ~ 8 Msun).
+  SNIa_DTD_exp_timescale_Gyr:           2.0             # Time-scale of the exponential decay of the SNIa rates in Gyr.
+  SNIa_DTD_exp_norm_p_Msun:             0.002           # Normalisation of the SNIa rates in inverse solar masses.
+  SNIa_energy_erg:                     1.0e51           # Energy of one SNIa explosion in ergs.
+  AGB_ejecta_velocity_km_p_s:          10.0             # Velocity of the AGB ejectas in km/s.
+  stellar_evolution_age_cut_Gyr:        0.1             # Stellar age in Gyr above which the enrichment is down-sampled.
+  stellar_evolution_sampling_rate:       10             # Number of time-steps in-between two enrichment events for a star above the age threshold.
+  SNII_yield_factor_Hydrogen:           1.0             # (Optional) Correction factor to apply to the Hydrogen yield from the SNII channel.
+  SNII_yield_factor_Helium:             1.0             # (Optional) Correction factor to apply to the Helium yield from the SNII channel.
+  SNII_yield_factor_Carbon:             0.5             # (Optional) Correction factor to apply to the Carbon yield from the SNII channel.
+  SNII_yield_factor_Nitrogen:           1.0             # (Optional) Correction factor to apply to the Nitrogen yield from the SNII channel.
+  SNII_yield_factor_Oxygen:             1.0             # (Optional) Correction factor to apply to the Oxygen yield from the SNII channel.
+  SNII_yield_factor_Neon:               1.0             # (Optional) Correction factor to apply to the Neon yield from the SNII channel.
+  SNII_yield_factor_Magnesium:          2.0             # (Optional) Correction factor to apply to the Magnesium yield from the SNII channel.
+  SNII_yield_factor_Silicon:            1.0             # (Optional) Correction factor to apply to the Silicon yield from the SNII channel.
+  SNII_yield_factor_Iron:               0.5             # (Optional) Correction factor to apply to the Iron yield from the SNII channel.
+
+# EAGLE AGN model
+EAGLEAGN:
+  subgrid_seed_mass_Msun:             1.0e4           # Black hole subgrid mass at creation time in solar masses.
+  use_multi_phase_bondi:              0               # Compute Bondi rates per neighbour particle?
+  use_subgrid_bondi:                  0               # Compute Bondi rates using the subgrid extrapolation of the gas properties around the BH?
+  with_angmom_limiter:                0               # Are we applying the Rosas-Guevara et al. (2015) viscous time-scale reduction term?
+  viscous_alpha:                      1e6             # Normalisation constant of the viscous time-scale in the accretion reduction term
+  with_boost_factor:                  0               # Are we using the model from Booth & Schaye (2009)?
+  boost_alpha_only:                   0               # If using the boost factor, are we using a constant boost only?
+  boost_alpha:                        1.              # Lowest value for the accretion effeciency for the Booth & Schaye 2009 accretion model.
+  boost_beta:                         2.              # Slope of the power law for the Booth & Schaye 2009 model, set beta to zero for constant alpha models.
+  boost_n_h_star_H_p_cm3:             0.1             # Normalization of the power law for the Booth & Schaye 2009 model in cgs (cm^-3).
+  with_fixed_T_near_EoS:              0               # Are we using a fixed temperature to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term?
+  fixed_T_above_EoS_dex:              0.3             # Distance above the entropy floor for which we use a fixed sound-speed
+  fixed_T_near_EoS_K:                 8000            # Fixed temperature assumed to compute the sound-speed of gas on the entropy floor in the Bondy-Hoyle accretion term
+  radiative_efficiency:               0.1             # Fraction of the accreted mass that gets radiated.
+  use_nibbling:                       1               # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]?
+  min_gas_mass_for_nibbling_Msun:     4e6             # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1.
+  max_eddington_fraction:             1.              # Maximal allowed accretion rate in units of the Eddington rate.
+  eddington_fraction_for_recording:   0.1             # Record the last time BHs reached an Eddington ratio above this threshold.
+  coupling_efficiency:                0.1             # Fraction of the radiated energy that couples to the gas in feedback events.
+  AGN_feedback_model:                 MinimumDistance # Feedback modes: Random, Isotropic, MinimumDistance, MinimumDensity
+  AGN_use_deterministic_feedback:     1               # Deterministic (reservoir) [1] or stochastic [0] AGN feedback?
+  use_variable_delta_T:               1               # Switch to enable adaptive calculation of AGN dT [1], rather than using a constant value [0].
+  AGN_with_locally_adaptive_delta_T:  1               # Switch to enable additional dependence of AGN dT on local gas density and temperature (only used if use_variable_delta_T is 1).
+  AGN_delta_T_mass_norm:              3e8             # Normalisation temperature of AGN dT scaling with BH subgrid mass [K] (only used if use_variable_delta_T is 1).
+  AGN_delta_T_mass_reference:         1e8             # BH subgrid mass at which the normalisation temperature set above applies [M_Sun] (only used if use_variable_delta_T is 1).
+  AGN_delta_T_mass_exponent:          0.666667        # Power-law index of AGN dT scaling with BH subgrid mass (only used if use_variable_delta_T is 1).
+  AGN_delta_T_crit_factor:            1.0             # Multiple of critical dT for numerical efficiency (Dalla Vecchia & Schaye 2012) to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1).
+  AGN_delta_T_background_factor:      0.0             # Multiple of local gas temperature to use as dT floor (only used if use_variable_delta_T and AGN_with_locally_adaptive_delta_T are both 1).
+  AGN_delta_T_min:                    1e7             # Minimum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1).
+  AGN_delta_T_max:                    3e9             # Maximum allowed value of AGN dT [K] (only used if use_variable_delta_T is 1).
+  AGN_delta_T_K:                      3.16228e8       # Change in temperature to apply to the gas particle in an AGN feedback event [K] (used if use_variable_delta_T is 0 or AGN_use_nheat_with_fixed_dT is 1 AND to initialise the BHs).
+  AGN_use_nheat_with_fixed_dT:        0               # Switch to use the constant AGN dT, rather than the adaptive one, for calculating the energy reservoir threshold.
+  AGN_use_adaptive_energy_reservoir_threshold: 0      # Switch to calculate an adaptive AGN energy reservoir threshold.
+  AGN_num_ngb_to_heat:                1.              # Target number of gas neighbours to heat in an AGN feedback event (only used if AGN_use_adaptive_energy_reservoir_threshold is 0).
+  max_reposition_mass:                1e20            # Maximal BH mass considered for BH repositioning in solar masses (large number implies we always reposition).
+  max_reposition_distance_ratio:      3.0             # Maximal distance a BH can be repositioned, in units of the softening length.
+  with_reposition_velocity_threshold: 0               # Should we only reposition to particles that move slowly w.r.t. the black hole?
+  max_reposition_velocity_ratio:      0.5             # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_threshold is 1.
+  min_reposition_velocity_threshold: -1.0             # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_threshold is 1.
+  set_reposition_speed:               0               # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum?
+  with_potential_correction:          1               # Should the BH's own contribution to the potential be removed from the neighbour's potentials when looking for repositioning targets.
+  threshold_major_merger:             0.333           # Mass ratio threshold to consider a BH merger as 'major'
+  threshold_minor_merger:             0.1             # Mass ratio threshold to consider a BH merger as 'minor'
+  merger_threshold_type:              DynamicalEscapeVelocity  # Type of velocity threshold for BH mergers ('CircularVelocity', 'EscapeVelocity', 'DynamicalEscapeVelocity').
+  merger_max_distance_ratio:          3.0             # Maximal distance over which two BHs can merge, in units of the softening length.
+  minimum_timestep_Myr:               0.1             # Minimum of the accretion-limited time-step length.
diff --git a/examples/IdealisedCluster/IdealisedCluster_M15/run.sh b/examples/IdealisedCluster/IdealisedCluster_M15/run.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c791af80b01585233d154582099ac2eaebdd56a2
--- /dev/null
+++ b/examples/IdealisedCluster/IdealisedCluster_M15/run.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+if [ ! -e M15_fiducial.hdf5 ] 
+then     
+    echo "Fetching initial conditions for the idealised cluster example..."
+    ./getIC.sh
+fi
+
+if [ ! -e UV_dust1_CR1_G1_shield1.hdf5 ]
+then
+    echo "Fetching PS20 cooling tables for the isolated galaxy example..."
+    ../getPS20CoolingTables.sh
+fi
+
+if [ ! -e yieldtables ] 
+then     
+    echo "Fetching EAGLE stellar yield tables for the isolated galaxy example..."
+    ../getYieldTable.sh
+fi
+
+if [ ! -e photometry ]
+then
+    echo "Fetching EAGLE photometry tables..."
+    ../getEaglePhotometryTable.sh
+fi
+
+../../../swift --threads=16 --feedback --external-gravity --self-gravity --stars --star-formation --cooling --temperature --hydro --limiter --sync --black-holes idealised_cluster_M15.yml 2>&1 | tee output.log
diff --git a/examples/IdealisedCluster/README b/examples/IdealisedCluster/README
index aba53aaec43c773bd13a2d96a9236cedcc2c8196..18689194da63f0ccba8bd627bae226b41d82a39c 100644
--- a/examples/IdealisedCluster/README
+++ b/examples/IdealisedCluster/README
@@ -8,7 +8,10 @@ for the three different halo masse used in Nobels et al. (2022).
 The fiducial ICs for the 10^13 Msun and 10^14 Msun idealised cluster
 contain resolution variations for 5 different resolutions. For each
 halo mass the central temperature variations discussed in the paper
-are provided. 
+are provided.
+The 10^15 Msun halo of the same series introduced by Husko et
+al. (2022) https://arxiv.org/abs/2206.06402 is also available at
+different resolution and different central temperatures.
 
 The code should be configured using a NFW profile and the desired subgrid
 models:
diff --git a/examples/RadiativeTransferTests/Advection_1D/makeIC.py b/examples/RadiativeTransferTests/Advection_1D/makeIC.py
index 14ac808ca0ba7ce942ad87ce3b6da687e3bc1314..c3396255f456ec8ad124188c8f82172b545be5e3 100755
--- a/examples/RadiativeTransferTests/Advection_1D/makeIC.py
+++ b/examples/RadiativeTransferTests/Advection_1D/makeIC.py
@@ -31,11 +31,10 @@
 # Third photon group: Gaussian.
 # -----------------------------------------------------------
 
-from swiftsimio import Writer
-
-import unyt
-import numpy as np
 import h5py
+import numpy as np
+import unyt
+from swiftsimio import Writer
 
 # define unit system to use
 unitsystem = unyt.unit_systems.cgs_unit_system
@@ -53,12 +52,11 @@ n_p = 1000
 outputfilename = "advection_1D.hdf5"
 
 
-def initial_condition(x, V):
+def initial_condition(x):
     """
     The initial conditions that will be advected
 
     x: particle position. 3D unyt array
-    V: particle "volume". 1D unyt array or scalar
 
     returns: 
     E: photon energy density for each photon group. List of scalars with size of nPhotonGroups
@@ -137,7 +135,7 @@ if __name__ == "__main__":
     w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s)
     w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 1000 * unyt.g
     w.gas.internal_energy = (
-        np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / (unyt.g)
+        np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g
     )
 
     # Generate initial guess for smoothing lengths based on MIPS
@@ -166,7 +164,7 @@ if __name__ == "__main__":
         parts.create_dataset(dsetname, data=fluxdata)
 
     for p in range(nparts):
-        E, Flux = initial_condition(xp[p], dx)
+        E, Flux = initial_condition(xp[p])
         for g in range(nPhotonGroups):
             Esetname = "PhotonEnergiesGroup{0:d}".format(g + 1)
             parts[Esetname][p] = E[g]
diff --git a/examples/RadiativeTransferTests/Advection_1D/plotSolution.py b/examples/RadiativeTransferTests/Advection_1D/plotSolution.py
index b46b4be56542ce8db698c7452372a4547f4f922d..ed2ac188579a3c37c8ce31dcaa65c2c82fd0879e 100755
--- a/examples/RadiativeTransferTests/Advection_1D/plotSolution.py
+++ b/examples/RadiativeTransferTests/Advection_1D/plotSolution.py
@@ -27,12 +27,13 @@
 # all snapshots available in the workdir
 # ----------------------------------------------
 
-import sys
 import os
-import swiftsimio
+import sys
+
+import matplotlib as mpl
 import numpy as np
+import swiftsimio
 from matplotlib import pyplot as plt
-import matplotlib as mpl
 
 # Parameters users should/may tweak
 plot_all_data = True  # plot all groups and all photon quantities
@@ -150,9 +151,8 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
                 advected_positions[overshooters] -= boxsize
 
         analytical_solutions = np.zeros((nparts, ngroups), dtype=np.float64)
-        dx = boxsize / nparts
         for p in range(part_positions.shape[0]):
-            E, F = initial_condition(advected_positions[p], dx)
+            E, F = initial_condition(advected_positions[p])
             for g in range(ngroups):
                 analytical_solutions[p, g] = E[g]
 
@@ -278,7 +278,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
                 ax.set_ylim(fixed_min, fixed_max)
 
     # add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time)
@@ -293,7 +293,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
 
 def get_minmax_vals(snaplist):
     """
-    Find minimal and maximal values for energy and flux
+    Find minimal and maximal values for energy and flux,
     so you can fix axes limits over all snapshots
 
     snaplist: list of snapshot filenames
diff --git a/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml b/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml
index ea084b3b2cd60800859cd300a0eff98692b3b06d..76fb70f54b4485e29eef7dcbbad088fa36c0ec19 100644
--- a/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml
+++ b/examples/RadiativeTransferTests/Advection_1D/rt_advection1D.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "RT_advection-1D"
+  run_name: RT_advection-1D
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -11,6 +11,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   4.e-1  # The end time of the simulation (in internal units).
   dt_min:     1.e-8 # The minimal time-step size of the simulation (in internal units).
@@ -36,27 +37,28 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./advection_1D.hdf5  # The file to read
-  periodic:   1                     # peridoc ICs
+  periodic:   1                     # periodic ICs
 
 Restarts:
   delta_hours:        72        # (Optional) decimal hours between dumps of restart files.
 
 GEARRT:
-    f_reduce_c: 1.              # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.99         # CFL condition for RT, independent of hydro
-    photon_groups_Hz: [1., 2.]  # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N).
-    use_const_emission_rates: 1 # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [1e-32, 1e-32, 1e-32]   # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set.
-    hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
-    skip_thermochemistry: 1                           # ignore thermochemistry.
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  f_reduce_c: 1.                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  f_limit_cooling_time: 0.0       # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off.
+  CFL_condition: 0.99             # CFL condition for RT, independent of hydro
+  photon_groups_Hz: [0., 1., 2.]  # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [0., 0., 0.]  # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.76                  # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  stellar_spectrum_type: 0                       # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17 # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  skip_thermochemistry: 1                        # ignore thermochemistry.
+  set_initial_ionization_mass_fractions: 1       # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.76                         # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 0.                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.24                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                         # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
 
 SPHM1RT:
   cred: 2.99792458e10              # reduce the speed of light in the code unit
diff --git a/examples/RadiativeTransferTests/Advection_1D/run.sh b/examples/RadiativeTransferTests/Advection_1D/run.sh
index f0a3f3faa9e0283359adbbe1aede36edfa01853d..0b64a9b773f87b40c1237b91960bd09311da6c38 100755
--- a/examples/RadiativeTransferTests/Advection_1D/run.sh
+++ b/examples/RadiativeTransferTests/Advection_1D/run.sh
@@ -18,7 +18,7 @@ fi
     --stars \
     --feedback \
     --external-gravity \
-    -e \
+    --fpe \
     ./rt_advection1D.yml 2>&1 | tee output.log
 
 python3 ./plotSolution.py
diff --git a/examples/RadiativeTransferTests/Advection_2D/makeIC.py b/examples/RadiativeTransferTests/Advection_2D/makeIC.py
index c6d9da0543680de5f9b7b2d60c97c9a1abcba35c..923f9a56bf5964d9fa4dcea4854ce62e64a9fc04 100755
--- a/examples/RadiativeTransferTests/Advection_2D/makeIC.py
+++ b/examples/RadiativeTransferTests/Advection_2D/makeIC.py
@@ -32,11 +32,10 @@
 # Fourth photon group: Circle moving radially from the center
 # -------------------------------------------------------------
 
-from swiftsimio import Writer
-import unyt
-import numpy as np
 import h5py
-
+import numpy as np
+import unyt
+from swiftsimio import Writer
 
 # define unit system to use
 unitsystem = unyt.unit_systems.cgs_unit_system
@@ -72,8 +71,8 @@ def initial_condition(x):
     # Group 1 Photons:
     # -------------------
 
-    in_x = x[0] > 0.33 * boxsize and x[0] < 0.66 * boxsize
-    in_y = x[1] > 0.33 * boxsize and x[1] < 0.66 * boxsize
+    in_x = 0.33 * boxsize < x[0] < 0.66 * boxsize
+    in_y = 0.33 * boxsize < x[1] < 0.66 * boxsize
     if in_x and in_y:
         E = 1.0
     else:
@@ -91,8 +90,8 @@ def initial_condition(x):
     # Group 2 Photons:
     # -------------------
 
-    in_x = x[0] > 0.33 * boxsize and x[0] < 0.66 * boxsize
-    in_y = x[1] > 0.33 * boxsize and x[1] < 0.66 * boxsize
+    in_x = 0.33 * boxsize < x[0] < 0.66 * boxsize
+    in_y = 0.33 * boxsize < x[1] < 0.66 * boxsize
     if in_x and in_y:
         E = 2.0
     else:
@@ -168,7 +167,7 @@ if __name__ == "__main__":
     w.gas.velocities = np.zeros((numPart, 3)) * (unyt.cm / unyt.s)
     w.gas.masses = np.ones(numPart, dtype=np.float64) * 1000 * unyt.g
     w.gas.internal_energy = (
-        np.ones(numPart, dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / (unyt.g)
+        np.ones(numPart, dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g
     )
 
     # Generate initial guess for smoothing lengths based on MIPS
@@ -188,7 +187,7 @@ if __name__ == "__main__":
 
     for grp in range(nPhotonGroups):
         dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1)
-        energydata = np.zeros((nparts), dtype=np.float32)
+        energydata = np.zeros(nparts, dtype=np.float32)
         parts.create_dataset(dsetname, data=energydata)
 
         dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1)
diff --git a/examples/RadiativeTransferTests/Advection_2D/plotSolution.py b/examples/RadiativeTransferTests/Advection_2D/plotSolution.py
index 29bb0b012fe75c6eaddcd1cb0ffcbed92e6c2f39..d0ebe4bf62817d989a72471246965cd461245d2e 100755
--- a/examples/RadiativeTransferTests/Advection_2D/plotSolution.py
+++ b/examples/RadiativeTransferTests/Advection_2D/plotSolution.py
@@ -26,12 +26,13 @@
 # all snapshots available in the workdir
 # ----------------------------------------------------
 
-import sys
+import gc
 import os
+import sys
+
+import matplotlib as mpl
 import swiftsimio
-import gc
 from matplotlib import pyplot as plt
-import matplotlib as mpl
 from mpl_toolkits.axes_grid1 import make_axes_locatable
 
 # Parameters users should/may tweak
@@ -219,7 +220,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
                 ax.set_ylabel("Energies")
 
     # Add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time)
@@ -235,7 +236,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
 
 def get_minmax_vals(snaplist):
     """
-    Find minimal and maximal values for energy and flux
+    Find minimal and maximal values for energy and flux,
     so you can fix axes limits over all snapshots
 
     snaplist: list of snapshot filenames
diff --git a/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py b/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py
index a5180c5507a90573b1f6e60a40782b2b523af81b..3a4c37b3027720eb270283ef834f5296d9777c84 100755
--- a/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py
+++ b/examples/RadiativeTransferTests/Advection_2D/plotSolutionScatter.py
@@ -26,12 +26,13 @@
 # all snapshots available in the workdir
 # ----------------------------------------------------
 
-import sys
+import gc
 import os
+import sys
+
+import matplotlib as mpl
 import swiftsimio
-import gc
 from matplotlib import pyplot as plt
-import matplotlib as mpl
 
 # Parameters users should/may tweak
 plot_all_data = True  # plot all groups and all photon quantities
@@ -177,10 +178,10 @@ def plot_photons(filename):
             ax.set_ylabel("Energies [$" + energy_units_str + "$]")
 
     # Add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
-        title += ", $z$ = {0:.2e}".format(meta.z)
-    title += ", $t$ = {0:.2e}".format(meta.time)
+        title += r", $z$ = {0:.2e}".format(meta.z)
+    title += r", $t$ = {0:.2e}".format(meta.time)
     fig.suptitle(title)
 
     plt.tight_layout()
diff --git a/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml b/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml
index 7e1abffec16d6d7c0b65bb21413b22a3f6a8a523..b3d7030f4ceb9afa7d0b0079a5fe8dc93a0f6596 100644
--- a/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml
+++ b/examples/RadiativeTransferTests/Advection_2D/rt_advection2D.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "RT_advection-2D"
+  run_name: RT_advection-2D
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -11,6 +11,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.     # The starting time of the simulation (in internal units).
   time_end:   3.4e-1 # The end time of the simulation (in internal units).
   dt_min:     1.e-08 # The minimal time-step size of the simulation (in internal units).
@@ -36,24 +37,25 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./advection_2D.hdf5  # The file to read
-  periodic:   1                    # peridoc ICs
+  periodic:   1                    # periodic ICs
 
 GEARRT:
-    f_reduce_c: 1.                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.99             # CFL condition for RT, independent of hydro
-    photon_groups_Hz: [1., 2., 3]   # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N).
-    use_const_emission_rates: 1     # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [1e-32, 1e-32, 1e-32, 1e-32]   # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set.
-    hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    skip_thermochemistry: 1                           # Skip thermochemsitry.
+  f_reduce_c: 1.                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  f_limit_cooling_time: 0.0                       # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off.
+  CFL_condition: 0.99                             # CFL condition for RT, independent of hydro
+  photon_groups_Hz: [1., 2., 3., 4.]              # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                 # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1e-32, 1e-32, 1e-32, 1e-32]   # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.76                   # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  stellar_spectrum_type: 0                        # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17  # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  set_initial_ionization_mass_fractions: 1        # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.76                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.24                         # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                         # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  skip_thermochemistry: 1                         # Skip thermochemsitry.
 
 SPHM1RT:
   cred: 2.99792458e10              # reduce the speed of light in the code unit
diff --git a/examples/RadiativeTransferTests/Advection_2D/run.sh b/examples/RadiativeTransferTests/Advection_2D/run.sh
index 2ea05d8e385609e23036943121b2b2a1f13838f1..56851dab7bec3104c985b77dde565557dc6ec995 100755
--- a/examples/RadiativeTransferTests/Advection_2D/run.sh
+++ b/examples/RadiativeTransferTests/Advection_2D/run.sh
@@ -25,7 +25,7 @@ fi
     --stars \
     --feedback \
     --external-gravity \
-    -e \
+    --fpe \
     ./rt_advection2D.yml 2>&1 | tee output.log
 
 python3 ./plotSolution.py
diff --git a/examples/RadiativeTransferTests/CoolingTest/README b/examples/RadiativeTransferTests/CoolingTest/README
index f298cab204a28f441736c7a7b771b56c6a32a05d..eb50535a856ff2a0a09f1440272ee9f22601d7e5 100644
--- a/examples/RadiativeTransferTests/CoolingTest/README
+++ b/examples/RadiativeTransferTests/CoolingTest/README
@@ -1,5 +1,7 @@
 Check on a simple case whether the cooling works correctly without any
 radiation being present: Use a uniform 3D box of hot gas and let it cool down.
 
+The reference solution assumes case A recombination.
+
 To run with GEAR-RT, compile swift with:
-    --with-rt=GEAR_4 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none  --with-grackle=$GRACKLE_ROOT 
+    --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none  --with-grackle=$GRACKLE_ROOT 
diff --git a/examples/RadiativeTransferTests/CoolingTest/makeIC.py b/examples/RadiativeTransferTests/CoolingTest/makeIC.py
index fce0b9220bee4de021f91e5f957b9b86dafae89c..a050adcf2463f4e95e2daaa7f6c283a267f80885 100755
--- a/examples/RadiativeTransferTests/CoolingTest/makeIC.py
+++ b/examples/RadiativeTransferTests/CoolingTest/makeIC.py
@@ -25,14 +25,12 @@
 # (ui.adsabs.harvard.edu/abs/2017MNRAS.466.2217S)
 # -----------------------------------------------------------
 
+import h5py
+import numpy as np
+import unyt
 from swiftsimio import Writer
 from swiftsimio.units import cosmo_units
 
-import unyt
-import numpy as np
-import h5py
-
-
 # number of particles in each dimension
 n_p = 20
 nparts = n_p ** 3
@@ -136,11 +134,11 @@ parts = F["/PartType0"]
 # Assume everything is ionized initially
 # NOTE: grackle doesn't really like exact zeroes, so
 # use something very small instead.
-HIdata = np.ones((nparts), dtype=np.float32) * 1e-12
-HIIdata = np.ones((nparts), dtype=np.float32) * XH
-HeIdata = np.ones((nparts), dtype=np.float32) * 1e-12
-HeIIdata = np.ones((nparts), dtype=np.float32) * 1e-12
-HeIIIdata = np.ones((nparts), dtype=np.float32) * XHe
+HIdata = np.ones(nparts, dtype=np.float32) * 1e-12
+HIIdata = np.ones(nparts, dtype=np.float32) * XH
+HeIdata = np.ones(nparts, dtype=np.float32) * 1e-12
+HeIIdata = np.ones(nparts, dtype=np.float32) * 1e-12
+HeIIIdata = np.ones(nparts, dtype=np.float32) * XHe
 
 parts.create_dataset("MassFractionHI", data=HIdata)
 parts.create_dataset("MassFractionHII", data=HIIdata)
diff --git a/examples/RadiativeTransferTests/CoolingTest/plotSolution.py b/examples/RadiativeTransferTests/CoolingTest/plotSolution.py
index 5c464512a1b7335f4d66e2e83469b873ebb2b6c9..b8622ff805835589115dac07804ea31095709c7e 100755
--- a/examples/RadiativeTransferTests/CoolingTest/plotSolution.py
+++ b/examples/RadiativeTransferTests/CoolingTest/plotSolution.py
@@ -23,12 +23,13 @@
 # weight, and mass fractions
 # -------------------------------------------
 
+import copy
+import os
+
 import numpy as np
-from matplotlib import pyplot as plt
-import unyt
 import swiftsimio
-import os
-import copy
+import unyt
+from matplotlib import pyplot as plt
 
 # arguments for plots of results
 plotkwargs = {"alpha": 0.5}
@@ -39,7 +40,6 @@ legendprops = {"size": 8}
 # snapshot basenames
 snapshot_base = "output"
 
-
 # -----------------------------------------------------------------------
 
 energy_units = unyt.Msun * unyt.kpc ** 2 / unyt.kyr ** 2
@@ -122,6 +122,7 @@ def get_ion_mass_fractions(swiftsimio_loaded_data):
     meta = data.metadata
     gas = data.gas
     with_rt = True
+    scheme = None
     try:
         scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8"))
     except KeyError:
@@ -152,6 +153,8 @@ def get_ion_mass_fractions(swiftsimio_loaded_data):
                     * mamu[column]
                 )
                 setattr(imf, column, mass_function)
+        else:
+            raise ValueError("Unknown scheme", scheme)
     else:
         # try to find solutions for cooling only runs
         imf = {
@@ -250,9 +253,19 @@ if __name__ == "__main__":
     if with_rt:
         ngroups = photon_energies.shape[0]
 
-    t_ref, dt_ref, T_ref, mu_ref, rho_ref, rhoHI_ref, rhoHII_ref, rhoHeI_ref, rhoHeII_ref, rhoHeIII_ref, rhoe_ref = np.loadtxt(
-        "RTCoolingTestReference.txt", dtype=np.float64, unpack=True
-    )
+    refdata = np.loadtxt("RTCoolingTestReference.txt", dtype=np.float64)
+    t_ref = refdata[:, 0]
+    dt_ref = refdata[:, 1]
+    T_ref = refdata[:, 2]
+    mu_ref = refdata[:, 3]
+    rho_ref = refdata[:, 4]
+    rhoHI_ref = refdata[:, 5]
+    rhoHII_ref = refdata[:, 6]
+    rhoHeI_ref = refdata[:, 7]
+    rhoHeII_ref = refdata[:, 8]
+    rhoHeIII_ref = refdata[:, 9]
+    rhoe_ref = refdata[:, 10]
+
     t_ref *= 1e-6  # turn to Myrs
     mass_fraction_ref = np.empty((t_ref.shape[0], 5))
     mass_fraction_ref[:, 0] = rhoHI_ref / rho_ref
@@ -322,7 +335,7 @@ if __name__ == "__main__":
             ax4.plot(
                 t,
                 photon_energies[g, :],
-                label=f"photon energies group {g+1}",
+                label=f"photon energies group {g + 1}",
                 **plotkwargs,
             )
         ax4.plot(t, u, label="gas internal energy", **plotkwargs)
diff --git a/examples/RadiativeTransferTests/CoolingTest/rt_cooling_test.yml b/examples/RadiativeTransferTests/CoolingTest/rt_cooling_test.yml
index 5ddf7552caa164122aaf9e9fa8ab4cc1d5a543d3..e24893955b6b0c21095c252457ea178540cc6a96 100644
--- a/examples/RadiativeTransferTests/CoolingTest/rt_cooling_test.yml
+++ b/examples/RadiativeTransferTests/CoolingTest/rt_cooling_test.yml
@@ -3,7 +3,7 @@ MetaData:
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
-  UnitMass_in_cgs:     1.98848e43    # 10^10 M_sun in grams
+  UnitMass_in_cgs:     1.98848e23    # 10^-10 M_sun in grams
   UnitLength_in_cgs:   3.08567758e21 # 1 kpc in cm
   UnitVelocity_in_cgs: 1e5           # 1 km/s in cm/s
   UnitCurrent_in_cgs:  1             # Amperes
@@ -12,6 +12,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.     # The starting time of the simulation (in internal units).
   time_end:   0.100  # The end time of the simulation (in internal units).
   dt_min:     1.e-8  # The minimal time-step size of the simulation (in internal units).
@@ -41,14 +42,16 @@ InitialConditions:
   periodic:   1                    # periodic ICs
 
 GEARRT:
-    f_reduce_c: 1.e-9               # This test is without actual radiation, so we don't care about this
-    CFL_condition: 0.9              # CFL condition for RT, independent of hydro
-    photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    star_emission_rates_LSol: [1., 1., 1., 1.]        # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:   0.76  # total hydrogen mass fraction
-    use_const_emission_rates: 1     # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter
-    stellar_spectrum_type: 0        # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  f_reduce_c: 1.e-9                                 # This test is without actual radiation, so we don't care about this
+  CFL_condition: 0.9                                # CFL condition for RT, independent of hydro
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [0., 0., 0.]     # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:   0.76                    # total hydrogen mass fraction
+  stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  case_B_recombination: 0                           # reference solution assumes case A recombination
+
 
 GrackleCooling:
   cloudy_table: CloudyData_UVB=HM2012.h5       # Name of the Cloudy Table (available on the grackle bitbucket repository)
diff --git a/examples/RadiativeTransferTests/HeatingTest/README b/examples/RadiativeTransferTests/HeatingTest/README
index 53780fbc881f3f528d1b9c6b7bcace64d656e4bb..18ba3a98b493955657afb9b6cdc309af72fbb4cd 100644
--- a/examples/RadiativeTransferTests/HeatingTest/README
+++ b/examples/RadiativeTransferTests/HeatingTest/README
@@ -3,4 +3,4 @@ sources are present, and the gas should heat up and ionize, while the
 radiation fields should decrease at the same rate.
 
 To run with GEAR-RT, compile swift with:
-    --with-rt=GEAR_4 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT
+    --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT
diff --git a/examples/RadiativeTransferTests/HeatingTest/generate_output_times.py b/examples/RadiativeTransferTests/HeatingTest/generate_output_times.py
new file mode 100755
index 0000000000000000000000000000000000000000..2bf2127b605dc6cce770045612e2c3d0e6fcbc27
--- /dev/null
+++ b/examples/RadiativeTransferTests/HeatingTest/generate_output_times.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+
+# ----------------------------------------------
+# Generate an output times list to your liking
+# ----------------------------------------------
+
+import yaml
+
+with open(r"rt_heating_test.yml") as paramfile:
+    params = yaml.load(paramfile, Loader=yaml.FullLoader)
+
+    unit_l = params["InternalUnitSystem"]["UnitLength_in_cgs"]
+    unit_l = float(unit_l)
+    unit_v = params["InternalUnitSystem"]["UnitVelocity_in_cgs"]
+    unit_v = float(unit_v)
+    t_begin = params["TimeIntegration"]["time_begin"]
+    t_begin = float(t_begin)
+    t_end = params["TimeIntegration"]["time_end"]
+    t_end = float(t_end)
+    dt_min = params["TimeIntegration"]["dt_min"]
+    dt_min = float(dt_min)
+    dt_max = params["TimeIntegration"]["dt_max"]
+    dt_max = float(dt_max)
+
+unit_t = unit_l / unit_v
+unit_myr = unit_t / (3600 * 24 * 365 * 1e6)
+
+
+# If you're rebuilding this output times list:
+# I read this 'dt' out from a run, then used it to generate the output list
+dt_heat = 1.001182e-03
+
+current_t = t_begin
+outputtimes = []
+# first 16 snapshots
+for i in range(16):
+    current_t += dt_heat
+    outputtimes.append(current_t)
+for i in range(16):
+    current_t += 4 * dt_heat
+    outputtimes.append(current_t)
+for i in range(16):
+    current_t += 8 * dt_heat
+    outputtimes.append(current_t)
+for i in range(16):
+    current_t += 16 * dt_heat
+    outputtimes.append(current_t)
+# fill up until heating stops
+while current_t + 32 * dt_heat < t_end:
+    current_t += 32 * dt_heat
+    outputtimes.append(current_t)
+outputtimes.append(t_end)
+
+print("preparing", len(outputtimes), "snapshots")
+
+with open(r"snaplist.txt", "w") as outfile:
+    # THIS HEADER IS IMPORTANT
+    outfile.write("# Time\n")
+    for t in outputtimes:
+        outfile.write("{0:.6g}\n".format(t))
+
+print("written snaplist.txt")
diff --git a/examples/RadiativeTransferTests/HeatingTest/makeIC.py b/examples/RadiativeTransferTests/HeatingTest/makeIC.py
index cc298c77c58ced2ccc923e8a88fa2c258eb0accc..01984de0f5dbca60e0e6e81af952042c9c8c40e1 100755
--- a/examples/RadiativeTransferTests/HeatingTest/makeIC.py
+++ b/examples/RadiativeTransferTests/HeatingTest/makeIC.py
@@ -33,7 +33,7 @@ import h5py
 
 
 # number of particles in each dimension
-n_p = 20
+n_p = 10
 nparts = n_p ** 3
 
 # filename of ICs to be generated
@@ -138,11 +138,11 @@ parts = F["/PartType0"]
 # Assume everything neutral initially
 # NOTE: grackle doesn't really like exact zeroes, so
 # use something very small instead.
-HIdata = np.ones((nparts), dtype=np.float32) * XH
-HIIdata = np.ones((nparts), dtype=np.float32) * 1e-12
-HeIdata = np.ones((nparts), dtype=np.float32) * XHe
-HeIIdata = np.ones((nparts), dtype=np.float32) * 1e-12
-HeIIIdata = np.ones((nparts), dtype=np.float32) * 1e-12
+HIdata = np.ones(nparts, dtype=np.float32) * XH
+HIIdata = np.ones(nparts, dtype=np.float32) * 1e-12
+HeIdata = np.ones(nparts, dtype=np.float32) * XHe
+HeIIdata = np.ones(nparts, dtype=np.float32) * 1e-12
+HeIIIdata = np.ones(nparts, dtype=np.float32) * 1e-12
 
 parts.create_dataset("MassFractionHI", data=HIdata)
 parts.create_dataset("MassFractionHII", data=HIIdata)
@@ -152,26 +152,33 @@ parts.create_dataset("MassFractionHeIII", data=HeIIIdata)
 
 
 # Add photon groups
-nPhotonGroups = 4
+nPhotonGroups = 3
 
 # with this IC, the radiative cooling is negligible.
-photon_energy = u_part * pmass * 5.0
-# with this IC, you can observe the loss of radiative cooling very clearly.
-#  photon_energy = u_part * pmass * 0.025
+#  photon_energy = u_part * pmass * 5.0
+#  photon_energy = np.arange(1, nPhotonGroups+1) * photon_energy
+
+# Fluxes from the Iliev Test0 part3
+fluxes_iliev = np.array([1.350e1, 2.779e1, 6.152e0]) * unyt.erg / unyt.s / unyt.cm ** 2
+energy_density = fluxes_iliev / unyt.c
+photon_energy = energy_density * boxsize ** 3 / nparts
+photon_energy = photon_energy * 0.00001
+
 photon_energy.convert_to_units(cosmo_units["energy"])
 photon_fluxes = 0.333333 * unyt.c * photon_energy
 photon_fluxes.convert_to_units(
     cosmo_units["energy"] * cosmo_units["length"] / cosmo_units["time"]
 )
 
+
 for grp in range(nPhotonGroups):
     dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1)
-    energydata = np.ones((nparts), dtype=np.float32) * photon_energy * (grp + 1)
+    energydata = np.ones(nparts, dtype=np.float32) * photon_energy[grp]
     parts.create_dataset(dsetname, data=energydata)
 
     dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1)
     fluxdata = np.ones((nparts, 3), dtype=np.float32)
-    fluxdata[:, 0] = photon_fluxes * (grp + 1)
+    fluxdata[:, 0] = photon_fluxes[grp]
     parts.create_dataset(dsetname, data=fluxdata)
 
 # close up, and we're done!
diff --git a/examples/RadiativeTransferTests/HeatingTest/plotSolution.py b/examples/RadiativeTransferTests/HeatingTest/plotSolution.py
index 584b4d9badb282dd475f77907f09ba67f50f2cca..2c6cee439cde01eed6161f026582e74d95f99373 100755
--- a/examples/RadiativeTransferTests/HeatingTest/plotSolution.py
+++ b/examples/RadiativeTransferTests/HeatingTest/plotSolution.py
@@ -23,11 +23,13 @@
 # weight, and mass fractions
 # -------------------------------------------
 
+import copy
+import os
+
 import numpy as np
-from matplotlib import pyplot as plt
-import unyt
 import swiftsimio
-import os
+import unyt
+from matplotlib import pyplot as plt
 
 # arguments for plots of results
 plotkwargs = {"alpha": 0.5}
@@ -119,12 +121,10 @@ def get_ion_mass_fractions(swiftsimio_loaded_data):
 
     data = swiftsimio_loaded_data
     meta = data.metadata
-    gas = data.gas
     try:
         scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8"))
     except KeyError:
-        print("This test needs to be run with RT on.")
-        quit()
+        raise ValueError("This test needs to be run with RT on.")
 
     if scheme.startswith("GEAR M1closure"):
         imf = data.gas.ion_mass_fractions
@@ -142,6 +142,8 @@ def get_ion_mass_fractions(swiftsimio_loaded_data):
                 * mamu[column]
             )
             setattr(imf, column, mass_function)
+    else:
+        raise ValueError("Unknown scheme", scheme)
 
     return imf
 
@@ -259,6 +261,7 @@ if __name__ == "__main__":
             t, photon_energies[g, :], label=f"photon energies group {g+1}", **plotkwargs
         )
     ax4.plot(t, u, label=r"gas internal energy", **plotkwargs)
+
     ax4.set_yscale("log")
     ax4.set_xlabel("time [Myr]")
     ax4.set_ylabel(
@@ -268,6 +271,8 @@ if __name__ == "__main__":
     ax4.legend(prop=legendprops)
     ax4.grid()
 
+    for ax in fig.axes:
+        ax.set_xscale("log")
+
     plt.tight_layout()
-    #  plt.show()
     plt.savefig("heating_test.png")
diff --git a/examples/RadiativeTransferTests/HeatingTest/rt_heating_test.yml b/examples/RadiativeTransferTests/HeatingTest/rt_heating_test.yml
index 7fb2339b6625bb94d8b32d49f00e751aca6dfbc0..eec44d2702f8f77b69a11984b6e5fce64bf05849 100644
--- a/examples/RadiativeTransferTests/HeatingTest/rt_heating_test.yml
+++ b/examples/RadiativeTransferTests/HeatingTest/rt_heating_test.yml
@@ -3,8 +3,8 @@ MetaData:
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
-  UnitMass_in_cgs:     1.98848e43    # 10^10 M_sun in grams
-  UnitLength_in_cgs:   3.08567758e21 # 1 kpc in cm
+  UnitMass_in_cgs:     1.98848e33    # 1 M_sun in grams
+  UnitLength_in_cgs:   3.08567758e18 # 1 pc in cm
   UnitVelocity_in_cgs: 1e5           # 1 km/s in cm/s
   UnitCurrent_in_cgs:  1             # Amperes
   UnitTemp_in_cgs:     1             # Kelvin
@@ -12,22 +12,25 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.     # The starting time of the simulation (in internal units).
-  dt_min:     1.e-10  # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1.e-5 # The maximal time-step size of the simulation (in internal units).
-  time_end:   0.00204  #  2 Myrs in internal units.
+  dt_min:     1.0208453377e-08  # 0.01 yr
+  dt_max:     0.0010208453      # 1 kyr
+  time_end:   4.10084           # 4 Myr
 
 
 # Parameters governing the snapshots
 Snapshots:
   basename:            output # Common part of the name of output files
   time_first:          0.     # Time of the first output (in internal units)
-  delta_time:          3.25e-5
+  output_list_on:      1  # (Optional) Enable the output list
+  output_list:         snaplist.txt # (Optional) File containing the output times (see documentation in "Parameter File" section)
+  
 
 # Parameters governing the conserved quantities statistics
 Statistics:
   time_first:          0.
-  delta_time:          3.25e-5
+  delta_time:          1.
 
 # Parameters for the hydrodynamics scheme
 SPH:
@@ -41,12 +44,14 @@ InitialConditions:
   periodic:   1                     # periodic ICs
 
 GEARRT:
-    f_reduce_c: 1e-6                                  # We don't care about the radiation propagation in this test, so let's speed things up
-    CFL_condition: 0.9                                # CFL condition for RT, independent of hydro
-    photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter
-    star_emission_rates_LSol: [1., 1., 1., 1.]        # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    hydrogen_mass_fraction:   0.76                    # total hydrogen mass fraction
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  f_reduce_c: 1e-6                                  # We don't care about the radiation propagation in this test, so let's speed things up
+  CFL_condition: 0.9                                # CFL condition for RT, independent of hydro
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1., 1., 1.]     # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  hydrogen_mass_fraction:   0.76                    # total hydrogen mass fraction
+  stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. 
+  case_B_recombination: 0                           # (Optional) use case B recombination interaction rates.
+
diff --git a/examples/RadiativeTransferTests/HeatingTest/run.sh b/examples/RadiativeTransferTests/HeatingTest/run.sh
index 24fe522a77d5278fba767579f419468d1a96b148..dc11c1fde493956a4e569484f138e0d38c1c365f 100755
--- a/examples/RadiativeTransferTests/HeatingTest/run.sh
+++ b/examples/RadiativeTransferTests/HeatingTest/run.sh
@@ -20,5 +20,4 @@ fi
     --feedback \
     ./rt_heating_test.yml 2>&1 | tee output.log
 
-
 python3 plotSolution.py
diff --git a/examples/RadiativeTransferTests/HeatingTest/snaplist.txt b/examples/RadiativeTransferTests/HeatingTest/snaplist.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a461e83c58b68260b8afbd5529814ce7abd1fe11
--- /dev/null
+++ b/examples/RadiativeTransferTests/HeatingTest/snaplist.txt
@@ -0,0 +1,179 @@
+# Time
+0.00100118
+0.00200236
+0.00300355
+0.00400473
+0.00500591
+0.00600709
+0.00700827
+0.00800946
+0.00901064
+0.0100118
+0.011013
+0.0120142
+0.0130154
+0.0140165
+0.0150177
+0.0160189
+0.0200236
+0.0240284
+0.0280331
+0.0320378
+0.0360426
+0.0400473
+0.044052
+0.0480567
+0.0520615
+0.0560662
+0.0600709
+0.0640756
+0.0680804
+0.0720851
+0.0760898
+0.0800946
+0.088104
+0.0961135
+0.104123
+0.112132
+0.120142
+0.128151
+0.136161
+0.14417
+0.15218
+0.160189
+0.168199
+0.176208
+0.184217
+0.192227
+0.200236
+0.208246
+0.224265
+0.240284
+0.256303
+0.272322
+0.28834
+0.304359
+0.320378
+0.336397
+0.352416
+0.368435
+0.384454
+0.400473
+0.416492
+0.432511
+0.44853
+0.464548
+0.496586
+0.528624
+0.560662
+0.5927
+0.624738
+0.656775
+0.688813
+0.720851
+0.752889
+0.784927
+0.816965
+0.849002
+0.88104
+0.913078
+0.945116
+0.977154
+1.00919
+1.04123
+1.07327
+1.1053
+1.13734
+1.16938
+1.20142
+1.23346
+1.26549
+1.29753
+1.32957
+1.36161
+1.39365
+1.42568
+1.45772
+1.48976
+1.5218
+1.55383
+1.58587
+1.61791
+1.64995
+1.68199
+1.71402
+1.74606
+1.7781
+1.81014
+1.84217
+1.87421
+1.90625
+1.93829
+1.97033
+2.00236
+2.0344
+2.06644
+2.09848
+2.13052
+2.16255
+2.19459
+2.22663
+2.25867
+2.2907
+2.32274
+2.35478
+2.38682
+2.41886
+2.45089
+2.48293
+2.51497
+2.54701
+2.57904
+2.61108
+2.64312
+2.67516
+2.7072
+2.73923
+2.77127
+2.80331
+2.83535
+2.86739
+2.89942
+2.93146
+2.9635
+2.99554
+3.02757
+3.05961
+3.09165
+3.12369
+3.15573
+3.18776
+3.2198
+3.25184
+3.28388
+3.31591
+3.34795
+3.37999
+3.41203
+3.44407
+3.4761
+3.50814
+3.54018
+3.57222
+3.60426
+3.63629
+3.66833
+3.70037
+3.73241
+3.76444
+3.79648
+3.82852
+3.86056
+3.8926
+3.92463
+3.95667
+3.98871
+4.02075
+4.05278
+4.08482
+4.10084
diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/advect_ions.yml b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/advect_ions.yml
index b667b2d57cf25221dae92778e48ff61cc9ec2ea0..1bf391f4a5da004dbe6cdf967fa7b0db9fb729ad 100644
--- a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/advect_ions.yml
+++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/advect_ions.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "advect ions"
+  run_name: advect_ions
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -11,6 +11,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.01   # end time: radiation reaches edge of box
   dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
@@ -40,15 +41,16 @@ InitialConditions:
   periodic:   1                      # periodic ICs. Keep them periodic so we don't loose photon energy.
 
 GEARRT:
-    f_reduce_c: 1.e-5                                 # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9
-    photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [0., 0., 0., 0.]        # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:  0.50                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    set_initial_ionization_mass_fractions: 0          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant over all frequencies. 1: blackbody spectrum.
-    stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
-    skip_thermochemistry: 1
+  f_reduce_c: 1.e-5                                 # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9
+  f_limit_cooling_time: 0.0                         # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off.
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [0., 0., 0.]     # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.50                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  set_initial_ionization_mass_fractions: 0          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant over all frequencies. 1: blackbody spectrum.
+  stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
+  skip_thermochemistry: 1
 
diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/makeIC.py b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/makeIC.py
index 614084f0c69c3b7a94db92bde554d090e55b0df5..0f5621e826d5aa99ada36f38046e7fd70137d2ca 100755
--- a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/makeIC.py
+++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/makeIC.py
@@ -52,7 +52,7 @@ if __name__ == "__main__":
 
     w = Writer(unit_system=cosmo_units, box_size=boxsize, dimension=2)
 
-    # write particle positions ans smoothing lengths
+    # write particle positions and smoothing lengths
     w.gas.coordinates = xp
     w.gas.smoothing_length = h
 
@@ -84,11 +84,11 @@ if __name__ == "__main__":
     pos = parts["Coordinates"]
 
     # Create initial ionization species mass fractions.
-    HIdata = np.ones((nparts), dtype=np.float32) * 0.76
-    HIIdata = np.zeros((nparts), dtype=np.float32)
-    HeIdata = np.ones((nparts), dtype=np.float32) * 0.24
-    HeIIdata = np.zeros((nparts), dtype=np.float32)
-    HeIIIdata = np.zeros((nparts), dtype=np.float32)
+    HIdata = np.ones(nparts, dtype=np.float32) * 0.76
+    HIIdata = np.zeros(nparts, dtype=np.float32)
+    HeIdata = np.ones(nparts, dtype=np.float32) * 0.24
+    HeIIdata = np.zeros(nparts, dtype=np.float32)
+    HeIIIdata = np.zeros(nparts, dtype=np.float32)
 
     mask1 = pos[:, 0] > edgelen / 3
     mask1 = np.logical_and(mask1, pos[:, 0] < 2 * edgelen / 3)
diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/plotIonization.py b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/plotIonization.py
index d3840daa34d6d0f39b753ec81d575d2c9f5467ff..57f79fc99b2e333184ba342c531444fcf690574b 100755
--- a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/plotIonization.py
+++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/plotIonization.py
@@ -23,16 +23,14 @@
 # Plot the ionizing species
 # ----------------------------------------------------
 
-import sys
 import os
+import sys
+
+import matplotlib as mpl
 import swiftsimio
-import numpy as np
-import gc
-import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
 from matplotlib.colors import SymLogNorm
+from mpl_toolkits.axes_grid1 import make_axes_locatable
 
 # Parameters users should/may tweak
 
@@ -213,7 +211,7 @@ def plot_ionization(filename):
         ax.set_xlabel("[kpc]")
         ax.set_ylabel("[kpc]")
 
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time.to("Myr"))
diff --git a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/testIonization.py b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/testIonization.py
index 4d859797ea18f349afd1b8683d8850bd519ee71b..3c737462e2ea28f61897c34c6b0d7642626aa463 100755
--- a/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/testIonization.py
+++ b/examples/RadiativeTransferTests/IonMassFractionAdvectionTest_2D/testIonization.py
@@ -24,16 +24,10 @@
 # remains constant
 # ----------------------------------------------------
 
-import sys
 import os
+
 import swiftsimio
-import numpy as np
-import gc
-import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
-from matplotlib.colors import SymLogNorm
 
 snapshot_base = "output"
 
diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml
index 4e69abd225566727028bee7a5b88a4bc95ad3d5c..f43485abb188ea586eb40797e2acb28e3bf4221e 100644
--- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml
+++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/ion_equil.yml
@@ -1,16 +1,17 @@
 MetaData:
-  run_name: "Ionization Equilibrium Test"
+  run_name: Ionization Equilibrium Test
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
-  UnitMass_in_cgs:     1123.
-  UnitLength_in_cgs:   45233.
-  UnitVelocity_in_cgs: 523.
-  UnitCurrent_in_cgs:  523152.
-  UnitTemp_in_cgs:     15323.
+  UnitMass_in_cgs:     1.98848e23    # 10^-10 M_sun in grams
+  UnitLength_in_cgs:   3.08567758e15 # 1 pc in cm
+  UnitVelocity_in_cgs: 1e5           # 1 km/s in cm/s
+  UnitCurrent_in_cgs:  1             # Amperes
+  UnitTemp_in_cgs:     1             # Kelvin
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   6.250000e-03  # The end time of the simulation (in internal units).
   dt_min:     1.e-6 # The minimal time-step size of the simulation (in internal units).
@@ -39,18 +40,18 @@ InitialConditions:
   periodic:   0                     # periodic ICs
 
 GEARRT:
-    f_reduce_c: 1.                                    # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9                                # CFL condition for RT, independent of hydro
-    photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter
-    star_emission_rates_LSol: [1., 1., 1., 1.]        # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    set_equilibrium_initial_ionization_mass_fractions: 1   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    hydrogen_mass_fraction:   0.76                    # total hydrogen mass fraction
-    set_initial_ionization_mass_fractions: 0          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI : 0.2
-    mass_fraction_HII: 0.2
-    mass_fraction_HeI: 0.2
-    mass_fraction_HeII: 0.2
-    mass_fraction_HeIII: 0.2
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  f_reduce_c: 1.                                    # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9                                # CFL condition for RT, independent of hydro
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1., 1., 1.]     # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  set_equilibrium_initial_ionization_mass_fractions: 1   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  hydrogen_mass_fraction:   0.76                    # total hydrogen mass fraction
+  set_initial_ionization_mass_fractions: 0          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI : 0.2
+  mass_fraction_HII: 0.2
+  mass_fraction_HeI: 0.2
+  mass_fraction_HeII: 0.2
+  mass_fraction_HeIII: 0.2
+  stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py
index dc23ced716b1ee17352aec815d55d40a49383000..33566530ab1d5b80fc63743a7c7f76a700957969 100755
--- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py
+++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/makeIC.py
@@ -25,27 +25,28 @@
 # ionization equilibrium IC setup within GEAR-RT.
 # -----------------------------------------------------------
 
-from swiftsimio import Writer
-
-import unyt
-import numpy as np
 import h5py
+import numpy as np
+import unyt
+from swiftsimio import Writer
+from swiftsimio.units import cosmo_units
 
 # define unit system to use
-unitsystem = unyt.unit_systems.cgs_unit_system
+#  unitsystem = unyt.unit_systems.cgs_unit_system
+unitsystem = cosmo_units
 
 # set minimal and maximal specific internal energy
 umin = 1e9 * unyt.erg / unyt.g
+umin = umin.to(unitsystem["energy"] / unitsystem["mass"])
 umax = 1e20 * unyt.erg / unyt.g
+umax = umax.to(unitsystem["energy"] / unitsystem["mass"])
 
 # Box is 1 Mpc
-boxsize = 1e15 * unitsystem["length"]
-
-# number of photon groups
-nPhotonGroups = 1
+boxsize = 1 * unyt.kpc
+boxsize = boxsize.to(unitsystem["length"])
 
 # number of particles in each dimension
-n_p = 30
+n_p = 15
 nparts = n_p ** 3
 
 # filename of ICs to be generated
@@ -55,6 +56,9 @@ outputfilename = "ionization_equilibrium_test.hdf5"
 xp = unyt.unyt_array(np.zeros((nparts, 3), dtype=np.float64), boxsize.units)
 dx = boxsize / n_p
 
+pmass = 1.0 * unyt.M_Sun
+pmass = pmass.to(unitsystem["mass"])
+
 ind = 0
 for i in range(n_p):
     x = (i + 0.5) * dx
@@ -71,9 +75,9 @@ w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, dimension=3)
 
 w.gas.coordinates = xp
 w.gas.velocities = np.zeros(xp.shape) * (unyt.cm / unyt.s)
-w.gas.masses = np.ones(nparts, dtype=np.float64) * 1000 * unyt.g
+w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * pmass
 w.gas.internal_energy = (
-    np.logspace(np.log10(umin.v), np.log10(umax.v), n_p) * unyt.erg / unyt.g
+    np.logspace(np.log10(umin.v), np.log10(umax.v), nparts) * umax.units
 )
 
 # Generate initial guess for smoothing lengths based on MIPS
@@ -83,7 +87,7 @@ w.gas.generate_smoothing_lengths(boxsize=boxsize, dimension=3)
 w.write(outputfilename)
 
 # Generate initial ionization mass fractions.
-# These are deliberately bogus so you can make sure that
+# These are deliberately bogus, so you can make sure that
 # they have been changed by SWIFT.
 
 F = h5py.File(outputfilename, "r+")
@@ -92,15 +96,15 @@ nparts = header.attrs["NumPart_ThisFile"][0]
 parts = F["/PartType0"]
 
 # this is bogus data for debugging checks.
-HIdata = np.ones((nparts), dtype=np.float32) * 0.11
+HIdata = np.ones(nparts, dtype=np.float32) * 0.11
 parts.create_dataset("MassFractionHI", data=HIdata)
-HIIdata = np.ones((nparts), dtype=np.float32) * 0.22
+HIIdata = np.ones(nparts, dtype=np.float32) * 0.22
 parts.create_dataset("MassFractionHII", data=HIIdata)
-HeIdata = np.ones((nparts), dtype=np.float32) * 0.123
+HeIdata = np.ones(nparts, dtype=np.float32) * 0.123
 parts.create_dataset("MassFractionHeI", data=HeIdata)
-HeIIdata = np.ones((nparts), dtype=np.float32) * 0.234
+HeIIdata = np.ones(nparts, dtype=np.float32) * 0.234
 parts.create_dataset("MassFractionHeII", data=HeIIdata)
-HeIIIdata = np.ones((nparts), dtype=np.float32) * 0.345
+HeIIIdata = np.ones(nparts, dtype=np.float32) * 0.345
 parts.create_dataset("MassFractionHeIII", data=HeIIIdata)
 
 F.close()
diff --git a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py
index 2922f3b577713dc4fcf4a650be431033f666761a..cd2f5c25b288304f0f6a8259aca3dbce7356c004 100755
--- a/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py
+++ b/examples/RadiativeTransferTests/IonizationEquilibriumICSetupTest/plotSolution.py
@@ -71,11 +71,13 @@ def gas_temperature(u, mu, gamma):
 data = swiftsimio.load("output_0000.hdf5")
 gas = data.gas
 u = gas.internal_energies.to("erg/g")
-XHI = gas.ion_mass_fractions.HI
-XHII = gas.ion_mass_fractions.HII
-XHeI = gas.ion_mass_fractions.HeI
-XHeII = gas.ion_mass_fractions.HeII
-XHeIII = gas.ion_mass_fractions.HeIII
+sortind = np.argsort(u)
+u = u[sortind]
+XHI = gas.ion_mass_fractions.HI[sortind]
+XHII = gas.ion_mass_fractions.HII[sortind]
+XHeI = gas.ion_mass_fractions.HeI[sortind]
+XHeII = gas.ion_mass_fractions.HeII[sortind]
+XHeIII = gas.ion_mass_fractions.HeIII[sortind]
 XH = XHI + XHII
 XHe = XHeI + XHeII + XHeIII
 mu = mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII)
@@ -127,6 +129,7 @@ ax3.semilogx(u, XHeIII, label="HeIII", ls="--", **plotkwargs)
 ax3.legend()
 ax3.set_xlabel("specific internal energy [erg/g]")
 ax3.set_ylabel("gas mass fractions [1]")
+ax3.set_yscale("log")
 ax3.grid()
 
 
diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/README b/examples/RadiativeTransferTests/RandomizedBox_3D/README
index 03d686ed719ea8693409699cae4f505362f04a73..1d8fb2855841811b5e7d05ad521e4f5af5e0a485 100644
--- a/examples/RadiativeTransferTests/RandomizedBox_3D/README
+++ b/examples/RadiativeTransferTests/RandomizedBox_3D/README
@@ -15,3 +15,12 @@ check that everything's right.
 
 Compile swift with:
     --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro-dimension=3 --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --enable-debugging-checks --with-grackle=$GRACKLE_ROOT
+
+
+
+Some specific notes for debugging:
+
+-   when running on 2 MPI ranks (at least in the first few steps) cells 27 and
+    151 have proxies on the other rank. However, those are made for the gravity
+    tasks, not for the RT tasks. So their foreign counterparts will have no RT
+    tasks at all.
diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py b/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py
index 3b1850a6dcb092a276d73684e939076e1bbe251d..e50dd0fa48e91b7b9c47d48ab023232323cc2a6b 100755
--- a/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py
+++ b/examples/RadiativeTransferTests/RandomizedBox_3D/makeIC.py
@@ -26,12 +26,11 @@
 # gas and stars that are sampled from a sine wave
 # ---------------------------------------------------------------------
 
+import numpy as np
+import unyt
 from swiftsimio import Writer
 from swiftsimio.units import cosmo_units
 
-import unyt
-import numpy as np
-
 np.random.seed(666)
 
 # Box is 1 Mpc
@@ -79,7 +78,7 @@ vels_max = 200.0
 
 
 def sine(x, amplitude=1.0):
-    # raise the sine by 1.01 so you don't get negative probablities
+    # raise the sine by 1.01, so you don't get negative probablities
     return amplitude * np.sin(x / boxsize.value * 2.0 * np.pi) + 1.01
 
 
@@ -148,7 +147,7 @@ vs_tot = np.vstack((vels, vels_sampled))
 vs = unyt.unyt_array(vs_tot, unyt.km / unyt.s)
 
 
-w = Writer(cosmo_units, boxsize)
+w = Writer(cosmo_units, boxsize, compress=False)
 w.gas.coordinates = xp
 w.stars.coordinates = xs
 w.gas.velocities = vp
diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py
index 04efa0fb13670d1088d9fc65c3c708db0bc123a9..6837842e09302839460955eaa855d8c16ecc50fb 100755
--- a/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py
+++ b/examples/RadiativeTransferTests/RandomizedBox_3D/plotRadiationProjection.py
@@ -27,16 +27,17 @@
 # all snapshots available in the workdir.
 # ----------------------------------------------------
 
-import sys
+import gc
 import os
-import swiftsimio
+import sys
+
+import matplotlib as mpl
 import numpy as np
-import gc
+import swiftsimio
 import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
 from matplotlib.colors import SymLogNorm
+from mpl_toolkits.axes_grid1 import make_axes_locatable
 
 # Parameters users should/may tweak
 # -----------------------------------
@@ -284,7 +285,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
                 ax.set_ylabel("Energies [$" + energy_units_str + "$]")
 
     # Add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time.to(time_units))
diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml b/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml
index 4c5aef09fb51a050668ca0dd12b3f2ab9b4eb613..d2bf103619a00cbbdc0fc18ec0eb52a814d2b669 100644
--- a/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml
+++ b/examples/RadiativeTransferTests/RandomizedBox_3D/randomized-rt.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "randomized-sine"
+  run_name: randomized-sine
 
 # Define the system of units to use internally.
 InternalUnitSystem:
@@ -11,9 +11,10 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 4
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.1   # The end time of the simulation (in internal units).
-  dt_min:     1.e-8 # The minimal time-step size of the simulation (in internal units).
+  dt_min:     1.e-6 # The minimal time-step size of the simulation (in internal units).
   dt_max:     1.e-3 # The maximal time-step size of the simulation (in internal units).
 
 # Parameters governing the snapshots
@@ -36,7 +37,7 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./randomized-sine.hdf5     # The file to read
-  periodic:   1                          # peridoc ICs
+  periodic:   1                          # periodic ICs
 
 # Properties of the stars
 Stars:
@@ -49,7 +50,7 @@ Scheduler:
 Restarts:
   delta_hours:        72        # (Optional) decimal hours between dumps of restart files.
   enable:             1          # (Optional) whether to enable dumping restarts at fixed intervals.
-  stop_steps:         500        # (Optional) how many steps to process before checking if the <subdir>/stop file exists. When present the application will attempt to exit early, dumping restart files first.
+  stop_steps:         128        # (Optional) how many steps to process before checking if the <subdir>/stop file exists. When present the application will attempt to exit early, dumping restart files first.
 
 # Parameters for the self-gravity scheme
 Gravity:
@@ -65,18 +66,18 @@ Gravity:
   # softening_ratio_background:    0.04      # Fraction of the mean inter-particle separation to use as Plummer-equivalent softening for the background DM particles.
 
 GEARRT:
-    f_reduce_c: 1e-3                                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.99                               # CFL condition for RT, independent of hydro
-    photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N).
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter
-    star_emission_rates_LSol: [20., 30., 40., 50.]    # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set.
-    hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    skip_thermochemistry: 1                           # skip thermochemistry.
+  f_reduce_c: 1e-3                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.99                               # CFL condition for RT, independent of hydro
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [20., 30., 40.]  # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  skip_thermochemistry: 1                           # skip thermochemistry.
diff --git a/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh b/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh
index 174fda2baad152abcd52c3abcd2afc1f34801e88..e2171960cfc21169ffb767ccf265cc9071697806 100755
--- a/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh
+++ b/examples/RadiativeTransferTests/RandomizedBox_3D/run.sh
@@ -9,12 +9,26 @@ if [ ! -f 'randomized-sine.hdf5' ]; then
     python3 makeIC.py
 fi
 
+# use cmdline args as shortcut to run with debugger/MPI
+# -g run with gdb
+# -m run with MPI
+# -mg run with MPI and gdb in individual xterm windows
+# -ml run with MPI and write individual output file per MPI rank
 cmd=../../../swift
 if [ $# -gt 0 ]; then
     case "$1" in 
-    g | gdb)
+    -g | g | gdb)
         cmd='gdb --args ../../../swift'
         ;;
+    -m | m | mpi)
+        cmd='mpirun -n 3 ../../../swift_mpi' 
+        ;;
+    -mg | -gm | gm | mg | gmpi | gdbmpi )
+        cmd='mpirun -n 3 xterm -e gdb -ex run --args ../../../swift_mpi'
+        ;;
+    -ml | ml | lm | mpilog | logmpi)
+        cmd='mpirun -n 2 --output-filename individual_rank_output --merge-stderr-to-stdout ../../../swift_mpi'
+        ;;
     *)
         echo unknown cmdline param, running without gdb
         ;;
@@ -24,8 +38,8 @@ fi
 # Run SWIFT with RT
 $cmd \
     --hydro \
-    --threads=9 \
-    --verbose=0  \
+    --threads=3 \
+    --verbose=0 \
     --radiation \
     --self-gravity \
     --stars \
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py
index bd9181c8bcb219950715e003610eae5fac62613f..fbba2ce80b42b3e24bdb86c0d5308630457ab15b 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/makeIC.py
@@ -23,11 +23,11 @@
 # Add a single star in the center of a glass distribution
 # ---------------------------------------------------------------------
 
+import h5py
+import numpy as np
+import unyt
 from swiftsimio import Writer
 from swiftsimio.units import cosmo_units
-import unyt
-import numpy as np
-import h5py
 
 gamma = 5.0 / 3.0
 
@@ -338,7 +338,7 @@ if __name__ == "__main__":
 
     w = Writer(unit_system=cosmo_units, box_size=boxsize, dimension=2)
 
-    # write particle positions ans smoothing lengths
+    # write particle positions and smoothing lengths
     w.gas.coordinates = xp
     w.stars.coordinates = xs
     w.gas.velocities = np.zeros(xp.shape) * (unitL / unyt.Myr)
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/makePropagationTestIC.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/makePropagationTestIC.py
index 189b9a8fe0b05d340217f9d5b80f4921f6866c18..c79156e5d56bdb26d945d41332109d48d4a6e67b 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_2D/makePropagationTestIC.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/makePropagationTestIC.py
@@ -24,11 +24,10 @@
 # Intended for the photon propagation test.
 # ---------------------------------------------------------------------
 
-from swiftsimio import Writer
-import unyt
-import numpy as np
 import h5py
-
+import numpy as np
+import unyt
+from swiftsimio import Writer
 
 glass = h5py.File("glassPlane_128.hdf5", "r")
 parts = glass["PartType0"]
@@ -73,7 +72,7 @@ w.stars.velocities = np.zeros(xs.shape) * (unyt.cm / unyt.s)
 w.gas.masses = np.ones(xp.shape[0], dtype=np.float64) * 100 * unyt.g
 w.stars.masses = np.ones(xs.shape[0], dtype=np.float64) * 100 * unyt.g
 w.gas.internal_energy = (
-    np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / (unyt.g)
+    np.ones(xp.shape[0], dtype=np.float64) * (300.0 * unyt.kb * unyt.K) / unyt.g
 )
 
 w.gas.smoothing_length = h
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py
index 2eb325c6f271c8b16168ad8ab8aa3a59d3ced813..97420caa8bc84af9b0209c4de24c55b1ce0c075e 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotPhotonPropagationCheck.py
@@ -35,14 +35,15 @@
 #   correctly.
 # ----------------------------------------------------------------------
 
-import sys
-import os
 import gc
-import swiftsimio
+import os
+import sys
+
+import matplotlib as mpl
 import numpy as np
+import swiftsimio
 import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
 from scipy import stats
 
 # Parameters users should/may tweak
@@ -136,8 +137,7 @@ def analytical_flux_magnitude_solution(L, time, r, rmax, scheme):
     elif scheme.startswith("SPH M1closure"):
         F = unyt.c.to(r.units / time.units) * E
     else:
-        print("Error: Unknown RT scheme " + scheme)
-        exit()
+        raise ValueError("Unknown RT scheme", scheme)
 
     return r, F
 
@@ -203,9 +203,8 @@ def plot_photons(filename, emin, emax, fmin, fmax):
 
     use_const_emission_rates = False
     if scheme.startswith("GEAR M1closure"):
-        use_const_emission_rates = bool(
-            meta.parameters["GEARRT:use_const_emission_rates"]
-        )
+        luminosity_model = meta.parameters["GEARRT:stellar_luminosity_model"]
+        use_const_emission_rates = luminosity_model.decode("utf-8") == "const"
     elif scheme.startswith("SPH M1closure"):
         use_const_emission_rates = bool(
             meta.parameters["SPHM1RT:use_const_emission_rates"]
@@ -218,9 +217,9 @@ def plot_photons(filename, emin, emax, fmin, fmax):
     if use_const_emission_rates:
         # read emission rate parameter as string
         if scheme.startswith("GEAR M1closure"):
-            emissionstr = meta.parameters["GEARRT:star_emission_rates_LSol"].decode(
-                "utf-8"
-            )
+            emissionstr = meta.parameters[
+                "GEARRT:const_stellar_luminosities_LSol"
+            ].decode("utf-8")
             # clean string up
             if emissionstr.startswith("["):
                 emissionstr = emissionstr[1:]
@@ -255,8 +254,7 @@ def plot_photons(filename, emin, emax, fmin, fmax):
             const_emission_rates = unyt.unyt_array(emlist, "erg/s")
             L = const_emission_rates[group_index]
         else:
-            print("Error: Unknown RT scheme " + scheme)
-            exit()
+            raise ValueError("Unknown RT scheme", scheme)
 
     if plot_anisotropy_estimate:
         ncols = 4
@@ -503,14 +501,14 @@ def plot_photons(filename, emin, emax, fmin, fmax):
             r_bin_centres[mask_sum],
             F_sum_bin[mask_sum] / fmag_sum_bin[mask_sum],
             **lineplot_kwargs,
-            label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
+            label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
         )
         ax4.plot(
             r_bin_centres[mask_max],
             F_sum_bin[mask_max] / fmag_max_bin[mask_max],
             **lineplot_kwargs,
             linestyle="--",
-            label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
+            label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
         )
 
     # -------------------------------------------
@@ -523,7 +521,7 @@ def plot_photons(filename, emin, emax, fmin, fmax):
         ax.legend(fontsize="x-small")
 
     # Add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time)
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py
index 6d1e0e21ecbdf183e0a3508a945c77dbf97e2bc0..06a6e95e32900d3fcde3748fefe74d8825093075 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotRadiationProjection.py
@@ -28,15 +28,16 @@
 # all snapshots available in the workdir.
 # ----------------------------------------------------
 
-import sys
+import gc
 import os
+import sys
+
+import matplotlib as mpl
 import swiftsimio
-import gc
 import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
 from matplotlib.colors import SymLogNorm
+from mpl_toolkits.axes_grid1 import make_axes_locatable
 
 # Parameters users should/may tweak
 
@@ -85,8 +86,8 @@ def get_units(scheme, unit_system="cgs_units"):
             flux_units = 1e10 * energy_units * unyt.cm / unyt.s
             flux_units_str = "10^{10} \\rm{erg} \\ \\rm{cm} \\ \\rm{s}^{-1}"
         else:
-            print("RT scheme not identified. Exit.")
-            exit()
+            raise ValueError("Unkown scheme", scheme)
+
     elif unit_system == "stromgren_units":
         time_units = unyt.Myr
         energy_units = 1e50 * unyt.erg
@@ -98,11 +99,10 @@ def get_units(scheme, unit_system="cgs_units"):
             flux_units = 1e50 * unyt.erg * unyt.kpc / unyt.Gyr
             flux_units_str = "10^{50} \\rm{erg} \\ \\rm{kpc} \\ \\rm{Gyr}^{-1}"
         else:
-            print("RT scheme not identified. Exit.")
-            exit()
+            raise ValueError("Unkown scheme", scheme)
     else:
-        print("Unit system not identified. Exit.")
-        exit()
+        raise ValueError("Unit system not identified. Exit.")
+
     return time_units, energy_units, energy_units_str, flux_units, flux_units_str
 
 
@@ -304,7 +304,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
                 ax.set_ylabel("Energies [$" + energy_units_str + "$]")
 
     # Add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time.to(time_units))
@@ -320,7 +320,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
 
 def get_minmax_vals(snaplist):
     """
-    Find minimal and maximal values for energy and flux
+    Find minimal and maximal values for energy and flux,
     so you can fix axes limits over all snapshots
 
     snaplist: list of snapshot filenames
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py
index 666970dff4bb903de58d554415572a3aa45c8aa0..ea136728f1bbe3f4f930ed0bc31a5e4556092e82 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/plotSolution.py
@@ -25,15 +25,16 @@
 # temperature, and hydrogen mass fraction.
 # ----------------------------------------------------
 
-import sys
+import gc
 import os
+import sys
+
+import matplotlib as mpl
 import swiftsimio
-import gc
 import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
 from matplotlib.colors import LogNorm
+from mpl_toolkits.axes_grid1 import make_axes_locatable
 
 # Parameters users should/may tweak
 
@@ -194,7 +195,7 @@ def plot_result(filename):
     density_map = mass_weighted_density_map / mass_map
     density_map = density_map[cutoff:-cutoff, cutoff:-cutoff]
     density_map = density_map.to("kg/cm**3")
-    density_map = density_map / unyt.proton_mass
+    number_density_map = density_map / unyt.proton_mass
 
     temperature_map = mass_weighted_temperature_map / mass_map
     temperature_map = temperature_map[cutoff:-cutoff, cutoff:-cutoff]
@@ -210,14 +211,14 @@ def plot_result(filename):
 
     try:
         im1 = ax1.imshow(
-            density_map.T,
+            number_density_map.T,
             **imshow_kwargs,
             norm=LogNorm(vmin=1e-4, vmax=1e-1),
             cmap="bone",
         )
         set_colorbar(ax1, im1)
-        ax1.set_title(r"Hydrogen Number Density [cm$^{-3}$]")
-    except ValueError:
+        ax1.set_title(r"Gas Number Density [cm$^{-3}$]")
+    except (ValueError, TypeError):
         print(
             filename,
             "densities wrong? min",
@@ -235,8 +236,8 @@ def plot_result(filename):
             cmap="cividis",
         )
         set_colorbar(ax2, im2)
-        ax2.set_title("Hydrogen Mass Fraction [1]")
-    except ValueError:
+        ax2.set_title("Neutral Hydrogen Mass Fraction [1]")
+    except (ValueError, TypeError):
         print(
             filename,
             "mass fraction wrong? min",
@@ -255,7 +256,7 @@ def plot_result(filename):
         )
         set_colorbar(ax3, im3)
         ax3.set_title(r"Pressure [g/cm/s$^2$]")
-    except ValueError:
+    except (ValueError, TypeError):
         print(
             filename,
             "pressures wrong? min",
@@ -274,7 +275,7 @@ def plot_result(filename):
         )
         set_colorbar(ax4, im4)
         ax4.set_title(r"Temperature [K]")
-    except ValueError:
+    except (ValueError, TypeError):
         print(
             filename,
             "temperatures wrong? min",
@@ -288,10 +289,10 @@ def plot_result(filename):
         ax.set_xlabel("[kpc]")
         ax.set_ylabel("[kpc]")
 
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
-        title += ", $z$ = {0:.2e}".format(meta.z)
-    title += ", $t$ = {0:.2e}".format(meta.time.to("Myr"))
+        title += ", $z$ = {0:.2f}".format(meta.z)
+    title += ", $t$ = {0:.2f}".format(meta.time.to("Myr"))
     fig.suptitle(title)
 
     plt.tight_layout()
@@ -308,3 +309,4 @@ if __name__ == "__main__":
 
     for f in snaplist:
         plot_result(f)
+        gc.collect()
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/propagationTest-2D.yml b/examples/RadiativeTransferTests/StromgrenSphere_2D/propagationTest-2D.yml
index 62d6a2d9b29796d4bde5972b8577f68e4bb44d30..72926eae55e25d0051dfd7e3b1db4c5cad4cd6ff 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_2D/propagationTest-2D.yml
+++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/propagationTest-2D.yml
@@ -11,6 +11,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.001 # end time: radiation reaches edge of box
   dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
@@ -36,26 +37,25 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./propagationTest-2D.hdf5     # The file to read
-  periodic:   1                             # peridioc ICs. Keep them periodic so we don't loose photon energy. TODO: CHANGE LATER WHEN YOU ACTUALLY DO GAS INTERACTIONS
+  periodic:   1                             # peridioc ICs. Keep them periodic so we don't loose photon energy.
 
 GEARRT:
-    f_reduce_c: 1.        # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.99
-    photon_groups_Hz: []  # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N).
-    use_const_emission_rates: 1         # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [1e-28]   # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set.
-    hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    skip_thermochemistry: 1                           # skip thermochemistry.
-    stars_max_timestep: 7.812500e-06                  # update stars every step!
-
+  f_reduce_c: 1.        # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.99
+  photon_groups_Hz: [0., 1., 2., ]                    # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                     # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1.e-28, 1.e-28, 1.e-28]  # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.76                       # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  stellar_spectrum_type: 0                            # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17      # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  set_initial_ionization_mass_fractions: 1            # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.76                              # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 0.                               # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.24                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                              # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  skip_thermochemistry: 1                             # skip thermochemistry.
+  stars_max_timestep: 7.812500e-06                    # update stars every step!
 
 SPHM1RT:
   cred: 2.99792458e10              # reduce the speed of light in the code unit
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml b/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml
index 280dd78a34cba1c7c85d7ff80576a97f0276253d..01ef80e23be754244016393c5d7e281f090dadd1 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml
+++ b/examples/RadiativeTransferTests/StromgrenSphere_2D/stromgrenSphere-2D.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "stromgrenSphere-2D"
+  run_name: stromgrenSphere-2D
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -11,6 +11,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 8
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.512 
   dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
@@ -42,19 +43,19 @@ Stars:
   resolution_eta:       2.2348        # (Optional) Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). Defaults to the SPH value.
 
 GEARRT:
-    f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9
-    photon_groups_Hz: []                              # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [247985.76636]          # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.999999                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 1.e-6                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
-    stars_max_timestep: 1.e-4                         # (Optional) restrict the maximal timestep of stars to this value (in internal units)
+  f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9
+  photon_groups_Hz: [3.288e15]                      # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [6.198024e+04]   # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.999999                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 1.e-6                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
+  stars_max_timestep: 1.6e-5                        # (Optional) restrict the maximal timestep of stars to this value (in internal units)
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/README b/examples/RadiativeTransferTests/StromgrenSphere_3D/README
index bad0264616b6fca11c2923931c49f73468b149d1..f4cf4fc6608f4fea5988933000877b1f375e2785 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/README
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/README
@@ -44,8 +44,21 @@ Additional scripts:
              
 
 To use the GEAR RT model, compile with :
+for the propagation test and the "singlebin" test:
     --with-rt=GEAR_1 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT
 
+for the others:
+    --with-rt=GEAR_3 --with-rt-riemann-solver=GLF --with-hydro=gizmo-mfv --with-riemann-solver=hllc --with-stars=GEAR --with-feedback=none --with-grackle=$GRACKLE_ROOT
+
+Note that the tests which have reference solutions can't be reproduced by the
+GEAR RT scheme as-is, because they require the gas density to be fixed throughout
+the entire run. This can't be done for Gizmo-MFV hydrodynamics as easily as for
+other SPH schemes, where fixing the particle positions suffices to keep the density
+constant. If you want to fix the particle densities nonetheless, please consult the
+instructions provided in 
+https://github.com/SWIFTSIM/swiftsim-rt-tools/blob/main/additionalTests/instructions/GEARRT_turn_off_hydrodynamics_evolution.md
+
+
 To use the SPHM1 RT model, compile with :
 for propagation test:
     --with-rt=SPHM1RT_1 --with-stars=basic --with-feedback=none   --with-sundials=$SUNDIALS_ROOT
@@ -61,4 +74,3 @@ SUNDIALS_ROOT=/cosma/local/sundials/5.1.0/
 
 
 
-
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py
index fc2c185fd7f3283b7c5423ffcb3e7cf860a27f00..473e05f97f4d77e2141df664491250c6c75be81d 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC.py
@@ -23,11 +23,13 @@
 # The gas is set up with pure hydrogen gas.
 # ---------------------------------------------------------------------
 
+import h5py
+import numpy as np
+import unyt
 from swiftsimio import Writer
 from swiftsimio.units import cosmo_units
-import unyt
-import numpy as np
-import h5py
+
+import stromgren_plotting_tools as spt
 
 gamma = 5.0 / 3.0
 
@@ -36,275 +38,6 @@ gamma = 5.0 / 3.0
 replace_gas = False
 
 
-def get_number_densities(Temp, XH, XHe):
-    """
-    Compute the number densities of all species at a given
-    temperature following Katz, Hernquist, and Weinberg 1996
-
-    Temp: temperature [unyt quantity]
-    XH: total mass fraction of all hydrogen species (HI + HII)
-    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
-    """
-
-    # n_H = X_H * rho_gas / m_H =
-    # n_He = X_He * rho_gas / m_He = (1 - X_H) / (4 X_H) * n_H
-    #      =  X_He / 4(1 - X_He) * nH = y * nH
-
-    if XH == 0:
-        nH = 0.0
-        nHe = 1.0
-    else:
-        nH = XH
-        nHe = XHe / 4
-
-    # NOTE: This is not the ionization threshold!
-    if Temp < 5000 * unyt.K:
-        nH0 = nH
-        nHp = 0.0
-        nHe0 = nHe
-        nHep = 0.0
-        nHepp = 0.0
-
-    else:
-
-        Temp.convert_to_cgs()
-        T = Temp.v
-        # Recombination rate for H+ in units of cm^3 s^-1
-        A_Hp = (
-            8.40e-11
-            / np.sqrt(T)
-            * (T * 1e-3) ** (-0.2)
-            * 1.0
-            / (1.0 + (T * 1e-6) ** 0.7)
-        )
-
-        # Dielectronic recombination rate for He+ in units of cm^3 s^-1
-        A_d = (
-            1.9e-3
-            / T ** 1.5
-            * np.exp(-470000.0 / T)
-            * (1.0 + 0.3 * np.exp(-94000.0 / T))
-        )
-        # Recombination rate for He+ in units of cm^3 s^-1
-        A_Hep = 1.5e-10 / T ** 0.6353
-        # Recombination rate for He++ in units of cm^3 s^-1
-        A_Hepp = (
-            3.36e-10
-            / np.sqrt(T)
-            * (T * 1e-3) ** (-0.2)
-            * 1.0
-            / (1.0 + (T * 1e-6) ** 0.7)
-        )
-        # collisional ionization rate for H0 in units of cm^3 s^-1
-        #  G_H0 = 1.17e-10 * np.sqrt(T) * np.exp(-157809.1 / T) * 1. / (1. + np.sqrt(T*1e-5))
-        G_H0 = (
-            5.85e-11
-            * np.sqrt(T)
-            * np.exp(-157809.1 / T)
-            * 1.0
-            / (1.0 + np.sqrt(T * 1e-5))
-        )
-        # collisional ionization rate for He0 in units of cm^3 s^-1
-        G_He0 = (
-            2.38e-11
-            * np.sqrt(T)
-            * np.exp(-285335.4 / T)
-            * 1.0
-            / (1.0 + np.sqrt(T * 1e-5))
-        )
-        # collisional ionization rate for He+ in units of cm^3 s^-1
-        G_Hep = (
-            5.68e-12
-            * np.sqrt(T)
-            * np.exp(-631515.0 / T)
-            * 1.0
-            / (1.0 + np.sqrt(T * 1e-5))
-        )
-
-        # Katz et al. 1996 eq. 33 - 38
-        # Note: We assume all photoionization rates to be zero.
-        # Also, here we don't care about the actual number density, i.e.
-        # about the volume, since it'll cancel out later when we compute
-        # the mass fractions.
-
-        nH0 = nH * A_Hp / (A_Hp + G_H0)
-        nHp = nH - nH0
-        nHep = nHe / (1.0 + (A_Hep + A_d) / G_He0 + G_Hep / A_Hepp)
-        nHe0 = nHep * (A_Hep + A_d) / G_He0
-        nHepp = nHep * G_Hep / A_Hepp
-
-    # electron density
-    ne = nHp + nHep + 2.0 * nHepp
-
-    return nH0, nHp, nHe0, nHep, nHepp, ne
-
-
-def get_number_densities_array(Temp, XH, XHe):
-    """
-    Compute the number densities of all species at a given
-    temperature following Katz, Hernquist, and Weinberg 1996
-
-    Temp: temperature [unyt array]
-    XH: total mass fraction of all hydrogen species (HI + HII)
-    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
-    """
-
-    # n_H = X_H * rho_gas / m_H =
-    # n_He = X_He * rho_gas / m_He = (1 - X_H) / (4 X_H) * n_H
-    #      =  X_He / 4(1 - X_He) * nH = y * nH
-
-    nH = np.zeros(XH.shape, dtype=np.float64)
-    nHe = np.zeros(XH.shape, dtype=np.float64)
-
-    mask = XH == 0
-    nH[mask] = 0.0
-    nHe[mask] = 1.0
-
-    inv_mask = np.logical_not(mask)
-    nH[inv_mask] = XH[inv_mask]
-    nHe[inv_mask] = 0.25 * XHe[inv_mask]
-
-    nH0 = np.zeros(XH.shape, dtype=np.float64)
-    nHp = np.zeros(XH.shape, dtype=np.float64)
-    nHe0 = np.zeros(XH.shape, dtype=np.float64)
-    nHep = np.zeros(XH.shape, dtype=np.float64)
-    nHepp = np.zeros(XH.shape, dtype=np.float64)
-
-    # NOTE: This is not the ionization threshold!
-    neutral = Temp < 5000 * unyt.K
-
-    nH0[neutral] = nH[neutral]
-    nHp[neutral] = 0.0
-    nHe0[neutral] = nHe[neutral]
-    nHep[neutral] = 0.0
-    nHepp[neutral] = 0.0
-
-    Temp.convert_to_cgs()
-    T = Temp.v
-    ionized = np.logical_not(neutral)
-
-    # Recombination rate for H+ in units of cm^3 s^-1
-    A_Hp = (
-        8.40e-11 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7)
-    )
-    # Dielectronic recombination rate for He+ in units of cm^3 s^-1
-    A_d = 1.9e-3 / T ** 1.5 * np.exp(-470000.0 / T) * (1.0 + 0.3 * np.exp(-94000.0 / T))
-    # Recombination rate for He+ in units of cm^3 s^-1
-    A_Hep = 1.5e-10 / T ** 0.6353
-    # Recombination rate for He++ in units of cm^3 s^-1
-    A_Hepp = (
-        3.36e-10 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7)
-    )
-    # collisional ionization rate for H0 in units of cm^3 s^-1
-    G_H0 = (
-        5.85e-11 * np.sqrt(T) * np.exp(-157809.1 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
-    )
-    # collisional ionization rate for He0 in units of cm^3 s^-1
-    G_He0 = (
-        2.38e-11 * np.sqrt(T) * np.exp(-285335.4 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
-    )
-    # collisional ionization rate for He+ in units of cm^3 s^-1
-    G_Hep = (
-        5.68e-12 * np.sqrt(T) * np.exp(-631515.0 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
-    )
-
-    # Katz et al. 1996 eq. 33 - 38
-    # Note: We assume all photoionization rates to be zero.
-    # Also, here we don't care about the actual number density, i.e.
-    # about the volume, since it'll cancel out later when we compute
-    # the mass fractions.
-
-    nH0[ionized] = nH[ionized] * A_Hp[ionized] / (A_Hp[ionized] + G_H0[ionized])
-    nHp[ionized] = nH[ionized] - nH0[ionized]
-    nHep[ionized] = nHe[ionized] / (
-        1.0
-        + (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized]
-        + G_Hep[ionized] / A_Hepp[ionized]
-    )
-    nHe0[ionized] = nHep[ionized] * (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized]
-    nHepp[ionized] = nHep[ionized] * G_Hep[ionized] / A_Hepp[ionized]
-
-    # electron density
-    ne = nHp + nHep + 2.0 * nHepp
-
-    return nH0, nHp, nHe0, nHep, nHepp, ne
-
-
-def get_mass_fractions(T, XH, XHe):
-    """
-    Compute the mass fractions of all species at a
-    given temperature
-
-    T: temperature
-    XH: total mass fraction of all hydrogen species (HI + HII)
-    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
-    """
-
-    # first get number densities
-    if isinstance(XH, np.ndarray):
-        nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities_array(T, XH, XHe)
-    else:
-        nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities(T, XH, XHe)
-
-    # now get mass denities in units of atomic mass units
-    mH0 = nH0
-    mHp = nHp
-    mHe0 = 4.0 * nHe0
-    mHep = 4.0 * nHep
-    mHepp = 4.0 * nHepp
-    # neglect electron mass contributions
-
-    mtot = mH0 + mHp + mHe0 + mHep + mHepp  # + me
-
-    XH0 = mH0 / mtot
-    XHp = mHp / mtot
-    XHe0 = mHe0 / mtot
-    XHep = mHep / mtot
-    XHepp = mHepp / mtot
-    #  Xe = me / mtot
-
-    return XH0, XHp, XHe0, XHep, XHepp
-
-
-def internal_energy(T, mu):
-    """
-    Compute the internal energy of the gas for a given
-    temperature and mean molecular weight
-    """
-    # Using u = 1 / (gamma - 1) * p / rho
-    #   and p = N/V * kT = rho / (mu * m_u) * kT
-
-    u = unyt.boltzmann_constant * T / (gamma - 1) / (mu * unyt.atomic_mass_unit)
-    return u
-
-
-def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp):
-    """
-    Determines the mean molecular weight for given 
-    mass fractions of
-        hydrogen:   XH0
-        H+:         XHp
-        He:         XHe0
-        He+:        XHep
-        He++:       XHepp
-
-    returns:
-        mu: mean molecular weight [in atomic mass units]
-        NOTE: to get the actual mean mass, you still need
-        to multiply it by m_u, as is tradition in the formulae
-    """
-
-    # 1/mu = sum_j X_j / A_j * (1 + E_j)
-    # A_H    = 1, E_H    = 0
-    # A_Hp   = 1, E_Hp   = 1
-    # A_He   = 4, E_He   = 0
-    # A_Hep  = 4, E_Hep  = 1
-    # A_Hepp = 4, E_Hepp = 2
-    one_over_mu = XH0 + 2 * XHp + 0.25 * XHe0 + 0.5 * XHep + 0.75 * XHepp
-
-    return 1.0 / one_over_mu
-
-
 if __name__ == "__main__":
 
     glass = h5py.File("glassCube_64.hdf5", "r")
@@ -315,7 +48,7 @@ if __name__ == "__main__":
 
     r = np.sqrt(np.sum((0.5 - xp) ** 2, axis=1))
 
-    if replace_gas == True:
+    if replace_gas:
         # replace a central gas particle with a star particle
         rmin = np.argmin(r)
         xs = xp[rmin]
@@ -370,9 +103,9 @@ if __name__ == "__main__":
 
     # get gas internal energy for a given temperature and composition
     T = 100 * unyt.K
-    XHI, XHII, XHeI, XHeII, XHeIII = get_mass_fractions(T, XH, XHe)
-    mu = mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII)
-    internal_energy = internal_energy(T, mu)
+    XHI, XHII, XHeI, XHeII, XHeIII = spt.get_mass_fractions(T, XH, XHe)
+    mu = spt.mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII)
+    internal_energy = spt.internal_energy(T, mu, gamma)
 
     w.gas.internal_energy = np.ones(xp.shape[0], dtype=np.float64) * internal_energy
 
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC_HHe.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC_HHe.py
index 347e6c382f748311447300e2b92b6fd32d7e90fb..ddb755db536be0a93287f8c96f54ecd4174ef5fb 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC_HHe.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/makeIC_HHe.py
@@ -8,286 +8,19 @@
 # Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x.
 # ---------------------------------------------------------------------
 
+import h5py
+import numpy as np
+import unyt
 from swiftsimio import Writer
 from swiftsimio.units import cosmo_units
-import unyt
-import numpy as np
-import h5py
+
+import stromgren_plotting_tools as spt
 
 gamma = 5.0 / 3.0
 
 # switch to replace the central gas particle with a star
 # else put the star particle among gas particles
-replace_gas = False
-
-
-def get_number_densities(Temp, XH, XHe):
-    """
-    Compute the number densities of all species at a given
-    temperature following Katz, Hernquist, and Weinberg 1996
-
-    Temp: temperature [unyt quantity]
-    XH: total mass fraction of all hydrogen species (HI + HII)
-    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
-    """
-
-    # n_H = X_H * rho_gas / m_H =
-    # n_He = X_He * rho_gas / m_He = (1 - X_H) / (4 X_H) * n_H
-    #      =  X_He / 4(1 - X_He) * nH = y * nH
-
-    if XH == 0:
-        nH = 0.0
-        nHe = 1.0
-    else:
-        nH = XH
-        nHe = XHe / 4
-
-    # NOTE: This is not the ionization threshold!
-    if Temp < 5000 * unyt.K:
-        nH0 = nH
-        nHp = 0.0
-        nHe0 = nHe
-        nHep = 0.0
-        nHepp = 0.0
-
-    else:
-
-        Temp.convert_to_cgs()
-        T = Temp.v
-        # Recombination rate for H+ in units of cm^3 s^-1
-        A_Hp = (
-            8.40e-11
-            / np.sqrt(T)
-            * (T * 1e-3) ** (-0.2)
-            * 1.0
-            / (1.0 + (T * 1e-6) ** 0.7)
-        )
-
-        # Dielectronic recombination rate for He+ in units of cm^3 s^-1
-        A_d = (
-            1.9e-3
-            / T ** 1.5
-            * np.exp(-470000.0 / T)
-            * (1.0 + 0.3 * np.exp(-94000.0 / T))
-        )
-        # Recombination rate for He+ in units of cm^3 s^-1
-        A_Hep = 1.5e-10 / T ** 0.6353
-        # Recombination rate for He++ in units of cm^3 s^-1
-        A_Hepp = (
-            3.36e-10
-            / np.sqrt(T)
-            * (T * 1e-3) ** (-0.2)
-            * 1.0
-            / (1.0 + (T * 1e-6) ** 0.7)
-        )
-        # collisional ionization rate for H0 in units of cm^3 s^-1
-        #  G_H0 = 1.17e-10 * np.sqrt(T) * np.exp(-157809.1 / T) * 1. / (1. + np.sqrt(T*1e-5))
-        G_H0 = (
-            5.85e-11
-            * np.sqrt(T)
-            * np.exp(-157809.1 / T)
-            * 1.0
-            / (1.0 + np.sqrt(T * 1e-5))
-        )
-        # collisional ionization rate for He0 in units of cm^3 s^-1
-        G_He0 = (
-            2.38e-11
-            * np.sqrt(T)
-            * np.exp(-285335.4 / T)
-            * 1.0
-            / (1.0 + np.sqrt(T * 1e-5))
-        )
-        # collisional ionization rate for He+ in units of cm^3 s^-1
-        G_Hep = (
-            5.68e-12
-            * np.sqrt(T)
-            * np.exp(-631515.0 / T)
-            * 1.0
-            / (1.0 + np.sqrt(T * 1e-5))
-        )
-
-        # Katz et al. 1996 eq. 33 - 38
-        # Note: We assume all photoionization rates to be zero.
-        # Also, here we don't care about the actual number density, i.e.
-        # about the volume, since it'll cancel out later when we compute
-        # the mass fractions.
-
-        nH0 = nH * A_Hp / (A_Hp + G_H0)
-        nHp = nH - nH0
-        nHep = nHe / (1.0 + (A_Hep + A_d) / G_He0 + G_Hep / A_Hepp)
-        nHe0 = nHep * (A_Hep + A_d) / G_He0
-        nHepp = nHep * G_Hep / A_Hepp
-
-    # electron density
-    ne = nHp + nHep + 2.0 * nHepp
-
-    return nH0, nHp, nHe0, nHep, nHepp, ne
-
-
-def get_number_densities_array(Temp, XH, XHe):
-    """
-    Compute the number densities of all species at a given
-    temperature following Katz, Hernquist, and Weinberg 1996
-
-    Temp: temperature [unyt array]
-    XH: total mass fraction of all hydrogen species (HI + HII)
-    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
-    """
-
-    # n_H = X_H * rho_gas / m_H =
-    # n_He = X_He * rho_gas / m_He = (1 - X_H) / (4 X_H) * n_H
-    #      =  X_He / 4(1 - X_He) * nH = y * nH
-
-    nH = np.zeros(XH.shape, dtype=np.float64)
-    nHe = np.zeros(XH.shape, dtype=np.float64)
-
-    mask = XH == 0
-    nH[mask] = 0.0
-    nHe[mask] = 1.0
-
-    inv_mask = np.logical_not(mask)
-    nH[inv_mask] = XH[inv_mask]
-    nHe[inv_mask] = 0.25 * XHe[inv_mask]
-
-    nH0 = np.zeros(XH.shape, dtype=np.float64)
-    nHp = np.zeros(XH.shape, dtype=np.float64)
-    nHe0 = np.zeros(XH.shape, dtype=np.float64)
-    nHep = np.zeros(XH.shape, dtype=np.float64)
-    nHepp = np.zeros(XH.shape, dtype=np.float64)
-
-    # NOTE: This is not the ionization threshold!
-    neutral = Temp < 5000 * unyt.K
-
-    nH0[neutral] = nH[neutral]
-    nHp[neutral] = 0.0
-    nHe0[neutral] = nHe[neutral]
-    nHep[neutral] = 0.0
-    nHepp[neutral] = 0.0
-
-    Temp.convert_to_cgs()
-    T = Temp.v
-    ionized = np.logical_not(neutral)
-
-    # Recombination rate for H+ in units of cm^3 s^-1
-    A_Hp = (
-        8.40e-11 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7)
-    )
-    # Dielectronic recombination rate for He+ in units of cm^3 s^-1
-    A_d = 1.9e-3 / T ** 1.5 * np.exp(-470000.0 / T) * (1.0 + 0.3 * np.exp(-94000.0 / T))
-    # Recombination rate for He+ in units of cm^3 s^-1
-    A_Hep = 1.5e-10 / T ** 0.6353
-    # Recombination rate for He++ in units of cm^3 s^-1
-    A_Hepp = (
-        3.36e-10 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7)
-    )
-    # collisional ionization rate for H0 in units of cm^3 s^-1
-    G_H0 = (
-        5.85e-11 * np.sqrt(T) * np.exp(-157809.1 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
-    )
-    # collisional ionization rate for He0 in units of cm^3 s^-1
-    G_He0 = (
-        2.38e-11 * np.sqrt(T) * np.exp(-285335.4 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
-    )
-    # collisional ionization rate for He+ in units of cm^3 s^-1
-    G_Hep = (
-        5.68e-12 * np.sqrt(T) * np.exp(-631515.0 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
-    )
-
-    # Katz et al. 1996 eq. 33 - 38
-    # Note: We assume all photoionization rates to be zero.
-    # Also, here we don't care about the actual number density, i.e.
-    # about the volume, since it'll cancel out later when we compute
-    # the mass fractions.
-
-    nH0[ionized] = nH[ionized] * A_Hp[ionized] / (A_Hp[ionized] + G_H0[ionized])
-    nHp[ionized] = nH[ionized] - nH0[ionized]
-    nHep[ionized] = nHe[ionized] / (
-        1.0
-        + (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized]
-        + G_Hep[ionized] / A_Hepp[ionized]
-    )
-    nHe0[ionized] = nHep[ionized] * (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized]
-    nHepp[ionized] = nHep[ionized] * G_Hep[ionized] / A_Hepp[ionized]
-
-    # electron density
-    ne = nHp + nHep + 2.0 * nHepp
-
-    return nH0, nHp, nHe0, nHep, nHepp, ne
-
-
-def get_mass_fractions(T, XH, XHe):
-    """
-    Compute the mass fractions of all species at a
-    given temperature
-
-    T: temperature
-    XH: total mass fraction of all hydrogen species (HI + HII)
-    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
-    """
-
-    # first get number densities
-    if isinstance(XH, np.ndarray):
-        nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities_array(T, XH, XHe)
-    else:
-        nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities(T, XH, XHe)
-
-    # now get mass denities in units of atomic mass units
-    mH0 = nH0
-    mHp = nHp
-    mHe0 = 4.0 * nHe0
-    mHep = 4.0 * nHep
-    mHepp = 4.0 * nHepp
-    # neglect electron mass contributions
-
-    mtot = mH0 + mHp + mHe0 + mHep + mHepp  # + me
-
-    XH0 = mH0 / mtot
-    XHp = mHp / mtot
-    XHe0 = mHe0 / mtot
-    XHep = mHep / mtot
-    XHepp = mHepp / mtot
-    #  Xe = me / mtot
-
-    return XH0, XHp, XHe0, XHep, XHepp
-
-
-def internal_energy(T, mu):
-    """
-    Compute the internal energy of the gas for a given
-    temperature and mean molecular weight
-    """
-    # Using u = 1 / (gamma - 1) * p / rho
-    #   and p = N/V * kT = rho / (mu * m_u) * kT
-
-    u = unyt.boltzmann_constant * T / (gamma - 1) / (mu * unyt.atomic_mass_unit)
-    return u
-
-
-def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp):
-    """
-    Determines the mean molecular weight for given 
-    mass fractions of
-        hydrogen:   XH0
-        H+:         XHp
-        He:         XHe0
-        He+:        XHep
-        He++:       XHepp
-
-    returns:
-        mu: mean molecular weight [in atomic mass units]
-        NOTE: to get the actual mean mass, you still need
-        to multiply it by m_u, as is tradition in the formulae
-    """
-
-    # 1/mu = sum_j X_j / A_j * (1 + E_j)
-    # A_H    = 1, E_H    = 0
-    # A_Hp   = 1, E_Hp   = 1
-    # A_He   = 4, E_He   = 0
-    # A_Hep  = 4, E_Hep  = 1
-    # A_Hepp = 4, E_Hepp = 2
-    one_over_mu = XH0 + 2 * XHp + 0.25 * XHe0 + 0.5 * XHep + 0.75 * XHepp
-
-    return 1.0 / one_over_mu
+replace_gas = True
 
 
 if __name__ == "__main__":
@@ -300,7 +33,7 @@ if __name__ == "__main__":
 
     r = np.sqrt(np.sum((0.5 - xp) ** 2, axis=1))
 
-    if replace_gas == True:
+    if replace_gas:
         # replace a central gas particle with a star particle
         rmin = np.argmin(r)
         xs = xp[rmin]
@@ -355,9 +88,9 @@ if __name__ == "__main__":
 
     # get gas internal energy for a given temperature and composition
     T = 100.0 * unyt.K
-    XHI, XHII, XHeI, XHeII, XHeIII = get_mass_fractions(T, XH, XHe)
-    mu = mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII)
-    internal_energy = internal_energy(T, mu)
+    XHI, XHII, XHeI, XHeII, XHeIII = spt.get_mass_fractions(T, XH, XHe)
+    mu = spt.mean_molecular_weight(XHI, XHII, XHeI, XHeII, XHeIII)
+    internal_energy = spt.internal_energy(T, mu, gamma)
 
     w.gas.internal_energy = np.ones(xp.shape[0], dtype=np.float64) * internal_energy
 
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/makePropagationTestIC.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/makePropagationTestIC.py
index 5e6d1184d202577ec732aa8b71af708ca2d116c3..0cd176c81b8702c0586d6e90ac028d38cb511957 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/makePropagationTestIC.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/makePropagationTestIC.py
@@ -25,11 +25,10 @@
 # Intended for the photon propagation test.
 # ---------------------------------------------------------------------
 
-from swiftsimio import Writer
-import unyt
-import numpy as np
 import h5py
-
+import numpy as np
+import unyt
+from swiftsimio import Writer
 
 glass = h5py.File("glassCube_64.hdf5", "r")
 parts = glass["PartType0"]
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py
index d2223ec273d99cd2a48f86dbf126f5a4be399c30..42f7ce447a28231cb7fbd56413022b1793fde3e6 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotPhotonPropagationCheck.py
@@ -38,6 +38,7 @@
 import gc
 import os
 import sys
+
 import matplotlib as mpl
 import numpy as np
 import swiftsimio
@@ -45,6 +46,7 @@ import unyt
 from matplotlib import pyplot as plt
 from scipy import stats
 from scipy.optimize import curve_fit
+
 import stromgren_plotting_tools as spt
 
 # Parameters users should/may tweak
@@ -137,6 +139,8 @@ def analytical_flux_magnitude_solution(L, time, r, rmax, scheme):
         F = unyt.c.to(r.units / time.units) * E / r.units ** 3
     elif scheme.startswith("SPH M1closure"):
         F = unyt.c.to(r.units / time.units) * E
+    else:
+        raise ValueError("Unknown scheme", scheme)
 
     return r, F
 
@@ -203,9 +207,8 @@ def plot_photons(filename, emin, emax, fmin, fmax):
 
     use_const_emission_rates = False
     if scheme.startswith("GEAR M1closure"):
-        use_const_emission_rates = bool(
-            meta.parameters["GEARRT:use_const_emission_rates"]
-        )
+        luminosity_model = meta.parameters["GEARRT:stellar_luminosity_model"]
+        use_const_emission_rates = luminosity_model.decode("utf-8") == "const"
     elif scheme.startswith("SPH M1closure"):
         use_const_emission_rates = bool(
             meta.parameters["SPHM1RT:use_const_emission_rates"]
@@ -219,7 +222,9 @@ def plot_photons(filename, emin, emax, fmin, fmax):
         if scheme.startswith("GEAR M1closure"):
             const_emission_rates = (
                 spt.trim_paramstr(
-                    meta.parameters["GEARRT:star_emission_rates_LSol"].decode("utf-8")
+                    meta.parameters["GEARRT:const_stellar_luminosities_LSol"].decode(
+                        "utf-8"
+                    )
                 )
                 * unyt.L_Sun
             )
@@ -486,16 +491,16 @@ def plot_photons(filename, emin, emax, fmin, fmax):
             r_bin_centres[mask_sum],
             F_sum_bin[mask_sum] / fmag_sum_bin[mask_sum],
             **lineplot_kwargs,
-            label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ "
-            + "/ $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
+            label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ "
+            + r"/ $\sum_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
         )
         ax4.plot(
             r_bin_centres[mask_max],
             F_sum_bin[mask_max] / fmag_max_bin[mask_max],
             **lineplot_kwargs,
             linestyle="--",
-            label="$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ "
-            + " / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
+            label=r"$\left| \sum_{i \in \mathrm{particles \ in \ bin}} \mathbf{F}_i \\right| $ "
+            + r" / $\max_{i \in \mathrm{particles \ in \ bin}} \left| \mathbf{F}_{i} \\right| $",
         )
 
     # -------------------------------------------
@@ -508,7 +513,7 @@ def plot_photons(filename, emin, emax, fmin, fmax):
         ax.legend(fontsize="x-small")
 
     # Add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time)
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py
index 0452384c17d618dd32acc68d027d79c7259fd343..09e78f504acf75ef86e923e7d04c7eb11d6b1268 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotRadiationProjection.py
@@ -28,16 +28,17 @@
 # all snapshots available in the workdir.
 # ----------------------------------------------------
 
-import sys
+import gc
 import os
-import swiftsimio
+import sys
+
+import matplotlib as mpl
 import numpy as np
-import gc
+import swiftsimio
 import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
 from matplotlib.colors import SymLogNorm
+from mpl_toolkits.axes_grid1 import make_axes_locatable
 
 # Parameters users should/may tweak
 
@@ -86,8 +87,7 @@ def get_units(scheme, unit_system="cgs_units"):
             flux_units = 1e10 * energy_units * unyt.cm / unyt.s
             flux_units_str = "10^{10} \\rm{erg} \\ \\rm{cm} \\ \\rm{s}^{-1}"
         else:
-            print("RT scheme not identified. Exit.")
-            exit()
+            raise ValueError("RT scheme not identified. Exit.")
     elif unit_system == "stromgren_units":
         time_units = unyt.Myr
         energy_units = 1e50 * unyt.erg
@@ -99,11 +99,10 @@ def get_units(scheme, unit_system="cgs_units"):
             flux_units = 1e50 * unyt.erg * unyt.kpc / unyt.Gyr
             flux_units_str = "10^{60} \\rm{erg} \\ \\rm{kpc} \\ \\rm{Gyr}^{-1}"
         else:
-            print("RT scheme not identified. Exit.")
-            exit()
+            raise ValueError("RT scheme not identified. Exit.")
     else:
-        print("Unit system not identified. Exit.")
-        exit()
+        raise ValueError("Unit system not identified. Exit.")
+
     return time_units, energy_units, energy_units_str, flux_units, flux_units_str
 
 
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotSolution.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotSolution.py
index e368ec9610db336382ad4d85f9e04fbdba5842d5..bb68a573b932dab72b570aa1446783fd80e76a38 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotSolution.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotSolution.py
@@ -20,19 +20,21 @@
 
 
 # ----------------------------------------------------
-# Plot slices of the hydrogen number density,
-# pressure, temperature, and hydrogen mass fraction.
+# Plot slices of the neutral hydrogen number density,
+# pressure, temperature, and neutral hydrogen mass fraction.
 # ----------------------------------------------------
 
+import gc
 import sys
+
+import matplotlib as mpl
 import swiftsimio
-import gc
 import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
 from matplotlib.colors import LogNorm
+from mpl_toolkits.axes_grid1 import make_axes_locatable
 from swiftsimio.visualisation.slice import slice_gas
+
 import stromgren_plotting_tools as spt
 
 # Parameters users should/may tweak
@@ -44,18 +46,18 @@ snapshot_base = "output"
 imshow_kwargs = {"origin": "lower"}
 
 # parameters for swiftsimio slices
-slice_kwargs = {"slice": 0.5, "resolution": 1000, "parallel": True}
+slice_kwargs = {"resolution": 1024, "parallel": True}
 
 # -----------------------------------------------------------------------
 
 
 # Read in cmdline arg: Are we plotting only one snapshot, or all?
 plot_all = False
+snapnr = -1
 try:
     snapnr = int(sys.argv[1])
 except IndexError:
     plot_all = True
-    snapnr = -1
 
 mpl.rcParams["text.usetex"] = True
 
@@ -88,42 +90,67 @@ def plot_result(filename):
         0.0 * meta.boxsize[1].v,
         0.9 * meta.boxsize[1].v,
     ]
+
+    try:
+        par = meta.parameters["InitialConditions:periodic"]
+        periodic = int(par) == 1
+    except KeyError:
+        periodic = False
+
+    # Add a cutoff to the image edges if the run wasn't periodic
     cutoff = int(0.05 * slice_kwargs["resolution"])
 
-    mass_map = slice_gas(data, project="masses", **slice_kwargs)
-    gamma = meta.hydro_scheme["Adiabatic index"][0]
+    mass_map = slice_gas(
+        data, project="masses", z_slice=0.5 * meta.boxsize[2], **slice_kwargs
+    )
+    gamma = meta.gas_gamma
 
     imf = spt.get_imf(scheme, data)
 
-    data.gas.mXHI = imf.HI * data.gas.masses
-    data.gas.mXHII = imf.HII * data.gas.masses
-    data.gas.mP = data.gas.pressures * data.gas.masses
-    data.gas.mrho = data.gas.densities * data.gas.masses
+    # use units that are most likely not to produce infinities and NaNs
+    masses_MSun = data.gas.masses.to("M_Sun")
+
+    data.gas.mXHI = imf.HI * masses_MSun
+    data.gas.mXHII = imf.HII * masses_MSun
+    data.gas.mP = data.gas.pressures * masses_MSun
+    data.gas.mrhoHI = imf.HI * data.gas.densities * masses_MSun
 
     mu = spt.mean_molecular_weight(imf.HI, imf.HII, imf.HeI, imf.HeII, imf.HeIII)
     data.gas.mT = (
-        spt.gas_temperature(data.gas.internal_energies, mu, gamma) * data.gas.masses
+        spt.gas_temperature(data.gas.internal_energies, mu, gamma) * masses_MSun
     )
 
-    mass_weighted_HI_map = slice_gas(data, project="mXHI", **slice_kwargs)
-    mass_weighted_pressure_map = slice_gas(data, project="mP", **slice_kwargs)
-    mass_weighted_density_map = slice_gas(data, project="mrho", **slice_kwargs)
-    mass_weighted_temperature_map = slice_gas(data, project="mT", **slice_kwargs)
+    mass_weighted_HI_map = slice_gas(
+        data, project="mXHI", z_slice=0.5 * meta.boxsize[2], **slice_kwargs
+    )
+    mass_weighted_pressure_map = slice_gas(
+        data, project="mP", z_slice=0.5 * meta.boxsize[2], **slice_kwargs
+    )
+    mass_weighted_HI_density_map = slice_gas(
+        data, project="mrhoHI", z_slice=0.5 * meta.boxsize[2], **slice_kwargs
+    )
+    mass_weighted_temperature_map = slice_gas(
+        data, project="mT", z_slice=0.5 * meta.boxsize[2], **slice_kwargs
+    )
 
     HI_map = mass_weighted_HI_map / mass_map
-    HI_map = HI_map[cutoff:-cutoff, cutoff:-cutoff]
+    if not periodic:
+        HI_map = HI_map[cutoff:-cutoff, cutoff:-cutoff]
 
     pressure_map = mass_weighted_pressure_map / mass_map
-    pressure_map = pressure_map[cutoff:-cutoff, cutoff:-cutoff]
+    if not periodic:
+        pressure_map = pressure_map[cutoff:-cutoff, cutoff:-cutoff]
     pressure_map = pressure_map.to("g/cm/s**2")
 
-    density_map = mass_weighted_density_map / mass_map
-    density_map = density_map[cutoff:-cutoff, cutoff:-cutoff]
-    density_map = density_map.to("kg/cm**3")
-    density_map = density_map / unyt.proton_mass
+    HI_density_map = mass_weighted_HI_density_map / mass_map
+    if not periodic:
+        HI_density_map = HI_density_map[cutoff:-cutoff, cutoff:-cutoff]
+    HI_density_map = HI_density_map.to("kg/cm**3")
+    HI_density_map = HI_density_map / unyt.proton_mass
 
     temperature_map = mass_weighted_temperature_map / mass_map
-    temperature_map = temperature_map[cutoff:-cutoff, cutoff:-cutoff]
+    if not periodic:
+        temperature_map = temperature_map[cutoff:-cutoff, cutoff:-cutoff]
     temperature_map = temperature_map.to("K")
 
     fig = plt.figure(figsize=(12, 12), dpi=200)
@@ -136,14 +163,14 @@ def plot_result(filename):
 
     try:
         im1 = ax1.imshow(
-            density_map.T,
+            HI_density_map.T,
             **imshow_kwargs,
-            norm=LogNorm(vmin=1e-4, vmax=1e-1),
+            norm=LogNorm(vmin=1.0e-6, vmax=1e-1),
             cmap="bone",
         )
         set_colorbar(ax1, im1)
-        ax1.set_title(r"Hydrogen Number Density [cm$^{-3}$]")
-    except ValueError:
+        ax1.set_title(r"Neutral Hydrogen Number Density [cm$^{-3}$]")
+    except (ValueError, TypeError):
         print(
             filename,
             "densities wrong? min",
@@ -155,12 +182,21 @@ def plot_result(filename):
 
     try:
         im2 = ax2.imshow(
-            HI_map.T, **imshow_kwargs, norm=LogNorm(vmin=1e-3, vmax=1.0), cmap="cividis"
+            HI_map.T,
+            **imshow_kwargs,
+            norm=LogNorm(vmin=1.0e-5, vmax=1.0),
+            cmap="cividis",
         )
         set_colorbar(ax2, im2)
-        ax2.set_title("HI Mass Fraction [1]")
-    except ValueError:
-        print(filename, "mass fraction wrong? min", imf.HI.min(), "max", imf.HI.max())
+        ax2.set_title("Neutral Hydrogen Mass Fraction [1]")
+    except (ValueError, TypeError):
+        print(
+            filename,
+            "mass fraction wrong? min",
+            data.gas.ion_mass_fractions.HI.min(),
+            "max",
+            data.gas.ion_mass_fractions.HI.max(),
+        )
         return
 
     try:
@@ -172,7 +208,7 @@ def plot_result(filename):
         )
         set_colorbar(ax3, im3)
         ax3.set_title(r"Pressure [g/cm/s$^2$]")
-    except ValueError:
+    except (ValueError, TypeError):
         print(
             filename,
             "pressures wrong? min",
@@ -186,12 +222,12 @@ def plot_result(filename):
         im4 = ax4.imshow(
             temperature_map.T,
             **imshow_kwargs,
-            norm=LogNorm(vmin=1e2, vmax=4e4),
+            norm=LogNorm(vmin=1e2, vmax=5e4),
             cmap="inferno",
         )
         set_colorbar(ax4, im4)
         ax4.set_title(r"Temperature [K]")
-    except ValueError:
+    except (ValueError, TypeError):
         print(
             filename,
             "temperatures wrong? min",
@@ -208,7 +244,7 @@ def plot_result(filename):
     title = filename.replace("_", "\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
-    title += ", $t$ = {0:.2e}".format(meta.time.to("Myr"))
+    title += ", $t$ = {0:.1f}".format(meta.time.to("Myr"))
     fig.suptitle(title)
 
     plt.tight_layout()
@@ -219,6 +255,8 @@ def plot_result(filename):
 
 
 if __name__ == "__main__":
+
     snaplist = spt.get_snapshot_list(snapshot_base, plot_all, snapnr)
+
     for f in snaplist:
         plot_result(f)
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFCheck.py
old mode 100644
new mode 100755
index 576ac1a22286450edc6e6c0259ebe112a2c3bc9d..2280d8c93ea0e19f57c259a3f620f8f79a7965fe
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFCheck.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFCheck.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
 # ----------------------------------------------------
 # Stromgren 3D with multifrequency bins
 # The test is identical to the test in Section 5.2.2 of Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x
@@ -5,13 +6,15 @@
 # Plot comparison of simulated neutral fraction and temperature with the solution.
 # ----------------------------------------------------
 
-import swiftsimio
-from matplotlib import pyplot as plt
+import sys
+
 import matplotlib as mpl
 import numpy as np
-import sys
-import stromgren_plotting_tools as spt
+import swiftsimio
 import unyt
+from matplotlib import pyplot as plt
+
+import stromgren_plotting_tools as spt
 
 # Plot parameters
 params = {
@@ -42,8 +45,8 @@ params = {
 mpl.rcParams.update(params)
 
 scatterplot_kwargs = {
-    "alpha": 0.6,
-    "s": 4,
+    "alpha": 0.1,
+    "s": 2,
     "marker": ".",
     "linewidth": 0.0,
     "facecolor": "blue",
@@ -79,11 +82,12 @@ def get_TT1Dsolution():
     rTtt1dlist = data[:, 0] * TT1D_runit
     Ttt1dlist = 10 ** data[:, 1] * unyt.K
 
-    outdict = {}
-    outdict["rtt1dlist"] = rtt1dlist
-    outdict["xtt1dlist"] = xtt1dlist
-    outdict["rTtt1dlist"] = rTtt1dlist
-    outdict["Ttt1dlist"] = Ttt1dlist
+    outdict = {
+        "rtt1dlist": rtt1dlist,
+        "xtt1dlist": xtt1dlist,
+        "rTtt1dlist": rTtt1dlist,
+        "Ttt1dlist": Ttt1dlist,
+    }
     return outdict
 
 
@@ -94,7 +98,7 @@ def plot_compare(filename):
     meta = data.metadata
     boxsize = meta.boxsize
     scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8"))
-    gamma = meta.hydro_scheme["Adiabatic index"][0]
+    gamma = meta.gas_gamma
 
     xstar = data.stars.coordinates
     xpart = data.gas.coordinates
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFHHeCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFHHeCheck.py
old mode 100644
new mode 100755
index a23cf0e98d50501174fa2b98206cd77df7ad1ca9..b4902fdb4d19eccda36dfa7b442861117637b4d9
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFHHeCheck.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DMFHHeCheck.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python3
 # ----------------------------------------------------
 # Stromgren 3D with multifrequency bins
 # The test is identical to the test in Section 5.3.2 of Pawlik & Schaye 2011 doi:10.1111/j.1365-2966.2010.18032.x
@@ -5,14 +6,16 @@
 # Plot comparison of simulated neutral fraction and temperature with the solution.
 # ----------------------------------------------------
 
-import swiftsimio
-from matplotlib import pyplot as plt
+import sys
+
 import matplotlib as mpl
 import matplotlib.lines as mlines
 import numpy as np
-import sys
-import stromgren_plotting_tools as spt
+import swiftsimio
 import unyt
+from matplotlib import pyplot as plt
+
+import stromgren_plotting_tools as spt
 
 # Plot parameters
 params = {
@@ -42,7 +45,7 @@ params = {
 }
 mpl.rcParams.update(params)
 
-scatterplot_kwargs = {"alpha": 0.1, "s": 4, "marker": ".", "linewidth": 0.0}
+scatterplot_kwargs = {"alpha": 0.1, "s": 2, "marker": ".", "linewidth": 0.0}
 
 # Read in cmdline arg: Are we plotting only one snapshot, or all?
 # WARNING: The reference solution is comparable with snapshot_102 only
@@ -90,19 +93,20 @@ def get_TT1Dsolution_HHe():
     rTtt1dlist = data[:, 0] * TT1D_runit
     Ttt1dlist = 10 ** data[:, 1] * unyt.K
 
-    outdict = {}
-    outdict["rHItt1dlist"] = rHItt1dlist
-    outdict["xHItt1dlist"] = xHItt1dlist
-    outdict["rHIItt1dlist"] = rHIItt1dlist
-    outdict["xHIItt1dlist"] = xHIItt1dlist
-    outdict["rHeItt1dlist"] = rHeItt1dlist
-    outdict["xHeItt1dlist"] = xHeItt1dlist
-    outdict["rHeIItt1dlist"] = rHeIItt1dlist
-    outdict["xHeIItt1dlist"] = xHeIItt1dlist
-    outdict["rHeIIItt1dlist"] = rHeIIItt1dlist
-    outdict["xHeIIItt1dlist"] = xHeIIItt1dlist
-    outdict["rTtt1dlist"] = rTtt1dlist
-    outdict["Ttt1dlist"] = Ttt1dlist
+    outdict = {
+        "rHItt1dlist": rHItt1dlist,
+        "xHItt1dlist": xHItt1dlist,
+        "rHIItt1dlist": rHIItt1dlist,
+        "xHIItt1dlist": xHIItt1dlist,
+        "rHeItt1dlist": rHeItt1dlist,
+        "xHeItt1dlist": xHeItt1dlist,
+        "rHeIItt1dlist": rHeIItt1dlist,
+        "xHeIItt1dlist": xHeIItt1dlist,
+        "rHeIIItt1dlist": rHeIIItt1dlist,
+        "xHeIIItt1dlist": xHeIIItt1dlist,
+        "rTtt1dlist": rTtt1dlist,
+        "Ttt1dlist": Ttt1dlist,
+    }
     return outdict
 
 
@@ -112,7 +116,7 @@ def plot_compare(filename):
     data = swiftsimio.load(filename)
     meta = data.metadata
     scheme = str(meta.subgrid_scheme["RT Scheme"].decode("utf-8"))
-    gamma = meta.hydro_scheme["Adiabatic index"][0]
+    gamma = meta.gas_gamma
 
     xstar = data.stars.coordinates
     xpart = data.gas.coordinates
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DsinglebinCheck.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DsinglebinCheck.py
old mode 100644
new mode 100755
index 7f5b19f27ad53b4b02ed63a164ef7ef473808044..34a0f9b8914256e789aab5f5811cb0a568fa123e
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DsinglebinCheck.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/plotStromgren3DsinglebinCheck.py
@@ -7,15 +7,16 @@
 # Plot comparison of simulated neutral fraction with analytic solution
 # ----------------------------------------------------
 
-import swiftsimio
-from matplotlib import pyplot as plt
+import sys
+
 import matplotlib as mpl
 import numpy as np
-import sys
-import stromgren_plotting_tools as spt
+import swiftsimio
 import unyt
+from matplotlib import pyplot as plt
 from scipy.integrate import odeint
 
+import stromgren_plotting_tools as spt
 
 # Plot parameters
 params = {
@@ -46,8 +47,8 @@ params = {
 mpl.rcParams.update(params)
 
 scatterplot_kwargs = {
-    "alpha": 0.6,
-    "s": 4,
+    "alpha": 0.1,
+    "s": 2,
     "marker": ".",
     "linewidth": 0.0,
     "facecolor": "blue",
@@ -111,10 +112,9 @@ def get_analytic_neutralfraction_stromgren3D(data, scheme):
             meta.parameters["SPHM1RT:alphaB"].decode("utf-8")
         ) * unyt.unyt_array(1.0, "cm**3/s")
     else:
-        print(
+        raise ValueError(
             "Error: Currently get_analytic_neutralfraction_stromgren3D can only work with SPHM1RT"
         )
-        exit()
     units = data.units
     unit_l_in_cgs = units.length.in_cgs()
     unit_v_in_cgs = (units.length / units.time).in_cgs()
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/propagationTest-3D.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/propagationTest-3D.yml
index 13c1f72080ee99279ca68a1ee4344826dc499610..db02aad603371173f86423a56f7db4f4d574b4cc 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/propagationTest-3D.yml
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/propagationTest-3D.yml
@@ -11,6 +11,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 8
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.001 # end time: radiation reaches edge of box
   dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
@@ -36,25 +37,25 @@ SPH:
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./propagationTest-3D.hdf5     # The file to read
-  periodic:   1                             # peridioc ICs. Keep them periodic so we don't loose photon energy. TODO: CHANGE LATER WHEN YOU ACTUALLY DO GAS INTERACTIONS
+  periodic:   1                             # peridioc ICs. Keep them periodic so we don't loose photon energy.
 
 GEARRT:
-    f_reduce_c: 1.        # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.99
-    photon_groups_Hz: []  # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N).
-    use_const_emission_rates: 1         # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [1e-28]   # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set.
-    hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    skip_thermochemistry: 1                           # skip thermochemistry.
-    stars_max_timestep: 1.562500e-05                  # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off.
+  f_reduce_c: 1.                                    # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.99
+  photon_groups_Hz: [0.]                            # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1e-28]          # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  skip_thermochemistry: 1                           # skip thermochemistry.
+  stars_max_timestep: 1.562500e-05                  # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off.
 
 
 SPHM1RT:
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh
index 4dd19ffd44aa840817cc6171d5c8900bf4ad123c..fa28e8d59c40c7ce7ae7a7365d33f5f8d6fe845e 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run.sh
@@ -22,7 +22,7 @@ fi
     stromgrenSphere-3D.yml 2>&1 | tee output.log
 
 # option with mpi
-# mpirun -np 2 ../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D.yml 2>&1 | tee output.log
+# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D.yml 2>&1 | tee output.log
 
 # Plot the Stromgren 3D checks.
 python3 ./plotSolution.py
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MF.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MF.sh
index 955f7a7d8788179d20ee78eb9ba529de917d5b46..dfdc64b884744aec9bd08fc2a4b7c9b7751edff1 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MF.sh
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MF.sh
@@ -16,13 +16,13 @@ if [ ! -f 'stromgrenSphere-3D.hdf5' ]; then
 fi
 
 # Run SWIFT with RT
-../../swift \
+../../../swift \
     --hydro --threads=4 --stars --external-gravity \
     --feedback --radiation \
     stromgrenSphere-3D-MF.yml 2>&1 | tee output.log
 
 # option with mpi
-# mpirun -np 2 ../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-MF.yml 2>&1 | tee output.log
+# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-MF.yml 2>&1 | tee output.log
 
 # Plot the Stromgren 3D multifrequency checks.
 python3 ./plotStromgren3DMFCheck.py 10
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MFHHe.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MFHHe.sh
index c16cbe8cba10d210f565063b8329e8b7b21b3713..f124e0f82c0c39a63804f4c668a8aaf2cc0c2288 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MFHHe.sh
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_MFHHe.sh
@@ -16,13 +16,13 @@ if [ ! -f 'stromgrenSphere-3D-HHe.hdf5' ]; then
 fi
 
 # Run SWIFT with RT
-../../swift \
+../../../swift \
     --hydro --threads=4 --stars --external-gravity \
     --feedback --radiation \
     stromgrenSphere-3D-MFHHe.yml 2>&1 | tee output.log
 
 # option with mpi
-# mpirun -np 2 ../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-MFHHe.yml 2>&1 | tee output.log
+# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-MFHHe.yml 2>&1 | tee output.log
 
 # Plot the Stromgren 3D with hydrogen and helium checks.
 python3 ./plotStromgren3DMFHHeCheck.py 10
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_singlebin.sh b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_singlebin.sh
index e32f4e53d0f7531d10f1e9f55ca97960b3542dea..b85a7e598480954dca64d4981fb45bea2f3e8ff8 100755
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/run_singlebin.sh
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/run_singlebin.sh
@@ -16,13 +16,13 @@ if [ ! -f 'stromgrenSphere-3D.hdf5' ]; then
 fi
 
 # Run SWIFT with RT
-../../swift \
+../../../swift \
     --hydro --threads=4 --stars --external-gravity \
     --feedback --radiation \
     stromgrenSphere-3D-singlebin.yml 2>&1 | tee output.log
 
 # option with mpi
-# mpirun -np 2 ../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-singlebin.yml 2>&1 | tee output.log
+# mpirun -np 2 ../../../swift_mpi --hydro --threads=14 --stars --external-gravity --feedback --radiation stromgrenSphere-3D-singlebin.yml 2>&1 | tee output.log
 
 # Plot the Stromgren 3D single frequency checks.
 python3 ./plotStromgren3DsinglebinCheck.py 50
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MF.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MF.yml
index 3364c3259a64d6b7e2e15514995777dbf5f99d75..2de98b310d37b85c5636ef73038b557cf34e3066 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MF.yml
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MF.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "StromgrenSphere-3D"
+  run_name: StromgrenSphere-3D-MF
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -12,6 +12,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.112 # end time
   dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
@@ -36,29 +37,29 @@ SPH:
 
 # Parameters related to the initial conditions
 InitialConditions:
-  file_name:  ./stromgrenSphere-3D.hdf5     # The file to read
-  periodic:   1                             # periodic ICs. 
+  file_name:  ./stromgrenSphere-3D.hdf5    # The file to read
+  periodic:   1                            # periodic ICs. 
 
 Stars:
   resolution_eta:       2.2348        # (Optional) Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). Defaults to the SPH value.
 
 GEARRT:
-    f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9
-    photon_groups_Hz: []                              # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [247985.76636]          # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.999999                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 1.e-6                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
-    stars_max_timestep: 5e-4                         # (Optional) restrict the maximal timestep of stars to this value (in internal units)
+  f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1.764e+04, 3.631e+04, 8.037e+03 ] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.999999                        # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 1.e-6                          # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.                             # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                            # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                           # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
+  stars_max_timestep: 6.250000e-05                  # (Optional) restrict the maximal timestep of stars to this value (in internal units)
 
 
 # commented parameters are for isothermal single-frequency bin Stromgren Sphere
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MFHHe.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MFHHe.yml
index 832001915f20cc90ef52cae14fcf9ae9e3063fbc..ca3a311cfcff596d0d0059671e3a8841e4b743a0 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MFHHe.yml
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-MFHHe.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "StromgrenSphere-3D"
+  run_name: StromgrenSphere-3D-MFHHe
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -12,6 +12,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.112 # end time
   dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
@@ -32,33 +33,33 @@ Statistics:
 SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.8      # Courant-Friedrich-Levy condition for time integration.
-  minimal_temperature:   10      # Kelvin
+  minimal_temperature:   10       # Kelvin
 
 # Parameters related to the initial conditions
 InitialConditions:
   file_name:  ./stromgrenSphere-3D-HHe.hdf5     # The file to read
-  periodic:   1                             # periodic ICs. 
+  periodic:   1                                 # periodic ICs. 
 
 Stars:
   resolution_eta:       2.2348        # (Optional) Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). Defaults to the SPH value.
 
 GEARRT:
-    f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9
-    photon_groups_Hz: []                              # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [247985.76636]          # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.999999                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 1.e-6                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
-    stars_max_timestep: 5e-4                         # (Optional) restrict the maximal timestep of stars to this value (in internal units)
+  f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1.764e+04, 3.631e+04, 8.037e+03 ] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.75                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.75                            # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 1.e-6                          # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.25                           # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 1.e-6                         # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 1.e-6                        # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
+  stars_max_timestep: 6.250000e-05                  # (Optional) restrict the maximal timestep of stars to this value (in internal units)
 
 
 # commented parameters are for isothermal single-frequency bin Stromgren Sphere
@@ -90,5 +91,3 @@ SPHM1RT:
   ionizing_photon_energy_erg: [3.0208e-11, 5.61973e-11, 1.05154e-10]  # (Optional) ionizing photon energy in erg averaged within frequency bins
   reinject:               1                          # (Optional) gather energy around injection radius and re-inject the energy
 
-
-
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-singlebin.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-singlebin.yml
index 68b2c7da377a90a243f65e2b89a2cfd721485a6d..203c41ad0c7eb08f05f7a32c999e1789f68db1a1 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-singlebin.yml
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D-singlebin.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "StromgrenSphere-3D"
+  run_name: StromgrenSphere-3D-singlebin
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -12,6 +12,7 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
+  max_nr_rt_subcycles: 1
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.512 # end time
   dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
@@ -43,22 +44,22 @@ Stars:
   resolution_eta:       2.2348        # (Optional) Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). Defaults to the SPH value.
 
 GEARRT:
-    f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9
-    photon_groups_Hz: []                              # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [247985.76636]          # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.999999                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 1.e-6                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
-    stars_max_timestep: 5e-4                         # (Optional) restrict the maximal timestep of stars to this value (in internal units)
+  f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9
+  photon_groups_Hz: [3.288e15]                      # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [6.198024e+04]   # # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.999999                        # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 1.e-6                          # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.                             # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                            # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                           # (Conditional) If set_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
+  stars_max_timestep: 6.250000e-05                  # (Optional) restrict the maximal timestep of stars to this value (in internal units)
 
 
 # commented parameters are for isothermal single-frequency bin Stromgren Sphere
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml
index b116b59e18b3339b5c45a898c1e0c1ebcb6b1628..41fd6d4f38875ef4c9ab7028eb5b5af2e4ef7d99 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgrenSphere-3D.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "StromgrenSphere-3D"
+  run_name: StromgrenSphere-3D
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -12,9 +12,10 @@ InternalUnitSystem:
 
 # Parameters governing the time integration
 TimeIntegration:
-  time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   0.512 # end time: radiation reaches edge of box
-  dt_min:     1.e-12 # The minimal time-step size of the simulation (in internal units).
+  max_nr_rt_subcycles: 16
+  time_begin: 0.      # The starting time of the simulation (in internal units).
+  time_end:   0.512   # end time
+  dt_min:     1.e-12  # The minimal time-step size of the simulation (in internal units).
   dt_max:     1.e-03  # The maximal time-step size of the simulation (in internal units).
 
 # Parameters governing the snapshots
@@ -43,22 +44,22 @@ Stars:
   resolution_eta:       1.2348        # (Optional) Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel). Defaults to the SPH value.
 
 GEARRT:
-    f_reduce_c: 0.01                                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9
-    photon_groups_Hz: []                              # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-    star_emission_rates_LSol: [247985.76636]          # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:  1.00                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    set_initial_ionization_mass_fractions: 1          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.999999                        # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 1.e-6                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    stellar_spectrum_type: 1                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_blackbody_temperature_K: 1.e5    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
-    stars_max_timestep: 5e-4                         # (Optional) restrict the maximal timestep of stars to this value (in internal units)
+  f_reduce_c: 0.01                                    # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1.764e+04, 3.631e+04, 8.037e+03 ] # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  1.00                       # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  set_initial_ionization_mass_fractions: 1            # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.999999                          # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 1.e-6                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.                               # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                              # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  stellar_spectrum_type: 1                            # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_blackbody_temperature_K: 1.e5      # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum.
+  stars_max_timestep: 6.26e-5                         # (Optional) restrict the maximal timestep of stars to this value (in internal units)
 
 
 SPHM1RT:
diff --git a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgren_plotting_tools.py b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgren_plotting_tools.py
index 4bf034bf286428f23963aab3f66b90ba2d25d196..8141897acdc9b1a93b482789080c1680f30651d7 100644
--- a/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgren_plotting_tools.py
+++ b/examples/RadiativeTransferTests/StromgrenSphere_3D/stromgren_plotting_tools.py
@@ -1,12 +1,291 @@
+# Contains commonly used functions related to plotting results
+# of the Strömgren Sphere Examples and other gas related functions.
+
+import copy
 import os
+
 import numpy as np
 import unyt
-import copy
-
 
+# species masses in atomic mass units
 mamu = {"e": 0.0, "HI": 1.0, "HII": 1.0, "HeI": 4.0, "HeII": 4.0, "HeIII": 4.0}
 
 
+def get_number_densities(Temp, XH, XHe):
+    """
+    Compute the number densities of all species at a given
+    temperature following Katz, Hernquist, and Weinberg 1996
+
+    Temp: temperature [unyt quantity]
+    XH: total mass fraction of all hydrogen species (HI + HII)
+    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
+    """
+
+    # n_H = X_H * rho_gas / m_H
+    # then m_H = rho_gas * X_H / n_H
+    # n_He = X_He * rho_gas / m_He =
+    #      = (1 - X_H) * rho_gas / (4 * m_H)
+    #      = (1 - X_H) * rho_gas / (4 * rho_gas * x_H / n_H)
+    #      = (1 - X_H) / (4 * X_H) * n_H
+    #      =  X_He / 4(1 - X_He) * n_H = y * n_H
+
+    if XH == 0:
+        nH = 0.0
+        nHe = 1.0
+    else:
+        nH = XH
+        nHe = XHe / 4
+
+    # NOTE: This is not the ionization threshold!
+    if Temp < 5000 * unyt.K:
+        nH0 = nH
+        nHp = 0.0
+        nHe0 = nHe
+        nHep = 0.0
+        nHepp = 0.0
+
+    else:
+
+        Temp.convert_to_cgs()
+        T = Temp.v
+        # Recombination rate for H+ in units of cm^3 s^-1
+        A_Hp = (
+            8.40e-11
+            / np.sqrt(T)
+            * (T * 1e-3) ** (-0.2)
+            * 1.0
+            / (1.0 + (T * 1e-6) ** 0.7)
+        )
+
+        # Dielectronic recombination rate for He+ in units of cm^3 s^-1
+        A_d = (
+            1.9e-3
+            / T ** 1.5
+            * np.exp(-470000.0 / T)
+            * (1.0 + 0.3 * np.exp(-94000.0 / T))
+        )
+        # Recombination rate for He+ in units of cm^3 s^-1
+        A_Hep = 1.5e-10 / T ** 0.6353
+        # Recombination rate for He++ in units of cm^3 s^-1
+        A_Hepp = (
+            3.36e-10
+            / np.sqrt(T)
+            * (T * 1e-3) ** (-0.2)
+            * 1.0
+            / (1.0 + (T * 1e-6) ** 0.7)
+        )
+        # collisional ionization rate for H0 in units of cm^3 s^-1
+        #  G_H0 = 1.17e-10 * np.sqrt(T) * np.exp(-157809.1 / T) * 1. / (1. + np.sqrt(T*1e-5))
+        G_H0 = (
+            5.85e-11
+            * np.sqrt(T)
+            * np.exp(-157809.1 / T)
+            * 1.0
+            / (1.0 + np.sqrt(T * 1e-5))
+        )
+        # collisional ionization rate for He0 in units of cm^3 s^-1
+        G_He0 = (
+            2.38e-11
+            * np.sqrt(T)
+            * np.exp(-285335.4 / T)
+            * 1.0
+            / (1.0 + np.sqrt(T * 1e-5))
+        )
+        # collisional ionization rate for He+ in units of cm^3 s^-1
+        G_Hep = (
+            5.68e-12
+            * np.sqrt(T)
+            * np.exp(-631515.0 / T)
+            * 1.0
+            / (1.0 + np.sqrt(T * 1e-5))
+        )
+
+        # Katz et al. 1996 eq. 33 - 38
+        # Note: We assume all photoionization rates to be zero.
+        # Also, here we don't care about the actual number density, i.e.
+        # about the volume, since it'll cancel out later when we compute
+        # the mass fractions.
+
+        nH0 = nH * A_Hp / (A_Hp + G_H0)
+        nHp = nH - nH0
+        nHep = nHe / (1.0 + (A_Hep + A_d) / G_He0 + G_Hep / A_Hepp)
+        nHe0 = nHep * (A_Hep + A_d) / G_He0
+        nHepp = nHep * G_Hep / A_Hepp
+
+    # electron density
+    ne = nHp + nHep + 2.0 * nHepp
+
+    return nH0, nHp, nHe0, nHep, nHepp, ne
+
+
+def get_number_densities_array(Temp, XH, XHe):
+    """
+    Compute the number densities of all species at a given
+    temperature following Katz, Hernquist, and Weinberg 1996
+
+    Temp: temperature [unyt array]
+    XH: total mass fraction of all hydrogen species (HI + HII)
+    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
+    """
+
+    # n_H = X_H * rho_gas / m_H
+    # then m_H = rho_gas * X_H / n_H
+    # n_He = X_He * rho_gas / m_He =
+    #      = (1 - X_H) * rho_gas / (4 * m_H)
+    #      = (1 - X_H) * rho_gas / (4 * rho_gas * x_H / n_H)
+    #      = (1 - X_H) / (4 * X_H) * n_H
+    #      =  X_He / 4(1 - X_He) * n_H = y * n_H
+
+    nH = np.zeros(XH.shape, dtype=np.float64)
+    nHe = np.zeros(XH.shape, dtype=np.float64)
+
+    mask = XH == 0
+    nH[mask] = 0.0
+    nHe[mask] = 1.0
+
+    inv_mask = np.logical_not(mask)
+    nH[inv_mask] = XH[inv_mask]
+    nHe[inv_mask] = 0.25 * XHe[inv_mask]
+
+    nH0 = np.zeros(XH.shape, dtype=np.float64)
+    nHp = np.zeros(XH.shape, dtype=np.float64)
+    nHe0 = np.zeros(XH.shape, dtype=np.float64)
+    nHep = np.zeros(XH.shape, dtype=np.float64)
+    nHepp = np.zeros(XH.shape, dtype=np.float64)
+
+    # NOTE: This is not the ionization threshold!
+    neutral = Temp < 5000 * unyt.K
+
+    nH0[neutral] = nH[neutral]
+    nHp[neutral] = 0.0
+    nHe0[neutral] = nHe[neutral]
+    nHep[neutral] = 0.0
+    nHepp[neutral] = 0.0
+
+    Temp.convert_to_cgs()
+    T = Temp.v
+    ionized = np.logical_not(neutral)
+
+    # Recombination rate for H+ in units of cm^3 s^-1
+    A_Hp = (
+        8.40e-11 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7)
+    )
+    # Dielectronic recombination rate for He+ in units of cm^3 s^-1
+    A_d = 1.9e-3 / T ** 1.5 * np.exp(-470000.0 / T) * (1.0 + 0.3 * np.exp(-94000.0 / T))
+    # Recombination rate for He+ in units of cm^3 s^-1
+    A_Hep = 1.5e-10 / T ** 0.6353
+    # Recombination rate for He++ in units of cm^3 s^-1
+    A_Hepp = (
+        3.36e-10 / np.sqrt(T) * (T * 1e-3) ** (-0.2) * 1.0 / (1.0 + (T * 1e-6) ** 0.7)
+    )
+    # collisional ionization rate for H0 in units of cm^3 s^-1
+    G_H0 = (
+        5.85e-11 * np.sqrt(T) * np.exp(-157809.1 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
+    )
+    # collisional ionization rate for He0 in units of cm^3 s^-1
+    G_He0 = (
+        2.38e-11 * np.sqrt(T) * np.exp(-285335.4 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
+    )
+    # collisional ionization rate for He+ in units of cm^3 s^-1
+    G_Hep = (
+        5.68e-12 * np.sqrt(T) * np.exp(-631515.0 / T) * 1.0 / (1.0 + np.sqrt(T * 1e-5))
+    )
+
+    # Katz et al. 1996 eq. 33 - 38
+    # Note: We assume all photoionization rates to be zero.
+    # Also, here we don't care about the actual number density, i.e.
+    # about the volume, since it'll cancel out later when we compute
+    # the mass fractions.
+
+    nH0[ionized] = nH[ionized] * A_Hp[ionized] / (A_Hp[ionized] + G_H0[ionized])
+    nHp[ionized] = nH[ionized] - nH0[ionized]
+    nHep[ionized] = nHe[ionized] / (
+        1.0
+        + (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized]
+        + G_Hep[ionized] / A_Hepp[ionized]
+    )
+    nHe0[ionized] = nHep[ionized] * (A_Hep[ionized] + A_d[ionized]) / G_He0[ionized]
+    nHepp[ionized] = nHep[ionized] * G_Hep[ionized] / A_Hepp[ionized]
+
+    # electron density
+    ne = nHp + nHep + 2.0 * nHepp
+
+    return nH0, nHp, nHe0, nHep, nHepp, ne
+
+
+def get_mass_fractions(T, XH, XHe):
+    """
+    Compute the mass fractions of all species at a
+    given temperature
+
+    T: temperature
+    XH: total mass fraction of all hydrogen species (HI + HII)
+    XHe: total mass fraction of all helium species (HeI + HeII + HeIII)
+    """
+
+    # first get number densities
+    if isinstance(XH, np.ndarray):
+        nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities_array(T, XH, XHe)
+    else:
+        nH0, nHp, nHe0, nHep, nHepp, ne = get_number_densities(T, XH, XHe)
+
+    # now get mass denities in units of atomic mass units
+    mH0 = nH0
+    mHp = nHp
+    mHe0 = 4.0 * nHe0
+    mHep = 4.0 * nHep
+    mHepp = 4.0 * nHepp
+    # neglect electron mass contributions
+
+    mtot = mH0 + mHp + mHe0 + mHep + mHepp  # + me
+
+    XH0 = mH0 / mtot
+    XHp = mHp / mtot
+    XHe0 = mHe0 / mtot
+    XHep = mHep / mtot
+    XHepp = mHepp / mtot
+    #  Xe = me / mtot
+
+    return XH0, XHp, XHe0, XHep, XHepp
+
+
+def internal_energy(T, mu, gamma):
+    """
+    Compute the internal energy of the gas for a given
+    temperature and mean molecular weight
+    """
+    # Using u = 1 / (gamma - 1) * p / rho
+    #   and p = N/V * kT = rho / (mu * m_u) * kT
+
+    u = unyt.boltzmann_constant * T / (gamma - 1) / (mu * unyt.atomic_mass_unit)
+    return u
+
+
+def get_soundspeed_from_internal_energy(data):
+    """
+    Compute the local sound speed for all particles.
+    data: swiftsimio.load() object.
+    """
+
+    u = data.gas.internal_energies
+    gamma = data.metadata.gas_gamma
+
+    return np.sqrt(u * gamma / (gamma - 1))
+
+
+def get_soundspeed_from_density_pressure(data):
+    """
+    Compute the local sound speed for all particles.
+    data: swiftsimio.load() object.
+    """
+
+    gamma = data.metadata.gas_gamma
+    P = data.gas.pressures
+    rho = data.gas.densities
+
+    return np.sqrt(gamma * P / rho)
+
+
 def mean_molecular_weight(XH0, XHp, XHe0, XHep, XHepp):
     """
     Determines the mean molecular weight for given 
@@ -98,6 +377,8 @@ def get_imf(scheme, data):
                 * mamu[column]
             )
             setattr(imf, column, mass_fraction)
+    else:
+        raise ValueError("Unknown scheme", scheme)
     return imf
 
 
@@ -128,10 +409,10 @@ def get_abundances(scheme, data):
         )
     elif scheme.startswith("SPH M1closure"):
         sA = data.gas.rt_species_abundances
+    else:
+        raise ValueError("Unknown scheme", scheme)
     return sA
 
-    # clean string up
-
 
 def trim_paramstr(paramstr):
     """
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py b/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py
index ff47c49a102869c399d55b2d62455be63839c288..b7193b583a3400e85f146ff4c1171d06b3a90230 100755
--- a/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/makeIC.py
@@ -25,11 +25,10 @@
 # there are some hydro particles that have no star neighbours.
 # ---------------------------------------------------------------------
 
-from swiftsimio import Writer
-
-import unyt
-import numpy as np
 import h5py
+import numpy as np
+import unyt
+from swiftsimio import Writer
 
 # Box is 1 Mpc
 boxsize = 100 * unyt.m
@@ -57,7 +56,7 @@ for i in range(n_p):
 
 # Generate star coordinates
 for i in range(n_s):
-    # factor 0.52 below: shift particles a bit so they don't overlap with hydro
+    # factor 0.52 below: shift particles a bit, so they don't overlap with hydro
     x = 0.4 * boxsize + (i + 0.52) * ds
     for j in range(n_s):
         y = 0.4 * boxsize + (j + 0.52) * ds
@@ -69,7 +68,7 @@ xp = unyt.unyt_array(xp, boxsize.units)
 xs = unyt.unyt_array(xs, boxsize.units)
 
 
-w = Writer(unyt.unit_systems.cgs_unit_system, boxsize)
+w = Writer(unyt.unit_systems.cgs_unit_system, boxsize, compress=False)
 
 w.gas.coordinates = xp
 w.stars.coordinates = xs
@@ -110,7 +109,7 @@ parts = F["/PartType0"]
 
 for grp in range(nPhotonGroups):
     dsetname = "PhotonEnergiesGroup{0:d}".format(grp + 1)
-    energydata = np.ones((nparts), dtype=np.float64) * (grp + 1)
+    energydata = np.ones(nparts, dtype=np.float64) * (grp + 1)
     parts.create_dataset(dsetname, data=energydata)
 
     dsetname = "PhotonFluxesGroup{0:d}".format(grp + 1)
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py b/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py
index 62183fd21359a8b9fb365f1186e39f40b4dd3c70..746c1e10ff297e7986382f1e1c5bcbf97a053326 100755
--- a/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/plotRadiationProjection.py
@@ -26,16 +26,17 @@
 # all snapshots available in the workdir.
 # ----------------------------------------------------
 
-import sys
+import gc
 import os
-import swiftsimio
+import sys
+
+import matplotlib as mpl
 import numpy as np
-import gc
+import swiftsimio
 import unyt
 from matplotlib import pyplot as plt
-import matplotlib as mpl
-from mpl_toolkits.axes_grid1 import make_axes_locatable
 from matplotlib.colors import SymLogNorm
+from mpl_toolkits.axes_grid1 import make_axes_locatable
 
 # Parameters users should/may tweak
 
@@ -280,7 +281,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
                 ax.set_ylabel("Energies [$" + energy_units_str + "$]")
 
     # Add title
-    title = filename.replace("_", "\_")  # exception handle underscore for latex
+    title = filename.replace("_", r"\_")  # exception handle underscore for latex
     if meta.cosmology is not None:
         title += ", $z$ = {0:.2e}".format(meta.z)
     title += ", $t$ = {0:.2e}".format(meta.time.to(time_units))
@@ -296,7 +297,7 @@ def plot_photons(filename, energy_boundaries=None, flux_boundaries=None):
 
 def get_minmax_vals(snaplist):
     """
-    Find minimal and maximal values for energy and flux
+    Find minimal and maximal values for energy and flux,
     so you can fix axes limits over all snapshots
 
     snaplist: list of snapshot filenames
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py
index 1f003bd0e54e255962b292b1fe4504fa698f06ec..3992573f8adbf0fb7aff7e790483d6bf90937e11 100755
--- a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks-GEAR.py
@@ -55,8 +55,8 @@ skip_plots = False  # skip showing plots for diagnosis
 float_comparison_tolerance = 1e-4
 # tolerance for a float that was summed up over all particles to vary
 float_particle_sum_comparison_tolerance = 5e-4
-# tolerance for meshless energy distribution scheme during injeciton comparison
-float_psi_comparison_tolerance = 5e-4
+# tolerance for energy conservation and injection during injeciton comparison
+energy_conservation_tolerance = 1e-3
 
 # -------------------------------------------------------------------------------
 
@@ -110,7 +110,7 @@ def check_essentials(snapdata, rundata):
                 if break_on_diff:
                     quit()
 
-        if snap.has_stars:
+        if snap.has_star_debug_data:
             injected_energies = snap.stars.InjectedPhotonEnergy
             ok = np.isfinite(injected_energies)
             if not ok.all():
@@ -142,8 +142,9 @@ def check_essentials(snapdata, rundata):
             groupfluxnorm = np.sqrt(
                 groupfluxes[:, 0] ** 2 + groupfluxes[:, 1] ** 2 + groupfluxes[:, 2] ** 2
             )
+            flux_units = groupfluxes.units
 
-            fishy = groupfluxnorm > max_fluxes
+            fishy = groupfluxnorm.to(flux_units).v > max_fluxes.to(flux_units).v
 
             if fishy.any():
                 print("In snapshot", snap.snapnr, ", group", g + 1, ":")
@@ -175,7 +176,15 @@ def check_injection(snapdata, rundata):
     # ----------------------------------------------------------------
 
     if not rundata.has_stars:
-        print("Found no stars in run. Skipping injection tests")
+        print("Found no stars in run. Skipping injection tests.")
+        return
+
+    if not rundata.has_star_debug_data:
+        print(
+            "Found no debug data in run.",
+            "Can't do injection tests without it.",
+            "Compile swift with debugging checks on.",
+        )
         return
 
     emission_rates = rundata.const_emission_rates
@@ -229,14 +238,18 @@ def check_injection(snapdata, rundata):
     #  Remember: The reason we have too little injected energy is because we
     #  don't inject any energy during the zeroth time step. We can't, since the
     #  zeroth time step is the one that determines the time step size of the star.
+    #  Also, the star-feedback loop is skipped.
 
     # TODO: this assumes a constant number of stars. You need to deal with SF
     # TODO: this assumes no cosmological expansion
 
+    # upper boundaries (analytically expected values) for the plots
     upper_boundary_for_plot = []
+    # snapshot numbers, used as x-axis in plots
     snaps_for_1bplot = []
 
     initial_time = snapdata[0].time
+
     if snapdata[0].has_stars:
         emission_at_initial_time = snapdata[0].stars.InjectedPhotonEnergy.sum(axis=0)
     else:
@@ -244,87 +257,95 @@ def check_injection(snapdata, rundata):
             np.zeros(rundata.ngroups, dtype=np.float64) * unyt.erg
         )
 
-    if rundata.use_const_emission_rate:
+    continue_test = True
+
+    if not rundata.use_const_emission_rate:
+        print("Can't run check 1b without constant emission rates")
+        continue_test = False
+
+    if continue_test:
         if len(snapdata) <= 2:
             # because it's useless to check only snap_0000
             print("Check 1b: You need at least 2 snapshots to do this particular test")
-        else:
-            diffs_for_plot = []
-            energies_for_plot = []
-            found_potential_error = False
-            for snap in snapdata[1:]:  # skip snapshot zero
-                dt = snap.time - initial_time
-                if snap.has_stars:
-                    injected_energies = np.atleast_1d(
-                        snap.stars.InjectedPhotonEnergy.sum(axis=0)
-                        - emission_at_initial_time
+            continue_test = False
+
+    if continue_test:
+
+        diffs_for_plot = []  # relative difference between expectation and data
+        energies_for_plot = []  # the injected energies that were found in data
+        found_potential_error = False
+
+        for snap in snapdata[1:]:  # skip snapshot zero
+            dt = snap.time - initial_time
+            if snap.has_stars:
+                injected_energies = np.atleast_1d(
+                    snap.stars.InjectedPhotonEnergy.sum(axis=0)
+                    - emission_at_initial_time
+                )
+            else:
+                injected_energies = np.zeros(ngroups) * unyt.erg
+            # get what energies we expect the stars to have injected
+            energies_expected = snap.nstars * emission_rates * dt
+            energies_expected = energies_expected.to(injected_energies.units)
+            # get relative difference
+            diff = np.array(injected_energies / energies_expected - 1.0)
+
+            # store data
+            upper_boundary_for_plot.append(energies_expected)
+            energies_for_plot.append(injected_energies)
+            diffs_for_plot.append(diff)
+            snaps_for_1bplot.append(snap.snapnr)
+
+            # diff should be < 0. Allow for some tolerance here
+            if (diff > energy_conservation_tolerance).any():
+                print(
+                    "Injection Energy Prediction upper boundary is wrong; "
+                    + "snapshot {0:d} tolerance {1:.2e}".format(
+                        snap.snapnr, energy_conservation_tolerance
                     )
-                else:
-                    injected_energies = np.zeros(ngroups) * unyt.erg
-                energies_expected = snap.nstars * emission_rates * dt
-                energies_expected = energies_expected.to(injected_energies.units)
-                diff = np.array(injected_energies / energies_expected - 1.0)
-
-                upper_boundary_for_plot.append(energies_expected)
-                energies_for_plot.append(injected_energies)
-                diffs_for_plot.append(diff)
-                snaps_for_1bplot.append(snap.snapnr)
-
-                # diff should be < 0. Allow for some tolerance here
-                if (diff > float_psi_comparison_tolerance).any():
+                )
+                for g in range(ngroups):
+                    #  if energies_expected[g] > injected_energies[g]:
+                    print("--- group", g + 1)
+                    print("----- injected:", injected_energies[g])
+                    print("----- expected:", energies_expected[g], "should be smaller")
                     print(
-                        "Injection Energy Prediction upper boundary is wrong; "
-                        + "snapshot {0:d} tolerance {1:.2e}".format(
-                            snap.snapnr, float_psi_comparison_tolerance
-                        )
+                        "----- ratio   :", (injected_energies[g] / energies_expected[g])
                     )
-                    for g in range(ngroups):
-                        #  if energies_expected[g] > injected_energies[g]:
-                        print("--- group", g + 1)
-                        print("----- injected:", injected_energies[g])
-                        print(
-                            "----- expected:", energies_expected[g], "should be smaller"
-                        )
-                        print(
-                            "----- ratio   :",
-                            (injected_energies[g] / energies_expected[g]),
-                        )
-                        print("----- diff    :", diff[g], "should be < 0")
-                        found_potential_error = True
+                    print("----- diff    :", diff[g], "should be < 0")
+                    found_potential_error = True
 
-                        if break_on_diff:
-                            quit()
+                    if break_on_diff:
+                        quit()
 
-            if not skip_plots and found_potential_error:
-                # Make this plot if there are possible errors
-                diffs_for_plot = np.array(diffs_for_plot)
-                plt.figure()
-                for g in range(ngroups):
-                    plt.plot(
-                        snaps_for_1bplot,
-                        diffs_for_plot[:, g],
-                        label="group {0:d}".format(g + 1),
-                    )
-                    plt.plot(
-                        [snaps_for_1bplot[0], snaps_for_1bplot[-1]],
-                        [0, 0],
-                        "k",
-                        label="upper boundary",
-                    )
-                plt.legend()
-                plt.xlabel("snapshot")
-                plt.ylabel("injected energy / expected energy - 1")
-                plt.title(
-                    "Difference from expected injected energy - something's fishy"
+        if not skip_plots and found_potential_error:
+            # Make this plot if there are possible errors
+            diffs_for_plot = np.array(diffs_for_plot)
+            plt.figure()
+            for g in range(ngroups):
+                plt.plot(
+                    snaps_for_1bplot,
+                    diffs_for_plot[:, g],
+                    label="group {0:d}".format(g + 1),
                 )
-                plt.show()
-                plt.close()
+            plt.plot(
+                [snaps_for_1bplot[0], snaps_for_1bplot[-1]],
+                [0, 0],
+                "k",
+                label="upper boundary",
+            )
+            plt.legend()
+            plt.xlabel("snapshot")
+            plt.ylabel("injected energy / expected energy - 1")
+            plt.title("Difference from expected injected energy - something's fishy")
+            plt.show()
+            plt.close()
 
     # --------------------------------
     # Create additional plots?
     # --------------------------------
 
-    if rundata.use_const_emission_rate:
+    if rundata.use_const_emission_rate and continue_test:
         if not skip_plots and len(energies_for_plot) > 2:
             # Show me the plot that the injected energy
             # is correctly bounded
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py
index f28dcc661d5d7dad9b97cfc6c89f60658c1fae1d..e5379b38ef2e67fd00daf46eb477d86b1dd8386c 100755
--- a/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_sanity_checks.py
@@ -56,18 +56,20 @@ else:
     file_prefix = "output"
 
 
-def check_hydro_sanity(snapdata):
+def check_hydro_sanity(snapdata, rundata):
     """
     Sanity checks for hydro variables.
     - injection always done?
     - gradients always done?
     - thermochemistry always done?
     - RT transport calls >= RT gradient calls?
+    - number of subcycles != 0?
     """
 
     npart = snapdata[0].gas.coords.shape[0]
 
     print("Checking hydro")
+    warning_printed = False
 
     # ----------------------------------------------
     # check absolute values of every snapshot
@@ -186,8 +188,17 @@ def check_hydro_sanity(snapdata):
         # at least the number of calls to transport interactions
         # in RT interactions
         # --------------------------------------------------------------
-        fishy = gas.RTCallsIactTransportInteraction < gas.RTCallsIactGradientInteraction
-        if fishy.any():
+        if rundata.with_mpi:
+            check = False
+            if not warning_printed:
+                print("- MPI run: skipping hydro sanity interaction call count checks")
+                warning_printed = True
+        else:
+            fishy = (
+                gas.RTCallsIactTransportInteraction < gas.RTCallsIactGradientInteraction
+            )
+            check = fishy.any()
+        if check:
             print("- checking hydro sanity pt2.5; snapshot", snap.snapnr)
             print(
                 "--- Found RT transport calls iact < gradient calls iact:",
@@ -207,10 +218,25 @@ def check_hydro_sanity(snapdata):
             if break_on_diff:
                 quit()
 
+        # -------------------------------------------------------------
+        # Check that the subcycle counter isn't zero.
+        # We expect a particle to be radioactive at least each time it
+        # is hydro active, so the subcycle counter must never be zero.
+        # -------------------------------------------------------------
+
+        fishy = gas.nsubcycles <= 0
+        if fishy.any():
+            print("- checking hydro sanity pt 2.6; snapshot", snap.snapnr)
+            print("Found nsubcycles <= 0:", np.count_nonzero(fishy), "/", npart)
+            if print_diffs:
+                print("nsubcycles:", gas.nsubcycles[fishy])
+            if break_on_diff:
+                quit()
+
     return
 
 
-def check_stars_sanity(snapdata):
+def check_stars_sanity(snapdata, rundata):
     """
     Sanity checks for stars variables.
     - total calls keep increasing?
@@ -230,6 +256,7 @@ def check_stars_sanity(snapdata):
 
         fishy = np.logical_and(this.EmissionRateSet == 0, this.RadiationEmittedTot > 0)
         if fishy.any():
+            count = 0
             print("- checking stars sanity pt1", snap.snapnr)
             print("--- Emitted with unset emission rate")
             for i in range(nspart):
@@ -250,7 +277,7 @@ def check_stars_sanity(snapdata):
     return
 
 
-def check_stars_hydro_interaction_sanity(snapdata):
+def check_stars_hydro_interaction_sanity(snapdata, rundata):
     """
     Sanity checks for hydro vs star interaction
     call counts.
@@ -278,18 +305,37 @@ def check_stars_hydro_interaction_sanity(snapdata):
             print("Found no stars")
             sum_star_tot_radiation = 0.0
 
-        if sum_gas_tot_radiation != sum_star_tot_radiation:
-            print("- checking hydro v star sanity pt1; snapshot", snap.snapnr)
-            print(
-                "--- Total emitted and absorbed radiation not equal: Gas",
-                sum_gas_tot_radiation,
-                "stars",
-                sum_star_tot_radiation,
-                "diff",
-                sum_star_tot_radiation - sum_gas_tot_radiation,
-            )
-            if break_on_diff:
-                quit()
+        if rundata.with_mpi:
+            # with MPI, stars may have missing integer counts in total
+            # since stars that have been sent over won't get the updated
+            # interaction count sent back to their origin. So we allow
+            # stars to have fewer interaction counts over MPI.
+            if sum_gas_tot_radiation < sum_star_tot_radiation:
+                print("- checking hydro v star sanity pt1; snapshot", snap.snapnr)
+                print(
+                    "--- More star iacts than gas iacts: Gas",
+                    sum_gas_tot_radiation,
+                    "stars",
+                    sum_star_tot_radiation,
+                    "diff",
+                    sum_star_tot_radiation - sum_gas_tot_radiation,
+                )
+                if break_on_diff:
+                    quit()
+
+        else:
+            if sum_gas_tot_radiation != sum_star_tot_radiation:
+                print("- checking hydro v star sanity pt1; snapshot", snap.snapnr)
+                print(
+                    "--- Total emitted and absorbed radiation not equal: Gas",
+                    sum_gas_tot_radiation,
+                    "stars",
+                    sum_star_tot_radiation,
+                    "diff",
+                    sum_star_tot_radiation - sum_gas_tot_radiation,
+                )
+                if break_on_diff:
+                    quit()
 
         # --------------------------------------------------------------
         # check that we have the correct amount of interactions recorded
@@ -329,9 +375,9 @@ def main():
         prefix=file_prefix, skip_snap_zero=skip_snap_zero, skip_last_snap=skip_last_snap
     )
 
-    check_hydro_sanity(snapdata)
-    check_stars_sanity(snapdata)
-    check_stars_hydro_interaction_sanity(snapdata)
+    check_hydro_sanity(snapdata, rundata)
+    check_stars_sanity(snapdata, rundata)
+    check_stars_hydro_interaction_sanity(snapdata, rundata)
 
     return
 
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py
index 93710c97759dc9c1d4f2cb655f7cd3c3f6ff6493..22b8987afd1e373bf469cbd7ac9ed6546047dff8 100755
--- a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks-GEAR.py
@@ -40,10 +40,9 @@
 # -----------------------------------------------------------------------
 
 
-import numpy as np
 from sys import argv
-from swift_rt_GEAR_io import get_snap_data
 
+from swift_rt_GEAR_io import get_snap_data
 
 # some behaviour options
 print_diffs = True  # print differences you find
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py
index c8a16ed9ed82d05d337ff000881a98149efa155f..e543243af94134ad6a6fa37064c00fce4b170262 100755
--- a/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/rt_uniform_box_checks.py
@@ -39,10 +39,11 @@
 # -----------------------------------------------------------------------
 
 
-import numpy as np
 from sys import argv
-from swift_rt_debug_io import get_snap_data
 
+import numpy as np
+
+from swift_rt_debug_io import get_snap_data
 
 # some behaviour options
 skip_snap_zero = True  # skip snap_0000.hdf5
@@ -222,6 +223,25 @@ def check_all_hydro_is_equal(snapdata):
         if (compare.gas.ThermochemistryDone[nzs] == 0).any():
             print("Oh no 3")
 
+        # ---------------------------------------------------------------
+        # Check numbers of subcycles.
+        # ---------------------------------------------------------------
+        fishy = ref.gas.nsubcycles != compare.gas.nsubcycles
+        if fishy.any():
+            print("- Comparing hydro", ref.snapnr, "->", compare.snapnr)
+            print(
+                "--- Subcycle Calls count differ: {0:8d} / {1:8d}; ".format(
+                    np.count_nonzero(fishy), npart
+                )
+            )
+            if not skip_last_snap:
+                print(
+                    "Note, this might be acceptable behaviour for the final snapshot. You currently aren't skipping it in this check."
+                )
+
+            if break_on_diff:
+                quit()
+
     return
 
 
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/run.sh b/examples/RadiativeTransferTests/UniformBox_3D/run.sh
index 9f81af658a3af253ce1faf407ac39b4cdffe0867..214533a4f6366f76ad19ec57c1c1effb80054c8b 100755
--- a/examples/RadiativeTransferTests/UniformBox_3D/run.sh
+++ b/examples/RadiativeTransferTests/UniformBox_3D/run.sh
@@ -9,12 +9,26 @@ if [ ! -f 'uniformBox-rt.hdf5' ]; then
     python3 makeIC.py
 fi
 
+# use cmdline args as shortcut to run with debugger/MPI
+# -g run with gdb
+# -m run with MPI
+# -mg run with MPI and gdb in individual xterm windows
+# -ml run with MPI and write individual output file per MPI rank
 cmd=../../../swift
 if [ $# -gt 0 ]; then
     case "$1" in 
-    g | gdb)
+    -g | g | gdb)
         cmd='gdb --args ../../../swift'
         ;;
+    -m | m | mpi)
+        cmd='mpirun -n 2 ../../../swift_mpi' 
+        ;;
+    -mg | -gm | gm | mg | gmpi | gdbmpi )
+        cmd='mpirun -n 2 xterm -e gdb -ex run --args ../../../swift_mpi'
+        ;;
+    -ml | ml | lm | mpilog | logmpi)
+        cmd='mpirun -n 2 --output-filename individual_rank_output --merge-stderr-to-stdout ../../../swift_mpi'
+        ;;
     *)
         echo unknown cmdline param, running without gdb
         ;;
@@ -24,8 +38,8 @@ fi
 # Run SWIFT with RT
 $cmd \
     --hydro --threads=4 --stars --external-gravity \
-    --feedback --radiation \
-    uniform_rt_timestep_output_sync.yml 2>&1 | tee output.log
+    --feedback --radiation --verbose=0 \
+    uniform_rt_timestep_output_sync.yml  2>&1 | tee output.log
 
 echo "running sanity checks"
 python3 ./rt_sanity_checks.py | tee sanity_check.log
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py
index bf4a878be449832b70c0b4c6a35ba7724d3ca9b0..6fe1efdd00fec032d93a892d81d98619a26cfa58 100644
--- a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_GEAR_io.py
@@ -26,9 +26,10 @@
 
 
 import os
-import unyt
-import swiftsimio
+
 import numpy as np
+import swiftsimio
+import unyt
 
 
 class RTGasData(object):
@@ -37,7 +38,6 @@ class RTGasData(object):
     """
 
     def __init__(self):
-
         self.IDs = None
         self.coords = None
         self.h = None
@@ -55,7 +55,6 @@ class RTStarData(object):
     """
 
     def __init__(self):
-
         self.IDs = None
         self.coords = None
         self.h = None
@@ -75,7 +74,8 @@ class RTSnapData(object):
         self.ncells = None
         self.boxsize = None
         self.time = None
-        self.has_stars = True
+        self.has_stars = False
+        self.has_star_debug_data = False
 
         self.nstars = None
         self.npart = None
@@ -95,11 +95,16 @@ class Rundata(object):
 
         self.use_const_emission_rate = False
         self.has_stars = False  # assume we don't have stars, check while reading in
+        self.has_star_debug_data = (
+            False
+        )  # assume we don't have stars, check while reading in
 
         self.ngroups = 0  # photon frequency groups
         self.const_emission_rates = None
         self.reduced_speed_of_light = -1.0
 
+        self.with_mpi = False
+
         return
 
 
@@ -156,11 +161,10 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
     try:
         scheme = str(firstfile.metadata.subgrid_scheme["RT Scheme"])
     except KeyError:
-        print(
+        raise ValueError(
             "These tests only work for the GEAR RT scheme.",
             "Compile swift --with-rt=GEAR_N",
         )
-        quit()
     if "GEAR" not in scheme:
         raise ValueError(
             "These tests only work for the GEAR RT scheme.",
@@ -169,15 +173,16 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
 
     ngroups = int(firstfile.metadata.subgrid_scheme["PhotonGroupNumber"])
     rundata.ngroups = ngroups
-    rundata.use_const_emission_rate = bool(
-        firstfile.metadata.parameters["GEARRT:use_const_emission_rates"]
-    )
+
+    luminosity_model = firstfile.metadata.parameters["GEARRT:stellar_luminosity_model"]
+    rundata.use_const_emission_rate = luminosity_model.decode("utf-8") == "const"
+
     rundata.units = firstfile.units
 
     if rundata.use_const_emission_rate:
         # read emission rate parameter as string
         emissionstr = firstfile.metadata.parameters[
-            "GEARRT:star_emission_rates_LSol"
+            "GEARRT:const_stellar_luminosities_LSol"
         ].decode("utf-8")
         # clean string up
         if emissionstr.startswith("["):
@@ -213,9 +218,18 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
                 )
             else:
                 quit()
+    else:
+        print(
+            "Didn't detect use of constant stellar emission rates. Proceeding without."
+        )
 
     rundata.reduced_speed_of_light = firstfile.metadata.reduced_lightspeed
 
+    with_mpi = False
+    if firstfile.metadata.code["MPI library"] != b"Non-MPI version of SWIFT":
+        with_mpi = True
+    rundata.with_mpi = with_mpi
+
     # -------------------
     # Read in all files
     # -------------------
@@ -268,23 +282,40 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
         newsnap.npart = Gas.IDs.shape[0]
 
         #  Get star data
+        Stars = RTStarData()
+        nstars = 0
+        has_stars = False
+        has_star_debug_data = False
         try:
-            Stars = RTStarData()
             Stars.IDs = data.stars.particle_ids
             Stars.coords = data.stars.coordinates
             Stars.h = data.stars.smoothing_lengths
-            inj = np.atleast_2d(data.stars.rtdebug_injected_photon_energy)
-            Stars.InjectedPhotonEnergy = np.reshape(inj, (Stars.IDs.shape[0], ngroups))
-            newsnap.stars = Stars
-            newsnap.nstars = Stars.IDs.shape[0]
+            nstars = Stars.IDs.shape[0]
+            has_stars = True
         except AttributeError:
-            newsnap.stars = RTStarData()
-            newsnap.has_stars = False
-            newsnap.nstars = 0
+            pass
+
+        if has_stars:
+            try:
+                inj = np.atleast_2d(data.stars.rtdebug_injected_photon_energy)
+                Stars.InjectedPhotonEnergy = np.reshape(
+                    inj, (Stars.IDs.shape[0], ngroups)
+                )
+                has_star_debug_data = True
+            except AttributeError:
+                pass
+
+        newsnap.stars = Stars
+        newsnap.nstars = nstars
+        newsnap.has_stars = has_stars
+        newsnap.has_star_debug_data = has_star_debug_data
 
         snapdata.append(newsnap)
 
     for snap in snapdata:
         rundata.has_stars = rundata.has_stars or snap.has_stars
+        rundata.has_star_debug_data = (
+            rundata.has_star_debug_data or snap.has_star_debug_data
+        )
 
     return snapdata, rundata
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py
index c68f9bd4dce8d9ba29a753ab381bd0b406f7168f..12e118e85c2cf28466077c2f8c19ea9adda0aaed 100644
--- a/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py
+++ b/examples/RadiativeTransferTests/UniformBox_3D/swift_rt_debug_io.py
@@ -50,7 +50,8 @@ class RTGasData(object):
         self.GradientsDone = None
 
         self.RadiationAbsorbedTot = None
-        self.InjectPrepCountsTot = None
+
+        self.nsubcycles = None
 
         return
 
@@ -95,6 +96,7 @@ class Rundata(object):
 
     def __init__(self):
         self.has_stars = False  # assume we don't have stars, check while reading in
+        self.with_mpi = False
 
         return
 
@@ -153,12 +155,10 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
     try:
         scheme = str(F["SubgridScheme"].attrs["RT Scheme"])
     except KeyError:
-        print(
+        raise ValueError(
             "These tests only work for the debug RT scheme.",
             "Compile swift --with-rt=debug",
         )
-        F.close()
-        quit()
 
     if "debug" not in scheme and "GEAR" not in scheme:
         raise ValueError(
@@ -166,6 +166,12 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
             "Compile swift --with-rt=debug",
         )
 
+    with_mpi = False
+    mpistr = F["Code"].attrs["MPI library"]
+    if mpistr != b"Non-MPI version of SWIFT":
+        with_mpi = True
+    rundata.with_mpi = with_mpi
+
     F.close()
 
     for f in hdf5files:
@@ -203,17 +209,13 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
         newsnap.gas.ThermochemistryDone = Gas["RTDebugThermochemistryDone"][:][inds]
 
         newsnap.gas.RadiationAbsorbedTot = Gas["RTDebugRadAbsorbedTot"][:][inds]
+        newsnap.gas.nsubcycles = Gas["RTDebugSubcycles"][:][inds]
 
-        has_stars = False
         try:
             Stars = F["PartType4"]
             ids = Stars["ParticleIDs"][:]
             has_stars = True
-        except KeyError:
-            has_stars = False
 
-        if has_stars:
-            newsnap.has_stars = has_stars
             inds = np.argsort(ids)
 
             newsnap.stars.IDs = ids[inds]
@@ -224,14 +226,18 @@ def get_snap_data(prefix="output", skip_snap_zero=False, skip_last_snap=False):
             newsnap.stars.InjectionInteractions = Stars["RTDebugHydroIact"][:][inds]
             newsnap.stars.RadiationEmittedTot = Stars["RTDebugRadEmittedTot"][:][inds]
 
+        except KeyError:
+            has_stars = False
+
+        newsnap.has_stars = has_stars
         snapdata.append(newsnap)
 
     for snap in snapdata:
         rundata.has_stars = rundata.has_stars or snap.has_stars
 
     if len(snapdata) == 0:
-        print(
-            "Didn't read in snapshot data. Do you only have 2 snapshots in total and skipping the first and the last?"
-        )
+        print("Didn't read in snapshot data.")
+        print("Do you only have 2 snapshots and are skipping the first and the last?")
+        quit()
 
     return snapdata, rundata
diff --git a/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml b/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml
index a997d0478583c41ad16241933c0c8732f6ce0d47..7f45a8a294a0e6e4974a1f18844395bc3f47f11a 100644
--- a/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml
+++ b/examples/RadiativeTransferTests/UniformBox_3D/uniform_rt_timestep_output_sync.yml
@@ -1,5 +1,5 @@
 MetaData:
-  run_name: "uniform_rt"
+  run_name: uniform_rt
 
 # Define the system of units to use internally. 
 InternalUnitSystem:
@@ -15,6 +15,7 @@ TimeIntegration:
   time_end:   9.536742e-07
   dt_min:     1.e-12   # The minimal time-step size of the simulation (in internal units).
   dt_max:     5.e-8   # The maximal time-step size of the simulation (in internal units).
+  max_nr_rt_subcycles: 4
 
 # Parameters governing the snapshots
 Snapshots:
@@ -43,11 +44,11 @@ Scheduler:
   dependency_graph_frequency: 0
 
 GEARRT:
-  f_reduce_c: 1e-3                                   # reduce the speed of light for the RT solver by multiplying c with this factor
-  CFL_condition: 0.99                                # CFL condition for RT, independent of hydro
-  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15]  # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N).
-  use_const_emission_rates: 1                        # (Optional) use constant emission rates for stars as defined with star_emission_rates_erg_LSol parameter
-  star_emission_rates_LSol: [7.839e-28, 1.5678e-27, 2.3517e-27, 3.1356e-27] # 1e6,2e6,3e6,4e6 erg/s in LSol
+  f_reduce_c: 1e-3                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.99                               # CFL condition for RT, independent of hydro
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [7.839e-28, 1.5678e-27, 2.3517e-27] # 1e6,2e6,3e6 erg/s in LSol
   hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
   stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
   stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
diff --git a/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py
index 598ebb41adddf23e474df252a739e9801cf62c04..68b07ca6ec7390b3eab4eb90b5cd991949d03326 100644
--- a/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py
+++ b/examples/SantaBarbara/SantaBarbara-256/plotTempEvolution.py
@@ -30,30 +30,11 @@ snapname = "santabarbara"
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.14,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.12,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-rcParams.update(params)
+
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 # Read the simulation data
 sim = h5py.File("%s_0000.hdf5" % snapname, "r")
@@ -63,6 +44,7 @@ scheme = sim["/HydroScheme"].attrs["Scheme"][0]
 kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
 neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
 eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
+alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
 H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
 H_transition_temp = sim["/HydroScheme"].attrs[
     "Hydrogen ionization transition temperature"
@@ -70,6 +52,10 @@ H_transition_temp = sim["/HydroScheme"].attrs[
 T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
 T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
 git = sim["Code"].attrs["Git Revision"]
+cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"].decode("utf-8")
+
+if cooling_model == "Constant Lambda":
+    Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0]
 
 # Cosmological parameters
 H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
@@ -83,7 +69,7 @@ unit_length_in_si = 0.01 * unit_length_in_cgs
 unit_mass_in_si = 0.001 * unit_mass_in_cgs
 unit_time_in_si = unit_time_in_cgs
 
-# Primoridal ean molecular weight as a function of temperature
+# Primoridal mean molecular weight as a function of temperature
 def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp):
     if T > T_trans:
         return 4.0 / (8.0 - 5.0 * (1.0 - H_frac))
@@ -147,28 +133,46 @@ a_evol = np.logspace(-3, 0, 60)
 T_cmb = (1.0 / a_evol) ** 2 * 2.72
 
 # Plot the interesting quantities
-figure()
-subplot(111, xscale="log", yscale="log")
-
-fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
-plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
-plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
-plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
-fill_between(
+plt.figure()
+plt.subplot(111, xscale="log", yscale="log")
+
+plt.fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
+plt.plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
+plt.plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
+plt.plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
+plt.fill_between(
     a,
     10 ** (T_log_mean - T_log_std),
     10 ** (T_log_mean + T_log_std),
     color="C1",
     alpha=0.1,
 )
-plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
-plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
-
-legend(loc="upper left", frameon=False, handlelength=1.5)
+plt.plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
+plt.plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
+
+plt.legend(loc="upper left", frameon=False, handlelength=1.5)
+
+# Cooling model
+if cooling_model == "Constant Lambda":
+    plt.text(
+        1e-2,
+        6e4,
+        "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$"
+        % (Lambda / 10.0 ** (int(log10(Lambda))), log10(Lambda)),
+        fontsize=7,
+    )
+elif cooling_model == "EAGLE":
+    plt.text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)")
+elif cooling_model == b"Grackle":
+    plt.text(1e-2, 6e4, "Grackle (Smith et al. 2016)")
+else:
+    plt.text(1e-2, 6e4, "No cooling")
 
 # Expected lines
-plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7)
-text(
+plt.plot(
+    [1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7
+)
+plt.text(
     2.5e-2,
     H_transition_temp * 1.07,
     "$T_{\\rm HII\\rightarrow HI}$",
@@ -176,10 +180,10 @@ text(
     alpha=0.7,
     fontsize=8,
 )
-plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
-text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
-plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
-text(
+plt.plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
+plt.text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
+plt.plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
+plt.text(
     a_evol[20],
     T_cmb[20] * 0.55,
     "$(1+z)^2\\times T_{\\rm CMB,0}$",
@@ -195,12 +199,14 @@ redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0])
 redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"]
 a_ticks = 1.0 / (redshift_ticks + 1.0)
 
-xticks(a_ticks, redshift_labels)
-minorticks_off()
+plt.xticks(a_ticks, redshift_labels)
+plt.minorticks_off()
+
+plt.xlabel("${\\rm Redshift}~z$", labelpad=0)
+plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
+plt.xlim(9e-3, 1.1)
+plt.ylim(20, 2.5e7)
 
-xlabel("${\\rm Redshift}~z$", labelpad=0)
-ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
-xlim(9e-3, 1.1)
-ylim(20, 2.5e7)
+plt.tight_layout()
 
-savefig("Temperature_evolution.png", dpi=200)
+plt.savefig("Temperature_evolution.png", dpi=200)
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/plotTempEvolution.py
deleted file mode 100644
index 3eb80e98e8dfef70cf5dedaad876f37a54a587f5..0000000000000000000000000000000000000000
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_Snipshots/plotTempEvolution.py
+++ /dev/null
@@ -1,206 +0,0 @@
-################################################################################
-# This file is part of SWIFT.
-# Copyright (c) 2018 Matthieu Schaller (schaller@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
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-################################################################################
-
-# Computes the temperature evolution of the gas in a cosmological box
-
-# Physical constants needed for internal energy to temperature conversion
-k_in_J_K = 1.38064852e-23
-mH_in_kg = 1.6737236e-27
-
-# Number of snapshots generated
-n_snapshots = 200
-
-import matplotlib
-
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.14,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.12,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-rcParams.update(params)
-
-# Read the simulation data
-sim = h5py.File("snap_0000.hdf5", "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-time = sim["/Header"].attrs["Time"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"][0]
-kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
-eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
-alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
-H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
-H_transition_temp = sim["/HydroScheme"].attrs[
-    "Hydrogen ionization transition temperature"
-][0]
-T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
-T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
-git = sim["Code"].attrs["Git Revision"]
-
-# Cosmological parameters
-H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
-gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
-
-unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
-unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
-unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
-
-unit_length_in_si = 0.01 * unit_length_in_cgs
-unit_mass_in_si = 0.001 * unit_mass_in_cgs
-unit_time_in_si = unit_time_in_cgs
-
-# Primoridal ean molecular weight as a function of temperature
-def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp):
-    if T > T_trans:
-        return 4.0 / (8.0 - 5.0 * (1.0 - H_frac))
-    else:
-        return 4.0 / (1.0 + 3.0 * H_frac)
-
-
-# Temperature of some primoridal gas with a given internal energy
-def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp):
-    T_over_mu = (gas_gamma - 1.0) * u * mH_in_kg / k_in_J_K
-    ret = np.ones(np.size(u)) * T_trans
-
-    # Enough energy to be ionized?
-    mask_ionized = T_over_mu > (T_trans + 1) / mu(T_trans + 1, H_frac, T_trans)
-    if np.sum(mask_ionized) > 0:
-        ret[mask_ionized] = T_over_mu[mask_ionized] * mu(T_trans * 10, H_frac, T_trans)
-
-    # Neutral gas?
-    mask_neutral = T_over_mu < (T_trans - 1) / mu((T_trans - 1), H_frac, T_trans)
-    if np.sum(mask_neutral) > 0:
-        ret[mask_neutral] = T_over_mu[mask_neutral] * mu(0, H_frac, T_trans)
-
-    return ret
-
-
-z = np.zeros(n_snapshots)
-a = np.zeros(n_snapshots)
-T_mean = np.zeros(n_snapshots)
-T_std = np.zeros(n_snapshots)
-T_log_mean = np.zeros(n_snapshots)
-T_log_std = np.zeros(n_snapshots)
-T_median = np.zeros(n_snapshots)
-T_min = np.zeros(n_snapshots)
-T_max = np.zeros(n_snapshots)
-
-# Loop over all the snapshots
-for i in range(n_snapshots):
-    sim = h5py.File("snap_%04d.hdf5" % i, "r")
-
-    z[i] = sim["/Cosmology"].attrs["Redshift"][0]
-    a[i] = sim["/Cosmology"].attrs["Scale-factor"][0]
-
-    u = sim["/PartType0/InternalEnergies"][:]
-
-    # Compute the temperature
-    u *= unit_length_in_si ** 2 / unit_time_in_si ** 2
-    u /= a[i] ** (3 * (gas_gamma - 1.0))
-    Temp = T(u)
-
-    # Gather statistics
-    T_median[i] = np.median(Temp)
-    T_mean[i] = Temp.mean()
-    T_std[i] = Temp.std()
-    T_log_mean[i] = np.log10(Temp).mean()
-    T_log_std[i] = np.log10(Temp).std()
-    T_min[i] = Temp.min()
-    T_max[i] = Temp.max()
-
-# CMB evolution
-a_evol = np.logspace(-3, 0, 60)
-T_cmb = (1.0 / a_evol) ** 2 * 2.72
-
-# Plot the interesting quantities
-figure()
-subplot(111, xscale="log", yscale="log")
-
-fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
-plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
-plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
-plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
-fill_between(
-    a,
-    10 ** (T_log_mean - T_log_std),
-    10 ** (T_log_mean + T_log_std),
-    color="C1",
-    alpha=0.1,
-)
-plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
-plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
-
-legend(loc="upper left", frameon=False, handlelength=1.5)
-
-# Expected lines
-plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7)
-text(
-    2.5e-2,
-    H_transition_temp * 1.07,
-    "$T_{\\rm HII\\rightarrow HI}$",
-    va="bottom",
-    alpha=0.7,
-    fontsize=8,
-)
-plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
-text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
-plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
-text(
-    a_evol[20],
-    T_cmb[20] * 0.55,
-    "$(1+z)^2\\times T_{\\rm CMB,0}$",
-    rotation=-34,
-    alpha=0.7,
-    fontsize=8,
-    va="top",
-    bbox=dict(facecolor="w", edgecolor="none", pad=1.0, alpha=0.9),
-)
-
-
-redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0])
-redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"]
-a_ticks = 1.0 / (redshift_ticks + 1.0)
-
-xticks(a_ticks, redshift_labels)
-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)
-
-savefig("Temperature_evolution.png", dpi=200)
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py
index e5db3348cd0f8c389643cbc12e79e58ccbaf8cbe..d758fd8a9f9ee81050e4336cb5a6421563532f92 100644
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotRhoT.py
@@ -26,35 +26,17 @@ mH_in_kg = 1.6737236e-27
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.15,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.13,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-rcParams.update(params)
+import sys
+
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 snap = int(sys.argv[1])
 
 # Read the simulation data
-sim = h5py.File("snap_%04d.hdf5" % snap, "r")
+sim = h5py.File("snapshots/snap_%04d.hdf5" % snap, "r")
 boxSize = sim["/Header"].attrs["BoxSize"][0]
 time = sim["/Header"].attrs["Time"][0]
 z = sim["/Cosmology"].attrs["Redshift"][0]
@@ -84,7 +66,7 @@ unit_length_in_si = 0.01 * unit_length_in_cgs
 unit_mass_in_si = 0.001 * unit_mass_in_cgs
 unit_time_in_si = unit_time_in_cgs
 
-# Primoridal ean molecular weight as a function of temperature
+# Primoridal mean molecular weight as a function of temperature
 def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp):
     if T > T_trans:
         return 4.0 / (8.0 - 5.0 * (1.0 - H_frac))
@@ -136,26 +118,28 @@ log_T_max = 8
 
 bins_x = np.linspace(log_rho_min, log_rho_max, 54)
 bins_y = np.linspace(log_T_min, log_T_max, 54)
-H, _, _ = histogram2d(log_rho, log_T, bins=[bins_x, bins_y], normed=True)
+H, _, _ = np.histogram2d(log_rho, log_T, bins=[bins_x, bins_y], normed=True)
 
 
 # Plot the interesting quantities
-figure()
+plt.figure()
 
-pcolormesh(bins_x, bins_y, np.log10(H).T)
+plt.pcolormesh(bins_x, bins_y, np.log10(H).T)
 
-text(-5, 8.0, "$z=%.2f$" % z)
+plt.text(-5, 8.0, "$z=%.2f$" % z)
 
-xticks(
+plt.xticks(
     [-5, -4, -3, -2, -1, 0, 1, 2, 3],
     ["", "$10^{-4}$", "", "$10^{-2}$", "", "$10^0$", "", "$10^2$", ""],
 )
-yticks(
+plt.yticks(
     [2, 3, 4, 5, 6, 7, 8], ["$10^{2}$", "", "$10^{4}$", "", "$10^{6}$", "", "$10^8$"]
 )
-xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0)
-ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2)
-xlim(-5.2, 3.2)
-ylim(1, 8.5)
+plt.xlabel("${\\rm Physical~Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0)
+plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
+plt.xlim(-5.2, 3.2)
+plt.ylim(1, 8.5)
+
+plt.tight_layout()
 
-savefig("rhoT_%04d.png" % snap, dpi=200)
+plt.savefig("rhoT_%04d.png" % snap, dpi=200)
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py
index 006f7742f23a8a0400b40ae9618e1d9d70868301..f42dd927d45384e65f7ee2fb8b41d93a3df7b1cc 100644
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/plotTempEvolution.py
@@ -29,33 +29,14 @@ n_snapshots = 200
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.14,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.12,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-rcParams.update(params)
+
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 # Read the simulation data
-sim = h5py.File("snap_0000.hdf5", "r")
+sim = h5py.File("snapshots/snap_0000.hdf5", "r")
 boxSize = sim["/Header"].attrs["BoxSize"][0]
 time = sim["/Header"].attrs["Time"][0]
 scheme = sim["/HydroScheme"].attrs["Scheme"][0]
@@ -70,7 +51,7 @@ H_transition_temp = sim["/HydroScheme"].attrs[
 T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
 T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
 git = sim["Code"].attrs["Git Revision"]
-cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"]
+cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"].decode("utf-8")
 
 if cooling_model == "Constant Lambda":
     Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0]
@@ -125,7 +106,7 @@ T_max = np.zeros(n_snapshots)
 
 # Loop over all the snapshots
 for i in range(n_snapshots):
-    sim = h5py.File("snap_%04d.hdf5" % i, "r")
+    sim = h5py.File("snapshots/snap_%04d.hdf5" % i, "r")
 
     z[i] = sim["/Cosmology"].attrs["Redshift"][0]
     a[i] = sim["/Cosmology"].attrs["Scale-factor"][0]
@@ -151,28 +132,28 @@ a_evol = np.logspace(-3, 0, 60)
 T_cmb = (1.0 / a_evol) ** 2 * 2.72
 
 # Plot the interesting quantities
-figure()
-subplot(111, xscale="log", yscale="log")
-
-fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
-plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
-plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
-plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
-fill_between(
+plt.figure()
+plt.subplot(111, xscale="log", yscale="log")
+
+plt.fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
+plt.plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
+plt.plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
+plt.plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
+plt.fill_between(
     a,
     10 ** (T_log_mean - T_log_std),
     10 ** (T_log_mean + T_log_std),
     color="C1",
     alpha=0.1,
 )
-plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
-plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
+plt.plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
+plt.plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
 
-legend(loc="upper left", frameon=False, handlelength=1.5)
+plt.legend(loc="upper left", frameon=False, handlelength=1.5)
 
 # Cooling model
 if cooling_model == "Constant Lambda":
-    text(
+    plt.text(
         1e-2,
         6e4,
         "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$"
@@ -180,15 +161,17 @@ if cooling_model == "Constant Lambda":
         fontsize=7,
     )
 elif cooling_model == "EAGLE":
-    text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)")
+    plt.text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)")
 elif cooling_model == b"Grackle":
-    text(1e-2, 6e4, "Grackle (Smith et al. 2016)")
+    plt.text(1e-2, 6e4, "Grackle (Smith et al. 2016)")
 else:
-    text(1e-2, 6e4, "No cooling")
+    plt.text(1e-2, 6e4, "No cooling")
 
 # Expected lines
-plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7)
-text(
+plt.plot(
+    [1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7
+)
+plt.text(
     2.5e-2,
     H_transition_temp * 1.07,
     "$T_{\\rm HII\\rightarrow HI}$",
@@ -196,10 +179,10 @@ text(
     alpha=0.7,
     fontsize=8,
 )
-plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
-text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
-plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
-text(
+plt.plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
+plt.text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
+plt.plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
+plt.text(
     a_evol[20],
     T_cmb[20] * 0.55,
     "$(1+z)^2\\times T_{\\rm CMB,0}$",
@@ -215,12 +198,14 @@ redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0])
 redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"]
 a_ticks = 1.0 / (redshift_ticks + 1.0)
 
-xticks(a_ticks, redshift_labels)
-minorticks_off()
+plt.xticks(a_ticks, redshift_labels)
+plt.minorticks_off()
+
+plt.xlabel("${\\rm Redshift}~z$", labelpad=0)
+plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
+plt.xlim(9e-3, 1.1)
+plt.ylim(20, 2.5e7)
 
-xlabel("${\\rm Redshift}~z$", labelpad=0)
-ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
-xlim(9e-3, 1.1)
-ylim(5, 2.5e7)
+plt.tight_layout()
 
-savefig("Temperature_evolution.png", dpi=200)
+plt.savefig("Temperature_evolution.png", dpi=200)
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml
index 1f93830629811b557705427204520b8c8db9ce53..c308cbd5382db94d36fef8e6d0d9b89594d08099 100644
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_cooling/small_cosmo_volume.yml
@@ -45,6 +45,7 @@ Snapshots:
   basename:            snap
   delta_time:          1.02
   scale_factor_first:  0.02
+  compression:         4
   
 # Parameters governing the conserved quantities statistics
 Statistics:
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py
index 3eb80e98e8dfef70cf5dedaad876f37a54a587f5..f42dd927d45384e65f7ee2fb8b41d93a3df7b1cc 100644
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/plotTempEvolution.py
@@ -29,33 +29,14 @@ n_snapshots = 200
 import matplotlib
 
 matplotlib.use("Agg")
-from pylab import *
+import matplotlib.pyplot as plt
+import numpy as np
 import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.14,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.12,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-rcParams.update(params)
+
+plt.style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 # Read the simulation data
-sim = h5py.File("snap_0000.hdf5", "r")
+sim = h5py.File("snapshots/snap_0000.hdf5", "r")
 boxSize = sim["/Header"].attrs["BoxSize"][0]
 time = sim["/Header"].attrs["Time"][0]
 scheme = sim["/HydroScheme"].attrs["Scheme"][0]
@@ -70,6 +51,10 @@ H_transition_temp = sim["/HydroScheme"].attrs[
 T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
 T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
 git = sim["Code"].attrs["Git Revision"]
+cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"].decode("utf-8")
+
+if cooling_model == "Constant Lambda":
+    Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0]
 
 # Cosmological parameters
 H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
@@ -83,7 +68,7 @@ unit_length_in_si = 0.01 * unit_length_in_cgs
 unit_mass_in_si = 0.001 * unit_mass_in_cgs
 unit_time_in_si = unit_time_in_cgs
 
-# Primoridal ean molecular weight as a function of temperature
+# Primoridal mean molecular weight as a function of temperature
 def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp):
     if T > T_trans:
         return 4.0 / (8.0 - 5.0 * (1.0 - H_frac))
@@ -121,7 +106,7 @@ T_max = np.zeros(n_snapshots)
 
 # Loop over all the snapshots
 for i in range(n_snapshots):
-    sim = h5py.File("snap_%04d.hdf5" % i, "r")
+    sim = h5py.File("snapshots/snap_%04d.hdf5" % i, "r")
 
     z[i] = sim["/Cosmology"].attrs["Redshift"][0]
     a[i] = sim["/Cosmology"].attrs["Scale-factor"][0]
@@ -147,28 +132,46 @@ a_evol = np.logspace(-3, 0, 60)
 T_cmb = (1.0 / a_evol) ** 2 * 2.72
 
 # Plot the interesting quantities
-figure()
-subplot(111, xscale="log", yscale="log")
-
-fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
-plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
-plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
-plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
-fill_between(
+plt.figure()
+plt.subplot(111, xscale="log", yscale="log")
+
+plt.fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
+plt.plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
+plt.plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
+plt.plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
+plt.fill_between(
     a,
     10 ** (T_log_mean - T_log_std),
     10 ** (T_log_mean + T_log_std),
     color="C1",
     alpha=0.1,
 )
-plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
-plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
-
-legend(loc="upper left", frameon=False, handlelength=1.5)
+plt.plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
+plt.plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
+
+plt.legend(loc="upper left", frameon=False, handlelength=1.5)
+
+# Cooling model
+if cooling_model == "Constant Lambda":
+    plt.text(
+        1e-2,
+        6e4,
+        "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$"
+        % (Lambda / 10.0 ** (int(log10(Lambda))), log10(Lambda)),
+        fontsize=7,
+    )
+elif cooling_model == "EAGLE":
+    plt.text(1e-2, 6e4, "EAGLE (Wiersma et al. 2009)")
+elif cooling_model == b"Grackle":
+    plt.text(1e-2, 6e4, "Grackle (Smith et al. 2016)")
+else:
+    plt.text(1e-2, 6e4, "No cooling")
 
 # Expected lines
-plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7)
-text(
+plt.plot(
+    [1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7
+)
+plt.text(
     2.5e-2,
     H_transition_temp * 1.07,
     "$T_{\\rm HII\\rightarrow HI}$",
@@ -176,10 +179,10 @@ text(
     alpha=0.7,
     fontsize=8,
 )
-plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
-text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
-plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
-text(
+plt.plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
+plt.text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
+plt.plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
+plt.text(
     a_evol[20],
     T_cmb[20] * 0.55,
     "$(1+z)^2\\times T_{\\rm CMB,0}$",
@@ -195,12 +198,14 @@ redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0])
 redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"]
 a_ticks = 1.0 / (redshift_ticks + 1.0)
 
-xticks(a_ticks, redshift_labels)
-minorticks_off()
+plt.xticks(a_ticks, redshift_labels)
+plt.minorticks_off()
+
+plt.xlabel("${\\rm Redshift}~z$", labelpad=0)
+plt.ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
+plt.xlim(9e-3, 1.1)
+plt.ylim(20, 2.5e7)
 
-xlabel("${\\rm Redshift}~z$", labelpad=0)
-ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
-xlim(9e-3, 1.1)
-ylim(20, 2.5e7)
+plt.tight_layout()
 
-savefig("Temperature_evolution.png", dpi=200)
+plt.savefig("Temperature_evolution.png", dpi=200)
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml
index da0a2c7686c9b2957043c77cd9f9cadb6aa7cc09..518800b6150593caa40d580f1dd85d32feee761d 100644
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml
+++ b/examples/SmallCosmoVolume/SmallCosmoVolume_hydro/small_cosmo_volume.yml
@@ -41,9 +41,10 @@ SPH:
 
 # Parameters governing the snapshots
 Snapshots:
+  subdir:              snapshots
   basename:            snap
-  delta_time:          1.05
-  scale_factor_first:  0.05
+  delta_time:          1.02
+  scale_factor_first:  0.02
   compression:         4
   
 # Parameters governing the conserved quantities statistics
@@ -61,8 +62,8 @@ InitialConditions:
   periodic:                    1
   cleanup_h_factors:           1    
   cleanup_velocity_factors:    1  
-  generate_gas_in_ics:         1      # Generate gas particles from the DM-only ICs
-  cleanup_smoothing_lengths:   1      # Since we generate gas, make use of the (expensive) cleaning-up procedure.
+  generate_gas_in_ics:         1    # Generate gas particles from the DM-only ICs
+  cleanup_smoothing_lengths:   1    # Since we generate gas, make use of the (expensive) cleaning-up procedure.
 
 # Parameters for the EAGLE "equation of state"
 EAGLEEntropyFloor:
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plotRhoT.py b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plotRhoT.py
deleted file mode 100644
index e5db3348cd0f8c389643cbc12e79e58ccbaf8cbe..0000000000000000000000000000000000000000
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plotRhoT.py
+++ /dev/null
@@ -1,161 +0,0 @@
-################################################################################
-# This file is part of SWIFT.
-# Copyright (c) 2018 Matthieu Schaller (schaller@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
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-################################################################################
-
-# Computes the temperature evolution of the gas in a cosmological box
-
-# Physical constants needed for internal energy to temperature conversion
-k_in_J_K = 1.38064852e-23
-mH_in_kg = 1.6737236e-27
-
-import matplotlib
-
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.15,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.13,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-rcParams.update(params)
-
-snap = int(sys.argv[1])
-
-# Read the simulation data
-sim = h5py.File("snap_%04d.hdf5" % snap, "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-time = sim["/Header"].attrs["Time"][0]
-z = sim["/Cosmology"].attrs["Redshift"][0]
-a = sim["/Cosmology"].attrs["Scale-factor"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"][0]
-kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
-eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
-alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
-H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
-H_transition_temp = sim["/HydroScheme"].attrs[
-    "Hydrogen ionization transition temperature"
-][0]
-T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
-T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
-git = sim["Code"].attrs["Git Revision"]
-
-# Cosmological parameters
-H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
-gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
-
-unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
-unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
-unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
-
-unit_length_in_si = 0.01 * unit_length_in_cgs
-unit_mass_in_si = 0.001 * unit_mass_in_cgs
-unit_time_in_si = unit_time_in_cgs
-
-# Primoridal ean molecular weight as a function of temperature
-def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp):
-    if T > T_trans:
-        return 4.0 / (8.0 - 5.0 * (1.0 - H_frac))
-    else:
-        return 4.0 / (1.0 + 3.0 * H_frac)
-
-
-# Temperature of some primoridal gas with a given internal energy
-def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp):
-    T_over_mu = (gas_gamma - 1.0) * u * mH_in_kg / k_in_J_K
-    ret = np.ones(np.size(u)) * T_trans
-
-    # Enough energy to be ionized?
-    mask_ionized = T_over_mu > (T_trans + 1) / mu(T_trans + 1, H_frac, T_trans)
-    if np.sum(mask_ionized) > 0:
-        ret[mask_ionized] = T_over_mu[mask_ionized] * mu(T_trans * 10, H_frac, T_trans)
-
-    # Neutral gas?
-    mask_neutral = T_over_mu < (T_trans - 1) / mu((T_trans - 1), H_frac, T_trans)
-    if np.sum(mask_neutral) > 0:
-        ret[mask_neutral] = T_over_mu[mask_neutral] * mu(0, H_frac, T_trans)
-
-    return ret
-
-
-rho = sim["/PartType0/Densities"][:]
-u = sim["/PartType0/InternalEnergies"][:]
-
-# Compute the temperature
-u *= unit_length_in_si ** 2 / unit_time_in_si ** 2
-u /= a ** (3 * (gas_gamma - 1.0))
-Temp = T(u)
-
-# Compute the physical density
-rho *= unit_mass_in_cgs / unit_length_in_cgs ** 3
-rho /= a ** 3
-rho /= mH_in_kg
-
-# Life is better in log-space
-log_T = np.log10(Temp)
-log_rho = np.log10(rho)
-
-
-# Make a 2D histogram
-log_rho_min = -6
-log_rho_max = 3
-log_T_min = 1
-log_T_max = 8
-
-bins_x = np.linspace(log_rho_min, log_rho_max, 54)
-bins_y = np.linspace(log_T_min, log_T_max, 54)
-H, _, _ = histogram2d(log_rho, log_T, bins=[bins_x, bins_y], normed=True)
-
-
-# Plot the interesting quantities
-figure()
-
-pcolormesh(bins_x, bins_y, np.log10(H).T)
-
-text(-5, 8.0, "$z=%.2f$" % z)
-
-xticks(
-    [-5, -4, -3, -2, -1, 0, 1, 2, 3],
-    ["", "$10^{-4}$", "", "$10^{-2}$", "", "$10^0$", "", "$10^2$", ""],
-)
-yticks(
-    [2, 3, 4, 5, 6, 7, 8], ["$10^{2}$", "", "$10^{4}$", "", "$10^{6}$", "", "$10^8$"]
-)
-xlabel("${\\rm Density}~n_{\\rm H}~[{\\rm cm^{-3}}]$", labelpad=0)
-ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=2)
-xlim(-5.2, 3.2)
-ylim(1, 8.5)
-
-savefig("rhoT_%04d.png" % snap, dpi=200)
diff --git a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plotTempEvolution.py b/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plotTempEvolution.py
deleted file mode 100644
index 006f7742f23a8a0400b40ae9618e1d9d70868301..0000000000000000000000000000000000000000
--- a/examples/SmallCosmoVolume/SmallCosmoVolume_lightcone/plotTempEvolution.py
+++ /dev/null
@@ -1,226 +0,0 @@
-################################################################################
-# This file is part of SWIFT.
-# Copyright (c) 2018 Matthieu Schaller (schaller@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
-# by the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-#
-################################################################################
-
-# Computes the temperature evolution of the gas in a cosmological box
-
-# Physical constants needed for internal energy to temperature conversion
-k_in_J_K = 1.38064852e-23
-mH_in_kg = 1.6737236e-27
-
-# Number of snapshots generated
-n_snapshots = 200
-
-import matplotlib
-
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-import os.path
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.14,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.12,
-    "figure.subplot.top": 0.99,
-    "figure.subplot.wspace": 0.15,
-    "figure.subplot.hspace": 0.12,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-rcParams.update(params)
-
-# Read the simulation data
-sim = h5py.File("snap_0000.hdf5", "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-time = sim["/Header"].attrs["Time"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"][0]
-kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
-eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
-alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
-H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
-H_transition_temp = sim["/HydroScheme"].attrs[
-    "Hydrogen ionization transition temperature"
-][0]
-T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
-T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
-git = sim["Code"].attrs["Git Revision"]
-cooling_model = sim["/SubgridScheme"].attrs["Cooling Model"]
-
-if cooling_model == "Constant Lambda":
-    Lambda = sim["/SubgridScheme"].attrs["Lambda/n_H^2 [cgs]"][0]
-
-# Cosmological parameters
-H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
-gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
-
-unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
-unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
-unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
-
-unit_length_in_si = 0.01 * unit_length_in_cgs
-unit_mass_in_si = 0.001 * unit_mass_in_cgs
-unit_time_in_si = unit_time_in_cgs
-
-# Primoridal mean molecular weight as a function of temperature
-def mu(T, H_frac=H_mass_fraction, T_trans=H_transition_temp):
-    if T > T_trans:
-        return 4.0 / (8.0 - 5.0 * (1.0 - H_frac))
-    else:
-        return 4.0 / (1.0 + 3.0 * H_frac)
-
-
-# Temperature of some primoridal gas with a given internal energy
-def T(u, H_frac=H_mass_fraction, T_trans=H_transition_temp):
-    T_over_mu = (gas_gamma - 1.0) * u * mH_in_kg / k_in_J_K
-    ret = np.ones(np.size(u)) * T_trans
-
-    # Enough energy to be ionized?
-    mask_ionized = T_over_mu > (T_trans + 1) / mu(T_trans + 1, H_frac, T_trans)
-    if np.sum(mask_ionized) > 0:
-        ret[mask_ionized] = T_over_mu[mask_ionized] * mu(T_trans * 10, H_frac, T_trans)
-
-    # Neutral gas?
-    mask_neutral = T_over_mu < (T_trans - 1) / mu((T_trans - 1), H_frac, T_trans)
-    if np.sum(mask_neutral) > 0:
-        ret[mask_neutral] = T_over_mu[mask_neutral] * mu(0, H_frac, T_trans)
-
-    return ret
-
-
-z = np.zeros(n_snapshots)
-a = np.zeros(n_snapshots)
-T_mean = np.zeros(n_snapshots)
-T_std = np.zeros(n_snapshots)
-T_log_mean = np.zeros(n_snapshots)
-T_log_std = np.zeros(n_snapshots)
-T_median = np.zeros(n_snapshots)
-T_min = np.zeros(n_snapshots)
-T_max = np.zeros(n_snapshots)
-
-# Loop over all the snapshots
-for i in range(n_snapshots):
-    sim = h5py.File("snap_%04d.hdf5" % i, "r")
-
-    z[i] = sim["/Cosmology"].attrs["Redshift"][0]
-    a[i] = sim["/Cosmology"].attrs["Scale-factor"][0]
-
-    u = sim["/PartType0/InternalEnergies"][:]
-
-    # Compute the temperature
-    u *= unit_length_in_si ** 2 / unit_time_in_si ** 2
-    u /= a[i] ** (3 * (gas_gamma - 1.0))
-    Temp = T(u)
-
-    # Gather statistics
-    T_median[i] = np.median(Temp)
-    T_mean[i] = Temp.mean()
-    T_std[i] = Temp.std()
-    T_log_mean[i] = np.log10(Temp).mean()
-    T_log_std[i] = np.log10(Temp).std()
-    T_min[i] = Temp.min()
-    T_max[i] = Temp.max()
-
-# CMB evolution
-a_evol = np.logspace(-3, 0, 60)
-T_cmb = (1.0 / a_evol) ** 2 * 2.72
-
-# Plot the interesting quantities
-figure()
-subplot(111, xscale="log", yscale="log")
-
-fill_between(a, T_mean - T_std, T_mean + T_std, color="C0", alpha=0.1)
-plot(a, T_max, ls="-.", color="C0", lw=1.0, label="${\\rm max}~T$")
-plot(a, T_min, ls=":", color="C0", lw=1.0, label="${\\rm min}~T$")
-plot(a, T_mean, color="C0", label="${\\rm mean}~T$", lw=1.5)
-fill_between(
-    a,
-    10 ** (T_log_mean - T_log_std),
-    10 ** (T_log_mean + T_log_std),
-    color="C1",
-    alpha=0.1,
-)
-plot(a, 10 ** T_log_mean, color="C1", label="${\\rm mean}~{\\rm log} T$", lw=1.5)
-plot(a, T_median, color="C2", label="${\\rm median}~T$", lw=1.5)
-
-legend(loc="upper left", frameon=False, handlelength=1.5)
-
-# Cooling model
-if cooling_model == "Constant Lambda":
-    text(
-        1e-2,
-        6e4,
-        "$\Lambda_{\\rm const}/n_{\\rm H}^2 = %.1f\\times10^{%d}~[\\rm{cgs}]$"
-        % (Lambda / 10.0 ** (int(log10(Lambda))), log10(Lambda)),
-        fontsize=7,
-    )
-elif cooling_model == "EAGLE":
-    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")
-
-# Expected lines
-plot([1e-10, 1e10], [H_transition_temp, H_transition_temp], "k--", lw=0.5, alpha=0.7)
-text(
-    2.5e-2,
-    H_transition_temp * 1.07,
-    "$T_{\\rm HII\\rightarrow HI}$",
-    va="bottom",
-    alpha=0.7,
-    fontsize=8,
-)
-plot([1e-10, 1e10], [T_minimal, T_minimal], "k--", lw=0.5, alpha=0.7)
-text(1e-2, T_minimal * 0.8, "$T_{\\rm min}$", va="top", alpha=0.7, fontsize=8)
-plot(a_evol, T_cmb, "k--", lw=0.5, alpha=0.7)
-text(
-    a_evol[20],
-    T_cmb[20] * 0.55,
-    "$(1+z)^2\\times T_{\\rm CMB,0}$",
-    rotation=-34,
-    alpha=0.7,
-    fontsize=8,
-    va="top",
-    bbox=dict(facecolor="w", edgecolor="none", pad=1.0, alpha=0.9),
-)
-
-
-redshift_ticks = np.array([0.0, 1.0, 2.0, 5.0, 10.0, 20.0, 50.0, 100.0])
-redshift_labels = ["$0$", "$1$", "$2$", "$5$", "$10$", "$20$", "$50$", "$100$"]
-a_ticks = 1.0 / (redshift_ticks + 1.0)
-
-xticks(a_ticks, redshift_labels)
-minorticks_off()
-
-xlabel("${\\rm Redshift}~z$", labelpad=0)
-ylabel("${\\rm Temperature}~T~[{\\rm K}]$", labelpad=0)
-xlim(9e-3, 1.1)
-ylim(5, 2.5e7)
-
-savefig("Temperature_evolution.png", dpi=200)
diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py b/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py
deleted file mode 100644
index 3d75a4069081b4d76dfd61ad72c581fae7dca823..0000000000000000000000000000000000000000
--- a/examples/SubgridTests/CosmologicalStellarEvolution/check_continuous_heating.py
+++ /dev/null
@@ -1,195 +0,0 @@
-# Script for plotting energy evolution of uniform box of gas with single star in the
-# centre when running with stochastic energy injection. It also checks that the change
-# in total energy of the gas particles is within a specified tolerance from what is
-# expected based on the mass of the star particle (Note that this tolerance could be
-# somewhat high because of Poisson noise and the relatively small number of injection
-# events)
-
-import matplotlib
-
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-import os.path
-import numpy as np
-import glob
-
-# Number of snapshots and elements
-newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime)
-n_snapshots = (
-    int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1
-)
-n_elements = 9
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.3,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.18,
-    "figure.subplot.top": 0.92,
-    "figure.subplot.wspace": 0.21,
-    "figure.subplot.hspace": 0.19,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-
-rcParams.update(params)
-
-# Read the simulation data
-sim = h5py.File("stellar_evolution_0000.hdf5", "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"][0]
-kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
-eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
-alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
-H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
-H_transition_temp = sim["/HydroScheme"].attrs[
-    "Hydrogen ionization transition temperature"
-][0]
-T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
-T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
-git = sim["Code"].attrs["Git Revision"]
-star_initial_mass = sim["/PartType4/Masses"][0]
-
-# Cosmological parameters
-H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
-gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
-
-# Units
-unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
-unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
-unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
-unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs
-unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs
-unit_length_in_si = 0.01 * unit_length_in_cgs
-unit_mass_in_si = 0.001 * unit_mass_in_cgs
-unit_time_in_si = unit_time_in_cgs
-
-# Calculate solar mass in internal units
-const_solar_mass = 1.98848e33 / unit_mass_in_cgs
-
-# Define Gyr
-Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0
-
-# Find out how many particles (gas and star) we have
-n_parts = sim["/Header"].attrs["NumPart_Total"][0]
-n_sparts = sim["/Header"].attrs["NumPart_Total"][4]
-
-# Declare arrays for data
-masses = zeros((n_parts, n_snapshots))
-star_masses = zeros((n_sparts, n_snapshots))
-internal_energy = zeros((n_parts, n_snapshots))
-velocity_parts = zeros((n_parts, 3, n_snapshots))
-time = zeros(n_snapshots)
-
-# Read fields we are checking from snapshots
-# for i in [0,n_snapshots-1]:
-for i in range(n_snapshots):
-    sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r")
-    print("reading snapshot " + str(i))
-    masses[:, i] = sim["/PartType0/Masses"]
-    internal_energy[:, i] = sim["/PartType0/InternalEnergies"]
-    velocity_parts[:, :, i] = sim["/PartType0/Velocities"]
-    time[i] = sim["/Header"].attrs["Time"][0]
-
-# Check that the total amount of enrichment is as expected.
-# Define tolerance. Note, relatively high value used due to
-# Poisson noise associated with stochastic energy injection.
-eps = 0.15
-
-# Stochastic heating
-vel2 = zeros((n_parts, n_snapshots))
-vel2[:, :] = (
-    velocity_parts[:, 0, :] * velocity_parts[:, 0, :]
-    + velocity_parts[:, 1, :] * velocity_parts[:, 1, :]
-    + velocity_parts[:, 2, :] * velocity_parts[:, 2, :]
-)
-total_kinetic_energy_cgs = (
-    np.sum(np.multiply(vel2, masses) * 0.5, axis=0) * unit_energy_in_cgs
-)
-total_energy_cgs = (
-    np.sum(np.multiply(internal_energy, masses), axis=0) * unit_energy_in_cgs
-)
-total_energy_released_cgs = (
-    total_energy_cgs[n_snapshots - 1]
-    - total_energy_cgs[0]
-    + total_kinetic_energy_cgs[n_snapshots - 1]
-    - total_kinetic_energy_cgs[0]
-)
-
-# Calculate energy released
-energy_per_sn = 1.0e51 / unit_energy_in_cgs
-SNIa_efficiency = 2.0e-3
-SNIa_timescale_Gyr = 2.0
-expected_energy_released_cgs = np.zeros(n_snapshots)
-for i in range(n_snapshots):
-    age_Gyr = time[i] * unit_time_in_cgs / Gyr_in_cgs
-    total_sn = (
-        SNIa_efficiency
-        * (1.0 - np.exp(-age_Gyr / SNIa_timescale_Gyr))
-        / const_solar_mass
-    )
-    expected_energy_released_cgs[i] = total_sn * energy_per_sn * unit_energy_in_cgs
-
-# Did we get it right?
-if (
-    abs(total_energy_released_cgs - expected_energy_released_cgs[n_snapshots - 1])
-    / expected_energy_released_cgs[n_snapshots - 1]
-    < eps
-):
-    print(
-        "total stochastic energy release consistent with expectation. total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-    )
-else:
-    print(
-        "total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        + " energy change fraction of total "
-        + str(
-            total_energy_released_cgs
-            / (total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        )
-    )
-
-# Plot the energy evolution
-figure()
-subplot(111)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    total_energy_cgs
-    + total_kinetic_energy_cgs
-    - total_energy_cgs[0]
-    - total_kinetic_energy_cgs[0],
-    color="k",
-    linewidth=0.5,
-    label="SWIFT",
-)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    expected_energy_released_cgs,
-    color="r",
-    linewidth=0.5,
-    label="expected",
-)
-xlabel("Time (Gyr)")
-ylabel("Total energy (erg)")
-legend()
-savefig("continuous_energy_evolution.png", dpi=200)
diff --git a/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py b/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py
deleted file mode 100644
index baaaca241a5bb290197763f385431b03f3e1c41a..0000000000000000000000000000000000000000
--- a/examples/SubgridTests/CosmologicalStellarEvolution/check_stochastic_heating.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# Script for plotting energy evolution of uniform box of gas with single star in the
-# centre when running with stochastic energy injection. It also checks that the change
-# in total energy of the gas particles is within a specified tolerance from what is
-# expected based on the mass of the star particle (Note that this tolerance could be
-# somewhat high because of Poisson noise and the relatively small number of injection
-# events)
-
-import matplotlib
-
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-import os.path
-import numpy as np
-import glob
-
-# Number of snapshots and elements
-newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime)
-n_snapshots = (
-    int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1
-)
-n_elements = 9
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.3,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.18,
-    "figure.subplot.top": 0.92,
-    "figure.subplot.wspace": 0.21,
-    "figure.subplot.hspace": 0.19,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-
-rcParams.update(params)
-
-# Read the simulation data
-sim = h5py.File("stellar_evolution_0000.hdf5", "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"][0]
-kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
-eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
-alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
-H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
-H_transition_temp = sim["/HydroScheme"].attrs[
-    "Hydrogen ionization transition temperature"
-][0]
-T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
-T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
-git = sim["Code"].attrs["Git Revision"]
-star_initial_mass = sim["/PartType4/Masses"][0]
-
-# Cosmological parameters
-H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
-gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
-
-# Units
-unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
-unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
-unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
-unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs
-unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs
-unit_length_in_si = 0.01 * unit_length_in_cgs
-unit_mass_in_si = 0.001 * unit_mass_in_cgs
-unit_time_in_si = unit_time_in_cgs
-
-# Calculate solar mass in internal units
-const_solar_mass = 1.98848e33 / unit_mass_in_cgs
-
-# Define Gyr
-Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0
-
-# Find out how many particles (gas and star) we have
-n_parts = sim["/Header"].attrs["NumPart_Total"][0]
-n_sparts = sim["/Header"].attrs["NumPart_Total"][4]
-
-# Declare arrays for data
-masses = zeros((n_parts, n_snapshots))
-star_masses = zeros((n_sparts, n_snapshots))
-internal_energy = zeros((n_parts, n_snapshots))
-velocity_parts = zeros((n_parts, 3, n_snapshots))
-time = zeros(n_snapshots)
-
-# Read fields we are checking from snapshots
-# for i in [0,n_snapshots-1]:
-for i in range(n_snapshots):
-    sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r")
-    print("reading snapshot " + str(i))
-    masses[:, i] = sim["/PartType0/Masses"]
-    internal_energy[:, i] = sim["/PartType0/InternalEnergies"]
-    velocity_parts[:, :, i] = sim["/PartType0/Velocities"]
-    time[i] = sim["/Header"].attrs["Time"][0]
-
-# Check that the total amount of enrichment is as expected.
-# Define tolerance. Note, relatively high value used due to
-# Poisson noise associated with stochastic energy injection.
-eps = 0.15
-
-# Stochastic heating
-vel2 = zeros((n_parts, n_snapshots))
-vel2[:, :] = (
-    velocity_parts[:, 0, :] * velocity_parts[:, 0, :]
-    + velocity_parts[:, 1, :] * velocity_parts[:, 1, :]
-    + velocity_parts[:, 2, :] * velocity_parts[:, 2, :]
-)
-total_kinetic_energy_cgs = (
-    np.sum(np.multiply(vel2, masses) * 0.5, axis=0) * unit_energy_in_cgs
-)
-total_energy_cgs = (
-    np.sum(np.multiply(internal_energy, masses), axis=0) * unit_energy_in_cgs
-)
-total_energy_released_cgs = (
-    total_energy_cgs[n_snapshots - 1]
-    - total_energy_cgs[0]
-    + total_kinetic_energy_cgs[n_snapshots - 1]
-    - total_kinetic_energy_cgs[0]
-)
-
-# Calculate energy released
-num_SNII_per_msun = 1.73621e-02
-energy_per_sn = 1.0e51 / unit_energy_in_cgs
-expected_energy_released_cgs = np.zeros(n_snapshots)
-for i in range(n_snapshots):
-    if time[i] * unit_time_in_cgs / Gyr_in_cgs < 0.03:
-        expected_energy_released_cgs[i] = 0
-    else:
-        expected_energy_released_cgs[i] = (
-            num_SNII_per_msun
-            * star_initial_mass
-            / const_solar_mass
-            * energy_per_sn
-            * unit_energy_in_cgs
-        )
-
-# Did we get it right?
-if (
-    abs(total_energy_released_cgs - expected_energy_released_cgs[n_snapshots - 1])
-    / expected_energy_released_cgs[n_snapshots - 1]
-    < eps
-):
-    print(
-        "total stochastic energy release consistent with expectation. total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-    )
-else:
-    print(
-        "total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        + " energy change fraction of total "
-        + str(
-            total_energy_released_cgs
-            / (total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        )
-    )
-
-# Plot the energy evolution
-figure()
-subplot(111)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    total_energy_cgs
-    + total_kinetic_energy_cgs
-    - total_energy_cgs[0]
-    - total_kinetic_energy_cgs[0],
-    color="k",
-    linewidth=0.5,
-    label="SWIFT",
-)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    expected_energy_released_cgs,
-    color="r",
-    linewidth=0.5,
-    label="expected",
-)
-xlabel("Time (Gyr)")
-ylabel("Total energy (erg)")
-legend()
-savefig("stochastic_energy_evolution.png", dpi=200)
diff --git a/examples/SubgridTests/SmoothedMetallicity/plotSolution.py b/examples/SubgridTests/SmoothedMetallicity/plotSolution.py
index 74c0a7fcdb884cf0bccd6838918855298845da3e..2ad291563f4d39d83b743b0b9262b5955c84731a 100644
--- a/examples/SubgridTests/SmoothedMetallicity/plotSolution.py
+++ b/examples/SubgridTests/SmoothedMetallicity/plotSolution.py
@@ -203,7 +203,7 @@ plt.subplot(222, frameon=False)
 
 plt.text(-0.49, 0.9, "Smoothed Metallicity in 3D at $t=%.2f$" % time, fontsize=10)
 plt.plot([-0.49, 0.1], [0.82, 0.82], "k-", lw=1)
-plt.text(-0.49, 0.7, "$\\textsc{Swift}$ %s" % git, fontsize=10)
+plt.text(-0.49, 0.7, "$SWIFT$ %s" % git, fontsize=10)
 plt.text(-0.49, 0.6, scheme, fontsize=10)
 plt.text(-0.49, 0.5, kernel, fontsize=10)
 plt.text(-0.49, 0.4, chemistry + "'s Chemistry", fontsize=10)
diff --git a/examples/SubgridTests/StellarEvolution/check_continuous_heating.py b/examples/SubgridTests/StellarEvolution/check_continuous_heating.py
deleted file mode 100644
index 3d75a4069081b4d76dfd61ad72c581fae7dca823..0000000000000000000000000000000000000000
--- a/examples/SubgridTests/StellarEvolution/check_continuous_heating.py
+++ /dev/null
@@ -1,195 +0,0 @@
-# Script for plotting energy evolution of uniform box of gas with single star in the
-# centre when running with stochastic energy injection. It also checks that the change
-# in total energy of the gas particles is within a specified tolerance from what is
-# expected based on the mass of the star particle (Note that this tolerance could be
-# somewhat high because of Poisson noise and the relatively small number of injection
-# events)
-
-import matplotlib
-
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-import os.path
-import numpy as np
-import glob
-
-# Number of snapshots and elements
-newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime)
-n_snapshots = (
-    int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1
-)
-n_elements = 9
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.3,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.18,
-    "figure.subplot.top": 0.92,
-    "figure.subplot.wspace": 0.21,
-    "figure.subplot.hspace": 0.19,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-
-rcParams.update(params)
-
-# Read the simulation data
-sim = h5py.File("stellar_evolution_0000.hdf5", "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"][0]
-kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
-eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
-alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
-H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
-H_transition_temp = sim["/HydroScheme"].attrs[
-    "Hydrogen ionization transition temperature"
-][0]
-T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
-T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
-git = sim["Code"].attrs["Git Revision"]
-star_initial_mass = sim["/PartType4/Masses"][0]
-
-# Cosmological parameters
-H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
-gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
-
-# Units
-unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
-unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
-unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
-unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs
-unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs
-unit_length_in_si = 0.01 * unit_length_in_cgs
-unit_mass_in_si = 0.001 * unit_mass_in_cgs
-unit_time_in_si = unit_time_in_cgs
-
-# Calculate solar mass in internal units
-const_solar_mass = 1.98848e33 / unit_mass_in_cgs
-
-# Define Gyr
-Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0
-
-# Find out how many particles (gas and star) we have
-n_parts = sim["/Header"].attrs["NumPart_Total"][0]
-n_sparts = sim["/Header"].attrs["NumPart_Total"][4]
-
-# Declare arrays for data
-masses = zeros((n_parts, n_snapshots))
-star_masses = zeros((n_sparts, n_snapshots))
-internal_energy = zeros((n_parts, n_snapshots))
-velocity_parts = zeros((n_parts, 3, n_snapshots))
-time = zeros(n_snapshots)
-
-# Read fields we are checking from snapshots
-# for i in [0,n_snapshots-1]:
-for i in range(n_snapshots):
-    sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r")
-    print("reading snapshot " + str(i))
-    masses[:, i] = sim["/PartType0/Masses"]
-    internal_energy[:, i] = sim["/PartType0/InternalEnergies"]
-    velocity_parts[:, :, i] = sim["/PartType0/Velocities"]
-    time[i] = sim["/Header"].attrs["Time"][0]
-
-# Check that the total amount of enrichment is as expected.
-# Define tolerance. Note, relatively high value used due to
-# Poisson noise associated with stochastic energy injection.
-eps = 0.15
-
-# Stochastic heating
-vel2 = zeros((n_parts, n_snapshots))
-vel2[:, :] = (
-    velocity_parts[:, 0, :] * velocity_parts[:, 0, :]
-    + velocity_parts[:, 1, :] * velocity_parts[:, 1, :]
-    + velocity_parts[:, 2, :] * velocity_parts[:, 2, :]
-)
-total_kinetic_energy_cgs = (
-    np.sum(np.multiply(vel2, masses) * 0.5, axis=0) * unit_energy_in_cgs
-)
-total_energy_cgs = (
-    np.sum(np.multiply(internal_energy, masses), axis=0) * unit_energy_in_cgs
-)
-total_energy_released_cgs = (
-    total_energy_cgs[n_snapshots - 1]
-    - total_energy_cgs[0]
-    + total_kinetic_energy_cgs[n_snapshots - 1]
-    - total_kinetic_energy_cgs[0]
-)
-
-# Calculate energy released
-energy_per_sn = 1.0e51 / unit_energy_in_cgs
-SNIa_efficiency = 2.0e-3
-SNIa_timescale_Gyr = 2.0
-expected_energy_released_cgs = np.zeros(n_snapshots)
-for i in range(n_snapshots):
-    age_Gyr = time[i] * unit_time_in_cgs / Gyr_in_cgs
-    total_sn = (
-        SNIa_efficiency
-        * (1.0 - np.exp(-age_Gyr / SNIa_timescale_Gyr))
-        / const_solar_mass
-    )
-    expected_energy_released_cgs[i] = total_sn * energy_per_sn * unit_energy_in_cgs
-
-# Did we get it right?
-if (
-    abs(total_energy_released_cgs - expected_energy_released_cgs[n_snapshots - 1])
-    / expected_energy_released_cgs[n_snapshots - 1]
-    < eps
-):
-    print(
-        "total stochastic energy release consistent with expectation. total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-    )
-else:
-    print(
-        "total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        + " energy change fraction of total "
-        + str(
-            total_energy_released_cgs
-            / (total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        )
-    )
-
-# Plot the energy evolution
-figure()
-subplot(111)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    total_energy_cgs
-    + total_kinetic_energy_cgs
-    - total_energy_cgs[0]
-    - total_kinetic_energy_cgs[0],
-    color="k",
-    linewidth=0.5,
-    label="SWIFT",
-)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    expected_energy_released_cgs,
-    color="r",
-    linewidth=0.5,
-    label="expected",
-)
-xlabel("Time (Gyr)")
-ylabel("Total energy (erg)")
-legend()
-savefig("continuous_energy_evolution.png", dpi=200)
diff --git a/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py
index 2e6cced06ab2346efe368f52e4e90a045e855713..a16101a1104191252b5a13312a6f5f43cdf0610f 100644
--- a/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py
+++ b/examples/SubgridTests/StellarEvolution/check_stellar_evolution.py
@@ -1,6 +1,22 @@
-import matplotlib
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Matthieu Schaller (schaller@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
+# 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/>.
+#
+##############################################################################
 
-matplotlib.use("Agg")
 from pylab import *
 import h5py
 import os.path
@@ -14,28 +30,6 @@ n_snapshots = (
 )
 n_elements = 9
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.3,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.18,
-    "figure.subplot.top": 0.92,
-    "figure.subplot.wspace": 0.21,
-    "figure.subplot.hspace": 0.19,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-
-rcParams.update(params)
-
 # Read the simulation data
 sim = h5py.File("stellar_evolution_0000.hdf5", "r")
 boxSize = sim["/Header"].attrs["BoxSize"][0]
@@ -94,16 +88,16 @@ for i in range(n_snapshots):
     sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r")
     print("reading snapshot " + str(i))
     abundances[:, :, i] = sim["/PartType0/ElementMassFractions"]
-    metallicity[:, i] = sim["/PartType0/Metallicities"]
+    metallicity[:, i] = sim["/PartType0/MetalMassFractions"]
     masses[:, i] = sim["/PartType0/Masses"]
     star_masses[:, i] = sim["/PartType4/Masses"]
-    mass_from_AGB[:, i] = sim["/PartType0/TotalMassFromAGB"]
-    metal_mass_frac_from_AGB[:, i] = sim["/PartType0/MetalMassFracFromAGB"]
-    mass_from_SNII[:, i] = sim["/PartType0/TotalMassFromSNII"]
-    metal_mass_frac_from_SNII[:, i] = sim["/PartType0/MetalMassFracFromSNII"]
-    mass_from_SNIa[:, i] = sim["/PartType0/TotalMassFromSNIa"]
-    metal_mass_frac_from_SNIa[:, i] = sim["/PartType0/MetalMassFracFromSNIa"]
-    iron_mass_frac_from_SNIa[:, i] = sim["/PartType0/IronMassFracFromSNIa"]
+    mass_from_AGB[:, i] = sim["/PartType0/MassesFromAGB"]
+    metal_mass_frac_from_AGB[:, i] = sim["/PartType0/MetalMassFractionsFromAGB"]
+    mass_from_SNII[:, i] = sim["/PartType0/MassesFromSNII"]
+    metal_mass_frac_from_SNII[:, i] = sim["/PartType0/MetalMassFractionsFromSNII"]
+    mass_from_SNIa[:, i] = sim["/PartType0/MassesFromSNIa"]
+    metal_mass_frac_from_SNIa[:, i] = sim["/PartType0/MetalMassFractionsFromSNIa"]
+    iron_mass_frac_from_SNIa[:, i] = sim["/PartType0/IronMassFractionsFromSNIa"]
     internal_energy[:, i] = sim["/PartType0/InternalEnergies"]
     velocity_parts[:, :, i] = sim["/PartType0/Velocities"]
     time[i] = sim["/Header"].attrs["Time"][0]
diff --git a/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py b/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py
deleted file mode 100644
index baaaca241a5bb290197763f385431b03f3e1c41a..0000000000000000000000000000000000000000
--- a/examples/SubgridTests/StellarEvolution/check_stochastic_heating.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# Script for plotting energy evolution of uniform box of gas with single star in the
-# centre when running with stochastic energy injection. It also checks that the change
-# in total energy of the gas particles is within a specified tolerance from what is
-# expected based on the mass of the star particle (Note that this tolerance could be
-# somewhat high because of Poisson noise and the relatively small number of injection
-# events)
-
-import matplotlib
-
-matplotlib.use("Agg")
-from pylab import *
-import h5py
-import os.path
-import numpy as np
-import glob
-
-# Number of snapshots and elements
-newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime)
-n_snapshots = (
-    int(newest_snap_name.replace("stellar_evolution_", "").replace(".hdf5", "")) + 1
-)
-n_elements = 9
-
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 9,
-    "legend.fontsize": 9,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (3.15, 3.15),
-    "figure.subplot.left": 0.3,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.18,
-    "figure.subplot.top": 0.92,
-    "figure.subplot.wspace": 0.21,
-    "figure.subplot.hspace": 0.19,
-    "lines.markersize": 6,
-    "lines.linewidth": 2.0,
-}
-
-rcParams.update(params)
-
-# Read the simulation data
-sim = h5py.File("stellar_evolution_0000.hdf5", "r")
-boxSize = sim["/Header"].attrs["BoxSize"][0]
-scheme = sim["/HydroScheme"].attrs["Scheme"][0]
-kernel = sim["/HydroScheme"].attrs["Kernel function"][0]
-neighbours = sim["/HydroScheme"].attrs["Kernel target N_ngb"][0]
-eta = sim["/HydroScheme"].attrs["Kernel eta"][0]
-alpha = sim["/HydroScheme"].attrs["Alpha viscosity"][0]
-H_mass_fraction = sim["/HydroScheme"].attrs["Hydrogen mass fraction"][0]
-H_transition_temp = sim["/HydroScheme"].attrs[
-    "Hydrogen ionization transition temperature"
-][0]
-T_initial = sim["/HydroScheme"].attrs["Initial temperature"][0]
-T_minimal = sim["/HydroScheme"].attrs["Minimal temperature"][0]
-git = sim["Code"].attrs["Git Revision"]
-star_initial_mass = sim["/PartType4/Masses"][0]
-
-# Cosmological parameters
-H_0 = sim["/Cosmology"].attrs["H0 [internal units]"][0]
-gas_gamma = sim["/HydroScheme"].attrs["Adiabatic index"][0]
-
-# Units
-unit_length_in_cgs = sim["/Units"].attrs["Unit length in cgs (U_L)"]
-unit_mass_in_cgs = sim["/Units"].attrs["Unit mass in cgs (U_M)"]
-unit_time_in_cgs = sim["/Units"].attrs["Unit time in cgs (U_t)"]
-unit_vel_in_cgs = unit_length_in_cgs / unit_time_in_cgs
-unit_energy_in_cgs = unit_mass_in_cgs * unit_vel_in_cgs * unit_vel_in_cgs
-unit_length_in_si = 0.01 * unit_length_in_cgs
-unit_mass_in_si = 0.001 * unit_mass_in_cgs
-unit_time_in_si = unit_time_in_cgs
-
-# Calculate solar mass in internal units
-const_solar_mass = 1.98848e33 / unit_mass_in_cgs
-
-# Define Gyr
-Gyr_in_cgs = 1e9 * 365 * 24 * 3600.0
-
-# Find out how many particles (gas and star) we have
-n_parts = sim["/Header"].attrs["NumPart_Total"][0]
-n_sparts = sim["/Header"].attrs["NumPart_Total"][4]
-
-# Declare arrays for data
-masses = zeros((n_parts, n_snapshots))
-star_masses = zeros((n_sparts, n_snapshots))
-internal_energy = zeros((n_parts, n_snapshots))
-velocity_parts = zeros((n_parts, 3, n_snapshots))
-time = zeros(n_snapshots)
-
-# Read fields we are checking from snapshots
-# for i in [0,n_snapshots-1]:
-for i in range(n_snapshots):
-    sim = h5py.File("stellar_evolution_%04d.hdf5" % i, "r")
-    print("reading snapshot " + str(i))
-    masses[:, i] = sim["/PartType0/Masses"]
-    internal_energy[:, i] = sim["/PartType0/InternalEnergies"]
-    velocity_parts[:, :, i] = sim["/PartType0/Velocities"]
-    time[i] = sim["/Header"].attrs["Time"][0]
-
-# Check that the total amount of enrichment is as expected.
-# Define tolerance. Note, relatively high value used due to
-# Poisson noise associated with stochastic energy injection.
-eps = 0.15
-
-# Stochastic heating
-vel2 = zeros((n_parts, n_snapshots))
-vel2[:, :] = (
-    velocity_parts[:, 0, :] * velocity_parts[:, 0, :]
-    + velocity_parts[:, 1, :] * velocity_parts[:, 1, :]
-    + velocity_parts[:, 2, :] * velocity_parts[:, 2, :]
-)
-total_kinetic_energy_cgs = (
-    np.sum(np.multiply(vel2, masses) * 0.5, axis=0) * unit_energy_in_cgs
-)
-total_energy_cgs = (
-    np.sum(np.multiply(internal_energy, masses), axis=0) * unit_energy_in_cgs
-)
-total_energy_released_cgs = (
-    total_energy_cgs[n_snapshots - 1]
-    - total_energy_cgs[0]
-    + total_kinetic_energy_cgs[n_snapshots - 1]
-    - total_kinetic_energy_cgs[0]
-)
-
-# Calculate energy released
-num_SNII_per_msun = 1.73621e-02
-energy_per_sn = 1.0e51 / unit_energy_in_cgs
-expected_energy_released_cgs = np.zeros(n_snapshots)
-for i in range(n_snapshots):
-    if time[i] * unit_time_in_cgs / Gyr_in_cgs < 0.03:
-        expected_energy_released_cgs[i] = 0
-    else:
-        expected_energy_released_cgs[i] = (
-            num_SNII_per_msun
-            * star_initial_mass
-            / const_solar_mass
-            * energy_per_sn
-            * unit_energy_in_cgs
-        )
-
-# Did we get it right?
-if (
-    abs(total_energy_released_cgs - expected_energy_released_cgs[n_snapshots - 1])
-    / expected_energy_released_cgs[n_snapshots - 1]
-    < eps
-):
-    print(
-        "total stochastic energy release consistent with expectation. total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-    )
-else:
-    print(
-        "total stochastic energy release "
-        + str(total_energy_released_cgs)
-        + " expected "
-        + str(expected_energy_released_cgs[n_snapshots - 1])
-        + " initial total internal energy "
-        + str(total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        + " energy change fraction of total "
-        + str(
-            total_energy_released_cgs
-            / (total_energy_cgs[0] + total_kinetic_energy_cgs[0])
-        )
-    )
-
-# Plot the energy evolution
-figure()
-subplot(111)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    total_energy_cgs
-    + total_kinetic_energy_cgs
-    - total_energy_cgs[0]
-    - total_kinetic_energy_cgs[0],
-    color="k",
-    linewidth=0.5,
-    label="SWIFT",
-)
-plot(
-    time * unit_time_in_cgs / Gyr_in_cgs,
-    expected_energy_released_cgs,
-    color="r",
-    linewidth=0.5,
-    label="expected",
-)
-xlabel("Time (Gyr)")
-ylabel("Total energy (erg)")
-legend()
-savefig("stochastic_energy_evolution.png", dpi=200)
diff --git a/examples/SubgridTests/StellarEvolution/plot_box_evolution.py b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py
index bdffbe180cc9ebbe990fe0e01ea4c2beda4a1c74..9c7268b87397c783f380dcd604ec2277dea3d489 100644
--- a/examples/SubgridTests/StellarEvolution/plot_box_evolution.py
+++ b/examples/SubgridTests/StellarEvolution/plot_box_evolution.py
@@ -33,27 +33,7 @@ import numpy as np
 import glob
 import os.path
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.05,
-    "figure.subplot.right": 0.995,
-    "figure.subplot.bottom": 0.06,
-    "figure.subplot.top": 0.92,
-    "figure.subplot.wspace": 0.25,
-    "figure.subplot.hspace": 0.2,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
-
+style.use("../../../tools/stylesheets/mnras.mplstyle")
 
 # Number of snapshots and elements
 newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"))  # , key=os.path.getctime)
@@ -193,7 +173,7 @@ eagle_total_metal_mass_SNIa = data[:, 2] * stellar_mass / Msun_in_cgs * unit_mas
 
 
 # Plot the interesting quantities
-figure()
+figure(figsize=(7, 7 / 1.6))
 
 suptitle("Star metallicity Z = %.4f" % Z_star)
 
@@ -345,4 +325,6 @@ xlabel("${\\rm Time~[Gyr]}$", labelpad=0)
 ylabel("Change in total metal mass of gas particles ${[\\rm M_\\odot]}$", labelpad=2)
 ticklabel_format(style="sci", axis="y", scilimits=(0, 0))
 
+tight_layout()
+
 savefig("box_evolution_Z_%.4f.png" % (Z_star), dpi=200)
diff --git a/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py
index 4b7c6e005547073859b065fafb52cafd302cc514..5e72bc477022ec5aa56aa8202d6c36afbba9fffa 100644
--- a/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py
+++ b/examples/SubgridTests/StellarEvolution/plot_particle_evolution.py
@@ -33,6 +33,8 @@ import numpy as np
 import glob
 import os.path
 
+style.use("../../../tools/stylesheets/mnras.mplstyle")
+
 # Function to find index in array a for each element in array b
 def find_indices(a, b):
     result = np.zeros(len(b))
@@ -41,28 +43,6 @@ def find_indices(a, b):
     return result
 
 
-# Plot parameters
-params = {
-    "axes.labelsize": 10,
-    "axes.titlesize": 10,
-    "font.size": 12,
-    "legend.fontsize": 12,
-    "xtick.labelsize": 10,
-    "ytick.labelsize": 10,
-    "text.usetex": True,
-    "figure.figsize": (9.90, 6.45),
-    "figure.subplot.left": 0.1,
-    "figure.subplot.right": 0.99,
-    "figure.subplot.bottom": 0.1,
-    "figure.subplot.top": 0.95,
-    "figure.subplot.wspace": 0.2,
-    "figure.subplot.hspace": 0.2,
-    "lines.markersize": 6,
-    "lines.linewidth": 3.0,
-}
-rcParams.update(params)
-
-
 # Number of snapshots and elements
 newest_snap_name = max(glob.glob("stellar_evolution_*.hdf5"), key=os.path.getctime)
 n_snapshots = (
@@ -143,7 +123,7 @@ for i in range(n_snapshots):
     P = sim["/PartType0/Pressures"][:]
     rho = sim["/PartType0/Densities"][:]
     mass = sim["/PartType0/Masses"][:]
-    metallicity = sim["/PartType0/Metallicities"][:]
+    metallicity = sim["/PartType0/MetalMassFractions"][:]
     internal_energy = sim["/PartType0/InternalEnergies"][:]
     IDs = sim["/PartType0/ParticleIDs"][:]
 
@@ -156,7 +136,7 @@ for i in range(n_snapshots):
 
 
 # Plot the interesting quantities
-figure()
+figure(figsize=(7, 7 / 1.6))
 
 # Radial velocity --------------------------------
 subplot(221)
@@ -218,4 +198,6 @@ xlabel("Time (Myr)", labelpad=0)
 ylabel("Metallicity", labelpad=2)
 ticklabel_format(style="sci", axis="y", scilimits=(0, 0))
 
+tight_layout()
+
 savefig("particle_evolution.png", dpi=200)
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index bb33b1351ce6a64ad0a8fb834202099fe0896fa0..0c71a23587eb3ba4cd707c621c1150b54b29d9c6 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -95,6 +95,7 @@ Gravity:
   max_physical_nu_softening:     0.0007    # Maximal Plummer-equivalent softening length in physical coordinates for neutrino particles (in internal units).
   softening_ratio_background:    0.04      # Fraction of the mean inter-particle separation to use as Plummer-equivalent softening for the background DM particles.
   rebuild_frequency:             0.01      # (Optional) Frequency of the gravity-tree rebuild in units of the number of g-particles (this is the default value).
+  rebuild_active_fraction:       1.01      # (Optional) Fraction of active gravity particles needed to trigger a gravity-tree rebuild (not triggered by this if > 1, which is the default value).
   a_smooth:                      1.25      # (Optional) Smoothing scale in top-level cell sizes to smooth the long-range forces over (this is the default value).
   r_cut_max:                     4.5       # (Optional) Cut-off in number of top-level cells beyond which no FMM forces are computed (this is the default value).
   r_cut_min:                     0.1       # (Optional) Cut-off in number of top-level cells below which no truncation of FMM forces are performed (this is the default value).
@@ -159,6 +160,7 @@ TimeIntegration:
   dt_max:              1e-2  # The maximal time-step size of the simulation (in internal units).
   max_dt_RMS_factor:   0.25  # (Optional) Dimensionless factor for the maximal displacement allowed based on the RMS velocities.
   dt_RMS_use_gas_only: 0     # (Optional) When computing the max RMS dt, should only the gas particles be considered in the baryon component calculation?
+  max_nr_rt_subcycles: 0     # (Optional) Maximal number of radiative transfer sub-cycles per hydro step for any particle. Set = 0 to disable subcycling. Needs to be a power of 2.
   
 # Parameters governing the snapshots
 Snapshots:
@@ -659,6 +661,73 @@ EAGLEAGN:
   merger_max_distance_ratio:          3.0        # Maximal distance over which two BHs can merge, in units of the softening length.
   minimum_timestep_Myr:               1.0        # Minimum time-step size for BHs in Mega-years. The time-step size is computed based on the accretion rate and this serves as a minimum. Defaults to FLT_MAX.
 
+# Spin-jet AGN model
+SPINJETAGN:
+  subgrid_seed_mass_Msun:             1e5        # Black hole subgrid mass at creation time in solar masses.
+  use_subgrid_mass_from_ics:          1          # (Optional) Use subgrid masses specified in ICs [1, default], or initialise them to particle masses [0]?
+  with_subgrid_mass_check:            1          # (Optional) Verify that initial black hole subgrid masses are positive [1, default]. Only used if use_subgrid_mass_from_ics is 1.
+  use_multi_phase_bondi:              0          # Compute Bondi rates per neighbour particle [1] or for the smoothed ambient gas around the black hole [0]?
+  use_subgrid_gas_properties:         1          # Use subgrid density [1] or dynamical density [0] to calculate BH accretion rates?
+  use_krumholz:                       1          # Use Krumholz et al. (2006) [1] or standard Bondi-Hoyle-Lyttleton formula [0] for black hole accretion rates? Only used if multi_phase_bondi is 0.
+  with_krumholz_vorticity:            0          # Include the vorticity term in Krumholz et al. formula? Only used if use_multi_phase_bondi is 0.
+  with_angmom_limiter:                0          # Are we applying the Rosas-Guevara (2015) viscous time-scale reduction term?
+  viscous_alpha:                      1e6        # Normalisation constant of the viscous time-scale in the accretion reduction term. Only used if with_angmom_limiter is 1.
+  with_boost_factor:                  0          # Are we using the model from Booth, Schaye (2009)?
+  boost_alpha:                        1.         # Lowest value for the accretion effeciency for the Booth, Schaye 2009 accretion model.
+  boost_beta:                         2.         # Slope of the power law for the Booth, Schaye 2009 model, set beta to zero for constant alpha models.
+  boost_n_h_star_cm3:                 0.1        # Normalization of the power law for the Booth Schaye 2009 model in cgs (cm^-3).
+  max_eddington_fraction:             1.         # Maximal allowed accretion rate in units of the Eddington rate.
+  eddington_fraction_for_recording:   0.1        # Record the last time BHs reached an Eddington ratio above this threshold.
+  use_nibbling:                       1          # Continuously transfer small amounts of mass from all gas neighbours to a black hole [1] or stochastically swallow whole gas particles [0]? 
+  min_gas_mass_for_nibbling_Msun:     9e5        # Minimum mass for a gas particle to be nibbled from [M_Sun]. Only used if use_nibbling is 1.
+  coupling_efficiency:                0.15       # Fraction of the radiated energy that couples to the gas in feedback events.
+  AGN_delta_T_K:                      3.16228e8  # Change in temperature to apply to the gas particle in an AGN feedback event in Kelvin.
+  AGN_num_ngb_to_heat:                1.         # Target number of gas neighbours to heat in an AGN feedback event.
+  with_potential_correction:          1          # Subtract BH's own contribution to the potential of neighbours when determining repositioning targets.
+  max_reposition_mass_Msun:           2e8        # Maximal BH mass considered for BH repositioning in solar masses.
+  max_reposition_distance_ratio:      3.0        # Maximal distance a BH can be repositioned, in units of the softening length.
+  with_reposition_velocity_threshold: 0          # Should we only reposition to particles that move slowly w.r.t. the black hole?
+  max_reposition_velocity_ratio:      0.25       # Maximal velocity offset of a particle to reposition a BH to, in units of the ambient sound speed of the BH. Only meaningful if with_reposition_velocity_ratio is 1.
+  min_reposition_velocity_threshold_km_p_s: -1.0 # Minimal value of the velocity threshold for repositioning [km/s], set to < 0 for no effect. Only meaningful if with_reposition_velocity_ratio is 1.
+  set_reposition_speed:               0          # Should we reposition black holes with (at most) a prescribed speed towards the potential minimum?
+  reposition_coefficient_upsilon:     0.001      # Repositioning speed normalisation [km/s/M_sun]. Only meaningful if set_reposition_speed is 1.
+  reposition_exponent_xi:             1.0        # (Optional) Scaling of repositioning velocity with BH subgrid mass (default: 1.0, linear). Only meaningful if set_reposition_speed is 1.
+  threshold_major_merger:             0.333      # Mass ratio threshold to consider a BH merger as 'major'
+  threshold_minor_merger:             0.1        # Mass ratio threshold to consider a BH merger as 'minor'
+  merger_threshold_type:              DynamicalEscapeVelocity   # Type of velocity threshold for BH mergers (CircularVelocity as in EAGLE, EscapeVelocity, or DynamicalEscapeVelocity)
+  merger_max_distance_ratio:          3.0        # Maximal distance over which two BHs can merge, in units of the softening length.
+  AGN_use_deterministic_feedback:     1          # Deterministic (1) or stochastic (0) AGN feedback model
+  AGN_feedback_model:                 Isotropic  # AGN feedback model (Isotropic or MinimumDistance)
+  minimum_timestep_yr:                1000.0     # Minimum time-step of black-hole particles
+  include_jets:                       1          # Global switch whether to include jet feedback [1] or not [0].
+  turn_off_radiative_feedback:        0          # Global switch whether to turn off radiative (thermal) feedback [1] or not [0]. This should only be used if 'include_jets' is set to 1, since we want feedback in some form or another.
+  alpha_acc:                          0.2        # Viscous alpha of the subgrid accretion disks. Likely to be within the 0.1-0.3 range. The main effect is that it sets the transition accretion rate between thin and thick disk, as dot(m) = 0.1 * alpha^2 + 0.035 * alpha.
+  AGN_jet_velocity_model:             BlackHoleMass          # How AGN jet velocities are calculated. If 'Constant', a single value is used. If 'BlackHoleMass', then an empirical relation between halo mass and black hole mass is used to calculate jet velocities. 'HaloMass' is currently not supported. 
+  v_jet_km_p_s:                       10000.     # Jet velocity to use if 'AGN_jet_velocity_model' is 'Constant'. Units are km/s.
+  v_jet_cs_ratio:                     10.        # This sets the jet velocity to v_jet_cs_ratio times the sound speed of the hot gas of the parent halo the black hole is in. This is used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+  v_jet_BH_mass_scaling_reference_mass_Msun: 3.4e3 # The reference mass used in the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+  v_jet_BH_mass_scaling_slope:        0.65       # The slope of the relation between halo mass and BH mass used to calculate jet velocities. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+  v_jet_min_km_p_s:                   500        # The minimal jet velocity. Only used if 'AGN_jet_velocity_model' is 'BlackHoleMass'.
+  opening_angle_in_degrees:           7.5        # The half-opening angle of the jet in degrees. Should use values < 15 unless for tests.
+  N_jet:                              2          # Target number of particles to kick as part of a single jet feedback event. Should be a multiple of 2 to ensure approximate momentum conservation (we always kick particles in pairs, one from each 'side' of the BH, relative to the spin vector).
+  AGN_jet_feedback_model:             SpinAxis   # Which particles to kick from the black hole smoothing kernels. Should be 'SpinAxis', 'MinimumDistance', 'MaximumDistance' or 'MinimumDensity'
+  eps_f_jet:                          1.         # Coupling efficiency for jet feedback. No reason to expect this to be less than 1.
+  fix_jet_efficiency:                 0          # Global switch whether to fix jet efficiency to a particular value [1], or use a spin-dependant formula [0]. If used, jets will be launched exclusively along the z axis. Should be set to 1 only for tests.
+  jet_efficiency:                     0.1        # The constant jet efficiency used if 'fix_jet_efficiency' is set to 1.
+  fix_radiative_efficiency:           0          # Global switch whether to fix the radiative efficiency to a particular value [1], or use a spin-dependant formula [0]. 
+  radiative_efficiency:               0.1        # The constant jet efficiency used if 'fix_radiative_efficiency' is set to 1. Otherwise, this value is used to define the Eddington accretion rate.
+  TD_region:                          C          # How to treat the subgrid accretion disk if it is thin, according to the Shakura & Sunyaev (1973) model. If set to B, region b will be used. If set to C, region c will be used.
+  include_GRMHD_spindown:             1          # Whether to include high jet spindown rates from GRMHD simulations [1], or use an analytical formula that assumes extraction of energy from the rotational mass/energy of the BH.
+  include_ADIOS_suppression:          0          # Whether to suppress the accretion rate in the fully thick disc regime [1] (Eddington rate below 0.2alpha^2) by the amount expected to be taken away by isotropic kinetic disk winds.
+  ADIOS_R_in:                         30.        # If include_ADIOS_accr_suppression is set to 1, this parameter controls the inner radius within which winds are not important.
+  ADIOS_s:                            0.4        # Slope of the accretion rate - radius relationship if include_ADIOS_accr_suppression is set to 1.
+  turn_off_secondary_feedback:        1          # If set to 1, there will be only radiative (thermal) feedback in the thin disk mode, and only jets in the thick disk mode.
+  jet_h_r_slope:                      1.         # The slope of the dependence of jet efficiency on aspect ratio of the subgrid accretion disk, H/R. Default value is 1, and another reasonable value is 0 (same jet efficiency for all disks). Reality could be anything in between. This parameter is only used if turn_off_secondary_feedback is set to 0.
+  delta_ADAF:                         0.2        # Electron heating parameter, which controls the strength of radiative feedback in thick disks. Should be between 0.1 and 0.5. This parameter is only used if turn_off_secondary_feedback is set to 0.
+  include_slim_disk:                  0          # Global switch whether to include super-Eddington accretion, modeled as the slim disk. If set to 0, disks will be considered thin even at very large accretion rates.
+  TD_SD_eps_r_threshold:              0.75       # Parameter controlling the transition from thin to slim disk. Accretion disk will be slim if radiative efficiency satisfies eps_slim < TD_SD_eps_r_threshold * eps_thin. This parameter is only used if include_slim_disk is set to 1.
+
+  
 # Parameters related to the neutrinos --------------------------------------------
 Neutrino:
   use_delta_f:                        1          # Use the delta-f method for shot noise reduction
@@ -684,33 +753,36 @@ XrayEmissivity:
 
 # Default sink particles
 DefaultSink:
-    cut_off_radius:        1e-3       # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution.
+  cut_off_radius:        1e-3       # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution.
 
 # GEAR sink particles
 GEARSink:
-    cut_off_radius:        1e-3       # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution.
+  cut_off_radius:        1e-3       # Cut off radius of the sink particles (in internal units). This parameter should be adapted with the resolution.
 
 # Parameters related to radiative transfer ---------------------------------------
 
 GEARRT:
-    f_reduce_c: 1e-3                                  # reduce the speed of light for the RT solver by multiplying c with this factor
-    CFL_condition: 0.9                                # CFL condition for RT, independent of hydro
-    photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Photon frequency group bin edges in Hz. Needs to be 1 less than the number of groups (N) requested during the configuration (--with-RT=GEAR_N). Outer edges of zero and infinity are assumed.
-    use_const_emission_rates: 1                       # (Optional) use constant emission rates for stars as defined with star_emission_rates_LSol parameter
-    star_emission_rates_LSol: [1., 1., 1., 1.]        # (Optional) constant star emission rates for each photon frequency group to use if use_constant_emission_rates is set, in units of Solar Luminosity.
-    hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
-    set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
-    set_initial_ionization_mass_fractions: 0          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
-    mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
-    mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
-    mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
-    mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
-    mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
-    skip_thermochemistry: 0                           # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT.
-    stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
-    stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
-    stellar_spectrum_blackbody_temperature_K: 1.e4    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. 
-    stars_max_timestep: -1.                           # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off.
+  f_reduce_c: 1e-3                                  # reduce the speed of light for the RT solver by multiplying c with this factor
+  CFL_condition: 0.9                                # CFL condition for RT, independent of hydro
+  f_limit_cooling_time: 0.9                         # (Optional) multiply the cooling time by this factor when estimating maximal next time step. Set to 0.0 to turn computation of cooling time off.
+  photon_groups_Hz: [3.288e15, 5.945e15, 13.157e15] # Lower photon frequency group bin edges in Hz. Needs to have exactly N elements, where N is the configured number of bins --with-RT=GEAR_N
+  stellar_luminosity_model: const                   # Which model to use to determine the stellar luminosities.
+  const_stellar_luminosities_LSol: [1., 1., 1.]     # (Conditional) constant star luminosities for each photon frequency group to use if stellar_luminosity_model:const is set, in units of Solar Luminosity.
+  hydrogen_mass_fraction:  0.76                     # total hydrogen (H + H+) mass fraction in the metal-free portion of the gas
+  set_equilibrium_initial_ionization_mass_fractions: 0   # (Optional) set the initial ionization fractions depending on gas temperature assuming ionization equilibrium.
+  set_initial_ionization_mass_fractions: 0          # (Optional) manually overwrite initial mass fraction of each species (using the values you set below)
+  mass_fraction_HI: 0.76                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HI mass fractions to this value
+  mass_fraction_HII: 0.                             # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HII mass fractions to this value
+  mass_fraction_HeI: 0.24                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeI mass fractions to this value
+  mass_fraction_HeII: 0.                            # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeII mass fractions to this value
+  mass_fraction_HeIII: 0.                           # (Conditional) If overwrite_initial_ionization_fractions=1, needed to set initial HeIII mass fractions to this value
+  skip_thermochemistry: 0                           # (Optional) skip the thermochemistry. This is intended only for debugging and testing the radiation transport, as it breaks the purpose of RT.
+  stellar_spectrum_type: 0                          # Which radiation spectrum to use. 0: constant from 0 until some max frequency set by stellar_spectrum_const_max_frequency_Hz. 1: blackbody spectrum.
+  stellar_spectrum_const_max_frequency_Hz: 1.e17    # (Conditional) if stellar_spectrum_type=0, use this maximal frequency for the constant spectrum. 
+  stellar_spectrum_blackbody_temperature_K: 1.e4    # (Conditional) if stellar_spectrum_type=1, use this temperature (in K) for the blackbody spectrum. 
+  stars_max_timestep: -1.                           # (Optional) restrict the maximal timestep of stars to this value (in internal units). Set to negative to turn off.
+  grackle_verbose: 0                                # (Optional) set grackle to verbose. (Default: 0)
+  case_B_recombination: 1                           # (Optional) use case B recombination interaction rates. (Default: 1)
 
 SPHM1RT:
   cred: 2.99792458e10                                 # value of reduced speed of light for the RT solver in code unit
@@ -807,4 +879,4 @@ Lightcone1:
   gas_filtering_enabled:      1      # Enable filtering out of certain gas particles from particle outputs
   min_z_for_gas_filtering:    0.025  # Filter gas particles above this redshift, output all below
   min_temp_for_filtered_gas:  1.0e5  # Above min_z_for_gas_filtering only output gas with temperature above this
-  min_nh_for_filtered_gas:    1.0e-6 # Above min_z_for_gas_filtering only output gas with nh/(1+z)^4 above this
\ No newline at end of file
+  min_nh_for_filtered_gas:    1.0e-6 # Above min_z_for_gas_filtering only output gas with nh/(1+z)^4 above this
diff --git a/format.sh b/format.sh
index dd154609e51b801b8bfd3a3d2f5287246779dc86..6d7c710001f5d98de7b136d0aeb5239840b31465 100755
--- a/format.sh
+++ b/format.sh
@@ -1,13 +1,13 @@
 #!/bin/bash
 
 # Clang format command, can be overridden using CLANG_FORMAT_CMD.
-# We currrently use version 10.0 so any overrides should provide that.
-clang=${CLANG_FORMAT_CMD:="clang-format-10"}
+# We currrently use version 13.0 so any overrides should provide that.
+clang=${CLANG_FORMAT_CMD:="clang-format-13"}
 
 # Formatting command
 cmd="$clang -style=file $(git ls-files | grep '\.[ch]$')"
 
-# Test if `clang-format-10` works
+# Test if `clang-format-13` works
 command -v $clang > /dev/null
 if [[ $? -ne 0 ]]
 then
diff --git a/m4/ax_lib_hdf5.m4 b/m4/ax_lib_hdf5.m4
index 9a64c8d0f31f93e5c81e3a3293777353c95f8c41..23e1bca980909af9dfc70e776d633b0d33dd1d9e 100644
--- a/m4/ax_lib_hdf5.m4
+++ b/m4/ax_lib_hdf5.m4
@@ -302,7 +302,7 @@ HDF5 support is being disabled.
           AC_MSG_WARN([Unable to compile HDF5 test program])
         fi
         dnl Look for HDF5's high level library
-        AC_HAVE_LIBRARY([hdf5_hl], [HDF5_LIBS="-lhdf5_hl $HDF5_LIBS"], [], [])
+        AC_CHECK_LIB([hdf5_hl],[main],[HDF5_LIBS="-lhdf5_hl $HDF5_LIBS"],[],[])
 
         CC=$ax_lib_hdf5_save_CC
         CPPFLAGS=$ax_lib_hdf5_save_CPPFLAGS
diff --git a/m4/ax_mpi.m4 b/m4/ax_mpi.m4
index 5b2322cb3c152c7a898792f2d0afa01ebb385828..7b6fb5e4297b384a9469d78c02e55d667da49e5d 100644
--- a/m4/ax_mpi.m4
+++ b/m4/ax_mpi.m4
@@ -67,7 +67,7 @@
 
 AU_ALIAS([ACX_MPI], [AX_MPI])
 AC_DEFUN([AX_MPI], [
-AC_PREREQ(2.50) dnl for AC_LANG_CASE
+AC_PREREQ([2.69]) dnl for AC_LANG_CASE
 
 AC_LANG_CASE([C], [
 	AC_REQUIRE([AC_PROG_CC])
@@ -135,16 +135,16 @@ if test x = x"$MPILIBS"; then
 	AC_CHECK_LIB(mpich, MPI_Init, [MPILIBS="-lmpich"])
 fi
 
-dnl We have to use AC_TRY_COMPILE and not AC_CHECK_HEADER because the
+dnl We have to use AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[],[]) and not AC_CHECK_HEADER because the
 dnl latter uses $CPP, not $CC (which may be mpicc).
 AC_LANG_CASE([C], [if test x != x"$MPILIBS"; then
 	AC_MSG_CHECKING([for mpi.h])
-	AC_TRY_COMPILE([#include <mpi.h>],[],[AC_MSG_RESULT(yes)], [MPILIBS=""
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <mpi.h>]], [[]])],[AC_MSG_RESULT(yes)],[MPILIBS=""
 		AC_MSG_RESULT(no)])
 fi],
 [C++], [if test x != x"$MPILIBS"; then
 	AC_MSG_CHECKING([for mpi.h])
-	AC_TRY_COMPILE([#include <mpi.h>],[],[AC_MSG_RESULT(yes)], [MPILIBS=""
+	AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <mpi.h>]], [[]])],[AC_MSG_RESULT(yes)],[MPILIBS=""
 		AC_MSG_RESULT(no)])
 fi],
 [Fortran 77], [if test x != x"$MPILIBS"; then
diff --git a/m4/gv_find_library.m4 b/m4/gv_find_library.m4
index 948a38a5432aa96d778a6c8cb72da79aecda2091..7c960f8c7a1cef63bdc124809961cc55be4f3939 100644
--- a/m4/gv_find_library.m4
+++ b/m4/gv_find_library.m4
@@ -16,7 +16,7 @@
 #
 #   Uses pkg-config to try to find package PKGNAME. If successful defines
 #   preprocessor macro HAVE_$VARNAME and sets USE_$VARNAME = "yes", otherwise
-#   sets USE_$VARNAME = "no". 
+#   sets USE_$VARNAME = "no".
 #
 #   Also sets ${VARNAME}_LIBS and ${VARNAME}_CFLAGS on success.
 #
@@ -30,7 +30,7 @@
 AC_DEFUN([GV_FIND_LIBRARY],[
 
 # Allow user to enable/disable library
-AC_ARG_WITH([$1], AC_HELP_STRING([--with-$1@<:@=PATH@:>@],[use the $1 library]),
+AC_ARG_WITH([$1], AS_HELP_STRING([--with-$1@<:@=PATH@:>@],[use the $1 library]),
 		  [USE_$2=$withval ; GV_SPEC="yes" ],[USE_$2="check" ; GV_SPEC="no" ])
 
 # Figure out if we have been given a path
@@ -59,10 +59,12 @@ if test $USE_$2 != "no" ; then
     export PKG_CONFIG_PATH=$GV_$2_PATH/pkgconfig/:$GV_$2_PATH/lib/pkgconfig/:$PKG_CONFIG_PATH
   fi
 
-  # Try to set it up with pkg-config
-  GV_PKG="yes"
-  PKG_CHECK_MODULES([$2], [$3], [], 
-  			  [echo Unable to find $1 with pkg-config, will try to link to it anyway... ; GV_PKG="no"])
+  # Try to set it up with pkg-config. First check we have the macro...
+  GV_PKG="no"
+  m4_ifdef([PKG_CHECK_MODULES],
+           [PKG_CHECK_MODULES([$2], [$3], [GV_PKG="yes"],
+              [AC_MSG_WARN([Unable to find $1 with pkg-config, will try to link to it anyway...])])],
+           [AC_MSG_WARN([No PKG_CHECK_MODULES macro, trying without pkg-config support])])
 
   # Restore original PKG_CONFIG_PATH
   export PKG_CONFIG_PATH=$TMP_PKG_CONFIG_PATH
@@ -84,12 +86,12 @@ if test $USE_$2 != "no" ; then
   # Try to link to the library
   TMP_LIBS=$LIBS
   LIBS="${$2_LIBS} ${LIBS}"
-  AC_CHECK_LIB([$4], [$5], [GV_FOUND="yes"], 
-  		     [echo ; echo WARNING: Unable to link to $1 library. See config.log for details ; echo])
+  AC_CHECK_LIB([$4], [$5], [GV_FOUND="yes"],
+  		     [AC_MSG_WARN([Unable to link to $1 library. See config.log for details])])
   LIBS=$TMP_LIBS
 
   if test $GV_FOUND = "no" ; then
-    # If we can't link the library and it was explicitly asked for, abort	
+    # If we can't link the library and it was explicitly asked for, abort
     if test $GV_SPEC = "yes" ; then
       AC_MSG_ERROR([Unable to link to requested library: $1])
     fi
@@ -116,4 +118,3 @@ if test $GV_FOUND = "yes" ; then
 fi
 
 ])
-
diff --git a/src/Makefile.am b/src/Makefile.am
index 0c244bd91dc550d4d09fa664084f70df4a3f8219..d453752bdafe8726dad4f73a07f618033df68a7e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,7 +42,7 @@ endif
 
 # List required headers
 include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h 
-include_HEADERS += cell_hydro.h cell_stars.h cell_grav.h cell_sinks.h cell_black_holes.h 
+include_HEADERS += cell_hydro.h cell_stars.h cell_grav.h cell_sinks.h cell_black_holes.h cell_rt.h
 include_HEADERS += engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h 
 include_HEADERS += common_io.h single_io.h distributed_io.h map.h tools.h  partition_fixed_costs.h 
 include_HEADERS += partition.h clocks.h parser.h physical_constants.h physical_constants_cgs.h potential.h version.h 
@@ -74,6 +74,7 @@ include_HEADERS += lightcone/lightcone_crossing.h lightcone/lightcone_array.h li
 include_HEADERS += lightcone/lightcone_map_types.h lightcone/projected_kernel.h lightcone/lightcone_shell.h
 include_HEADERS += lightcone/healpix_util.h lightcone/pixel_index.h
 include_HEADERS += power_spectrum.h
+include_HEADERS += ghost_stats.h
 
 # source files for EAGLE extra I/O
 EAGLE_EXTRA_IO_SOURCES=
@@ -130,7 +131,7 @@ endif
 # source files for GEAR RT
 GEAR_RT_SOURCES =
 if HAVEGEARRT
-GEAR_RT_SOURCES += rt/GEAR/rt_interaction_rates.c
+GEAR_RT_SOURCES += rt/GEAR/rt_interaction_cross_sections.c
 endif
 
 # source files for SPHM1RT cooling
@@ -178,6 +179,7 @@ AM_SOURCES += lightcone/lightcone.c lightcone/lightcone_particle_io.c lightcone/
 AM_SOURCES += lightcone/healpix_util.c lightcone/lightcone_array.c lightcone/lightcone_map.c
 AM_SOURCES += lightcone/lightcone_map_types.c lightcone/projected_kernel.c lightcone/lightcone_shell.c
 AM_SOURCES += power_spectrum.c
+AM_SOURCES += ghost_stats.c
 AM_SOURCES += $(EAGLE_EXTRA_IO_SOURCES)
 AM_SOURCES += $(QLA_COOLING_SOURCES) $(QLA_EAGLE_COOLING_SOURCES) 
 AM_SOURCES += $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES) 
@@ -304,25 +306,28 @@ nobase_noinst_HEADERS += rt/debug/rt_io.h
 nobase_noinst_HEADERS += rt/debug/rt_parameters.h
 nobase_noinst_HEADERS += rt/debug/rt_properties.h 
 nobase_noinst_HEADERS += rt/debug/rt_struct.h
-nobase_noinst_HEADERS += rt/GEAR/rt.h  
 nobase_noinst_HEADERS += rt/GEAR/rt_additions.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_blackbody.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_debugging.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_flux.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_getters.h 
+nobase_noinst_HEADERS += rt/GEAR/rt_grackle_utils.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_gradients.h 
+nobase_noinst_HEADERS += rt/GEAR/rt.h  
 nobase_noinst_HEADERS += rt/GEAR/rt_iact.h 
+nobase_noinst_HEADERS += rt/GEAR/rt_interaction_cross_sections.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_interaction_rates.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_io.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_ionization_equilibrium.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_parameters.h
 nobase_noinst_HEADERS += rt/GEAR/rt_properties.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_riemann_GLF.h 
-nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL_eigenvalues.h 
-# nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_cell.h
+nobase_noinst_HEADERS += rt/GEAR/rt_riemann_HLL.h 
+nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_cell.h
 nobase_noinst_HEADERS += rt/GEAR/rt_slope_limiters_face.h 
-nobase_noinst_HEADERS += rt/GEAR/rt_stellar_emission_rate.h 
+nobase_noinst_HEADERS += rt/GEAR/rt_species.h 
+nobase_noinst_HEADERS += rt/GEAR/rt_stellar_emission_model.h 
 nobase_noinst_HEADERS += rt/GEAR/rt_struct.h
 nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry.h
 nobase_noinst_HEADERS += rt/GEAR/rt_thermochemistry_utils.h
@@ -453,6 +458,11 @@ nobase_noinst_HEADERS += black_holes/EAGLE/black_holes.h black_holes/EAGLE/black
 nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_part.h black_holes/EAGLE/black_holes_iact.h 
 nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_properties.h black_holes/EAGLE/black_holes_parameters.h 
 nobase_noinst_HEADERS += black_holes/EAGLE/black_holes_struct.h black_holes/EAGLE/black_holes_debug.h
+nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes.h black_holes/SPIN_JET/black_holes_io.h 
+nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_part.h black_holes/SPIN_JET/black_holes_iact.h 
+nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_properties.h black_holes/SPIN_JET/black_holes_parameters.h
+nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_spin.h black_holes/SPIN_JET/black_holes_struct.h 
+nobase_noinst_HEADERS += black_holes/SPIN_JET/black_holes_debug.h 
 nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor.h pressure_floor/none/pressure_floor.h 
 nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_iact.h pressure_floor/none/pressure_floor_iact.h 
 nobase_noinst_HEADERS += pressure_floor/GEAR/pressure_floor_struct.h pressure_floor/none/pressure_floor_struct.h 
diff --git a/src/accumulate.h b/src/accumulate.h
index 877ac23baa1e9041c45a8d3dcc10a1d660ea6c68..f5a87e86cd29bf2f78261b1192f2c47eb9162938 100644
--- a/src/accumulate.h
+++ b/src/accumulate.h
@@ -20,7 +20,7 @@
 #define SWIFT_ACCUMULATE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "atomic.h"
diff --git a/src/active.h b/src/active.h
index f6b674cd47d836909217959901e85455b45ffe01..bbdedf77d79bd9816849e4ce555a63c7e0833395 100644
--- a/src/active.h
+++ b/src/active.h
@@ -20,7 +20,7 @@
 #define SWIFT_ACTIVE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "cell.h"
@@ -144,6 +144,26 @@ __attribute__((always_inline)) INLINE static int cell_are_bpart_drifted(
   return (c->black_holes.ti_old_part == e->ti_current);
 }
 
+/**
+ * @brief Check that the #part in a #cell have been drifted to the current time.
+ * This is just a prototype function to keep the iact functions clean. As we
+ * don't care about the drifts during the RT sub-cycling, this always just
+ * returns true.
+ *
+ * @param c The #cell.
+ * @param e The #engine containing information about the current time.
+ * @return 1 if the #cell has been drifted to the current time, 0 otherwise.
+ */
+__attribute__((always_inline)) INLINE static int
+cell_are_part_drifted_rt_sub_cycle(const struct cell *c,
+                                   const struct engine *e) {
+
+  /* Note: we can't just use "cell_are_part_drifted" in the hydro_iact
+   * functions, because an RT sub-cycle may be called during a main
+   * step for a cell that is hydro inactive and thus may be not drifted. */
+  return 1;
+}
+
 /* Are cells / particles active for regular tasks ? */
 
 /**
@@ -168,6 +188,44 @@ __attribute__((always_inline)) INLINE static int cell_is_active_hydro(
   return (c->hydro.ti_end_min == e->ti_current);
 }
 
+/**
+ * @brief Does a cell contain any particle finishing their RT 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_rt_active(
+    struct cell *c, const struct engine *e) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Each cell doing RT needs to have the rt_advance_cell_time task.
+   * If it doesn't, it's not doing RT. This can happen for foreign
+   * cells which have been sent to a foreign node for other interactions,
+   * e.g. gravity. However, foreign cells may have tasks on levels below
+   * the rt_advance_cell_time, so allow for that exception in this check. */
+
+  int has_rt_advance_cell_time = 0;
+  if (c->super != NULL)
+    has_rt_advance_cell_time = c->super->rt.rt_advance_cell_time != NULL;
+
+  if (has_rt_advance_cell_time &&
+      (c->rt.ti_rt_end_min < e->ti_current_subcycle)) {
+    error(
+        "cell %lld in an impossible time-zone! c->ti_rt_end_min=%lld (t=%e) "
+        "and e->ti_current=%lld (t=%e, a=%e) c->nodeID=%d ACT=%d count=%d",
+        c->cellID, c->rt.ti_rt_end_min, c->rt.ti_rt_end_min * e->time_base,
+        e->ti_current_subcycle, e->ti_current_subcycle * e->time_base,
+        e->cosmology->a, c->nodeID, c->rt.rt_advance_cell_time != NULL,
+        c->hydro.count);
+  }
+#endif
+
+  /* If there are no sub-cycles, e->ti_current_subcycle = e->ti_current.
+   * This is also the case if we're currently doing a normal SWIFT step. */
+  return (c->rt.ti_rt_end_min == e->ti_current_subcycle);
+}
+
 /**
  * @brief Does a cell contain any g-particle finishing their time-step now ?
  *
@@ -323,6 +381,33 @@ __attribute__((always_inline)) INLINE static int part_is_active_no_debug(
   return (part_bin <= max_active_bin);
 }
 
+/**
+ * @brief Is this particle finishing its RT time-step now ?
+ *
+ * @param p The #part.
+ * @param e The #engine containing information about the current time.
+ * @return 1 if the #part is active, 0 otherwise.
+ */
+__attribute__((always_inline)) INLINE static int part_is_rt_active(
+    const struct part *p, const struct engine *e) {
+
+  const timebin_t max_active_bin = e->max_active_bin_subcycle;
+  const timebin_t part_bin = p->rt_time_data.time_bin;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  const integertime_t ti_current_subcycle = e->ti_current_subcycle;
+  const integertime_t ti_end =
+      get_integer_time_end(ti_current_subcycle, p->rt_time_data.time_bin);
+  if (ti_end < ti_current_subcycle)
+    error(
+        "particle in an impossible time-zone! p->ti_end_subcycle=%lld "
+        "e->ti_current_subcycle=%lld",
+        ti_end, ti_current_subcycle);
+#endif
+
+  return (part_bin <= max_active_bin);
+}
+
 /**
  * @brief Is this g-particle finishing its time-step now ?
  *
diff --git a/src/adiabatic_index.h b/src/adiabatic_index.h
index f35f01d6982af45ddaa9da71f84327fe33069795..a623d40722278b41181642617f223218c1b5f156 100644
--- a/src/adiabatic_index.h
+++ b/src/adiabatic_index.h
@@ -27,7 +27,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/atomic.h b/src/atomic.h
index d5f2da40595d2896eb43934bca2409df4f73d15f..5d8621985da55d1c2d9b4416b6539956ce9b7794 100644
--- a/src/atomic.h
+++ b/src/atomic.h
@@ -20,7 +20,7 @@
 #define SWIFT_ATOMIC_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "inline.h"
diff --git a/src/barrier.h b/src/barrier.h
index 15796dbeac8fb531ff3bf721e05e09b1e3dd7220..9adb004cc56ef486deb7589fa5013cd66b0f7d3a 100644
--- a/src/barrier.h
+++ b/src/barrier.h
@@ -38,7 +38,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers */
 #include <pthread.h>
diff --git a/src/binomial.h b/src/binomial.h
index b1caf1258a75b47b0f47c26b2e16ac01d76f6454..21b4828d92fffe65414e5aafb4eb9dac5553ebac 100644
--- a/src/binomial.h
+++ b/src/binomial.h
@@ -20,7 +20,7 @@
 #define SWIFT_BINOMIAL_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "error.h"
diff --git a/src/black_holes.h b/src/black_holes.h
index cd3004067a05a34cc1419385fe9cb5fa917d2cc9..2c0f04ea7d3b7a7a846cb76fcc868c0c341fc87f 100644
--- a/src/black_holes.h
+++ b/src/black_holes.h
@@ -20,7 +20,7 @@
 #define SWIFT_BLACK_HOLES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct BH model */
 #if defined(BLACK_HOLES_NONE)
@@ -29,6 +29,9 @@
 #elif defined(BLACK_HOLES_EAGLE)
 #include "./black_holes/EAGLE/black_holes.h"
 #include "./black_holes/EAGLE/black_holes_iact.h"
+#elif defined(BLACK_HOLES_SPIN_JET)
+#include "./black_holes/SPIN_JET/black_holes.h"
+#include "./black_holes/SPIN_JET/black_holes_iact.h"
 #else
 #error "Invalid choice of black hole model"
 #endif
diff --git a/src/black_holes/Default/black_holes.h b/src/black_holes/Default/black_holes.h
index bcaf8af38e7c36a055fddb68752676220ac005d1..5994a0bde26011f37fdf3d903a54be104093c9d5 100644
--- a/src/black_holes/Default/black_holes.h
+++ b/src/black_holes/Default/black_holes.h
@@ -211,11 +211,12 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part(
  * @param time Time since the start of the simulation (non-cosmo mode).
  * @param with_cosmology Are we running with cosmology?
  * @param props The properties of the black hole scheme.
+ * @param constants The physical constants in internal units.
  */
 __attribute__((always_inline)) INLINE static void black_holes_swallow_bpart(
     struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo,
     const double time, const int with_cosmology,
-    const struct black_holes_props* props) {
+    const struct black_holes_props* props, const struct phys_const* constants) {
 
   /* Nothing to do here: No merging in the default model */
 }
@@ -314,11 +315,13 @@ black_holes_store_potential_in_part(struct black_holes_part_data* p_data,
  * @param cosmo The current cosmological model.
  * @param p The #part that became a black hole.
  * @param xp The #xpart that became a black hole.
+ * @param ti_current the current time on the time-line.
  */
 INLINE static void black_holes_create_from_gas(
     struct bpart* bp, const struct black_holes_props* props,
     const struct phys_const* constants, const struct cosmology* cosmo,
-    const struct part* p, const struct xpart* xp) {
+    const struct part* p, const struct xpart* xp,
+    const integertime_t ti_current) {
 
   /* First initialisation */
   black_holes_init_bpart(bp);
diff --git a/src/black_holes/EAGLE/black_holes.h b/src/black_holes/EAGLE/black_holes.h
index 9b11fa5c274c118bee5ba68c85c479d73d22f16e..3ef5eb4d907d035d875796c8e20aac43a42f9934 100644
--- a/src/black_holes/EAGLE/black_holes.h
+++ b/src/black_holes/EAGLE/black_holes.h
@@ -447,11 +447,12 @@ __attribute__((always_inline)) INLINE static void black_holes_swallow_part(
  * @param time Time since the start of the simulation (non-cosmo mode).
  * @param with_cosmology Are we running with cosmology?
  * @param props The properties of the black hole scheme.
+ * @param constants The physical constants in internal units.
  */
 __attribute__((always_inline)) INLINE static void black_holes_swallow_bpart(
     struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo,
     const double time, const int with_cosmology,
-    const struct black_holes_props* props) {
+    const struct black_holes_props* props, const struct phys_const* constants) {
 
   /* Get the current dynamical masses */
   const float bpi_dyn_mass = bpi->mass;
@@ -1246,11 +1247,13 @@ black_holes_store_potential_in_part(struct black_holes_part_data* p_data,
  * @param cosmo The current cosmological model.
  * @param p The #part that became a black hole.
  * @param xp The #xpart that became a black hole.
+ * @param ti_current the current time on the time-line.
  */
 INLINE static void black_holes_create_from_gas(
     struct bpart* bp, const struct black_holes_props* props,
     const struct phys_const* constants, const struct cosmology* cosmo,
-    const struct part* p, const struct xpart* xp) {
+    const struct part* p, const struct xpart* xp,
+    const integertime_t ti_current) {
 
   /* All the non-basic properties of the black hole have been zeroed
    * in the FOF code. We update them here.
diff --git a/src/black_holes/EAGLE/black_holes_iact.h b/src/black_holes/EAGLE/black_holes_iact.h
index b889628177eeb914ce156aa0b13e5582f974acb0..dfd336c88afb0382acb88db3d64002707bb789e1 100644
--- a/src/black_holes/EAGLE/black_holes_iact.h
+++ b/src/black_holes/EAGLE/black_holes_iact.h
@@ -93,11 +93,13 @@ runner_iact_nonsym_bh_gas_density(
      * re-calculate the sound speed using the fixed internal energy */
     const float u_EoS = entropy_floor_temperature(pj, cosmo, floor_props) *
                         bh_props->temp_to_u_factor;
-    const float u = hydro_get_drifted_comoving_internal_energy(pj);
+    const float u = hydro_get_drifted_physical_internal_energy(pj, cosmo);
+
     if (u < u_EoS * bh_props->fixed_T_above_EoS_factor &&
         u > bh_props->fixed_u_for_soundspeed) {
       cj = gas_soundspeed_from_internal_energy(
-          pj->rho, bh_props->fixed_u_for_soundspeed);
+               pj->rho, bh_props->fixed_u_for_soundspeed) /
+           cosmo->a_factor_sound_speed;
     }
   }
   bi->sound_speed_gas += mj * wi * cj;
diff --git a/src/black_holes/EAGLE/black_holes_io.h b/src/black_holes/EAGLE/black_holes_io.h
index 4c1b09d49bdeab2de885240750d4ec5f4a7de69b..cdff3735f6ffeea03f582882df650c635c8c9136 100644
--- a/src/black_holes/EAGLE/black_holes_io.h
+++ b/src/black_holes/EAGLE/black_holes_io.h
@@ -230,7 +230,9 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts,
   list[11] = io_make_output_field(
       "TotalAccretedMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts,
       total_accreted_mass,
-      "Total mass accreted onto the particles since its birth");
+      "Total mass accreted onto the main progenitor of the black holes since "
+      "birth. This does not include any mass accreted onto any merged black "
+      "holes.");
 
   list[12] = io_make_output_field(
       "CumulativeNumberOfSeeds", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
@@ -395,13 +397,14 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts,
       "NumberOfHeatingEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
       AGN_number_of_energy_injections,
       "Integer number of (thermal) energy injections the black hole has had "
-      "so far");
+      "so far. This counts each heated gas particle separately, and so can "
+      "increase by more than one during a single time step.");
 
   list[35] = io_make_output_field(
       "NumberOfAGNEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
       AGN_number_of_AGN_events,
       "Integer number of AGN events the black hole has had so far"
-      " (the number of times the BH did AGN feedback)");
+      " (the number of time steps in which the BH did AGN feedback).");
 
   if (with_cosmology) {
     list[36] = io_make_output_field(
@@ -417,7 +420,9 @@ INLINE static void black_holes_write_particles(const struct bpart* bparts,
 
   list[37] = io_make_output_field(
       "AccretionLimitedTimeSteps", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts,
-      dt_heat, "Accretion-limited time-steps of black holes.");
+      dt_heat,
+      "Accretion-limited time steps of black holes. The actual time step of "
+      "the particles may differ due to the minimum allowed value.");
 
   list[38] = io_make_output_field(
       "AGNTotalInjectedEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts,
diff --git a/src/black_holes/EAGLE/black_holes_parameters.h b/src/black_holes/EAGLE/black_holes_parameters.h
index 959af299a571acd8bdb8dd4fcac672d4c3fed51f..0b6d4d8ba5f035e02aad6910131c934bb9d328e7 100644
--- a/src/black_holes/EAGLE/black_holes_parameters.h
+++ b/src/black_holes/EAGLE/black_holes_parameters.h
@@ -20,7 +20,7 @@
 #define SWIFT_EAGLE_BLACK_HOLES_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /**
  * @file EAGLE/black_holes_parameters.h
diff --git a/src/black_holes/EAGLE/black_holes_properties.h b/src/black_holes/EAGLE/black_holes_properties.h
index 33e51714ab076c599d8673a6025cbc3fcd978285..47ea2d2fbd9dffc19b2b2ce0aff0eada913a7282 100644
--- a/src/black_holes/EAGLE/black_holes_properties.h
+++ b/src/black_holes/EAGLE/black_holes_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_EAGLE_BLACK_HOLES_PROPERTIES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "chemistry.h"
diff --git a/src/black_holes/SPIN_JET/black_holes.h b/src/black_holes/SPIN_JET/black_holes.h
new file mode 100644
index 0000000000000000000000000000000000000000..1609aaee4fa4744d52b3546baa6c55489543e3dd
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes.h
@@ -0,0 +1,1530 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ * Copyright (c) 2022 Filip Husko (filip.husko@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#ifndef SWIFT_SPIN_JET_BLACK_HOLES_H
+#define SWIFT_SPIN_JET_BLACK_HOLES_H
+
+/* Local includes */
+#include "black_holes_properties.h"
+#include "black_holes_spin.h"
+#include "black_holes_struct.h"
+#include "cooling_properties.h"
+#include "cosmology.h"
+#include "dimension.h"
+#include "gravity.h"
+#include "kernel_hydro.h"
+#include "minmax.h"
+#include "physical_constants.h"
+#include "random.h"
+#include "rays.h"
+
+/* Standard includes */
+#include <float.h>
+#include <math.h>
+
+/**
+ * @brief Computes the time-step of a given black hole particle.
+ *
+ * @param bp Pointer to the s-particle data.
+ * @param props The properties of the black hole scheme.
+ * @param constants The physical constants (in internal units).
+ */
+__attribute__((always_inline)) INLINE static float black_holes_compute_timestep(
+    const struct bpart* const bp, const struct black_holes_props* props,
+    const struct phys_const* constants, const struct cosmology* cosmo) {
+
+  /* Do something if and only if the accretion rate is non-zero */
+  if (bp->accretion_rate > 0.f) {
+
+    /* Gather some physical constants (all in internal units) */
+    const double c = constants->const_speed_light_c;
+
+    /* Compute instantaneous energy supply rate to the BH energy reservoir
+     * which is proportional to the BH mass accretion rate */
+    const double Energy_rate = bp->radiative_efficiency * props->epsilon_f *
+                               bp->accretion_rate * c * c;
+
+    /* Compute instantaneous jet energy supply rate to the BH jet reservoir
+     * which is proportional to the BH mass accretion rate */
+    const double Jet_rate =
+        bp->jet_efficiency * props->eps_f_jet * bp->accretion_rate * c * c;
+
+    /* Compute average heating energy in AGN feedback, both thermal and jet */
+
+    /* Average particle mass in BH's kernel */
+    const double mean_ngb_mass = bp->ngb_mass / ((double)bp->num_ngbs);
+    /* Without multiplying by mean_ngb_mass we'd get energy per unit mass */
+    const double E_heat =
+        props->AGN_delta_T_desired * props->temp_to_u_factor * mean_ngb_mass;
+    const double E_jet = 0.5 * mean_ngb_mass * bp->v_jet * bp->v_jet;
+
+    /* Compute average time between energy injections for the given accretion
+     * rate. The time is multiplied by the number of Ngbs to heat because
+     * if more particles are heated at once then the time between different
+     * AGN feedback events increases proportionally. */
+    const double dt_heat = E_heat * props->num_ngbs_to_heat / Energy_rate;
+
+    /* Similar for jets, with N_jet the target number of particles to kick. */
+    const double dt_jet = E_jet * props->N_jet / Jet_rate;
+
+    /* Pick the shorter of the two feedback time steps */
+    double bh_timestep = min(dt_jet, dt_heat);
+
+    /* See if the spin evolution time step is even smaller */
+    bh_timestep = min(bh_timestep, bp->dt_ang_mom);
+
+    /* The final timestep of the BH cannot be smaller than the miminum allowed
+     * time-step */
+    bh_timestep = max(bh_timestep, props->time_step_min);
+
+    return bh_timestep;
+  }
+  return FLT_MAX;
+}
+
+/**
+ * @brief Initialises the b-particles for the first time
+ *
+ * This function is called only once just after the ICs have been
+ * read in to do some conversions.
+ *
+ * @param bp The particle to act upon
+ * @param props The properties of the black holes model.
+ */
+__attribute__((always_inline)) INLINE static void black_holes_first_init_bpart(
+    struct bpart* bp, const struct black_holes_props* props) {
+
+  bp->time_bin = 0;
+  if (props->use_subgrid_mass_from_ics == 0) {
+    bp->subgrid_mass = bp->mass;
+
+  } else if (props->with_subgrid_mass_check && bp->subgrid_mass <= 0)
+    error(
+        "Black hole %lld has a subgrid mass of %f (internal units).\n"
+        "If this is because the ICs do not contain a 'SubgridMass' data "
+        "set, you should set the parameter "
+        "'EAGLEAGN:use_subgrid_mass_from_ics' to 0 to initialize the "
+        "black hole subgrid masses to the corresponding dynamical masses.\n"
+        "If the subgrid mass is intentionally set to this value, you can "
+        "disable this error by setting 'EAGLEAGN:with_subgrid_mass_check' "
+        "to 0.",
+        bp->id, bp->subgrid_mass);
+  bp->total_accreted_mass = 0.f;
+  bp->accretion_rate = 0.f;
+  bp->formation_time = -1.f;
+  bp->energy_reservoir = 0.f;
+  bp->cumulative_number_seeds = 1;
+  bp->number_of_mergers = 0;
+  bp->number_of_gas_swallows = 0;
+  bp->number_of_direct_gas_swallows = 0;
+  bp->number_of_repositions = 0;
+  bp->number_of_reposition_attempts = 0;
+  bp->number_of_time_steps = 0;
+  bp->last_high_Eddington_fraction_scale_factor = -1.f;
+  bp->last_minor_merger_time = -1.;
+  bp->last_major_merger_time = -1.;
+  bp->last_AGN_event_time = -1.;
+  bp->swallowed_angular_momentum[0] = 0.f;
+  bp->swallowed_angular_momentum[1] = 0.f;
+  bp->swallowed_angular_momentum[2] = 0.f;
+  bp->accreted_angular_momentum[0] = 0.f;
+  bp->accreted_angular_momentum[1] = 0.f;
+  bp->accreted_angular_momentum[2] = 0.f;
+  bp->dt_heat = 0.f;
+  bp->AGN_number_of_AGN_events = 0;
+  bp->AGN_number_of_energy_injections = 0;
+  bp->aspect_ratio = 0.01f;
+  bp->jet_efficiency = 0.1f;
+  bp->radiative_efficiency = 0.1f;
+  bp->accretion_disk_angle = 0.01f;
+  bp->accretion_mode = BH_thin_disc;
+  bp->eddington_fraction = 0.01f;
+  bp->jet_reservoir = 0.f;
+  bp->total_jet_energy = 0.f;
+  bp->dt_jet = 0.f;
+  bp->dt_ang_mom = 0.f;
+  bp->AGN_number_of_AGN_jet_events = 0;
+  bp->AGN_number_of_jet_injections = 0;
+  bp->group_mass = 0.f;
+
+  black_holes_mark_bpart_as_not_swallowed(&bp->merger_data);
+}
+
+/**
+ * @brief Prepares a b-particle for its interactions
+ *
+ * @param bp The particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void black_holes_init_bpart(
+    struct bpart* bp) {
+
+#ifdef DEBUG_INTERACTIONS_BLACK_HOLES
+  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
+    bp->ids_ngbs_density[i] = -1;
+  bp->num_ngb_density = 0;
+#endif
+
+  bp->density.wcount = 0.f;
+  bp->density.wcount_dh = 0.f;
+  bp->rho_gas = 0.f;
+  bp->sound_speed_gas = 0.f;
+  bp->velocity_gas[0] = 0.f;
+  bp->velocity_gas[1] = 0.f;
+  bp->velocity_gas[2] = 0.f;
+  bp->spec_angular_momentum_gas[0] = 0.f;
+  bp->spec_angular_momentum_gas[1] = 0.f;
+  bp->spec_angular_momentum_gas[2] = 0.f;
+  bp->curl_v_gas[0] = 0.f;
+  bp->curl_v_gas[1] = 0.f;
+  bp->curl_v_gas[2] = 0.f;
+  bp->velocity_dispersion_gas = 0.f;
+  bp->ngb_mass = 0.f;
+  bp->num_ngbs = 0;
+  bp->reposition.delta_x[0] = -FLT_MAX;
+  bp->reposition.delta_x[1] = -FLT_MAX;
+  bp->reposition.delta_x[2] = -FLT_MAX;
+  bp->reposition.min_potential = FLT_MAX;
+  bp->reposition.potential = FLT_MAX;
+  bp->accretion_rate = 0.f; /* Optionally accumulated ngb-by-ngb */
+  bp->f_visc = FLT_MAX;
+  bp->mass_at_start_of_step = bp->mass; /* bp->mass may grow in nibbling mode */
+
+  /* Reset the rays carried by this BH */
+  ray_init(bp->rays, spinjet_blackhole_number_of_rays);
+  ray_init(bp->rays_jet, spinjet_blackhole_number_of_rays);
+  ray_init(bp->rays_jet_pos, spinjet_blackhole_number_of_rays);
+}
+
+/**
+ * @brief Predict additional particle fields forward in time when drifting
+ *
+ * The fields do not get predicted but we move the BH to its new position
+ * if a new one was calculated in the repositioning loop.
+ *
+ * @param bp The particle
+ * @param dt_drift The drift time-step for positions.
+ */
+__attribute__((always_inline)) INLINE static void black_holes_predict_extra(
+    struct bpart* restrict bp, float dt_drift) {
+
+  /* Are we doing some repositioning? */
+  if (bp->reposition.min_potential != FLT_MAX) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+    if (bp->reposition.delta_x[0] == -FLT_MAX ||
+        bp->reposition.delta_x[1] == -FLT_MAX ||
+        bp->reposition.delta_x[2] == -FLT_MAX) {
+      error("Something went wrong with the new repositioning position");
+    }
+
+    const double dx = bp->reposition.delta_x[0];
+    const double dy = bp->reposition.delta_x[1];
+    const double dz = bp->reposition.delta_x[2];
+    const double d = sqrt(dx * dx + dy * dy + dz * dz);
+    if (d > 1.01 * kernel_gamma * bp->h)
+      error("Repositioning BH beyond the kernel support!");
+#endif
+
+    /* Move the black hole */
+    bp->x[0] += bp->reposition.delta_x[0];
+    bp->x[1] += bp->reposition.delta_x[1];
+    bp->x[2] += bp->reposition.delta_x[2];
+
+    /* Move its gravity properties as well */
+    bp->gpart->x[0] += bp->reposition.delta_x[0];
+    bp->gpart->x[1] += bp->reposition.delta_x[1];
+    bp->gpart->x[2] += bp->reposition.delta_x[2];
+
+    /* Store the delta position */
+    bp->x_diff[0] -= bp->reposition.delta_x[0];
+    bp->x_diff[1] -= bp->reposition.delta_x[1];
+    bp->x_diff[2] -= bp->reposition.delta_x[2];
+
+    /* Reset the reposition variables */
+    bp->reposition.delta_x[0] = -FLT_MAX;
+    bp->reposition.delta_x[1] = -FLT_MAX;
+    bp->reposition.delta_x[2] = -FLT_MAX;
+    bp->reposition.min_potential = FLT_MAX;
+
+    /* Count the jump */
+    bp->number_of_repositions++;
+  }
+}
+
+/**
+ * @brief Sets the values to be predicted in the drifts to their values at a
+ * kick time
+ *
+ * @param bp The particle.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_reset_predicted_values(struct bpart* bp) {}
+
+/**
+ * @brief Kick the additional variables
+ *
+ * @param bp The particle to act upon
+ * @param dt The time-step for this kick
+ */
+__attribute__((always_inline)) INLINE static void black_holes_kick_extra(
+    struct bpart* bp, float dt) {}
+
+/**
+ * @brief Finishes the calculation of density on black holes
+ *
+ * @param bp The particle to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void black_holes_end_density(
+    struct bpart* bp, const struct cosmology* cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = bp->h;
+  const float h_inv = 1.0f / h;                       /* 1/h */
+  const float h_inv_dim = pow_dimension(h_inv);       /* 1/h^d */
+  const float h_inv_dim_plus_one = h_inv_dim * h_inv; /* 1/h^(d+1) */
+
+  /* --- Finish the calculation by inserting the missing h factors --- */
+  bp->density.wcount *= h_inv_dim;
+  bp->density.wcount_dh *= h_inv_dim_plus_one;
+  bp->rho_gas *= h_inv_dim;
+  const float rho_inv = 1.f / bp->rho_gas;
+
+  /* For the following, we also have to undo the mass smoothing
+   * (N.B.: bp->velocity_gas is in BH frame, in internal units). */
+  bp->sound_speed_gas *= h_inv_dim * rho_inv;
+  bp->velocity_gas[0] *= h_inv_dim * rho_inv;
+  bp->velocity_gas[1] *= h_inv_dim * rho_inv;
+  bp->velocity_gas[2] *= h_inv_dim * rho_inv;
+  bp->velocity_dispersion_gas *= h_inv_dim * rho_inv;
+  bp->spec_angular_momentum_gas[0] *= h_inv_dim * rho_inv;
+  bp->spec_angular_momentum_gas[1] *= h_inv_dim * rho_inv;
+  bp->spec_angular_momentum_gas[2] *= h_inv_dim * rho_inv;
+
+  /* ... and for the curl, we also need to divide by an extra h factor */
+  bp->curl_v_gas[0] *= h_inv_dim_plus_one * rho_inv;
+  bp->curl_v_gas[1] *= h_inv_dim_plus_one * rho_inv;
+  bp->curl_v_gas[2] *= h_inv_dim_plus_one * rho_inv;
+
+  /* Calculate circular velocity at the smoothing radius from specific
+   * angular momentum (extra h_inv) */
+  bp->circular_velocity_gas[0] = bp->spec_angular_momentum_gas[0] * h_inv;
+  bp->circular_velocity_gas[1] = bp->spec_angular_momentum_gas[1] * h_inv;
+  bp->circular_velocity_gas[2] = bp->spec_angular_momentum_gas[2] * h_inv;
+
+  /* Calculate (actual) gas velocity dispersion. Currently, the variable
+   * 'velocity_dispersion_gas' holds <v^2> instead. */
+  const double speed_gas2 = bp->velocity_gas[0] * bp->velocity_gas[0] +
+                            bp->velocity_gas[1] * bp->velocity_gas[1] +
+                            bp->velocity_gas[2] * bp->velocity_gas[2];
+
+  bp->velocity_dispersion_gas -= speed_gas2;
+  bp->velocity_dispersion_gas = sqrt(fabs(bp->velocity_dispersion_gas));
+}
+
+/**
+ * @brief Sets all particle fields to sensible values when the #spart has 0
+ * ngbs.
+ *
+ * @param bp The particle to act upon
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_bpart_has_no_neighbours(struct bpart* bp,
+                                    const struct cosmology* cosmo) {
+
+  /* Some smoothing length multiples. */
+  const float h = bp->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 */
+  bp->density.wcount = kernel_root * h_inv_dim;
+  bp->density.wcount_dh = 0.f;
+
+  bp->velocity_gas[0] = FLT_MAX;
+  bp->velocity_gas[1] = FLT_MAX;
+  bp->velocity_gas[2] = FLT_MAX;
+
+  bp->velocity_dispersion_gas = FLT_MAX;
+
+  bp->curl_v_gas[0] = FLT_MAX;
+  bp->curl_v_gas[1] = FLT_MAX;
+  bp->curl_v_gas[2] = FLT_MAX;
+}
+
+/**
+ * @brief Return the current instantaneous accretion rate of the BH.
+ *
+ * @param bp the #bpart.
+ */
+__attribute__((always_inline)) INLINE static double
+black_holes_get_accretion_rate(const struct bpart* bp) {
+  return bp->accretion_rate;
+}
+
+/**
+ * @brief Return the total accreted gas mass of this BH.
+ *
+ * @param bp the #bpart.
+ */
+__attribute__((always_inline)) INLINE static double
+black_holes_get_accreted_mass(const struct bpart* bp) {
+  return bp->total_accreted_mass;
+}
+
+/**
+ * @brief Return the subgrid mass of this BH.
+ *
+ * @param bp the #bpart.
+ */
+__attribute__((always_inline)) INLINE static double
+black_holes_get_subgrid_mass(const struct bpart* bp) {
+  return bp->subgrid_mass;
+}
+
+/**
+ * @brief Update the properties of a black hole particles by swallowing
+ * a gas particle.
+ *
+ * @param bp The #bpart to update.
+ * @param p The #part that is swallowed.
+ * @param xp The #xpart that is swallowed.
+ * @param cosmo The current cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void black_holes_swallow_part(
+    struct bpart* bp, const struct part* p, const struct xpart* xp,
+    const struct cosmology* cosmo) {
+
+  /* Get the current dynamical masses */
+  const float gas_mass = hydro_get_mass(p);
+  const float BH_mass = bp->mass;
+
+  /* Increase the dynamical mass of the BH. */
+  bp->mass += gas_mass;
+  bp->gpart->mass += gas_mass;
+
+  /* Physical velocity difference between the particles */
+  const float dv[3] = {(bp->v[0] - p->v[0]) * cosmo->a_inv,
+                       (bp->v[1] - p->v[1]) * cosmo->a_inv,
+                       (bp->v[2] - p->v[2]) * cosmo->a_inv};
+
+  /* Physical distance between the particles */
+  const float dx[3] = {(bp->x[0] - p->x[0]) * cosmo->a,
+                       (bp->x[1] - p->x[1]) * cosmo->a,
+                       (bp->x[2] - p->x[2]) * cosmo->a};
+
+  /* Collect the swallowed angular momentum */
+  bp->swallowed_angular_momentum[0] +=
+      gas_mass * (dx[1] * dv[2] - dx[2] * dv[1]);
+  bp->swallowed_angular_momentum[1] +=
+      gas_mass * (dx[2] * dv[0] - dx[0] * dv[2]);
+  bp->swallowed_angular_momentum[2] +=
+      gas_mass * (dx[0] * dv[1] - dx[1] * dv[0]);
+
+  /* Update the BH momentum */
+  const float BH_mom[3] = {BH_mass * bp->v[0] + gas_mass * p->v[0],
+                           BH_mass * bp->v[1] + gas_mass * p->v[1],
+                           BH_mass * bp->v[2] + gas_mass * p->v[2]};
+
+  bp->v[0] = BH_mom[0] / bp->mass;
+  bp->v[1] = BH_mom[1] / bp->mass;
+  bp->v[2] = BH_mom[2] / bp->mass;
+  bp->gpart->v_full[0] = bp->v[0];
+  bp->gpart->v_full[1] = bp->v[1];
+  bp->gpart->v_full[2] = bp->v[2];
+
+  const float dr = sqrt(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]);
+  message(
+      "BH %lld swallowing gas particle %lld "
+      "(Delta_v = [%f, %f, %f] U_V, "
+      "Delta_x = [%f, %f, %f] U_L, "
+      "Delta_v_rad = %f)",
+      bp->id, p->id, -dv[0], -dv[1], -dv[2], -dx[0], -dx[1], -dx[2],
+      (dv[0] * dx[0] + dv[1] * dx[1] + dv[2] * dx[2]) / dr);
+
+  /* Update the BH metal masses */
+  struct chemistry_bpart_data* bp_chem = &bp->chemistry_data;
+  const struct chemistry_part_data* p_chem = &p->chemistry_data;
+  chemistry_add_part_to_bpart(bp_chem, p_chem, gas_mass);
+
+  /* This BH swallowed a gas particle */
+  bp->number_of_gas_swallows++;
+  bp->number_of_direct_gas_swallows++;
+
+  /* This BH lost a neighbour */
+  bp->num_ngbs--;
+  bp->ngb_mass -= gas_mass;
+
+  /* The ray(s) should not point to the no-longer existing particle */
+  ray_reset_part_id(bp->rays, spinjet_blackhole_number_of_rays, p->id);
+  ray_reset_part_id(bp->rays_jet, spinjet_blackhole_number_of_rays, p->id);
+  ray_reset_part_id(bp->rays_jet_pos, spinjet_blackhole_number_of_rays, p->id);
+}
+
+/**
+ * @brief Update the properties of a black hole particles by swallowing
+ * a BH particle.
+ *
+ * @param bpi The #bpart to update.
+ * @param bpj The #bpart that is swallowed.
+ * @param cosmo The current cosmological model.
+ * @param time Time since the start of the simulation (non-cosmo mode).
+ * @param with_cosmology Are we running with cosmology?
+ * @param props The properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static void black_holes_swallow_bpart(
+    struct bpart* bpi, const struct bpart* bpj, const struct cosmology* cosmo,
+    const double time, const int with_cosmology,
+    const struct black_holes_props* props, const struct phys_const* constants) {
+
+  /* Get the current dynamical masses */
+  const float bpi_dyn_mass = bpi->mass;
+  const float bpj_dyn_mass = bpj->mass;
+
+  /* Is this merger ratio above the threshold for recording? */
+  const double merger_ratio = bpj->subgrid_mass / bpi->subgrid_mass;
+  if (merger_ratio > props->major_merger_threshold) {
+    if (with_cosmology) {
+      bpi->last_major_merger_scale_factor = cosmo->a;
+    } else {
+      bpi->last_major_merger_time = time;
+    }
+  } else if (merger_ratio > props->minor_merger_threshold) {
+    if (with_cosmology) {
+      bpi->last_minor_merger_scale_factor = cosmo->a;
+    } else {
+      bpi->last_minor_merger_time = time;
+    }
+  }
+
+  /* Evolve the black hole spin according to Rezzolla et al. (2008) fit */
+  merger_spin_evolve(bpi, bpj, constants);
+
+  /* Increase the masses of the BH. */
+  bpi->mass += bpj->mass;
+  bpi->gpart->mass += bpj->mass;
+  bpi->subgrid_mass += bpj->subgrid_mass;
+
+  /* We need to see if the new spin is positive or negative, by implementing
+     King et al. (2005) condition of (counter-)alignment. */
+  const float gas_spec_ang_mom_norm = sqrtf(
+      bpi->spec_angular_momentum_gas[0] * bpi->spec_angular_momentum_gas[0] +
+      bpi->spec_angular_momentum_gas[1] * bpi->spec_angular_momentum_gas[1] +
+      bpi->spec_angular_momentum_gas[2] * bpi->spec_angular_momentum_gas[2]);
+
+  float dot_product = -1.;
+  if (gas_spec_ang_mom_norm > 0.) {
+    dot_product = 1. / gas_spec_ang_mom_norm *
+                  (bpi->spec_angular_momentum_gas[0] *
+                       bpi->angular_momentum_direction[0] +
+                   bpi->spec_angular_momentum_gas[1] *
+                       bpi->angular_momentum_direction[1] +
+                   bpi->spec_angular_momentum_gas[2] *
+                       bpi->angular_momentum_direction[2]);
+  } else {
+    dot_product = 0.;
+  }
+
+  if (j_BH(bpi, constants) * dot_product <
+      -0.5 * j_warp(bpi, constants, props)) {
+    bpi->spin = -1. * fabsf(bpi->spin);
+  } else {
+    bpi->spin = fabsf(bpi->spin);
+  }
+
+  /* Update various quantities with new spin */
+  decide_mode(bpi, props);
+  bpi->aspect_ratio = aspect_ratio(bpi, constants, props);
+  bpi->accretion_disk_angle = dot_product;
+  bpi->radiative_efficiency = rad_efficiency(bpi, props);
+  bpi->jet_efficiency = jet_efficiency(bpi, props);
+
+  /* Collect the swallowed angular momentum */
+  bpi->swallowed_angular_momentum[0] += bpj->swallowed_angular_momentum[0];
+  bpi->swallowed_angular_momentum[1] += bpj->swallowed_angular_momentum[1];
+  bpi->swallowed_angular_momentum[2] += bpj->swallowed_angular_momentum[2];
+
+  /* Update the BH momentum */
+  const float BH_mom[3] = {bpi_dyn_mass * bpi->v[0] + bpj_dyn_mass * bpj->v[0],
+                           bpi_dyn_mass * bpi->v[1] + bpj_dyn_mass * bpj->v[1],
+                           bpi_dyn_mass * bpi->v[2] + bpj_dyn_mass * bpj->v[2]};
+
+  bpi->v[0] = BH_mom[0] / bpi->mass;
+  bpi->v[1] = BH_mom[1] / bpi->mass;
+  bpi->v[2] = BH_mom[2] / bpi->mass;
+  bpi->gpart->v_full[0] = bpi->v[0];
+  bpi->gpart->v_full[1] = bpi->v[1];
+  bpi->gpart->v_full[2] = bpi->v[2];
+
+  /* Update the BH metal masses */
+  struct chemistry_bpart_data* bpi_chem = &bpi->chemistry_data;
+  const struct chemistry_bpart_data* bpj_chem = &bpj->chemistry_data;
+  chemistry_add_bpart_to_bpart(bpi_chem, bpj_chem);
+
+  /* Update the energy reservoir */
+  bpi->energy_reservoir += bpj->energy_reservoir;
+
+  /* Update the jet reservoir */
+  bpi->jet_reservoir += bpj->jet_reservoir;
+
+  /* Add up all the BH seeds */
+  bpi->cumulative_number_seeds += bpj->cumulative_number_seeds;
+
+  /* Add up all the gas particles we swallowed */
+  bpi->number_of_gas_swallows += bpj->number_of_gas_swallows;
+
+  /* Add the subgrid angular momentum that we swallowed */
+  bpi->accreted_angular_momentum[0] += bpj->accreted_angular_momentum[0];
+  bpi->accreted_angular_momentum[1] += bpj->accreted_angular_momentum[1];
+  bpi->accreted_angular_momentum[2] += bpj->accreted_angular_momentum[2];
+
+  /* We had another merger */
+  bpi->number_of_mergers++;
+}
+
+/**
+ * @brief Compute the accretion rate of the black hole and all the quantites
+ * required for the feedback loop.
+ *
+ * @param bp The black hole particle.
+ * @param props The properties of the black hole scheme.
+ * @param constants The physical constants (in internal units).
+ * @param cosmo The cosmological model.
+ * @param cooling Properties of the cooling model.
+ * @param floor_props Properties of the entropy fllor.
+ * @param time Time since the start of the simulation (non-cosmo mode).
+ * @param with_cosmology Are we running with cosmology?
+ * @param dt The time-step size (in physical internal units).
+ * @param ti_begin Integer time value at the beginning of timestep
+ */
+__attribute__((always_inline)) INLINE static void black_holes_prepare_feedback(
+    struct bpart* restrict bp, const struct black_holes_props* props,
+    const struct phys_const* constants, const struct cosmology* cosmo,
+    const struct cooling_function_data* cooling,
+    const struct entropy_floor_properties* floor_props, const double time,
+    const int with_cosmology, const double dt, const integertime_t ti_begin) {
+
+  /* Record that the black hole has another active time step */
+  bp->number_of_time_steps++;
+
+  if (dt == 0. || bp->rho_gas == 0.) return;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (bp->num_ngbs <= 0) {
+    error(
+        "The number of BH neighbours is %d, despite the fact that the gas "
+        " density in the BH kernel is non-zero.",
+        bp->num_ngbs);
+  }
+#endif
+
+  /* Gather some physical constants (all in internal units) */
+  const double G = constants->const_newton_G;
+  const double c = constants->const_speed_light_c;
+  const double proton_mass = constants->const_proton_mass;
+  const double sigma_Thomson = constants->const_thomson_cross_section;
+
+  /* (Subgrid) mass of the BH (internal units) */
+  const double BH_mass = bp->subgrid_mass;
+
+  /* Convert the quantities we gathered to physical frame (all internal units).
+   * Note: for the velocities this means peculiar velocities */
+  const double gas_c_phys = bp->sound_speed_gas * cosmo->a_factor_sound_speed;
+  const double gas_c_phys2 = gas_c_phys * gas_c_phys;
+  const double gas_v_circular[3] = {
+      bp->circular_velocity_gas[0] * cosmo->a_inv,
+      bp->circular_velocity_gas[1] * cosmo->a_inv,
+      bp->circular_velocity_gas[2] * cosmo->a_inv};
+
+  /* Norm of the circular velocity of the gas around the BH */
+  const double tangential_velocity2 = gas_v_circular[0] * gas_v_circular[0] +
+                                      gas_v_circular[1] * gas_v_circular[1] +
+                                      gas_v_circular[2] * gas_v_circular[2];
+  const double tangential_velocity = sqrt(tangential_velocity2);
+
+  /* We can now compute the accretion rate (internal units) */
+  double accr_rate;
+
+  if (props->use_multi_phase_bondi) {
+
+    /* In this case, we are in 'multi-phase-Bondi' mode -- otherwise,
+     * the accretion_rate is still zero (was initialised to this) */
+    const float hi_inv = 1.f / bp->h;
+    const float hi_inv_dim = pow_dimension(hi_inv); /* 1/h^d */
+    accr_rate = bp->accretion_rate *
+                (4. * M_PI * G * G * BH_mass * BH_mass * hi_inv_dim);
+  } else {
+
+    /* Standard approach: compute accretion rate for all gas simultaneously.
+     *
+     * Convert the quantities we gathered to physical frame (all internal
+     * units). Note: velocities are already in black hole frame. */
+    const double gas_rho_phys = bp->rho_gas * cosmo->a3_inv;
+    const double gas_v_phys[3] = {bp->velocity_gas[0] * cosmo->a_inv,
+                                  bp->velocity_gas[1] * cosmo->a_inv,
+                                  bp->velocity_gas[2] * cosmo->a_inv};
+
+    const double gas_v_norm2 = gas_v_phys[0] * gas_v_phys[0] +
+                               gas_v_phys[1] * gas_v_phys[1] +
+                               gas_v_phys[2] * gas_v_phys[2];
+
+    /* In the Bondi-Hoyle-Lyttleton formula, the bulk flow of gas is
+     * added to the sound speed in quadrature. Treated separately (below)
+     * in the Krumholz et al. (2006) prescription */
+    const double denominator2 =
+        props->use_krumholz ? gas_c_phys2 : gas_v_norm2 + gas_c_phys2;
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Make sure that the denominator is strictly positive */
+    if (denominator2 <= 0)
+      error(
+          "Invalid denominator for BH particle %lld in Bondi rate "
+          "calculation.",
+          bp->id);
+#endif
+    const double denominator_inv = 1. / sqrt(denominator2);
+    accr_rate = 4. * M_PI * G * G * BH_mass * BH_mass * gas_rho_phys *
+                denominator_inv * denominator_inv * denominator_inv;
+
+    if (props->use_krumholz) {
+
+      /* Compute the additional correction factors from Krumholz+06,
+       * accounting for bulk flow and turbulence of ambient gas. */
+      const double lambda = 1.1;
+      const double gas_v_dispersion =
+          bp->velocity_dispersion_gas * cosmo->a_inv;
+      const double mach_turb = gas_v_dispersion / gas_c_phys;
+      const double mach_bulk = sqrt(gas_v_norm2) / gas_c_phys;
+      const double mach2 = mach_turb * mach_turb + mach_bulk * mach_bulk;
+      const double m1 = 1. + mach2;
+      const double mach_factor =
+          sqrt((lambda * lambda + mach2) / (m1 * m1 * m1 * m1));
+      accr_rate *= mach_factor;
+    }
+
+    if (props->with_krumholz_vorticity) {
+
+      /* Change the accretion rate to equation (3) of Krumholz et al. (2006)
+       * by adding a vorticity-dependent term in inverse quadrature */
+
+      /* Convert curl to vorticity in physical units */
+      const double gas_curlv_phys[3] = {bp->curl_v_gas[0] * cosmo->a2_inv,
+                                        bp->curl_v_gas[1] * cosmo->a2_inv,
+                                        bp->curl_v_gas[2] * cosmo->a2_inv};
+      const double gas_vorticity = sqrt(gas_curlv_phys[0] * gas_curlv_phys[0] +
+                                        gas_curlv_phys[1] * gas_curlv_phys[1] +
+                                        gas_curlv_phys[2] * gas_curlv_phys[2]);
+
+      const double Bondi_radius = G * BH_mass / gas_c_phys2;
+      const double omega_star = gas_vorticity * Bondi_radius / gas_c_phys;
+      const double f_omega_star = 1.0 / (1.0 + pow(omega_star, 0.9));
+      const double mdot_omega = 4. * M_PI * gas_rho_phys * G * G * BH_mass *
+                                BH_mass * denominator_inv * denominator_inv *
+                                denominator_inv * 0.34 * f_omega_star;
+
+      const double accr_rate_inv = 1. / accr_rate;
+      const double mdot_omega_inv = 1. / mdot_omega;
+      accr_rate = 1. / sqrt(accr_rate_inv * accr_rate_inv +
+                            mdot_omega_inv * mdot_omega_inv);
+    } /* ends calculation of vorticity addition to Krumholz prescription */
+
+  } /* ends section without multi-phase accretion */
+
+  /* Compute the boost factor from Booth, Schaye (2009) */
+  if (props->with_boost_factor) {
+    const double gas_rho_phys = bp->rho_gas * cosmo->a3_inv;
+    const double n_H = gas_rho_phys * 0.75 / proton_mass;
+    const double boost_ratio = n_H / props->boost_n_h_star;
+    const double boost_factor =
+        max(pow(boost_ratio, props->boost_beta), props->boost_alpha);
+    accr_rate *= boost_factor;
+  }
+  /* Compute the reduction factor from Rosas-Guevara et al. (2015) */
+  if (props->with_angmom_limiter) {
+    const double Bondi_radius = G * BH_mass / gas_c_phys2;
+    const double Bondi_time = Bondi_radius / gas_c_phys;
+    const double r_times_v_tang = Bondi_radius * tangential_velocity;
+    const double r_times_v_tang_3 =
+        r_times_v_tang * r_times_v_tang * r_times_v_tang;
+    const double viscous_time =
+        2. * M_PI * r_times_v_tang_3 /
+        (1e-6 * props->alpha_visc * G * G * BH_mass * BH_mass);
+
+    const double f_visc = min(Bondi_time / viscous_time, 1.);
+    bp->f_visc = f_visc;
+
+    /* Limit the accretion rate by the Bondi-to-viscous time ratio */
+    accr_rate *= f_visc;
+  } else {
+    bp->f_visc = 1.0;
+  }
+
+  /* Compute the Eddington rate (internal units) */
+  const double Eddington_rate =
+      4. * M_PI * G * BH_mass * proton_mass /
+      (props->radiative_efficiency * c * sigma_Thomson);
+
+  /* Should we record this time as the most recent high accretion rate? */
+  if (accr_rate > props->f_Edd_recording * Eddington_rate) {
+    if (with_cosmology) {
+      bp->last_high_Eddington_fraction_scale_factor = cosmo->a;
+    } else {
+      bp->last_high_Eddington_fraction_time = time;
+    }
+  }
+
+  /* Limit the accretion rate to a fraction of the Eddington rate */
+  accr_rate = min(accr_rate, props->f_Edd * Eddington_rate);
+  bp->accretion_rate = accr_rate;
+  bp->eddington_fraction = accr_rate / Eddington_rate;
+
+  /* Define feedback-related quantities that we will update and need later on */
+  double luminosity = 0.;
+  double jet_power = 0.;
+
+  /* Check whether we are including ADIOS winds in the thick disk */
+  if ((bp->accretion_mode == BH_thick_disc) &&
+      (props->include_ADIOS_suppression)) {
+    const double Bondi_R = G * BH_mass / gas_c_phys2;
+    const float ADIOS_suppression = powf(
+        Bondi_R / (props->ADIOS_R_in * R_gravitational(BH_mass, constants)),
+        props->ADIOS_s);
+    accr_rate = accr_rate / ADIOS_suppression;
+    bp->accretion_rate = accr_rate;
+    bp->eddington_fraction = accr_rate / Eddington_rate;
+  }
+
+  /* How much mass will be consumed over this time step? */
+  double delta_m_0 = bp->accretion_rate * dt;
+
+  /* Norm of the specific angular momentum, will be needed in a moment. */
+  float spec_ang_mom_norm = sqrtf(max(
+      0.,
+      bp->spec_angular_momentum_gas[0] * bp->spec_angular_momentum_gas[0] +
+          bp->spec_angular_momentum_gas[1] * bp->spec_angular_momentum_gas[1] +
+          bp->spec_angular_momentum_gas[2] * bp->spec_angular_momentum_gas[2]));
+
+  /* Cosine of the angle between the spin vector and the specific angular
+     momentum vector of gas around the BH. */
+  float dot_product = -1.;
+  if (spec_ang_mom_norm > 0.) {
+    dot_product =
+        1. / spec_ang_mom_norm *
+        (bp->spec_angular_momentum_gas[0] * bp->angular_momentum_direction[0] +
+         bp->spec_angular_momentum_gas[1] * bp->angular_momentum_direction[1] +
+         bp->spec_angular_momentum_gas[2] * bp->angular_momentum_direction[2]);
+  } else {
+    dot_product = 0.;
+  }
+
+  /* Decide if accretion is prograde (spin positive) or retrograde
+     (spin negative) based on condition from King et al. (2005) */
+  if ((j_BH(bp, constants) * dot_product <
+       -0.5 * j_warp(bp, constants, props)) &&
+      (fabsf(bp->spin) > 0.001)) {
+    bp->spin = -1. * fabsf(bp->spin);
+  } else {
+    bp->spin = fabsf(bp->spin);
+  }
+
+  /* Calculate how many warp increments the BH will swallow over this time
+     step */
+  double n_i = 0.;
+  if (bp->accretion_rate > 0.) {
+    n_i = delta_m_0 / m_warp(bp, constants, props);
+  }
+
+  /* Update the angular momentum vector of the BH based on how many
+     increments of warp angular momenta have been consumed. */
+  if (spec_ang_mom_norm > 0.) {
+
+    /* If spin is at its floor value of 0.01, we immediately redirect
+       the spin in the direction of the accreting gas */
+    if (fabsf(bp->spin) <= 0.001) {
+      bp->angular_momentum_direction[0] =
+          bp->spec_angular_momentum_gas[0] / spec_ang_mom_norm;
+      bp->angular_momentum_direction[1] =
+          bp->spec_angular_momentum_gas[1] / spec_ang_mom_norm;
+      bp->angular_momentum_direction[2] =
+          bp->spec_angular_momentum_gas[2] / spec_ang_mom_norm;
+    } else {
+      const double ang_mom_total[3] = {
+          bp->angular_momentum_direction[0] * j_BH(bp, constants) +
+              n_i * bp->spec_angular_momentum_gas[0] / spec_ang_mom_norm *
+                  j_warp(bp, constants, props),
+          bp->angular_momentum_direction[1] * j_BH(bp, constants) +
+              n_i * bp->spec_angular_momentum_gas[1] / spec_ang_mom_norm *
+                  j_warp(bp, constants, props),
+          bp->angular_momentum_direction[2] * j_BH(bp, constants) +
+              n_i * bp->spec_angular_momentum_gas[2] / spec_ang_mom_norm *
+                  j_warp(bp, constants, props)};
+
+      /* Modulus of the new J_BH */
+      const double modulus = sqrt(ang_mom_total[0] * ang_mom_total[0] +
+                                  ang_mom_total[1] * ang_mom_total[1] +
+                                  ang_mom_total[2] * ang_mom_total[2]);
+
+      if (modulus > 0.) {
+        bp->angular_momentum_direction[0] = ang_mom_total[0] / modulus;
+        bp->angular_momentum_direction[1] = ang_mom_total[1] / modulus;
+        bp->angular_momentum_direction[2] = ang_mom_total[2] / modulus;
+      }
+    }
+  }
+
+  /* Check if we are fixing the jet along the z-axis. */
+  if (props->fix_jet_efficiency) {
+    bp->angular_momentum_direction[0] = 0.;
+    bp->angular_momentum_direction[1] = 0.;
+    bp->angular_momentum_direction[2] = 1;
+  }
+
+  /* The amount of mass the BH is actually swallowing, including the
+     effects of efficiencies. */
+  const double delta_m_real =
+      delta_m_0 * (1. - rad_efficiency(bp, props) - jet_efficiency(bp, props));
+
+  /* Increase the reservoir*/
+  bp->jet_reservoir +=
+      delta_m_0 * c * c * props->eps_f_jet * jet_efficiency(bp, props);
+  bp->energy_reservoir +=
+      delta_m_0 * c * c * props->epsilon_f * rad_efficiency(bp, props);
+
+  float spin_final = -1.;
+  /* Calculate the change in the BH spin */
+  if (bp->subgrid_mass > 0.) {
+    spin_final = bp->spin + delta_m_0 / bp->subgrid_mass *
+                                da_dln_mbh_0(bp, constants, props);
+  } else {
+    error(
+        "Black hole with id %lld tried to evolve spin with zero "
+        "(or less) subgrid mass. ",
+        bp->id);
+  }
+
+  /* Make sure that the spin does not shoot above 1 or below -1. Physically
+     this wouldn't happen because the spinup function goes to 0 at a=1, but
+     numerically it may happen due to finite increments. If this happens,
+     many spin-related quantities begin to diverge. We also want to avoid
+     spin equal to zero, or very close to it. The black hole time steps
+     become shorter and shorter as the BH approaches spin 0, so we simply
+     'jump' through 0 instead, choosing a small value of 0.001 (in magnitude)
+     as a floor for the spin. The spin will always jump to +0.001 since the
+     spinup function will always make spin go from negative to positive at
+     these small values. */
+  if (spin_final > 0.998) {
+    spin_final = 0.998;
+  } else if (spin_final < -0.998) {
+    spin_final = -0.998;
+  } else if (fabsf(spin_final) < 0.001) {
+    spin_final = 0.001;
+  }
+
+  /* Update the spin and mass. */
+  bp->spin = spin_final;
+  bp->subgrid_mass = bp->subgrid_mass + delta_m_real;
+  bp->total_accreted_mass = bp->total_accreted_mass + delta_m_real;
+
+  if (bp->subgrid_mass < 0.) {
+    warning(
+        "Black hole %lld has reached a negative mass (%f) due"
+        " to jet spindown.",
+        bp->id, bp->subgrid_mass);
+    bp->subgrid_mass = props->subgrid_seed_mass;
+  }
+
+  /* Update other quantities. */
+  bp->accretion_disk_angle = dot_product;
+  bp->aspect_ratio = aspect_ratio(bp, constants, props);
+  if (props->fix_radiative_efficiency) {
+    bp->radiative_efficiency = props->radiative_efficiency;
+  } else {
+    bp->radiative_efficiency = rad_efficiency(bp, props);
+  }
+  if (props->fix_jet_efficiency) {
+    bp->jet_efficiency = props->jet_efficiency;
+  } else {
+    bp->jet_efficiency = jet_efficiency(bp, props);
+  }
+
+  /* Final jet power at the end of the step */
+  jet_power = bp->jet_efficiency * accr_rate * c * c;
+
+  /* Final luminosity at the end of the step */
+  luminosity = bp->radiative_efficiency * accr_rate * c * c;
+
+  /* Increase the subgrid angular momentum according to what we accreted
+   * (already in physical units, a factors from velocity and radius cancel) */
+  bp->accreted_angular_momentum[0] +=
+      bp->spec_angular_momentum_gas[0] * delta_m_real;
+  bp->accreted_angular_momentum[1] +=
+      bp->spec_angular_momentum_gas[1] * delta_m_real;
+  bp->accreted_angular_momentum[2] +=
+      bp->spec_angular_momentum_gas[2] * delta_m_real;
+
+  if (props->use_nibbling && bp->subgrid_mass < bp->mass) {
+    /* In this case, the BH is still accreting from its (assumed) subgrid gas
+     * mass reservoir left over when it was formed. There is some loss in this
+     * due to radiative and jet losses, so we must decrease the particle mass
+     * in proprtion to its current accretion rate. We do not account for this
+     * in the swallowing approach, however. */
+    bp->mass -=
+        (bp->radiative_efficiency + bp->jet_efficiency) * accr_rate * dt;
+    if (bp->mass < 0)
+      error(
+          "Black hole %lld has reached a negative mass (%f). This is "
+          "not a great situation, so I am stopping.",
+          bp->id, bp->mass);
+  }
+
+  /* Below we compute energy required to have a feedback event(s)
+   * Note that we have subtracted the particles we swallowed from the ngb_mass
+   * and num_ngbs accumulators. */
+
+  /* Mean gas particle mass in the BH's kernel */
+  const double mean_ngb_mass = bp->ngb_mass / ((double)bp->num_ngbs);
+  /* Energy per unit mass corresponding to the temperature jump delta_T */
+  double delta_u = props->AGN_delta_T_desired * props->temp_to_u_factor;
+  /* Number of energy injections at this time-step (will be computed below) */
+  int number_of_energy_injections;
+  /* Average total energy needed to heat the target number of Ngbs */
+  const double E_feedback_event =
+      delta_u * mean_ngb_mass * props->num_ngbs_to_heat;
+
+  /* Compute and store BH heating-limited time-step */
+  if (luminosity > 0.) {
+    const float dt_acc = delta_u * mean_ngb_mass * props->num_ngbs_to_heat /
+                         (luminosity * props->epsilon_f);
+    bp->dt_heat = max(dt_acc, props->time_step_min);
+  } else {
+    bp->dt_heat = FLT_MAX;
+  }
+
+  /* Compute and store BH jet-limited time-step */
+  bp->v_jet = black_hole_feedback_dv_jet(bp, props, cosmo, constants);
+  const float V_jet = bp->v_jet;
+  if (jet_power > 0.) {
+    const float dt_acc2 =
+        0.5 * mean_ngb_mass * V_jet * V_jet / (jet_power * props->eps_f_jet);
+    bp->dt_jet = max(dt_acc2, props->time_step_min);
+  } else {
+    bp->dt_jet = FLT_MAX;
+  }
+
+  /* Are we doing some feedback? */
+  if (bp->energy_reservoir > E_feedback_event) {
+
+    /* Probability of heating. Relevant only for the stochastic model. */
+    double prob = bp->energy_reservoir / (delta_u * bp->ngb_mass);
+
+    /* Compute the number of energy injections based on probability if and
+     * only if we are using the stochastic (i.e. not deterministic) model
+     * and the probability prob < 1. */
+    if (prob < 1. && !props->AGN_deterministic) {
+
+      /* Initialise counter of energy injections */
+      number_of_energy_injections = 0;
+
+      /* How many AGN energy injections will we get? */
+      for (int i = 0; i < bp->num_ngbs; i++) {
+        const double rand = random_unit_interval_part_ID_and_index(
+            bp->id, i, ti_begin, random_number_BH_feedback);
+        /* Increase the counter if we are lucky */
+        if (rand < prob) number_of_energy_injections++;
+      }
+    }
+    /* Deterministic model or prob >= 1. */
+    else {
+      /* We want to use up all energy available in the reservoir. Therefore,
+       * number_of_energy_injections is > or = props->num_ngbs_to_heat */
+      number_of_energy_injections =
+          (int)(bp->energy_reservoir / (delta_u * mean_ngb_mass));
+    }
+
+    /* Maximum number of energy injections allowed */
+    const int N_energy_injections_allowed =
+        min(spinjet_blackhole_number_of_rays, bp->num_ngbs);
+
+    /* If there are more energy-injection events than min(the number of Ngbs in
+     * the kernel, maximum number of rays) then lower the number of events &
+     * proportionally increase the energy per event */
+    if (number_of_energy_injections > N_energy_injections_allowed) {
+
+      /* Increase the thermal energy per event */
+      const double alpha_thermal = (double)number_of_energy_injections /
+                                   (double)N_energy_injections_allowed;
+      delta_u *= alpha_thermal;
+      /* Lower the maximum number of events to the max allowed value */
+      number_of_energy_injections = N_energy_injections_allowed;
+    }
+
+    /* Compute how much energy will be deposited onto the gas */
+    /* Note that it will in general be different from E_feedback_event if
+     * gas particles are of different mass. */
+    double Energy_deposited = 0.0;
+
+    /* Count the number of unsuccessful energy injections (e.g., if the particle
+     * that the BH wants to heat has been swallowed and thus no longer exists)
+     */
+    int N_unsuccessful_energy_injections = 0;
+
+    for (int i = 0; i < number_of_energy_injections; i++) {
+      /* If the gas particle that the BH wants to heat has just been swallowed
+       * by the same BH, increment the counter of unsuccessful injections. If
+       * the particle has not been swallowed by the BH, increase the energy that
+       * will later be subtracted from the BH's energy reservoir. */
+      if (bp->rays[i].id_min_length != -1)
+        Energy_deposited += delta_u * bp->rays[i].mass;
+      else
+        N_unsuccessful_energy_injections++;
+    }
+
+    /* Store all of this in the black hole for delivery onto the gas. */
+    bp->to_distribute.AGN_delta_u = delta_u;
+    bp->to_distribute.AGN_number_of_energy_injections =
+        number_of_energy_injections;
+
+    /* Subtract the deposited energy from the BH energy reservoir. Note
+     * that in the stochastic case, the resulting value might be negative.
+     * This happens when (due to the probabilistic nature of the model) the
+     * BH injects more energy than it actually has in the reservoir. */
+    bp->energy_reservoir -= Energy_deposited;
+
+    /* Total number successful energy injections at this time-step. In each
+     * energy injection, a certain gas particle from the BH's kernel gets
+     * heated. (successful = the particle(s) that is going to get heated by
+     * this BH has not been swallowed by the same BH). */
+    const int N_successful_energy_injections =
+        number_of_energy_injections - N_unsuccessful_energy_injections;
+
+    /* Increase the number of energy injections the black hole has heated so
+     * far. Note that in the isotropic model, a gas particle may receive AGN
+     * energy several times at the same time-step. In this case, the number of
+     * particles heated at this time-step for this BH will be smaller than the
+     * total number of energy injections for this BH. */
+    bp->AGN_number_of_energy_injections += N_successful_energy_injections;
+
+    /* Increase the number of AGN events the black hole has had so far.
+     * If the BH does feedback, the number of AGN events is incremented by one
+     */
+    bp->AGN_number_of_AGN_events += N_successful_energy_injections > 0;
+
+    /* Update the total (cumulative) energy used for gas heating in AGN feedback
+     * by this BH */
+    bp->AGN_cumulative_energy += Energy_deposited;
+
+    /* Store the time/scale factor when the BH last did AGN feedback */
+    if (N_successful_energy_injections) {
+      if (with_cosmology) {
+        bp->last_AGN_event_scale_factor = cosmo->a;
+      } else {
+        bp->last_AGN_event_time = time;
+      }
+    }
+  } else {
+
+    /* Flag that we don't want to heat anyone */
+    bp->to_distribute.AGN_delta_u = 0.f;
+    bp->to_distribute.AGN_number_of_energy_injections = 0;
+  }
+
+  /* Calculate energy required for a jet event (with N_jet particles kicked) */
+  const double E_jet_event = 0.5 * V_jet * V_jet * mean_ngb_mass * props->N_jet;
+
+  /* Are we doing some feedback? */
+  if (bp->jet_reservoir > E_jet_event) {
+
+    int number_of_jet_injections;
+    double delta_u_jet = 0.5 * V_jet * V_jet;
+
+    number_of_jet_injections =
+        (int)(bp->jet_reservoir / (0.5 * V_jet * V_jet * mean_ngb_mass));
+
+    /* Limit the number of injections to 2 x N_jet. The jet feedback time
+       steps will try to target so that the BH ejects N_jet particles every
+       time step, but excesses may build up. Allowing for more than N_jet
+       kicks lets these excesses get released.*/
+    if (number_of_jet_injections > 2 * props->N_jet) {
+      number_of_jet_injections = 2 * props->N_jet;
+    } else {
+      number_of_jet_injections = props->N_jet;
+    }
+
+    /* Compute how much jet energy will be deposited onto the gas */
+    /* Note that it will in general be different from E_feedback_event if
+     * gas particles are of different mass. */
+    double Jet_energy_deposited = 0.0;
+
+    /* Count the number of unsuccessful energy injections (e.g., if the particle
+     * that the BH wants to heat has been swallowed and thus no longer exists)
+     */
+    int N_unsuccessful_jet_injections = 0;
+
+    for (int i = 0; i < number_of_jet_injections; i++) {
+
+      /* If the gas particle that the BH wants to heat has just been swallowed
+       * by the same BH, increment the counter of unsuccessful injections. If
+       * the particle has not been swallowed by the BH, increase the energy that
+       * will later be subtracted from the BH's energy reservoir.
+       * Loop over jet both rays.*/
+
+      if (i % 2 == 0) {
+        if (bp->rays_jet[i].id_min_length != -1) {
+          Jet_energy_deposited += delta_u_jet * bp->rays_jet[i].mass;
+        } else {
+          N_unsuccessful_jet_injections++;
+        }
+      } else {
+        if (bp->rays_jet_pos[i].id_min_length != -1) {
+          Jet_energy_deposited += delta_u_jet * bp->rays_jet_pos[i].mass;
+        } else {
+          N_unsuccessful_jet_injections++;
+        }
+      }
+    }
+
+    /* Store all of this in the black hole for delivery onto the gas. */
+    bp->to_distribute.AGN_delta_u_jet = delta_u_jet;
+    bp->to_distribute.AGN_number_of_jet_injections = number_of_jet_injections;
+
+    /* Subtract the deposited energy from the BH jet energy reservoir. Note
+     * that in the stochastic case, the resulting value might be negative.
+     * This happens when (due to the probabilistic nature of the model) the
+     * BH injects more energy than it actually has in the reservoir. */
+    bp->jet_reservoir -= Jet_energy_deposited;
+
+    /* Total number successful jet energy injections at this time-step. In each
+     * energy injection, a certain gas particle from the BH's kernel gets
+     * heated. (successful = the particle(s) that is going to get heated by
+     * this BH has not been swallowed by the same BH). */
+    const int N_successful_jet_injections =
+        number_of_jet_injections - N_unsuccessful_jet_injections;
+
+    /* Increase the number of jet energy injections the black hole has kicked
+     * so far.  */
+    bp->AGN_number_of_jet_injections += N_successful_jet_injections;
+
+    /* Increase the number of AGN jet events the black hole has had so far.
+     * If the BH does feedback, the number of AGN events is incremented by one.
+     */
+    bp->AGN_number_of_AGN_jet_events += N_successful_jet_injections > 0;
+
+    /* Update the total (cumulative) energy used for gas heating in AGN jet
+      feedback by this BH */
+    bp->total_jet_energy += Jet_energy_deposited;
+
+    /* Store the time/scale factor when the BH last did AGN jet  feedback */
+    if (N_successful_jet_injections) {
+      if (with_cosmology) {
+        bp->last_AGN_jet_event_scale_factor = cosmo->a;
+      } else {
+        bp->last_AGN_jet_event_time = time;
+      }
+    }
+  } else {
+
+    /* Flag that we don't want to kick anyone */
+    bp->to_distribute.AGN_number_of_jet_injections = 0;
+    bp->to_distribute.AGN_delta_u_jet = 0.f;
+  }
+
+  /* Decide the accretion mode of the BH, based on the new spin and Eddington
+   * fraction */
+  decide_mode(bp, props);
+
+  /* Calculate a BH angular momentum evolution time step. Ths timestep is
+     chosen so that the BH spin changes by around 10% relative to the current
+     spin value */
+  if ((fabsf(bp->spin) > 0.001) && (bp->accretion_rate > 0.)) {
+    const float dt_ang_mom = 0.1 * fabsf(bp->spin) /
+                             fabsf(da_dln_mbh_0(bp, constants, props)) *
+                             bp->subgrid_mass / bp->accretion_rate;
+    bp->dt_ang_mom = dt_ang_mom;
+  } else {
+    bp->dt_ang_mom = FLT_MAX;
+  }
+}
+
+/**
+ * @brief Finish the calculation of the new BH position.
+ *
+ * Here, we check that the BH should indeed be moved in the next drift.
+ *
+ * @param bp The black hole particle.
+ * @param props The properties of the black hole scheme.
+ * @param constants The physical constants (in internal units).
+ * @param cosmo The cosmological model.
+ * @param dt The black hole particle's time step.
+ * @param ti_begin The time at the start of the step
+ */
+__attribute__((always_inline)) INLINE static void black_holes_end_reposition(
+    struct bpart* restrict bp, const struct black_holes_props* props,
+    const struct phys_const* constants, const struct cosmology* cosmo,
+    const double dt, const integertime_t ti_begin) {
+
+  /* First check: did we find any eligible neighbour particle to jump to? */
+  if (bp->reposition.min_potential != FLT_MAX) {
+
+    /* Record that we have a (possible) repositioning situation */
+    bp->number_of_reposition_attempts++;
+
+    /* Is the potential lower (i.e. the BH is at the bottom already)
+     * OR is the BH massive enough that we don't reposition? */
+    const float potential = gravity_get_comoving_potential(bp->gpart);
+    if (potential < bp->reposition.min_potential ||
+        bp->subgrid_mass > props->max_reposition_mass) {
+
+      /* No need to reposition */
+      bp->reposition.min_potential = FLT_MAX;
+      bp->reposition.delta_x[0] = -FLT_MAX;
+      bp->reposition.delta_x[1] = -FLT_MAX;
+      bp->reposition.delta_x[2] = -FLT_MAX;
+    } else if (props->set_reposition_speed) {
+
+      /* If we are re-positioning, move the BH a fraction of delta_x, so
+       * that we have a well-defined re-positioning velocity. We have
+       * checked already that reposition_coefficient_upsilon is positive. */
+      const float repos_vel =
+          props->reposition_coefficient_upsilon *
+          pow(bp->subgrid_mass / constants->const_solar_mass,
+              props->reposition_exponent_xi);
+
+      const double dx = bp->reposition.delta_x[0];
+      const double dy = bp->reposition.delta_x[1];
+      const double dz = bp->reposition.delta_x[2];
+      const double d = sqrt(dx * dx + dy * dy + dz * dz);
+
+      /* Convert target reposition velocity to a fractional reposition
+       * along reposition.delta_x */
+
+      /* Exclude the pathological case of repositioning by zero distance */
+      if (d > 0) {
+        double repos_frac = repos_vel * dt / d;
+
+        /* We should never get negative repositioning fractions... */
+        if (repos_frac < 0)
+          error("Wanting to reposition by negative fraction (%g)?", repos_frac);
+
+        /* ... but fractions > 1 can occur if the target velocity is high.
+         * We do not want this, because it could lead to overshooting the
+         * actual potential minimum. */
+        if (repos_frac > 1) repos_frac = 1.;
+
+        bp->reposition.delta_x[0] *= repos_frac;
+        bp->reposition.delta_x[1] *= repos_frac;
+        bp->reposition.delta_x[2] *= repos_frac;
+      }
+
+      /* ends section for fractional repositioning */
+    } else {
+
+      /* We _should_ reposition, but not fractionally. Here, we will
+       * reposition exactly on top of another gas particle - which
+       * could cause issues, so we add on a small fractional offset
+       * of magnitude 0.001 h in the reposition delta. */
+
+      /* Generate three random numbers in the interval [-0.5, 0.5[; id,
+       * id**2, and id**3 are required to give unique random numbers (as
+       * random_unit_interval is completely reproducible). */
+      const float offset_dx =
+          random_unit_interval(bp->id, ti_begin, random_number_BH_reposition) -
+          0.5f;
+      const float offset_dy =
+          random_unit_interval(bp->id * bp->id, ti_begin,
+                               random_number_BH_reposition) -
+          0.5f;
+      const float offset_dz =
+          random_unit_interval(bp->id * bp->id * bp->id, ti_begin,
+                               random_number_BH_reposition) -
+          0.5f;
+
+      const float length_inv =
+          1.0f / sqrtf(offset_dx * offset_dx + offset_dy * offset_dy +
+                       offset_dz * offset_dz);
+
+      const float norm = 0.001f * bp->h * length_inv;
+
+      bp->reposition.delta_x[0] += offset_dx * norm;
+      bp->reposition.delta_x[1] += offset_dy * norm;
+      bp->reposition.delta_x[2] += offset_dz * norm;
+    }
+  } /* ends section if we found eligible repositioning target(s) */
+}
+
+/**
+ * @brief Reset acceleration fields of a particle
+ *
+ * This is the equivalent of hydro_reset_acceleration.
+ * We do not compute the acceleration on black hole, therefore no need to use
+ * it.
+ *
+ * @param bp The particle to act upon
+ */
+__attribute__((always_inline)) INLINE static void black_holes_reset_feedback(
+    struct bpart* restrict bp) {
+
+  bp->to_distribute.AGN_delta_u = 0.f;
+  bp->to_distribute.AGN_number_of_energy_injections = 0;
+
+#ifdef DEBUG_INTERACTIONS_BLACK_HOLES
+  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
+    bp->ids_ngbs_force[i] = -1;
+  bp->num_ngb_force = 0;
+#endif
+}
+
+/**
+ * @brief Store the gravitational potential of a black hole by copying it from
+ * its #gpart friend.
+ *
+ * @param bp The black hole particle.
+ * @param gp The black hole's #gpart.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_store_potential_in_bpart(struct bpart* bp, const struct gpart* gp) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (bp->gpart != gp) error("Copying potential to the wrong black hole!");
+#endif
+
+  bp->reposition.potential = gp->potential;
+}
+
+/**
+ * @brief Store the gravitational potential of a particle by copying it from
+ * its #gpart friend.
+ *
+ * @param p_data The black hole data of a gas particle.
+ * @param gp The black hole's #gpart.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_store_potential_in_part(struct black_holes_part_data* p_data,
+                                    const struct gpart* gp) {
+  p_data->potential = gp->potential;
+}
+
+/**
+ * @brief Initialise a BH particle that has just been seeded.
+ *
+ * @param bp The #bpart to initialise.
+ * @param props The properties of the black hole scheme.
+ * @param constants The physical constants in internal units.
+ * @param cosmo The current cosmological model.
+ * @param p The #part that became a black hole.
+ * @param xp The #xpart that became a black hole.
+ */
+INLINE static void black_holes_create_from_gas(
+    struct bpart* bp, const struct black_holes_props* props,
+    const struct phys_const* constants, const struct cosmology* cosmo,
+    const struct part* p, const struct xpart* xp,
+    const integertime_t ti_current) {
+
+  /* All the non-basic properties of the black hole have been zeroed
+   * in the FOF code. We update them here.
+   * (i.e. position, velocity, mass, time-step have been set) */
+
+  /* Birth time */
+  bp->formation_scale_factor = cosmo->a;
+
+  /* Initial seed mass */
+  bp->subgrid_mass = props->subgrid_seed_mass;
+
+  /* Small initial spin in random direction*/
+  bp->spin = 0.001f;
+
+  const float rand_cos_theta =
+      2. *
+      (0.5 - random_unit_interval(bp->id, ti_current, random_number_BH_spin));
+  const float rand_sin_theta =
+      sqrtf(max(0., (1. - rand_cos_theta) * (1. + rand_cos_theta)));
+  const float rand_phi =
+      2. * M_PI *
+      random_unit_interval(bp->id * bp->id, ti_current, random_number_BH_spin);
+
+  bp->angular_momentum_direction[0] = rand_sin_theta * cos(rand_phi);
+  bp->angular_momentum_direction[1] = rand_sin_theta * sin(rand_phi);
+  bp->angular_momentum_direction[2] = rand_cos_theta;
+
+  bp->aspect_ratio = 0.01f;
+  bp->jet_efficiency = 0.1f;
+  bp->radiative_efficiency = 0.1f;
+  bp->accretion_disk_angle = 0.01f;
+  bp->accretion_mode = BH_thin_disc;
+  bp->eddington_fraction = 0.01f;
+  bp->jet_reservoir = 0.f;
+  bp->total_jet_energy = 0.f;
+  bp->dt_jet = 0.f;
+  bp->dt_ang_mom = 0.f;
+  bp->v_jet = black_hole_feedback_dv_jet(bp, props, cosmo, constants);
+  bp->AGN_number_of_AGN_jet_events = 0;
+  bp->AGN_number_of_jet_injections = 0;
+
+  /* We haven't accreted anything yet */
+  bp->total_accreted_mass = 0.f;
+  bp->cumulative_number_seeds = 1;
+  bp->number_of_mergers = 0;
+  bp->number_of_gas_swallows = 0;
+  bp->number_of_direct_gas_swallows = 0;
+  bp->number_of_time_steps = 0;
+
+  /* We haven't repositioned yet, nor attempted it */
+  bp->number_of_repositions = 0;
+  bp->number_of_reposition_attempts = 0;
+
+  /* Copy over the splitting struct */
+  bp->split_data = xp->split_data;
+
+  /* Initial metal masses */
+  const float gas_mass = hydro_get_mass(p);
+  struct chemistry_bpart_data* bp_chem = &bp->chemistry_data;
+  const struct chemistry_part_data* p_chem = &p->chemistry_data;
+  chemistry_bpart_from_part(bp_chem, p_chem, gas_mass);
+
+  /* No swallowed angular momentum */
+  bp->swallowed_angular_momentum[0] = 0.f;
+  bp->swallowed_angular_momentum[1] = 0.f;
+  bp->swallowed_angular_momentum[2] = 0.f;
+
+  /* Last time this BH had a high Eddington fraction */
+  bp->last_high_Eddington_fraction_scale_factor = -1.f;
+
+  /* Last time of mergers */
+  bp->last_minor_merger_time = -1.;
+  bp->last_major_merger_time = -1.;
+
+  /* First initialisation */
+  black_holes_init_bpart(bp);
+
+  black_holes_mark_bpart_as_not_swallowed(&bp->merger_data);
+}
+
+/**
+ * @brief Store the halo mass in the fof algorithm for the black
+ * hole particle.
+ *
+ * @param p_data The black hole particle data.
+ * @param halo_mass The halo mass to update.
+ */
+__attribute__((always_inline)) INLINE static void black_holes_update_halo_mass(
+    struct bpart* bp, float halo_mass) {
+  bp->group_mass = halo_mass;
+}
+
+#endif /* SWIFT_SPIN_JET_BLACK_HOLES_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_debug.h b/src/black_holes/SPIN_JET/black_holes_debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..62cf4802dc7121e6a2a73ceba414c71530102771
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_debug.h
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2022 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_BLACK_HOLES_SPIN_JET_DEBUG_H
+#define SWIFT_BLACK_HOLES_SPIN_JET_DEBUG_H
+
+__attribute__((always_inline)) INLINE static void black_holes_debug_particle(
+    const struct part* p, const struct xpart* xp) {
+
+  warning("[PID%lld] black_holes_part_data:", p->id);
+  warning("[PID%lld] swallow_id=%lld, potential=%.3e", p->id,
+          p->black_holes_data.swallow_id, p->black_holes_data.potential);
+}
+
+#endif /* SWIFT_BLACK_HOLES_SPIN_JET_DEBUG_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_iact.h b/src/black_holes/SPIN_JET/black_holes_iact.h
new file mode 100644
index 0000000000000000000000000000000000000000..bb2234d436a282a6e2e6c2e8907779d93b551e3b
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_iact.h
@@ -0,0 +1,1096 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ * Copyright (c) 2022 Filip Husko (filip.husko@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_SPIN_JET_BH_IACT_H
+#define SWIFT_SPIN_JET_BH_IACT_H
+
+/* Local includes */
+#include "black_holes_parameters.h"
+#include "engine.h"
+#include "equation_of_state.h"
+#include "gravity.h"
+#include "gravity_iact.h"
+#include "hydro.h"
+#include "random.h"
+#include "rays.h"
+#include "space.h"
+#include "timestep_sync_part.h"
+#include "tracers.h"
+
+/**
+ * @brief Density interaction between two particles (non-symmetric).
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param bi First particle (black hole).
+ * @param pj Second particle (gas, not updated).
+ * @param xpj The extended data of the second particle (not updated).
+ * @param with_cosmology Are we doing a cosmological run?
+ * @param cosmo The cosmological model.
+ * @param grav_props The properties of the gravity scheme (softening, G, ...).
+ * @param bh_props The properties of the BH scheme
+ * @param ti_current Current integer time value (for random numbers).
+ * @param time Current physical time in the simulation.
+ * @param step The current time-step.
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_bh_gas_density(
+    const float r2, const float *dx, const float hi, const float hj,
+    struct bpart *bi, const struct part *pj, const struct xpart *xpj,
+    const int with_cosmology, const struct cosmology *cosmo,
+    const struct gravity_props *grav_props,
+    const struct black_holes_props *bh_props,
+    const struct entropy_floor_properties *floor_props,
+    const integertime_t ti_current, const double time) {
+
+  float wi, wi_dx;
+
+  /* Get r. */
+  const float r = sqrtf(r2);
+  const float r_inv = 1.f / r;
+
+  /* Compute the kernel function */
+  const float hi_inv = 1.0f / hi;
+  const float ui = r * hi_inv;
+  kernel_deval(ui, &wi, &wi_dx);
+
+  /* Compute contribution to the number of neighbours */
+  bi->density.wcount += wi;
+  bi->density.wcount_dh -= (hydro_dimension * wi + ui * wi_dx);
+
+  /* Contribution to the number of neighbours */
+  bi->num_ngbs++;
+
+  /* Neighbour gas mass */
+  const float mj = hydro_get_mass(pj);
+
+  /* Contribution to the BH gas density */
+  bi->rho_gas += mj * wi;
+
+  /* Contribution to the total neighbour mass */
+  bi->ngb_mass += mj;
+
+  /* Neighbour's sound speed */
+  float cj;
+  if (bh_props->use_subgrid_gas_properties && engine_current_step >= 0) {
+    const float pressure_j = hydro_get_comoving_pressure(pj);
+    const float subgrid_dens = cooling_get_subgrid_density(pj, xpj);
+    cj = gas_soundspeed_from_pressure(
+        subgrid_dens * cosmo->a * cosmo->a * cosmo->a, pressure_j);
+  } else {
+    cj = hydro_get_comoving_soundspeed(pj);
+  }
+
+  /* Contribution to the smoothed sound speed */
+  bi->sound_speed_gas += mj * cj * wi;
+
+  /* Neighbour's (drifted) velocity in the frame of the black hole
+   * (we do include a Hubble term) */
+  const float dv[3] = {pj->v[0] - bi->v[0], pj->v[1] - bi->v[1],
+                       pj->v[2] - bi->v[2]};
+
+  const float a = cosmo->a;
+  const float H = cosmo->H;
+  const float a2H = a * a * H;
+
+  /* Calculate the velocity with the Hubble flow */
+  const float v_plus_H_flow[3] = {a2H * dx[0] + dv[0], a2H * dx[1] + dv[1],
+                                  a2H * dx[2] + dv[2]};
+
+  /* Contribution to the smoothed velocity (gas w.r.t. black hole) */
+  bi->velocity_gas[0] += mj * dv[0] * wi;
+  bi->velocity_gas[1] += mj * dv[1] * wi;
+  bi->velocity_gas[2] += mj * dv[2] * wi;
+
+  /* Contribution to the specific angular momentum of gas, which is later
+   * converted to the circular velocity at the smoothing length */
+  bi->spec_angular_momentum_gas[0] -= mj * wi * (dx[1] * dv[2] - dx[2] * dv[1]);
+  bi->spec_angular_momentum_gas[1] -= mj * wi * (dx[2] * dv[0] - dx[0] * dv[2]);
+  bi->spec_angular_momentum_gas[2] -= mj * wi * (dx[0] * dv[1] - dx[1] * dv[0]);
+
+  /* Contribution to the smoothed squared relative velocity (for dispersion)
+   * We will convert this to actual dispersion later. */
+  const float norm_v2 = v_plus_H_flow[0] * v_plus_H_flow[0] +
+                        v_plus_H_flow[1] * v_plus_H_flow[1] +
+                        v_plus_H_flow[2] * v_plus_H_flow[2];
+
+  bi->velocity_dispersion_gas += norm_v2 * wi * mj;
+
+  if (bh_props->use_multi_phase_bondi) {
+    /* Contribution to BH accretion rate
+     *
+     * i) Calculate denominator in Bondi formula */
+    const double gas_v_phys[3] = {dv[0] * cosmo->a_inv, dv[1] * cosmo->a_inv,
+                                  dv[2] * cosmo->a_inv};
+    const double gas_v_norm2 = gas_v_phys[0] * gas_v_phys[0] +
+                               gas_v_phys[1] * gas_v_phys[1] +
+                               gas_v_phys[2] * gas_v_phys[2];
+
+    const double gas_c_phys = cj * cosmo->a_factor_sound_speed;
+    const double gas_c_phys2 = gas_c_phys * gas_c_phys;
+    const double denominator2 = gas_v_norm2 + gas_c_phys2;
+
+#ifdef SWIFT_DEBUG_CHECKS
+    /* Make sure that the denominator is strictly positive */
+    if (denominator2 <= 0)
+      error(
+          "Invalid denominator for BH particle %lld and gas particle "
+          "%lld in Bondi rate calculation.",
+          bi->id, pj->id);
+#endif
+    const double denominator_inv = 1. / sqrt(denominator2);
+
+    /* ii) Contribution of gas particle to the BH accretion rate
+     *     (without constant pre-factor)
+     *     N.B.: rhoj is the weighted contribution to BH gas density. */
+    const float rhoj = mj * wi * cosmo->a3_inv;
+    bi->accretion_rate +=
+        rhoj * denominator_inv * denominator_inv * denominator_inv;
+  } /* End of accretion contribution calculation */
+
+  /* Need to compute gas vorticity around the black hole, in analogy to
+   * calculation for Balsara switch in hydro part */
+
+  /* Factor to make sure we get curl, not angular momentum */
+  const float faci = mj * wi_dx * r_inv;
+
+  /* Compute dv cross r */
+  const float v_cross_r[3] = {dv[1] * dx[2] - dv[2] * dx[1],
+                              dv[2] * dx[0] - dv[0] * dx[2],
+                              dv[0] * dx[1] - dv[1] * dx[0]};
+
+  bi->curl_v_gas[0] += faci * v_cross_r[0];
+  bi->curl_v_gas[1] += faci * v_cross_r[1];
+  bi->curl_v_gas[2] += faci * v_cross_r[2];
+
+#ifdef DEBUG_INTERACTIONS_BH
+  /* Update ngb counters */
+  if (si->num_ngb_density < MAX_NUM_OF_NEIGHBOURS_BH)
+    bi->ids_ngbs_density[si->num_ngb_density] = pj->id;
+
+  /* Update ngb counters */
+  ++si->num_ngb_density;
+#endif
+
+  /* Gas particle id */
+  const long long gas_id = pj->id;
+
+  /* Choose AGN feedback model */
+  switch (bh_props->feedback_model) {
+    case AGN_isotropic_model: {
+      /* Compute arc lengths in AGN isotropic feedback and collect
+       * relevant data for later use in the feedback_apply loop */
+
+      /* Loop over rays */
+      for (int i = 0; i < spinjet_blackhole_number_of_rays; i++) {
+
+        /* We generate two random numbers that we use
+        to randomly select the direction of the ith ray */
+
+        /* Random number in [0, 1[ */
+        const double rand_theta = random_unit_interval_part_ID_and_index(
+            bi->id, i, ti_current,
+            random_number_isotropic_AGN_feedback_ray_theta);
+
+        /* Random number in [0, 1[ */
+        const double rand_phi = random_unit_interval_part_ID_and_index(
+            bi->id, i, ti_current,
+            random_number_isotropic_AGN_feedback_ray_phi);
+
+        /* Compute arc length */
+        ray_minimise_arclength(dx, r, bi->rays + i,
+                               /*ray_type=*/ray_feedback_thermal, gas_id,
+                               rand_theta, rand_phi, mj, /*ray_ext=*/NULL,
+                               /*v=*/NULL);
+      }
+      break;
+    }
+    case AGN_minimum_distance_model: {
+      /* Compute the size of the array that we want to sort. If the current
+       * function is called for the first time (at this time-step for this BH),
+       * then bi->num_ngbs = 1 and there is nothing to sort. Note that the
+       * maximum size of the sorted array cannot be larger then the maximum
+       * number of rays. */
+      const int arr_size = min(bi->num_ngbs, spinjet_blackhole_number_of_rays);
+
+      /* Minimise separation between the gas particles and the BH. The rays
+       * structs with smaller ids in the ray array will refer to the particles
+       * with smaller distances to the BH. */
+      ray_minimise_distance(r, bi->rays, arr_size, gas_id, mj);
+      break;
+    }
+  }
+
+  const int arr_size_jet = min(bi->num_ngbs, spinjet_blackhole_number_of_rays);
+
+  /* Scalar product of the spin vector and position vector of the gas particle
+     relative to the BH */
+  float cosine_theta = -dx[0] * bi->angular_momentum_direction[0] -
+                       dx[1] * bi->angular_momentum_direction[1] -
+                       dx[2] * bi->angular_momentum_direction[2];
+
+  /* Norm of the scalar product (ang. mom. dir. is already normalized) */
+  const float norm = sqrtf(dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2]);
+
+  /* Apply the norm to find the cosine of the angle between the two vectors,
+     if norm is 0 then manually set to small value */
+  if (norm > 0.) {
+    cosine_theta = cosine_theta / norm;
+  } else {
+    cosine_theta = 0.001;
+  }
+
+  /* Define a variable which will be used in ray sorting to make sure that
+     jet-kicked particles always end up at the end of the rays*/
+  float ray_jet_correction = 0.;
+
+  /* Define variables that we will minimize. Two because we have two rays. */
+  float quantity_to_minimize = 0.;
+  float quantity_to_minimize_pos = 0.;
+
+  /* Calculate relative velocity of particle and BH, to be used to see if
+     this particle was recently kicked */
+  const float relative_velocity =
+      sqrtf((bi->v[0] - pj->v[0]) * (bi->v[0] - pj->v[0]) +
+            (bi->v[1] - pj->v[1]) * (bi->v[1] - pj->v[1]) +
+            (bi->v[2] - pj->v[2]) * (bi->v[2] - pj->v[2])) *
+      cosmo->a_inv;
+
+  /* Choose AGN jet feedback model. Here we calculate the quantities to
+     minimize, depending on the model. We calculate two numbers, one for each
+     side of the BH. Particles are prioritized to be kicked from the 'same'
+     hemisphere as defined by the BH spin vector. However, there may be cases
+     in which one hemisphere is empty, so particles from the other side are
+     used. If the particle is on the 'wrong' side of the BH, relative to the
+     spin vector, we still put it in each ray, but they are pushed to the end
+     (modulo other particles that were already kicked). We push them to the end
+     so that they can still be used if the other hemisphere is empty. The way
+     we push them to the end of the ray is by multiplying whatever quantity is
+     being minimized by arbitrary numbers (large or small, depending on what is
+     being minimized). This way, these particles never compete with the ones
+     that are in the correct hemisphere of the BH. */
+  switch (bh_props->jet_feedback_model) {
+    case AGN_jet_minimum_distance_model: {
+
+      /* Check if the relative velocity is a significant fraction of the jet
+         launching velocity. If it is, set the ray correction variable to some
+         arbitrarily large value. */
+      if (relative_velocity > 0.3 * bi->v_jet) {
+        ray_jet_correction = 1e11 * bi->h;
+      }
+
+      /* In this case we minimize using particle separations from the BH, with
+         the order closest --> farthest.  */
+      if (cosine_theta < 0) {
+        quantity_to_minimize = r + ray_jet_correction;
+        quantity_to_minimize_pos = 1e8 * r + ray_jet_correction;
+      } else {
+        quantity_to_minimize = 1e8 * r + ray_jet_correction;
+        quantity_to_minimize_pos = r + ray_jet_correction;
+      }
+      break;
+    }
+    case AGN_jet_maximum_distance_model: {
+
+      /* Check if the relative velocity is a significant fraction of the jet
+         launching velocity. If it is, set the ray correction variable to some
+         arbitrarily large value. */
+      if (relative_velocity > 0.3 * bi->v_jet) {
+        ray_jet_correction = 1e13 * 1. / bi->h;
+      }
+
+      /* In this case we minimize using particle separations from the BH, with
+         the order farthest --> closest  */
+      if (cosine_theta < 0) {
+        quantity_to_minimize = r_inv + ray_jet_correction;
+        quantity_to_minimize_pos = 1e8 * r_inv + ray_jet_correction;
+      } else {
+        quantity_to_minimize = 1e8 * r_inv + ray_jet_correction;
+        quantity_to_minimize_pos = r_inv + ray_jet_correction;
+      }
+      break;
+    }
+    case AGN_jet_spin_axis_model: {
+
+      /* Check if the relative velocity is a significant fraction of the jet
+         launching velocity. If it is, set the ray correction variable to some
+         arbitrarily large value. */
+      if (relative_velocity > 0.3 * bi->v_jet) {
+        ray_jet_correction = 1e3;
+      }
+
+      /* In this case we minimize using the angle (cosine) between the position
+         vector of the particle (relative to the BH) and the spin vector of the
+         BH. I.e. we launch particles along the spin axis, regardless of the
+         distances from the BH. */
+      if (cosine_theta < 0) {
+        quantity_to_minimize = -fabsf(cosine_theta) + ray_jet_correction;
+        quantity_to_minimize_pos =
+            -1e-8 * fabsf(cosine_theta) + ray_jet_correction;
+      } else {
+        quantity_to_minimize = -1e-8 * fabsf(cosine_theta) + ray_jet_correction;
+        quantity_to_minimize_pos = -fabsf(cosine_theta) + ray_jet_correction;
+      }
+      break;
+    }
+    case AGN_jet_minimum_density_model: {
+
+      /* Check if the relative velocity is a significant fraction of the jet
+         launching velocity. If it is, set the ray correction variable to some
+         arbitrarily large value. */
+      if (relative_velocity > 0.3 * bi->v_jet) {
+        ray_jet_correction = 1e15 * pj->rho;
+      }
+
+      /* In this case we minimize using particle densities, i.e. we target the
+         low-density gas.  */
+      if (cosine_theta < 0) {
+        quantity_to_minimize = pj->rho + ray_jet_correction;
+        quantity_to_minimize_pos = 1e8 * pj->rho + ray_jet_correction;
+      } else {
+        quantity_to_minimize = 1e8 * pj->rho + ray_jet_correction;
+        quantity_to_minimize_pos = pj->rho + ray_jet_correction;
+      }
+      break;
+    }
+  }
+
+  /* Loop over rays and do the actual minimization */
+  for (int i = 0; i < spinjet_blackhole_number_of_rays; i++) {
+    ray_minimise_distance(quantity_to_minimize, bi->rays_jet, arr_size_jet,
+                          gas_id, pj->mass);
+    ray_minimise_distance(quantity_to_minimize_pos, bi->rays_jet_pos,
+                          arr_size_jet, gas_id, pj->mass);
+  }
+}
+
+/**
+ * @brief Swallowing interaction between two particles (non-symmetric).
+ *
+ * Function used to identify the gas particle that this BH may move towards.
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param bi First particle (black hole).
+ * @param pj Second particle (gas)
+ * @param xpj The extended data of the second particle.
+ * @param with_cosmology Are we doing a cosmological run?
+ * @param cosmo The cosmological model.
+ * @param grav_props The properties of the gravity scheme (softening, G, ...).
+ * @param bh_props The properties of the BH scheme
+ * @param ti_current Current integer time value (for random numbers).
+ * @param time Current physical time in the simulation.
+ * @param step The current time-step.
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_bh_gas_repos(
+    const float r2, const float *dx, const float hi, const float hj,
+    struct bpart *bi, const struct part *pj, const struct xpart *xpj,
+    const int with_cosmology, const struct cosmology *cosmo,
+    const struct gravity_props *grav_props,
+    const struct black_holes_props *bh_props,
+    const struct entropy_floor_properties *floor_props,
+    const integertime_t ti_current, const double time) {
+
+  float wi;
+
+  /* Get r. */
+  const float r = sqrtf(r2);
+
+  /* Compute the kernel function */
+  const float hi_inv = 1.0f / hi;
+  const float ui = r * hi_inv;
+  kernel_eval(ui, &wi);
+
+  /* Start by checking the repositioning criteria */
+
+  /* (Square of) Max repositioning distance allowed based on the softening */
+  const float max_dist_repos2 =
+      kernel_gravity_softening_plummer_equivalent_inv *
+      kernel_gravity_softening_plummer_equivalent_inv *
+      bh_props->max_reposition_distance_ratio *
+      bh_props->max_reposition_distance_ratio * grav_props->epsilon_baryon_cur *
+      grav_props->epsilon_baryon_cur;
+
+  /* Is this gas neighbour close enough that we can consider its potential
+     for repositioning? */
+  if (r2 < max_dist_repos2) {
+
+    /* Flag to check whether neighbour is slow enough to be considered
+     * as repositioning target. Always true if velocity cut is switched off. */
+    int neighbour_is_slow_enough = 1;
+    if (bh_props->with_reposition_velocity_threshold) {
+
+      /* Compute relative peculiar velocity between the two BHs
+       * Recall that in SWIFT v is (v_pec * a) */
+      const float delta_v[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1],
+                                bi->v[2] - pj->v[2]};
+      const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] +
+                       delta_v[2] * delta_v[2];
+      const float v2_pec = v2 * cosmo->a2_inv;
+
+      /* Compute the maximum allowed velocity */
+      float v2_max = bh_props->max_reposition_velocity_ratio *
+                     bh_props->max_reposition_velocity_ratio *
+                     bi->sound_speed_gas * bi->sound_speed_gas;
+
+      /* If desired, limit the value of the threshold (v2_max) to be no
+       * smaller than a user-defined value */
+      if (bh_props->min_reposition_velocity_threshold > 0) {
+        const float v2_min_thresh =
+            bh_props->min_reposition_velocity_threshold *
+            bh_props->min_reposition_velocity_threshold;
+        v2_max = max(v2_max, v2_min_thresh);
+      }
+
+      /* Is the neighbour too fast to jump to? */
+      if (v2_pec >= v2_max) neighbour_is_slow_enough = 0;
+    }
+
+    if (neighbour_is_slow_enough) {
+
+      float potential = pj->black_holes_data.potential;
+
+      if (bh_props->correct_bh_potential_for_repositioning) {
+        /* Let's not include the contribution of the BH
+         * itself to the potential of the gas particle */
+
+        /* Note: This assumes the BH and gas have the same
+         * softening, which is currently true */
+        const float eps = gravity_get_softening(bi->gpart, grav_props);
+        const float eps2 = eps * eps;
+        const float eps_inv = 1.f / eps;
+        const float eps_inv3 = eps_inv * eps_inv * eps_inv;
+        const float BH_mass = bi->mass;
+
+        /* Compute the Newtonian or truncated potential the BH
+         * exherts onto the gas particle */
+        float dummy, pot_ij;
+        runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy,
+                                 &pot_ij);
+
+        /* Deduct the BH contribution */
+        potential -= pot_ij * grav_props->G_Newton;
+      }
+
+      /* Is the potential lower? */
+      if (potential < bi->reposition.min_potential) {
+
+        /* Store this as our new best */
+        bi->reposition.min_potential = potential;
+        bi->reposition.delta_x[0] = -dx[0];
+        bi->reposition.delta_x[1] = -dx[1];
+        bi->reposition.delta_x[2] = -dx[2];
+      }
+    }
+  }
+}
+
+/**
+ * @brief Swallowing interaction between two particles (non-symmetric).
+ *
+ * Function used to flag the gas particles that will be swallowed
+ * by the black hole particle.
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param bi First particle (black hole).
+ * @param pj Second particle (gas)
+ * @param xpj The extended data of the second particle.
+ * @param with_cosmology Are we doing a cosmological run?
+ * @param cosmo The cosmological model.
+ * @param grav_props The properties of the gravity scheme (softening, G, ...).
+ * @param bh_props The properties of the BH scheme
+ * @param ti_current Current integer time value (for random numbers).
+ * @param time Current physical time in the simulation.
+ * @param step The current time-step.
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_bh_gas_swallow(
+    const float r2, const float *dx, const float hi, const float hj,
+    struct bpart *bi, struct part *pj, struct xpart *xpj,
+    const int with_cosmology, const struct cosmology *cosmo,
+    const struct gravity_props *grav_props,
+    const struct black_holes_props *bh_props,
+    const struct entropy_floor_properties *floor_props,
+    const integertime_t ti_current, const double time) {
+
+  float wi;
+
+  /* Get r. */
+  const float r = sqrtf(r2);
+
+  /* Compute the kernel function */
+  const float hi_inv = 1.0f / hi;
+  const float hi_inv_dim = pow_dimension(hi_inv);
+  const float ui = r * hi_inv;
+  kernel_eval(ui, &wi);
+
+  /* Check if the BH needs to be fed. If not, we're done here */
+  const float bh_mass_deficit = bi->subgrid_mass - bi->mass_at_start_of_step;
+  if (bh_mass_deficit <= 0) return;
+
+  if (bh_props->use_nibbling) {
+
+    /* If we do nibbling, things are quite straightforward. We transfer
+     * the mass and all associated quantities right here. */
+
+    const float bi_mass_orig = bi->mass;
+    const float pj_mass_orig = hydro_get_mass(pj);
+
+    /* Don't nibble from particles that are too small already */
+    if (pj_mass_orig < bh_props->min_gas_mass_for_nibbling) return;
+
+    /* Next line is equivalent to w_ij * m_j / Sum_j (w_ij * m_j) */
+    const float particle_weight = hi_inv_dim * wi * pj_mass_orig / bi->rho_gas;
+    float nibble_mass = bh_mass_deficit * particle_weight;
+
+    /* We radiated away some of the accreted mass, so need to take slightly
+     * more from the gas than the BH gained */
+    const float excess_fraction = 1.0 / (1.0 - bi->radiative_efficiency);
+
+    /* Need to check whether nibbling would push gas mass below minimum
+     * allowed mass */
+    float new_gas_mass = pj_mass_orig - nibble_mass * excess_fraction;
+    if (new_gas_mass < bh_props->min_gas_mass_for_nibbling) {
+      new_gas_mass = bh_props->min_gas_mass_for_nibbling;
+      nibble_mass = (pj_mass_orig - bh_props->min_gas_mass_for_nibbling) /
+                    excess_fraction;
+    }
+
+    /* Correct for nibbling the particle mass that is stored in rays */
+    for (int i = 0; i < spinjet_blackhole_number_of_rays; i++) {
+      if (bi->rays[i].id_min_length == pj->id) bi->rays[i].mass = new_gas_mass;
+      if (bi->rays_jet[i].id_min_length == pj->id) {
+        bi->rays_jet[i].mass = new_gas_mass;
+      }
+      if (bi->rays_jet_pos[i].id_min_length == pj->id) {
+        bi->rays_jet_pos[i].mass = new_gas_mass;
+      }
+    }
+
+    /* Transfer (dynamical) mass from the gas particle to the BH */
+    bi->mass += nibble_mass;
+    hydro_set_mass(pj, new_gas_mass);
+
+    /* Add the angular momentum of the accreted gas to the BH total.
+     * Note no change to gas here. The cosmological conversion factors for
+     * velocity (a^-1) and distance (a) cancel out, so the angular momentum
+     * is already in physical units. */
+    const float dv[3] = {bi->v[0] - pj->v[0], bi->v[1] - pj->v[1],
+                         bi->v[2] - pj->v[2]};
+    bi->swallowed_angular_momentum[0] +=
+        nibble_mass * (dx[1] * dv[2] - dx[2] * dv[1]);
+    bi->swallowed_angular_momentum[1] +=
+        nibble_mass * (dx[2] * dv[0] - dx[0] * dv[2]);
+    bi->swallowed_angular_momentum[2] +=
+        nibble_mass * (dx[0] * dv[1] - dx[1] * dv[0]);
+
+    /* Update the BH momentum and velocity. Again, no change to gas here. */
+    const float bi_mom[3] = {bi_mass_orig * bi->v[0] + nibble_mass * pj->v[0],
+                             bi_mass_orig * bi->v[1] + nibble_mass * pj->v[1],
+                             bi_mass_orig * bi->v[2] + nibble_mass * pj->v[2]};
+
+    bi->v[0] = bi_mom[0] / bi->mass;
+    bi->v[1] = bi_mom[1] / bi->mass;
+    bi->v[2] = bi_mom[2] / bi->mass;
+
+    const float nibbled_mass = nibble_mass * excess_fraction;
+    const float nibbled_fraction = nibbled_mass / pj_mass_orig;
+
+    /* Update the BH and also gas metal masses */
+    struct chemistry_bpart_data *bi_chem = &bi->chemistry_data;
+    struct chemistry_part_data *pj_chem = &pj->chemistry_data;
+    chemistry_transfer_part_to_bpart(bi_chem, pj_chem, nibbled_mass,
+                                     nibbled_fraction);
+
+  } else { /* ends nibbling section, below comes swallowing */
+
+    /* Probability to swallow this particle
+     * Recall that in SWIFT the SPH kernel is recovered by computing
+     * kernel_eval() and muliplying by (1/h^d) */
+    const float prob =
+        (bi->subgrid_mass - bi->mass) * hi_inv_dim * wi / bi->rho_gas;
+
+    /* Draw a random number (Note mixing both IDs) */
+    const float rand = random_unit_interval(bi->id + pj->id, ti_current,
+                                            random_number_BH_swallow);
+
+    /* Are we lucky? */
+    if (rand < prob) {
+
+      /* This particle is swallowed by the BH with the largest ID of all the
+       * candidates wanting to swallow it */
+      if (pj->black_holes_data.swallow_id < bi->id) {
+
+        message("BH %lld wants to swallow gas particle %lld", bi->id, pj->id);
+
+        pj->black_holes_data.swallow_id = bi->id;
+
+      } else {
+
+        message(
+            "BH %lld wants to swallow gas particle %lld BUT CANNOT (old "
+            "swallow id=%lld)",
+            bi->id, pj->id, pj->black_holes_data.swallow_id);
+      }
+    }
+  } /* ends section for swallowing */
+}
+
+/**
+ * @brief Swallowing interaction between two BH particles (non-symmetric).
+ *
+ * Function used to identify the BH particle that this BH may move towards.
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param bi First particle (black hole).
+ * @param bj Second particle (black hole)
+ * @param cosmo The cosmological model.
+ * @param grav_props The properties of the gravity scheme (softening, G, ...).
+ * @param bh_props The properties of the BH scheme
+ * @param ti_current Current integer time value (for random numbers).
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_bh_bh_repos(const float r2, const float *dx, const float hi,
+                               const float hj, struct bpart *bi,
+                               struct bpart *bj, const struct cosmology *cosmo,
+                               const struct gravity_props *grav_props,
+                               const struct black_holes_props *bh_props,
+                               const integertime_t ti_current) {
+
+  /* Compute relative peculiar velocity between the two BHs
+   * Recall that in SWIFT v is (v_pec * a) */
+  const float delta_v[3] = {bi->v[0] - bj->v[0], bi->v[1] - bj->v[1],
+                            bi->v[2] - bj->v[2]};
+  const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] +
+                   delta_v[2] * delta_v[2];
+
+  const float v2_pec = v2 * cosmo->a2_inv;
+
+  /* (Square of) Max repositioning distance allowed based on the softening */
+  const float max_dist_repos2 =
+      kernel_gravity_softening_plummer_equivalent_inv *
+      kernel_gravity_softening_plummer_equivalent_inv *
+      bh_props->max_reposition_distance_ratio *
+      bh_props->max_reposition_distance_ratio * grav_props->epsilon_baryon_cur *
+      grav_props->epsilon_baryon_cur;
+
+  /* Is this BH neighbour close enough that we can consider its potential
+     for repositioning? */
+  if (r2 < max_dist_repos2) {
+
+    /* Flag to check whether neighbour is slow enough to be considered
+     * as repositioning target. Always true if velocity cut switched off */
+    int neighbour_is_slow_enough = 1;
+    if (bh_props->with_reposition_velocity_threshold) {
+
+      /* Compute the maximum allowed velocity */
+      float v2_max = bh_props->max_reposition_velocity_ratio *
+                     bh_props->max_reposition_velocity_ratio *
+                     bi->sound_speed_gas * bi->sound_speed_gas;
+
+      /* If desired, limit the value of the threshold (v2_max) to be no
+       * smaller than a user-defined value */
+      if (bh_props->min_reposition_velocity_threshold > 0) {
+        const float v2_min_thresh =
+            bh_props->min_reposition_velocity_threshold *
+            bh_props->min_reposition_velocity_threshold;
+        v2_max = max(v2_max, v2_min_thresh);
+      }
+
+      /* Is the neighbour too fast to jump to? */
+      if (v2_pec >= v2_max) neighbour_is_slow_enough = 0;
+    }
+
+    if (neighbour_is_slow_enough) {
+
+      float potential = bj->reposition.potential;
+
+      if (bh_props->correct_bh_potential_for_repositioning) {
+
+        /* Let's not include the contribution of the BH i
+         * to the potential of the BH j */
+
+        const float eps = gravity_get_softening(bi->gpart, grav_props);
+        const float eps2 = eps * eps;
+        const float eps_inv = 1.f / eps;
+        const float eps_inv3 = eps_inv * eps_inv * eps_inv;
+        const float BH_mass = bi->mass;
+
+        /* Compute the Newtonian or truncated potential the BH
+         * exherts onto the gas particle */
+        float dummy, pot_ij;
+        runner_iact_grav_pp_full(r2, eps2, eps_inv, eps_inv3, BH_mass, &dummy,
+                                 &pot_ij);
+
+        /* Deduct the BH contribution */
+        potential -= pot_ij * grav_props->G_Newton;
+      }
+
+      /* Is the potential lower? */
+      if (potential < bi->reposition.min_potential) {
+
+        /* Store this as our new best */
+        bi->reposition.min_potential = potential;
+        bi->reposition.delta_x[0] = -dx[0];
+        bi->reposition.delta_x[1] = -dx[1];
+        bi->reposition.delta_x[2] = -dx[2];
+      }
+    }
+  }
+}
+
+/**
+ * @brief Swallowing interaction between two BH particles (non-symmetric).
+ *
+ * Function used to flag the BH particles that will be swallowed
+ * by the black hole particle.
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param bi First particle (black hole).
+ * @param bj Second particle (black hole)
+ * @param cosmo The cosmological model.
+ * @param grav_props The properties of the gravity scheme (softening, G, ...).
+ * @param bh_props The properties of the BH scheme
+ * @param ti_current Current integer time value (for random numbers).
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_bh_bh_swallow(const float r2, const float *dx,
+                                 const float hi, const float hj,
+                                 struct bpart *bi, struct bpart *bj,
+                                 const struct cosmology *cosmo,
+                                 const struct gravity_props *grav_props,
+                                 const struct black_holes_props *bh_props,
+                                 const integertime_t ti_current) {
+
+  /* Compute relative peculiar velocity between the two BHs
+   * Recall that in SWIFT v is (v_pec * a) */
+  const float delta_v[3] = {bi->v[0] - bj->v[0], bi->v[1] - bj->v[1],
+                            bi->v[2] - bj->v[2]};
+  const float v2 = delta_v[0] * delta_v[0] + delta_v[1] * delta_v[1] +
+                   delta_v[2] * delta_v[2];
+
+  const float v2_pec = v2 * cosmo->a2_inv;
+
+  /* Find the most massive of the two BHs */
+  float M = bi->subgrid_mass;
+  float h = hi;
+  if (bj->subgrid_mass > M) {
+    M = bj->subgrid_mass;
+    h = hj;
+  }
+
+  /* (Square of) max swallowing distance allowed based on the softening */
+  const float max_dist_merge2 =
+      kernel_gravity_softening_plummer_equivalent_inv *
+      kernel_gravity_softening_plummer_equivalent_inv *
+      bh_props->max_merging_distance_ratio *
+      bh_props->max_merging_distance_ratio * grav_props->epsilon_baryon_cur *
+      grav_props->epsilon_baryon_cur;
+
+  const float G_Newton = grav_props->G_Newton;
+
+  /* The BH with the smaller mass will be merged onto the one with the
+   * larger mass.
+   * To avoid rounding issues, we additionally check for IDs if the BHs
+   * have the exact same mass. */
+  if ((bj->subgrid_mass < bi->subgrid_mass) ||
+      (bj->subgrid_mass == bi->subgrid_mass && bj->id < bi->id)) {
+
+    /* Maximum velocity difference between BHs allowed to merge */
+    float v2_threshold;
+
+    if (bh_props->merger_threshold_type == BH_mergers_circular_velocity) {
+
+      /* 'Old-style' merger threshold using circular velocity at the
+       * edge of the more massive BH's kernel (note: we are using the kernel
+       * support radius here and not just the smoothing length). */
+      v2_threshold = G_Newton * M / (kernel_gamma * h);
+    } else {
+
+      /* Arguably better merger threshold using the escape velocity at
+       * the distance between the BHs */
+
+      if (bh_props->merger_threshold_type == BH_mergers_escape_velocity) {
+        /* Standard formula (not softening BH interactions) */
+        v2_threshold = 2.f * G_Newton * M / sqrt(r2);
+      } else if (bh_props->merger_threshold_type ==
+                 BH_mergers_dynamical_escape_velocity) {
+        /* General two-body escape velocity based on dynamical masses */
+        v2_threshold = 2.f * G_Newton * (bi->mass + bj->mass) / sqrt(r2);
+      } else {
+        error("Unexpected BH merger threshold type!");
+        v2_threshold = 0.f;
+      }
+    } /* Ends sections for different merger thresholds */
+
+    if ((v2_pec < v2_threshold) && (r2 < max_dist_merge2)) {
+
+      /* This particle is swallowed by the BH with the largest ID of all the
+       * candidates wanting to swallow it */
+      if ((bj->merger_data.swallow_mass < bi->subgrid_mass) ||
+          (bj->merger_data.swallow_mass == bi->subgrid_mass &&
+           bj->merger_data.swallow_id < bi->id)) {
+
+        message("BH %lld wants to swallow BH particle %lld", bi->id, bj->id);
+
+        bj->merger_data.swallow_id = bi->id;
+        bj->merger_data.swallow_mass = bi->subgrid_mass;
+
+      } else {
+
+        message(
+            "BH %lld wants to swallow gas particle %lld BUT CANNOT (old "
+            "swallow id=%lld)",
+            bi->id, bj->id, bj->merger_data.swallow_id);
+      }
+    }
+  }
+}
+
+/**
+ * @brief Feedback interaction between two particles (non-symmetric).
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param bi First particle (black hole).
+ * @param pj Second particle (gas)
+ * @param xpj The extended data of the second particle.
+ * @param with_cosmology Are we doing a cosmological run?
+ * @param cosmo The cosmological model.
+ * @param grav_props The properties of the gravity scheme (softening, G, ...).
+ * @param bh_props The properties of the BH scheme
+ * @param ti_current Current integer time value (for random numbers).
+ * @param time current physical time in the simulation
+ * @param step The current time-step.
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_bh_gas_feedback(
+    const float r2, const float *dx, const float hi, const float hj,
+    const struct bpart *bi, struct part *pj, struct xpart *xpj,
+    const int with_cosmology, const struct cosmology *cosmo,
+    const struct gravity_props *grav_props,
+    const struct black_holes_props *bh_props,
+    const struct entropy_floor_properties *floor_props,
+    const integertime_t ti_current, const double time) {
+
+  /* Number of energy injections per BH per time-step */
+  const int num_energy_injections_per_BH =
+      bi->to_distribute.AGN_number_of_energy_injections;
+
+  /* Are we doing some feedback? */
+  if (num_energy_injections_per_BH > 0) {
+
+    /* Number of energy injections that have reached this gas particle */
+    int num_of_energy_inj_received_by_gas = 0;
+
+    /* Find out how many rays (= energy injections) this gas particle
+     * has received */
+    for (int i = 0; i < num_energy_injections_per_BH; i++) {
+      if (pj->id == bi->rays[i].id_min_length)
+        num_of_energy_inj_received_by_gas++;
+    }
+
+    /* If the number of received rays is non-zero, inject
+     * AGN energy in thermal form */
+    if (num_of_energy_inj_received_by_gas > 0) {
+
+      /* Compute new energy per unit mass of this particle
+       * The energy the particle receives is proportional to the number of rays
+       * (num_of_energy_inj_received_by_gas) to which the particle was found to
+       * be closest. */
+      const double u_init = hydro_get_physical_internal_energy(pj, xpj, cosmo);
+      const float delta_u = bi->to_distribute.AGN_delta_u *
+                            (float)num_of_energy_inj_received_by_gas;
+      const double u_new = u_init + delta_u;
+
+      hydro_set_physical_internal_energy(pj, xpj, cosmo, u_new);
+      hydro_set_drifted_physical_internal_energy(pj, cosmo, u_new);
+
+      /* Impose maximal viscosity */
+      hydro_diffusive_feedback_reset(pj);
+
+      /* Store the feedback energy */
+      const double delta_energy = delta_u * hydro_get_mass(pj);
+      tracers_after_black_holes_feedback(pj, xpj, with_cosmology, cosmo->a,
+                                         time, delta_energy);
+
+      /* message( */
+      /*     "We did some AGN heating! id %llu BH id %llu probability " */
+      /*     " %.5e  random_num %.5e du %.5e du/ini %.5e", */
+      /*     pj->id, bi->id, prob, rand, delta_u, delta_u / u_init); */
+
+      /* Synchronize the particle on the timeline */
+      timestep_sync_part(pj);
+    }
+  }
+
+  /* Number of jet injections per BH per time-step */
+  const int num_jet_injections_per_BH =
+      bi->to_distribute.AGN_number_of_jet_injections;
+
+  /* Are we doing some jet feedback? */
+  if (num_jet_injections_per_BH > 0) {
+
+    /* Number of jet injections that have reached this gas particle */
+    int num_of_jet_inj_received_by_gas = 0;
+
+    /* Define a variable to assign a velocity kick direction depending
+       on which side of the BH smoothing kernel the particle is */
+    float direction = 0.;
+
+    /* Find out if this gas particle has received any jet injections (rays).
+    Loop through num_jet_injections divided by 2 because of two sets of rays */
+    for (int i = 0; i < num_jet_injections_per_BH / 2; i++) {
+      if (pj->id == bi->rays_jet[i].id_min_length) {
+
+        num_of_jet_inj_received_by_gas++;
+
+        /*This particle is in the 'negative' hemisphere (pointing away from the
+          spin vector of the BH), so it receives a negative kick direction */
+        direction = -1.;
+      }
+    }
+
+    for (int i = 0; i < num_jet_injections_per_BH / 2; i++) {
+      if (pj->id == bi->rays_jet_pos[i].id_min_length) {
+
+        num_of_jet_inj_received_by_gas++;
+
+        /* This particle is in the 'positive' hemisphere (pointing in the
+           direction of the spin vector of the BH), so it receives a positive
+           kick direction */
+        direction = 1.;
+      }
+    }
+
+    /* If the number of received rays is non-zero, inject
+     * AGN jet energy as a kinetic kick */
+    if (num_of_jet_inj_received_by_gas > 0) {
+
+      /* Get the kinetic energy per unit mass */
+      const float delta_u_jet = bi->to_distribute.AGN_delta_u_jet *
+                                (float)num_of_jet_inj_received_by_gas;
+
+      /* Get the (physical) kick velocity, and convert to code units */
+      const float vel_kick = sqrtf(2. * delta_u_jet) * cosmo->a;
+
+      /* Compute velocity kick direction using a function for generating a
+       * random unit vector within a cone around the spin vector.*/
+      float vel_kick_direction[3];
+      random_direction_in_cone(bi->id, pj->id, ti_current,
+                               random_number_BH_kick, bh_props->opening_angle,
+                               bi->angular_momentum_direction,
+                               vel_kick_direction);
+
+      /* Include the -1./1. factor (direction) which accounts for kicks in the
+       * opposite direction of the spin vector */
+      vel_kick_direction[0] = direction * vel_kick_direction[0];
+      vel_kick_direction[1] = direction * vel_kick_direction[1];
+      vel_kick_direction[2] = direction * vel_kick_direction[2];
+
+      /* Get the initial velocity */
+      const float v_init[3] = {xpj->v_full[0], xpj->v_full[1], xpj->v_full[2]};
+
+      /* We compute this final velocity by requiring that the final energy and
+       * the inital one differ by the energy received by the particle, i.e.
+       *
+       *        (pi + delta_pi)^2 / (2m) - pi^2 / (2m) = u,
+       *
+       * u here being the energy per unit mass received by the particle. pi is
+       * the initial momentum, and the momenta terms are expressed in vector
+       * form. The equation, if expressed in terms of velocities, amounts to
+       *
+       *   norm(delta_v) + 2 * norm(delta_v) * norm(v_i) * cos_theta_v = v_k^2.
+       *
+       * Here, delta_v is the change in velocity which we wish to apply, v_i is
+       * the initial velocity, cos_theta_v the cosine of the angle between the
+       * two and v_k^2 is the vel_kick term computed from the energy received
+       * by the particle. The delta_v applied to the particle will differ in
+       * norm from the parameter v_j used for jet feedback for two reasons:
+       * 1) v_k is slightly different from v_j if the particle mass is not
+       * equal to the mean neighbour mass, and 2) the presence of the initial
+       * velocity means we need to increase the magnitude of the velocity by
+       * less than v_j in order to increase its energy by (1/2)mv_j^2. We solve
+       * the above quadratic equation for the norm(delta_v). We begin by
+       * calculating norm(v_i) * cos_theta_v, which is the initial velocity
+       * projected onto the velocity kick direction. */
+      const float v_init_proj = v_init[0] * vel_kick_direction[0] +
+                                v_init[1] * vel_kick_direction[1] +
+                                v_init[2] * vel_kick_direction[2];
+      const float delta_v =
+          sqrtf(max(0., v_init_proj * v_init_proj + vel_kick * vel_kick)) -
+          v_init_proj;
+
+      /* Calculate final velocity by adding delta_v in the direction of the kick
+       */
+      xpj->v_full[0] += delta_v * vel_kick_direction[0];
+      xpj->v_full[1] += delta_v * vel_kick_direction[1];
+      xpj->v_full[2] += delta_v * vel_kick_direction[2];
+
+#ifdef SWIFT_DEBUG_CHECKS
+      message(
+          "Black hole with id %lld kicked particle with id %lld , with a final "
+          "velocity of (%f, %f, %f).",
+          bi->id, pj->id, xpj->v_full[0], xpj->v_full[1], xpj->v_full[2]);
+#endif
+
+      /* Store the jet energy */
+      const double delta_energy_jet = delta_u_jet * hydro_get_mass(pj);
+      tracers_after_jet_feedback(pj, xpj, with_cosmology, cosmo->a, time,
+                                 delta_energy_jet);
+
+      /* Impose maximal viscosity */
+      hydro_diffusive_feedback_reset(pj);
+
+      /* Update the signal velocity */
+      hydro_set_v_sig_based_on_velocity_kick(pj, cosmo,
+                                             sqrtf(2. * delta_u_jet));
+
+      /* Synchronize particle on the time-line */
+      timestep_sync_part(pj);
+    }
+  }
+
+#ifdef DEBUG_INTERACTIONS_BH
+  /* Update ngb counters */
+  if (si->num_ngb_force < MAX_NUM_OF_NEIGHBOURS_BH)
+    bi->ids_ngbs_force[si->num_ngb_force] = pj->id;
+
+  /* Update ngb counters */
+  ++si->num_ngb_force;
+#endif
+}
+
+#endif /* SWIFT_SPIN_JET_BH_IACT_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_io.h b/src/black_holes/SPIN_JET/black_holes_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..55d43c3305cfc0dfe2eee4afa256ca1dfe02e6c2
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_io.h
@@ -0,0 +1,526 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ * Copyright (c) 2022 Filip Husko (filip.husko@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_SPIN_JET_BLACK_HOLES_IO_H
+#define SWIFT_SPIN_JET_BLACK_HOLES_IO_H
+
+#include "adiabatic_index.h"
+#include "black_holes_part.h"
+#include "io_properties.h"
+
+/**
+ * @brief Specifies which b-particle fields to read from a dataset
+ *
+ * @param bparts The b-particle array.
+ * @param list The list of i/o properties to read.
+ * @param num_fields The number of i/o fields to read.
+ */
+INLINE static void black_holes_read_particles(struct bpart* bparts,
+                                              struct io_props* list,
+                                              int* num_fields) {
+
+  /* Say how much we want to read */
+  *num_fields = 9;
+
+  /* List what we want to read */
+  list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY,
+                                UNIT_CONV_LENGTH, bparts, x);
+  list[1] = io_make_input_field("Velocities", FLOAT, 3, COMPULSORY,
+                                UNIT_CONV_SPEED, bparts, v);
+  list[2] = io_make_input_field("Masses", FLOAT, 1, COMPULSORY, UNIT_CONV_MASS,
+                                bparts, mass);
+  list[3] = io_make_input_field("ParticleIDs", LONGLONG, 1, COMPULSORY,
+                                UNIT_CONV_NO_UNITS, bparts, id);
+  list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL,
+                                UNIT_CONV_LENGTH, bparts, h);
+  list[5] = io_make_input_field("EnergyReservoir", FLOAT, 1, OPTIONAL,
+                                UNIT_CONV_ENERGY, bparts, energy_reservoir);
+  list[6] = io_make_input_field("SubgridMasses", FLOAT, 1, OPTIONAL,
+                                UNIT_CONV_MASS, bparts, subgrid_mass);
+  list[7] = io_make_input_field("Spins", FLOAT, 1, COMPULSORY,
+                                UNIT_CONV_NO_UNITS, bparts, spin);
+  list[8] = io_make_input_field("AngularMomentumDirections", FLOAT, 3,
+                                COMPULSORY, UNIT_CONV_NO_UNITS, bparts,
+                                angular_momentum_direction);
+}
+
+INLINE static void convert_bpart_pos(const struct engine* e,
+                                     const struct bpart* bp, double* ret) {
+
+  const struct space* s = e->s;
+  if (s->periodic) {
+    ret[0] = box_wrap(bp->x[0], 0.0, s->dim[0]);
+    ret[1] = box_wrap(bp->x[1], 0.0, s->dim[1]);
+    ret[2] = box_wrap(bp->x[2], 0.0, s->dim[2]);
+  } else {
+    ret[0] = bp->x[0];
+    ret[1] = bp->x[1];
+    ret[2] = bp->x[2];
+  }
+}
+
+INLINE static void convert_bpart_vel(const struct engine* e,
+                                     const struct bpart* bp, float* ret) {
+
+  const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const struct cosmology* cosmo = e->cosmology;
+  const integertime_t ti_current = e->ti_current;
+  const double time_base = e->time_base;
+
+  const integertime_t ti_beg = get_integer_time_begin(ti_current, bp->time_bin);
+  const integertime_t ti_end = get_integer_time_end(ti_current, bp->time_bin);
+
+  /* Get time-step since the last kick */
+  float dt_kick_grav;
+  if (with_cosmology) {
+    dt_kick_grav = cosmology_get_grav_kick_factor(cosmo, ti_beg, ti_current);
+    dt_kick_grav -=
+        cosmology_get_grav_kick_factor(cosmo, ti_beg, (ti_beg + ti_end) / 2);
+  } else {
+    dt_kick_grav = (ti_current - ((ti_beg + ti_end) / 2)) * time_base;
+  }
+
+  /* Extrapolate the velocites to the current time */
+  const struct gpart* gp = bp->gpart;
+  ret[0] = gp->v_full[0] + gp->a_grav[0] * dt_kick_grav;
+  ret[1] = gp->v_full[1] + gp->a_grav[1] * dt_kick_grav;
+  ret[2] = gp->v_full[2] + gp->a_grav[2] * dt_kick_grav;
+
+  /* Conversion from internal to physical units */
+  ret[0] *= cosmo->a_inv;
+  ret[1] *= cosmo->a_inv;
+  ret[2] *= cosmo->a_inv;
+}
+
+INLINE static void convert_bpart_potential(const struct engine* e,
+                                           const struct bpart* bp, float* ret) {
+
+  if (bp->gpart != NULL)
+    ret[0] = gravity_get_comoving_potential(bp->gpart);
+  else
+    ret[0] = 0.f;
+}
+
+INLINE static void convert_bpart_gas_vel(const struct engine* e,
+                                         const struct bpart* bp, float* ret) {
+
+  const struct cosmology* cosmo = e->cosmology;
+
+  /* Convert relative velocities to physical units */
+  ret[0] = bp->velocity_gas[0] * cosmo->a_inv;
+  ret[1] = bp->velocity_gas[1] * cosmo->a_inv;
+  ret[2] = bp->velocity_gas[2] * cosmo->a_inv;
+}
+
+INLINE static void convert_bpart_gas_circular_vel(const struct engine* e,
+                                                  const struct bpart* bp,
+                                                  float* ret) {
+
+  const struct cosmology* cosmo = e->cosmology;
+
+  /* Conversion from internal to physical units */
+  ret[0] = bp->circular_velocity_gas[0] * cosmo->a_inv;
+  ret[1] = bp->circular_velocity_gas[1] * cosmo->a_inv;
+  ret[2] = bp->circular_velocity_gas[2] * cosmo->a_inv;
+}
+
+INLINE static void convert_bpart_gas_velocity_dispersion(const struct engine* e,
+                                                         const struct bpart* bp,
+                                                         float* ret) {
+
+  const struct cosmology* cosmo = e->cosmology;
+
+  /* Conversion from internal to physical units */
+  ret[0] = bp->velocity_dispersion_gas * cosmo->a_inv;
+}
+
+INLINE static void convert_bpart_gas_velocity_curl(const struct engine* e,
+                                                   const struct bpart* bp,
+                                                   float* ret) {
+
+  const struct cosmology* cosmo = e->cosmology;
+
+  /* Conversion from internal to physical units */
+  ret[0] = bp->curl_v_gas[0] * cosmo->a2_inv;
+  ret[1] = bp->curl_v_gas[1] * cosmo->a2_inv;
+  ret[2] = bp->curl_v_gas[2] * cosmo->a2_inv;
+}
+
+/**
+ * @brief Specifies which b-particle fields to write to a dataset
+ *
+ * @param bparts The b-particle array.
+ * @param list The list of i/o properties to write.
+ * @param num_fields The number of i/o fields to write.
+ * @param with_cosmology Are we running a cosmological simulation?
+ */
+INLINE static void black_holes_write_particles(const struct bpart* bparts,
+                                               struct io_props* list,
+                                               int* num_fields,
+                                               int with_cosmology) {
+
+  /* Say how much we want to write */
+  *num_fields = 52;
+
+  /* List what we want to write */
+  list[0] = io_make_output_field_convert_bpart(
+      "Coordinates", DOUBLE, 3, UNIT_CONV_LENGTH, 1.f, bparts,
+      convert_bpart_pos, "Co-moving position of the particles");
+
+  list[1] = io_make_output_field_convert_bpart(
+      "Velocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts, convert_bpart_vel,
+      "Peculiar velocities of the particles. This is a * dx/dt where x is the "
+      "co-moving position of the particles.");
+
+  list[2] =
+      io_make_output_field("DynamicalMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f,
+                           bparts, mass, "Dynamical masses of the particles");
+
+  list[3] =
+      io_make_output_field("ParticleIDs", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0.f,
+                           bparts, id, "Unique ID of the particles");
+
+  list[4] = io_make_output_field(
+      "SmoothingLengths", FLOAT, 1, UNIT_CONV_LENGTH, 1.f, bparts, h,
+      "Co-moving smoothing lengths (FWHM of the kernel) of the particles");
+
+  list[5] = io_make_output_field("SubgridMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f,
+                                 bparts, subgrid_mass,
+                                 "Subgrid masses of the particles");
+
+  if (with_cosmology) {
+    list[6] = io_make_output_field(
+        "FormationScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+        formation_scale_factor, "Scale-factors at which the BHs were formed");
+  } else {
+    list[6] = io_make_output_field("FormationTimes", FLOAT, 1, UNIT_CONV_TIME,
+                                   0.f, bparts, formation_time,
+                                   "Times at which the BHs were formed");
+  }
+
+  list[7] = io_make_output_field(
+      "GasDensities", FLOAT, 1, UNIT_CONV_DENSITY, 0.f, bparts, rho_gas,
+      "Co-moving densities of the gas around the particles");
+
+  list[8] = io_make_output_field(
+      "GasSoundSpeeds", FLOAT, 1, UNIT_CONV_SPEED,
+      -1.5f * hydro_gamma_minus_one, bparts, sound_speed_gas,
+      "Co-moving sound-speeds of the gas around the particles");
+
+  list[9] = io_make_output_field(
+      "EnergyReservoirs", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts,
+      energy_reservoir,
+      "Physcial energy contained in the feedback reservoir of the particles");
+
+  list[10] = io_make_output_field(
+      "AccretionRates", FLOAT, 1, UNIT_CONV_MASS_PER_UNIT_TIME, 0.f, bparts,
+      accretion_rate,
+      "Physical instantaneous accretion rates of the particles");
+
+  list[11] = io_make_output_field(
+      "TotalAccretedMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts,
+      total_accreted_mass,
+      "Total mass accreted onto the main progenitor of the black holes since "
+      "birth. This does not include any mass accreted onto any merged black "
+      "holes.");
+
+  list[12] = io_make_output_field(
+      "CumulativeNumberOfSeeds", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      cumulative_number_seeds,
+      "Total number of BH seeds that have merged into this black hole");
+
+  list[13] =
+      io_make_output_field("NumberOfMergers", INT, 1, UNIT_CONV_NO_UNITS, 0.f,
+                           bparts, number_of_mergers,
+                           "Number of mergers the black holes went through. "
+                           "This does not include the number of mergers "
+                           "accumulated by any merged black hole.");
+
+  if (with_cosmology) {
+    list[14] = io_make_output_field(
+        "LastHighEddingtonFractionScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS,
+        0.f, bparts, last_high_Eddington_fraction_scale_factor,
+        "Scale-factors at which the black holes last reached a large Eddington "
+        "ratio. -1 if never reached.");
+  } else {
+    list[14] = io_make_output_field(
+        "LastHighEddingtonFractionTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts,
+        last_high_Eddington_fraction_time,
+        "Times at which the black holes last reached a large Eddington ratio. "
+        "-1 if never reached.");
+  }
+
+  if (with_cosmology) {
+    list[15] = io_make_output_field(
+        "LastMinorMergerScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f,
+        bparts, last_minor_merger_scale_factor,
+        "Scale-factors at which the black holes last had a minor merger.");
+  } else {
+    list[15] = io_make_output_field(
+        "LastMinorMergerScaleTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts,
+        last_minor_merger_time,
+        "Times at which the black holes last had a minor merger.");
+  }
+
+  if (with_cosmology) {
+    list[16] = io_make_output_field(
+        "LastMajorMergerScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f,
+        bparts, last_major_merger_scale_factor,
+        "Scale-factors at which the black holes last had a major merger.");
+  } else {
+    list[16] = io_make_output_field(
+        "LastMajorMergerScaleTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts,
+        last_major_merger_time,
+        "Times at which the black holes last had a major merger.");
+  }
+
+  list[17] = io_make_output_field(
+      "SwallowedAngularMomenta", FLOAT, 3, UNIT_CONV_ANGULAR_MOMENTUM, 0.f,
+      bparts, swallowed_angular_momentum,
+      "Physical angular momenta that the black holes have accumulated by "
+      "swallowing gas particles.");
+
+  list[18] = io_make_output_field_convert_bpart(
+      "GasRelativeVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts,
+      convert_bpart_gas_vel,
+      "Peculiar relative velocities of the gas particles around the black "
+      "holes. This is a * dx/dt where x is the co-moving position of the "
+      "particles.");
+
+  list[19] = io_make_output_field_convert_bpart(
+      "GasCircularVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts,
+      convert_bpart_gas_circular_vel,
+      "Circular velocities of the gas around the black hole at the "
+      "smoothing radius. This is j / h_BH, where j is the smoothed, peculiar "
+      "specific angular momentum of gas around the black holes, and h_BH is "
+      "the smoothing length of each black hole.");
+
+  list[20] =
+      io_make_output_field("TimeBins", CHAR, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+                           time_bin, "Time-bins of the particles");
+
+  list[21] = io_make_output_field(
+      "NumberOfSwallows", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      number_of_gas_swallows,
+      "Number of gas particles the black holes have swallowed. "
+      "This includes the particles swallowed by any of the black holes that "
+      "merged into this one.");
+
+  list[22] = io_make_output_field(
+      "NumberOfDirectSwallows", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      number_of_direct_gas_swallows,
+      "Number of gas particles the black holes have swallowed. "
+      "This does not include any particles swallowed by any of the black holes "
+      "that merged into this one.");
+
+  list[23] = io_make_output_field(
+      "NumberOfRepositions", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      number_of_repositions,
+      "Number of repositioning events the black holes went through. This does "
+      "not include the number of reposition events accumulated by any merged "
+      "black holes.");
+
+  list[24] = io_make_output_field(
+      "NumberOfRepositionAttempts", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      number_of_reposition_attempts,
+      "Number of time steps in which the black holes had an eligible particle "
+      "to reposition to. They may or may not have ended up moving there, "
+      "depending on their subgrid mass and on whether these particles were at "
+      "a lower or higher potential than the black holes themselves. It does "
+      "not include attempted repositioning events accumulated by any merged "
+      "black holes.");
+
+  list[25] = io_make_output_field(
+      "NumberOfTimeSteps", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      number_of_time_steps,
+      "Total number of time steps at which the black holes were active.");
+
+  list[26] = io_make_output_field(
+      "ViscosityFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, f_visc,
+      "Multiplicative factors by which the Bondi-Hoyle-Lyttleton accretion "
+      "rates have been suppressed by the Rosas-Guevara et al. (2015) "
+      "accretion disc model.");
+
+  list[27] = io_make_output_field_convert_bpart(
+      "GasVelocityDispersions", FLOAT, 1, UNIT_CONV_SPEED, 0.f, bparts,
+      convert_bpart_gas_velocity_dispersion,
+      "Velocity dispersion (3D) of the gas particles around the black "
+      "holes. This is a * sqrt(<|dx/dt|^2> - <|dx/dt|>^2) where x is the "
+      "co-moving position of the particles relative to the black holes.");
+
+  list[28] = io_make_output_field_convert_bpart(
+      "GasCurlVelocities", FLOAT, 3, UNIT_CONV_SPEED, 0.f, bparts,
+      convert_bpart_gas_velocity_curl,
+      "Velocity curl (3D) of the gas particles around the black holes.");
+
+  list[29] = io_make_output_field(
+      "AccretedAngularMomenta", FLOAT, 3, UNIT_CONV_ANGULAR_MOMENTUM, 0.f,
+      bparts, accreted_angular_momentum,
+      "Physical angular momenta that the black holes have accumulated through "
+      "subgrid accretion.");
+
+  list[30] = io_make_output_field(
+      "NumberOfGasNeighbours", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      num_ngbs,
+      "Integer number of gas neighbour particles within the black hole "
+      "kernels.");
+
+  list[31] = io_make_output_field(
+      "NumberOfHeatingEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      AGN_number_of_energy_injections,
+      "Integer number of (thermal) energy injections the black hole has had "
+      "so far. This counts each heated gas particle separately, and so can "
+      "increase by more than one during a single time step.");
+
+  list[32] = io_make_output_field(
+      "NumberOfAGNEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      AGN_number_of_AGN_events,
+      "Integer number of AGN events the black hole has had so far"
+      " (the number of time steps the BH did AGN feedback)");
+
+  if (with_cosmology) {
+    list[33] = io_make_output_field(
+        "LastAGNFeedbackScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f,
+        bparts, last_AGN_event_scale_factor,
+        "Scale-factors at which the black holes last had an AGN event.");
+  } else {
+    list[33] = io_make_output_field(
+        "LastAGNFeedbackTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts,
+        last_AGN_event_time,
+        "Times at which the black holes last had an AGN event.");
+  }
+
+  list[34] = io_make_output_field(
+      "AccretionLimitedTimeSteps", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts,
+      dt_heat,
+      "Accretion-limited time steps of black holes. The actual time step of "
+      "the particles may differ due to the minimum allowed value.");
+
+  list[35] = io_make_output_field(
+      "AGNTotalInjectedEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts,
+      AGN_cumulative_energy,
+      "Total (cumulative) physical energies injected into gas particles "
+      "in AGN feedback.");
+
+  list[36] = io_make_output_field_convert_bpart(
+      "Potentials", FLOAT, 1, UNIT_CONV_POTENTIAL, -1.f, bparts,
+      convert_bpart_potential, "Gravitational potentials of the particles");
+
+  list[37] = io_make_output_field(
+      "Spins", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, spin,
+      "Dimensionless spins of the black holes. "
+      "Negative values indicate retrograde accretion.");
+
+  list[38] = io_make_output_field(
+      "AngularMomentumDirections", FLOAT, 3, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      angular_momentum_direction,
+      "Direction of the black hole spin vector, normalised to unity.");
+
+  list[39] = io_make_output_field(
+      "AccretionDiscAspectRatios", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      aspect_ratio,
+      "The aspect ratio, h/r, of the subgrid accretion disc "
+      "around the black hole.");
+
+  list[40] = io_make_output_field(
+      "JetEfficiencies", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      jet_efficiency, "Jet power divided by accretion rate.");
+
+  list[41] = io_make_output_field(
+      "RadiativeEfficiencies", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      radiative_efficiency, "AGN luminosity divided by accretion rate.");
+
+  list[42] = io_make_output_field("CosAccretionDiskAngle", FLOAT, 1,
+                                  UNIT_CONV_NO_UNITS, 0.f, bparts,
+                                  accretion_disk_angle,
+                                  "Cosine of the angle between the spin vector "
+                                  "and the accreting gas angular momentum.");
+
+  list[43] = io_make_output_field(
+      "AccretionModes", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts, accretion_mode,
+      "Accretion flow regime. 0 - Thick disk, 1 - Thin disk, 2 - Slim disk");
+
+  list[44] = io_make_output_field(
+      "JetReservoir", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts, jet_reservoir,
+      "Total jet energy waiting to be released (once it "
+      "grows large enough to kick a single particle).");
+
+  list[45] = io_make_output_field(
+      "InjectedJetEnergies", FLOAT, 1, UNIT_CONV_ENERGY, 0.f, bparts,
+      total_jet_energy, "Total jet energy injected into AGN surroundings.");
+
+  list[46] = io_make_output_field(
+      "JetTimeSteps", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts, dt_jet,
+      "Jet-launching-limited time-steps of black holes.");
+
+  list[47] = io_make_output_field(
+      "NumberOfJetParticlesLaunched", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      AGN_number_of_jet_injections,
+      "Integer number of (kinetic) energy injections the black hole has had "
+      "so far");
+
+  list[48] = io_make_output_field(
+      "NumberOfAGNJetEvents", INT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      AGN_number_of_AGN_jet_events,
+      "Integer number of AGN jet launching events the black hole has had"
+      " (the number of times the BH did AGN jet feedback)");
+
+  if (with_cosmology) {
+    list[49] = io_make_output_field(
+        "LastAGNJetScaleFactors", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+        last_AGN_jet_event_scale_factor,
+        "Scale-factors at which the black holes last had an AGN jet event.");
+  } else {
+    list[49] = io_make_output_field(
+        "LastAGNJetTimes", FLOAT, 1, UNIT_CONV_TIME, 0.f, bparts,
+        last_AGN_jet_event_time,
+        "Times at which the black holes last had an AGN jet event.");
+  }
+
+  list[50] = io_make_output_field(
+      "EddingtonFractions", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, bparts,
+      eddington_fraction,
+      "Accretion rates of black holes in units of their Eddington rates. "
+      "This is based on the unlimited accretion rates, so these fractions "
+      "can be above the limiting fEdd.");
+
+  list[51] = io_make_output_field(
+      "FOFGroupMasses", FLOAT, 1, UNIT_CONV_MASS, 0.f, bparts, group_mass,
+      "Parent halo masses of the black holes, as determined from the FOF  "
+      "algorithm.");
+
+#ifdef DEBUG_INTERACTIONS_BLACK_HOLES
+
+  list += *num_fields;
+  *num_fields += 4;
+
+  list[0] = io_make_output_field("Num_ngb_density", INT, 1, UNIT_CONV_NO_UNITS,
+                                 bparts, num_ngb_density);
+  list[1] = io_make_output_field("Num_ngb_force", INT, 1, UNIT_CONV_NO_UNITS,
+                                 bparts, num_ngb_force);
+  list[2] = io_make_output_field("Ids_ngb_density", LONGLONG,
+                                 MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES,
+                                 UNIT_CONV_NO_UNITS, bparts, ids_ngbs_density);
+  list[3] = io_make_output_field("Ids_ngb_force", LONGLONG,
+                                 MAX_NUM_OF_NEIGHBOURS_BLACK_HOLES,
+                                 UNIT_CONV_NO_UNITS, bparts, ids_ngbs_force);
+#endif
+}
+
+#endif /* SWIFT_SPIN_JET_BLACK_HOLES_IO_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_parameters.h b/src/black_holes/SPIN_JET/black_holes_parameters.h
new file mode 100644
index 0000000000000000000000000000000000000000..9259ba8b75f61e042a73a1f39c21298aedce6ee9
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_parameters.h
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2020 Matthieu Schaller (schaller@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
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_SPIN_JET_BLACK_HOLES_PARAMETERS_H
+#define SWIFT_SPIN_JET_BLACK_HOLES_PARAMETERS_H
+
+/* Configuration file */
+#include <config.h>
+
+/**
+ * @file EAGLE/black_holes_parameters.h
+ * @brief Parameters of the EAGLE black holes
+ *        model that need to be defined at compile time.
+ *
+ * @note In this branch, these properties are not used anywhere!
+ */
+
+/*! Maximal distance for merging particles in units of the (spline not Plummer)
+ *  softening length. */
+#define const_max_merging_distance_ratio 3.f
+
+/*! Maximal distance for repositioning particles in units of the (spline not
+ * Plummer) softening length. */
+#define const_max_repositioning_distance_ratio 3.f
+
+#endif /* SWIFT_SPIN_JET_BLACK_HOLES_PARAMETERS_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_part.h b/src/black_holes/SPIN_JET/black_holes_part.h
new file mode 100644
index 0000000000000000000000000000000000000000..c97a8ea105f753a2544d689729a8e879cc23eb1f
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_part.h
@@ -0,0 +1,360 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ * Copyright (c) 2022 Filip Husko (filip.husko@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_SPIN_JET_BLACK_HOLE_PART_H
+#define SWIFT_SPIN_JET_BLACK_HOLE_PART_H
+
+/*! The total number of rays used in AGN feedback */
+#define spinjet_blackhole_number_of_rays 50
+
+#include "black_holes_struct.h"
+#include "chemistry_struct.h"
+#include "particle_splitting_struct.h"
+#include "rays_struct.h"
+#include "timeline.h"
+
+/*! The possible accretion modes every black hole can take. */
+enum BH_accretion_modes { BH_thick_disc, BH_thin_disc, BH_slim_disc };
+
+/**
+ * @brief Particle fields for the black hole particles.
+ *
+ * All quantities related to gravity are stored in the associate #gpart.
+ */
+struct bpart {
+
+  /*! Particle ID. */
+  long long id;
+
+  /*! Pointer to corresponding gravity part. */
+  struct gpart* gpart;
+
+  /*! Particle position. */
+  double x[3];
+
+  /* Offset between current position and position at last tree rebuild. */
+  float x_diff[3];
+
+  /*! Particle velocity. */
+  float v[3];
+
+  /*! Black hole mass */
+  float mass;
+
+  /*! Black hole mass at the start of each step, prior to any nibbling */
+  float mass_at_start_of_step;
+
+  /* Particle cutoff radius. */
+  float h;
+
+  /*! Particle time bin */
+  timebin_t time_bin;
+
+  struct {
+
+    /* Number of neighbours. */
+    float wcount;
+
+    /* Number of neighbours spatial derivative. */
+    float wcount_dh;
+
+  } density;
+
+  /*! Union for the formation time and formation scale factor */
+  union {
+
+    /*! Formation time */
+    float formation_time;
+
+    /*! Formation scale factor */
+    float formation_scale_factor;
+  };
+
+  /*! Subgrid mass of the black hole */
+  float subgrid_mass;
+
+  /*! Total accreted mass of the black hole (not including mass merged in
+   * from other black holes) */
+  float total_accreted_mass;
+
+  /*! Energy reservoir for feedback */
+  float energy_reservoir;
+
+  /*! Instantaneous accretion rate */
+  float accretion_rate;
+
+  /*! Density of the gas surrounding the black hole. */
+  float rho_gas;
+
+  /*! Smoothed sound speed of the gas surrounding the black hole. */
+  float sound_speed_gas;
+
+  /*! Smoothed velocity of the gas surrounding the black hole,
+   * in the frame of the black hole (internal units) */
+  float velocity_gas[3];
+
+  /*! Smoothed velocity dispersion (peculiar) of the gas around the
+   * black hole */
+  float velocity_dispersion_gas;
+
+  /*! Circular velocity of the gas around the black hole at the smoothing
+   * radius (calculated as j_gas / h_BH, where j is specific ang. mom.) */
+  float circular_velocity_gas[3];
+
+  /*! Specific angular momentum of the gas around the black hole */
+  float spec_angular_momentum_gas[3];
+
+  /*! Curl of the gas velocity around the black hole. */
+  float curl_v_gas[3];
+
+  /*! Multiplicative factor for accretion rates, from Rosas-Guevara et al.
+   * (2015) angular momentum based accretion disc model */
+  float f_visc;
+
+  /*! Total mass of the gas neighbours. */
+  float ngb_mass;
+
+  /*! Integer number of neighbours */
+  int num_ngbs;
+
+  /*! Number of seeds in this BH (i.e. itself + the merged ones) */
+  int cumulative_number_seeds;
+
+  /*! Total number of BH merger events (i.e. not including all progenies) */
+  int number_of_mergers;
+
+  /*! Total number of gas particles swallowed (including particles swallowed
+   * by merged-in black holes) */
+  int number_of_gas_swallows;
+
+  /*! Total number of gas particles swallowed (excluding particles swallowed
+   * by merged-in black holes) */
+  int number_of_direct_gas_swallows;
+
+  /*! Total number of times the black hole has been repositioned (excluding
+   * repositionings of merged-in black holes) */
+  int number_of_repositions;
+
+  /*! Total number of times a black hole attempted repositioning (including
+   * cases where it was aborted because the black hole was already at a
+   * lower potential than all eligible neighbours) */
+  int number_of_reposition_attempts;
+
+  /*! Total number of time steps in which the black hole was active. */
+  int number_of_time_steps;
+
+  /*! Total (physical) angular momentum accumulated by swallowing particles */
+  float swallowed_angular_momentum[3];
+
+  /*! Total (physical) angular momentum accumulated from subgrid accretion */
+  float accreted_angular_momentum[3];
+
+  /*! Integer (cumulative) number of energy injections in AGN feedback. At a
+   * given time-step, an AGN-active BH may produce multiple energy injections.
+   * The number of energy injections is equal to or more than the number of
+   * particles heated by the BH during this time-step. */
+  int AGN_number_of_energy_injections;
+
+  /*! Integer (cumulative) number of AGN events. If a BH does feedback at a
+   * given time-step, the number of its AGN events is incremented by 1. Each
+   * AGN event may have multiple energy injections. */
+  int AGN_number_of_AGN_events;
+
+  /* Total energy injected into the gas in AGN feedback by this BH */
+  float AGN_cumulative_energy;
+
+  /*! Union for the last AGN event time and the last AGN event scale factor */
+  union {
+
+    /*! Last AGN event time */
+    float last_AGN_event_time;
+
+    /*! Last AGN event scale-factor */
+    float last_AGN_event_scale_factor;
+  };
+
+  /*! BH accretion-limited time-step */
+  float dt_heat;
+
+  /*! Eddington fraction */
+  float eddington_fraction;
+
+  /*! BH dimensionless spin */
+  float spin;
+
+  /*! The normalized spin/angular momentum vector of the BH */
+  float angular_momentum_direction[3];
+
+  /*! The jet efficiency */
+  float jet_efficiency;
+
+  /*! The radiative efficiency */
+  float radiative_efficiency;
+
+  /*! Cosine of the angle between the spin vector and the angular momentum
+      of the gas in the smoothing kernel */
+  float accretion_disk_angle;
+
+  /*! Aspect ratio of the subgrid accretion disk */
+  float aspect_ratio;
+
+  /*! Which type is the subgrid accretion disk (thick, thin or slim) */
+  enum BH_accretion_modes accretion_mode;
+
+  /*! The jet-limited time-step of the BH */
+  float dt_jet;
+
+  /*! The angular momentum evolution limited time-step of the BH */
+  float dt_ang_mom;
+
+  /*! The current jet kick velocity to be applied */
+  float v_jet;
+
+  /*! The energ in the jet reservoir */
+  float jet_reservoir;
+
+  /*! Total jet energy launched so far */
+  float total_jet_energy;
+
+  /*! Total number of jet kicks */
+  int AGN_number_of_jet_injections;
+
+  /*! Total number of jet kicking events */
+  int AGN_number_of_AGN_jet_events;
+
+  /*! Halo mass the black hole is assigned to */
+  float group_mass;
+
+  union {
+
+    /*! Last time a jet event occurred */
+    float last_AGN_jet_event_time;
+
+    /*! Last scale factor a jet event occurred */
+    float last_AGN_jet_event_scale_factor;
+  };
+
+  /*! Union for the last high Eddington ratio point in time */
+  union {
+
+    /*! Last time the BH had a a high Eddington fraction */
+    float last_high_Eddington_fraction_time;
+
+    /*! Last scale factor the BH had a a high Eddington fraction */
+    float last_high_Eddington_fraction_scale_factor;
+  };
+
+  /*! Union for the last minor merger point in time */
+  union {
+
+    /*! Last time the BH had a a high Eddington fraction */
+    float last_minor_merger_time;
+
+    /*! Last scale factor the BH had a a high Eddington fraction */
+    float last_minor_merger_scale_factor;
+  };
+
+  /*! Union for the last major merger point in time */
+  union {
+
+    /*! Last time the BH had a a high Eddington fraction */
+    float last_major_merger_time;
+
+    /*! Last scale factor the BH had a a high Eddington fraction */
+    float last_major_merger_scale_factor;
+  };
+
+  /*! Properties used in the feedback loop to distribute to gas neighbours. */
+  struct {
+
+    /*! Energy per unit mass in a single AGN energy-injection event */
+    float AGN_delta_u;
+
+    /*! Number of energy injections per time-step */
+    int AGN_number_of_energy_injections;
+
+    /*! Number of energy injections per time-step */
+    int AGN_number_of_jet_injections;
+
+    /*! Change in energy from SNII feedback energy injection */
+    float AGN_delta_u_jet;
+
+  } to_distribute;
+
+  struct {
+
+    /*! Gravitational potential copied from the #gpart. */
+    float potential;
+
+    /*! Value of the minimum potential across all neighbours. */
+    float min_potential;
+
+    /*! Delta position to apply after the reposition procedure */
+    double delta_x[3];
+
+  } reposition;
+
+  /*! Splitting structure */
+  struct particle_splitting_data split_data;
+
+  /*! Chemistry information (e.g. metal content at birth, swallowed metal
+   * content, etc.) */
+  struct chemistry_bpart_data chemistry_data;
+
+  /*! Black holes merger information (e.g. merging ID) */
+  struct black_holes_bpart_data merger_data;
+
+  /*! Isotropic AGN feedback information */
+  struct ray_data rays[spinjet_blackhole_number_of_rays];
+
+  /*! Jet AGN feedback information. This ray is used to kick particles along
+      the spin axis. */
+  struct ray_data rays_jet[spinjet_blackhole_number_of_rays];
+
+  /*! Jet AGN feedback information. This ray is used to kick particles in the
+      oppposite direction from the spin axis. */
+  struct ray_data rays_jet_pos[spinjet_blackhole_number_of_rays];
+
+#ifdef SWIFT_DEBUG_CHECKS
+
+  /* Time of the last drift */
+  integertime_t ti_drift;
+
+  /* Time of the last kick */
+  integertime_t ti_kick;
+
+#endif
+
+#ifdef DEBUG_INTERACTIONS_BLACK_HOLES
+  /*! 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_BLACK_HOLES];
+
+  /*! 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_BLACK_HOLES];
+#endif
+
+} SWIFT_STRUCT_ALIGN;
+
+#endif /* SWIFT_SPIN_JET_BLACK_HOLE_PART_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_properties.h b/src/black_holes/SPIN_JET/black_holes_properties.h
new file mode 100644
index 0000000000000000000000000000000000000000..e51660cde621110de206cf0c8b4c523ea1a4ce9f
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_properties.h
@@ -0,0 +1,945 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2020 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ * Copyright (c) 2022 Filip Husko (filip.husko@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_SPIN_JET_BLACK_HOLES_PROPERTIES_H
+#define SWIFT_SPIN_JET_BLACK_HOLES_PROPERTIES_H
+
+#include "chemistry.h"
+#include "hydro_properties.h"
+#include "string.h"
+
+enum AGN_feedback_models {
+  AGN_isotropic_model,       /*< Isotropic model of AGN feedback */
+  AGN_minimum_distance_model /*< Minimum-distance model of AGN feedback */
+};
+
+enum AGN_jet_feedback_models {
+  AGN_jet_minimum_distance_model, /*< Minimum-distance model of AGN feedback */
+  AGN_jet_maximum_distance_model, /*< Maximum-distance model of AGN feedback */
+  AGN_jet_spin_axis_model,        /*< Kicking-along-spin
+                                      axis model of AGN feedback */
+  AGN_jet_minimum_density_model   /*< Minimum-density model of AGN feedback */
+};
+
+enum AGN_jet_velocity_models {
+  AGN_jet_velocity_constant, /*< Use a constant jet velocity */
+  AGN_jet_velocity_BH_mass,  /*< Scale the jet velocity with BH mass */
+  AGN_jet_velocity_halo_mass /*< Scale the jet velocity with halo mass
+                                 (NOT WORKING) */
+};
+
+enum BH_merger_thresholds {
+  BH_mergers_circular_velocity,        /*< v_circ at separation, as in EAGLE */
+  BH_mergers_escape_velocity,          /*< v_esc at separation */
+  BH_mergers_dynamical_escape_velocity /*< combined v_esc_dyn at separation */
+};
+
+enum thin_disc_regions {
+  TD_region_B, /*< Region B from Shakura & Sunyaev (1973) */
+  TD_region_C  /*< Region C from Shakura & Sunyaev (1973) */
+};
+
+/**
+ * @brief Properties of black holes and AGN feedback in the EAGEL model.
+ */
+struct black_holes_props {
+
+  /* ----- Basic neighbour search properties ------ */
+
+  /*! Resolution parameter */
+  float eta_neighbours;
+
+  /*! Target weightd number of neighbours (for info only)*/
+  float target_neighbours;
+
+  /*! Smoothing length tolerance */
+  float h_tolerance;
+
+  /*! Tolerance on neighbour number  (for info only)*/
+  float delta_neighbours;
+
+  /*! Maximal number of iterations to converge h */
+  int max_smoothing_iterations;
+
+  /*! Maximal change of h over one time-step */
+  float log_max_h_change;
+
+  /* ----- Initialisation properties  ------ */
+
+  /*! Mass of a BH seed at creation time */
+  float subgrid_seed_mass;
+
+  /*! Should we use the subgrid mass specified in ICs? */
+  int use_subgrid_mass_from_ics;
+
+  /*! Should we enforce positive subgrid masses initially? */
+  int with_subgrid_mass_check;
+
+  /* ----- Properties of the accretion model ------ */
+
+  /*! Calculate Bondi accretion rate based on subgrid properties? */
+  int use_subgrid_gas_properties;
+
+  /*! Calculate Bondi accretion rate for individual neighbours? */
+  int use_multi_phase_bondi;
+
+  /*! Switch between Bondi [0] or Krumholz [1] accretion rates */
+  int use_krumholz;
+
+  /*! In Krumholz mode, should we include the vorticity term? */
+  int with_krumholz_vorticity;
+
+  /*! Are we applying the angular-momentum-based multiplicative term from
+   * Rosas-Guevara et al. (2015)? */
+  int with_angmom_limiter;
+
+  /*! Normalisation of the viscuous angular momentum accretion reduction */
+  float alpha_visc;
+
+  /*! Maximal fraction of the Eddington rate allowed. */
+  float f_Edd;
+
+  /*! Eddington fraction threshold for recording */
+  float f_Edd_recording;
+
+  /*! Switch for the Booth, Schaye 2009 model */
+  int with_boost_factor;
+
+  /*! Lowest value of the boost of the Booth, Schaye 2009 model */
+  float boost_alpha;
+
+  /*! Power law slope for the boost of the Booth, Schaye 2009 model */
+  float boost_beta;
+
+  /*! Normalisation density (internal units) for the boost of the Booth, Schaye
+   * 2009 model */
+  double boost_n_h_star;
+
+  /*! Switch for nibbling mode */
+  int use_nibbling;
+
+  /*! Minimum gas particle mass in nibbling mode */
+  float min_gas_mass_for_nibbling;
+
+  /* ---- Properties of the feedback model ------- */
+
+  /*! AGN feedback model: isotropic or minimum distance */
+  enum AGN_feedback_models feedback_model;
+
+  /*! Is the AGN feedback model deterministic or stochastic? */
+  int AGN_deterministic;
+
+  /*! Feedback coupling efficiency of the black holes. */
+  float epsilon_f;
+
+  /*! Temperature increase induced by AGN feedback (Kelvin) */
+  float AGN_delta_T_desired;
+
+  /*! Number of gas neighbours to heat in a feedback event */
+  float num_ngbs_to_heat;
+
+  /* ---- Properties of the repositioning model --- */
+
+  /*! Maximal mass of BH to reposition */
+  float max_reposition_mass;
+
+  /*! Maximal distance to reposition, in units of softening length */
+  float max_reposition_distance_ratio;
+
+  /*! Switch to enable a relative velocity limit for particles to which the
+   * black holes can reposition */
+  int with_reposition_velocity_threshold;
+
+  /*! Maximal velocity offset of particles to which the black hole can
+   * reposition, in units of the ambient sound speed of the black hole */
+  float max_reposition_velocity_ratio;
+
+  /*! Minimum value of the velocity repositioning threshold */
+  float min_reposition_velocity_threshold;
+
+  /*! Switch to enable repositioning at fixed (maximum) speed */
+  int set_reposition_speed;
+
+  /*! Normalisation factor for repositioning velocity */
+  float reposition_coefficient_upsilon;
+
+  /*! Repositioning velocity scaling with black hole mass */
+  float reposition_exponent_xi;
+
+  /*! Correct potential of BH? */
+  int correct_bh_potential_for_repositioning;
+
+  /* ---- Properties of the merger model ---------- */
+
+  /*! Mass ratio above which a merger is considered 'minor' */
+  float minor_merger_threshold;
+
+  /*! Mass ratio above which a merger is considered 'major' */
+  float major_merger_threshold;
+
+  /*! Type of merger threshold */
+  enum BH_merger_thresholds merger_threshold_type;
+
+  /*! Maximal distance over which BHs merge, in units of softening length */
+  float max_merging_distance_ratio;
+
+  /* ---- Common conversion factors --------------- */
+
+  /*! Conversion factor from temperature to internal energy */
+  float temp_to_u_factor;
+
+  /* ---- Black hole time-step properties ---------- */
+
+  /*! -- Minimum allowed time-step of BH in internal units */
+  float time_step_min;
+
+  /* ---- Black hole accretion disk parameters ---------- */
+
+  /*! Viscous alpha of the accretion disk, and various factors and powers
+      involving it. Here alpha_factor_x refers to alpha raised to the power of
+      x, expressed as e.g. 0549 if x is 0.549 (these are the decimal
+      expressions for powers of alpha that appear in accretion disk theory).
+      alpha_factor_x_inv refers to the inverse of a similar number, or
+      equivalently alpha raised to the power of -x. alpha_factor_x_inv_10 is
+      alpha * 10 raised to the power of -x, a combination that also often
+      appears in the literature.  */
+  float alpha_acc;
+  float alpha_acc_2;
+  float alpha_acc_2_inv;
+  float alpha_factor_01;
+  float alpha_factor_02;
+  float alpha_factor_08;
+  float alpha_factor_08_inv;
+  float alpha_factor_08_inv_10;
+  float alpha_factor_0549;
+  float alpha_factor_06222;
+
+  /*! Transition accretion rate between thick (ADAF) and thin disk. */
+  float mdot_crit_ADAF;
+
+  /*! Gas-to-total pressure ratio of the accretion disk */
+  float beta_acc;
+  float beta_acc_inv;
+
+  /*! Critical accretion rate that separates two different states in the
+      thick disk regime, related to radiation */
+  float edd_crit_thick;
+
+  /*! Effective adiabatic index of the accretion disk */
+  float gamma_acc;
+
+  /*! Epsilon parameter of the ADAF (thick disk), which appears in
+      Narayan & Yi (1994, 1995) model */
+  float eps_ADAF;
+
+  /*! Numerical coefficient relating sound speed in ADAF (thick disk) to the
+      Keplerian velocity, which appears in Narayan & Yi (1994, 1995) model */
+  float cs_0_ADAF;
+
+  /*! Numerical coefficient relating radial velocity in ADAF (thick disk) to
+   the Keplerian velocity, which appears in Narayan & Yi (1994, 1995) model */
+  float v_0_ADAF;
+
+  /*! Numerical coefficient relating angular velocity in ADAF (thick disk) to
+      the Keplerian angular velocity, which appears in Narayan & Yi
+      (1994, 1995) model */
+  float omega_0_ADAF;
+
+  /*! Aspect ratio of the ADAF (thick disk), which appears in Narayan & Yi
+      (1994, 1995) model */
+  float h_0_ADAF;
+  float h_0_ADAF_2;
+
+  /*! Electron heating parameter of the ADAF (thick disk), which appears in
+      Narayan & Yi (1994, 1995) model */
+  float delta_ADAF;
+
+  /*! The gamma parameter of the slim disk, which appears in the Wang & Zhou
+      (1999) model */
+  float gamma_SD;
+  float gamma_SD_inv;
+
+  /*! The ratio of vertical to horizontal kinematic viscosity of the thin disk.
+      Note that we use the definition xi = nu_2 / nu_1, whereas xi =
+      (nu_2 / nu_1) * 2 * alpha^2 is often used, e.g. Fiacconi et al. (2018) */
+  float xi_TD;
+
+  /*! Which region of the thin disc (Shakura & Sunyaev) we are assuming the
+      subgrid accretion disk is represented as:
+       B - region b; gas pressure dominates over radiation pressure,
+                     electron scattering dominates the opacity
+       C - region c; gas pressure dominates over radiation pressure,
+                     free-free absorption dominates the opacity */
+  enum thin_disc_regions TD_region;
+
+  /*! Parameter controlling when thin disk transitions into slim disk. This
+      occurs when the radiative efficiency of the slim disk falls below
+      TD_SD_eps_r_threshold times the radiative efficiency of the thin disk. */
+  float TD_SD_eps_r_threshold;
+
+  /* ---- Jet feedback - related parameters ---------- */
+
+  /*! Global switch for whether to include jets [1] or not [0]. */
+  int include_jets;
+
+  /*! Global switch for whether to turn off radiative feedback [1] or not [0].
+   */
+  int turn_off_radiative_feedback;
+
+  /*! Global switch for whether to turn off radiation in the thick disk and
+      jets in the thin disk [1] or not [0] */
+  int turn_off_secondary_feedback;
+
+  /* Whether we want to include super-Eddington accretion, modeled as the slim
+     disk */
+  int include_slim_disk;
+
+  /* Whether to use GRMHD fits for the spindown rate due to jets */
+  int include_GRMHD_spindown;
+
+  /* Whether to include the expected suppression of the accretion rate due to
+   * ADIOS winds in the thick disk regime */
+  int include_ADIOS_suppression;
+
+  /* The inner radius in the accretion rate - R relation, if ADIOS suppression
+   * is included */
+  float ADIOS_R_in;
+
+  /* The slope of the accretion rate - R relation, if ADIOS suppression
+   * is included */
+  float ADIOS_s;
+
+  /*! Whether to fix the radiative efficiency to some value [1] or not [0]. */
+  int fix_radiative_efficiency;
+
+  /*! The radiative efficiency to use if fix_radiative_efficiency is 1. If
+       fix_radiative_efficiency is set to 0, this will still be used to
+       define the Eddington accretion rate. */
+  float radiative_efficiency;
+
+  /*! How many particles to aim to kick as part of jet feedback. */
+  int N_jet;
+
+  /*! The type of jet velocity scaling to use. */
+  enum AGN_jet_velocity_models AGN_jet_velocity_model;
+
+  /*! Jet velocity if use_var_v_jet is 0 */
+  float v_jet;
+
+  /*! Parameters of the scaling between AGN jet velocity and BH mass */
+  float v_jet_BH_mass_scaling_reference_mass;
+  float v_jet_BH_mass_scaling_slope;
+  float v_jet_min;
+
+  /*! Sets the launching velocity of the jet to v_jet_cs_ratio times the
+      sound speed of the hot gas in the halo, assuming it is at virial
+      temperature. */
+  float v_jet_cs_ratio;
+
+  /*! The effective (half-)opening angle of the jet. */
+  float opening_angle;
+
+  /*! The slope of the dependence of jet efficiency on aspect ratio of the
+      subgrid accretion disk, H/R. Default value is 1, and another reasonable
+      value is 0 (same jet efficiency for all disks). */
+  float jet_h_r_slope;
+
+  /*! The coupling efficiency for jet feedback. */
+  float eps_f_jet;
+
+  /*! Whether to fix the jet efficiency to some value [1] or not [0]. If yes,
+      the jets will be pointed along the z-axis. */
+  int fix_jet_efficiency;
+
+  /*! The jet efficiency to use if fix_jet_efficiency is 1. */
+  float jet_efficiency;
+
+  /*! The jet launching scheme to use: minimum distance,
+      maximum distance, closest to spin axis or minimum density. */
+  enum AGN_jet_feedback_models jet_feedback_model;
+};
+
+/**
+ * @brief Initialise the black hole properties from the parameter file.
+ *
+ * For the basic black holes neighbour finding properties we use the
+ * defaults from the hydro scheme if the users did not provide specific
+ * values.
+ *
+ * @param bp The #black_holes_props.
+ * @param phys_const The physical constants in the internal unit system.
+ * @param us The internal unit system.
+ * @param params The parsed parameters.
+ * @param hydro_props The already read-in properties of the hydro scheme.
+ * @param cosmo The cosmological model.
+ */
+INLINE static void black_holes_props_init(struct black_holes_props *bp,
+                                          const struct phys_const *phys_const,
+                                          const struct unit_system *us,
+                                          struct swift_params *params,
+                                          const struct hydro_props *hydro_props,
+                                          const struct cosmology *cosmo) {
+
+  /* Read in the basic neighbour search properties or default to the hydro
+     ones if the user did not provide any different values */
+
+  /* Kernel properties */
+  bp->eta_neighbours = parser_get_opt_param_float(
+      params, "BlackHoles:resolution_eta", hydro_props->eta_neighbours);
+
+  /* Tolerance for the smoothing length Newton-Raphson scheme */
+  bp->h_tolerance = parser_get_opt_param_float(params, "BlackHoles:h_tolerance",
+                                               hydro_props->h_tolerance);
+
+  /* Get derived properties */
+  bp->target_neighbours = pow_dimension(bp->eta_neighbours) * kernel_norm;
+  const float delta_eta = bp->eta_neighbours * (1.f + bp->h_tolerance);
+  bp->delta_neighbours =
+      (pow_dimension(delta_eta) - pow_dimension(bp->eta_neighbours)) *
+      kernel_norm;
+
+  /* Number of iterations to converge h */
+  bp->max_smoothing_iterations =
+      parser_get_opt_param_int(params, "BlackHoles:max_ghost_iterations",
+                               hydro_props->max_smoothing_iterations);
+
+  /* Time integration properties */
+  const float max_volume_change =
+      parser_get_opt_param_float(params, "BlackHoles:max_volume_change", -1);
+  if (max_volume_change == -1)
+    bp->log_max_h_change = hydro_props->log_max_h_change;
+  else
+    bp->log_max_h_change = logf(powf(max_volume_change, hydro_dimension_inv));
+
+  /* Initialisation properties  ---------------------------- */
+
+  bp->subgrid_seed_mass =
+      parser_get_param_float(params, "SPINJETAGN:subgrid_seed_mass_Msun");
+
+  /* Convert to internal units */
+  bp->subgrid_seed_mass *= phys_const->const_solar_mass;
+
+  bp->use_subgrid_mass_from_ics = parser_get_opt_param_int(
+      params, "SPINJETAGN:use_subgrid_mass_from_ics", 1);
+  if (bp->use_subgrid_mass_from_ics)
+    bp->with_subgrid_mass_check = parser_get_opt_param_int(
+        params, "SPINJETAGN:with_subgrid_mass_check", 1);
+
+  /* Accretion parameters ---------------------------------- */
+
+  bp->use_subgrid_gas_properties =
+      parser_get_param_int(params, "SPINJETAGN:use_subgrid_gas_properties");
+  bp->use_multi_phase_bondi =
+      parser_get_param_int(params, "SPINJETAGN:use_multi_phase_bondi");
+  if (!bp->use_multi_phase_bondi) {
+    bp->use_krumholz = parser_get_param_int(params, "SPINJETAGN:use_krumholz");
+    bp->with_krumholz_vorticity =
+        parser_get_param_int(params, "SPINJETAGN:with_krumholz_vorticity");
+  }
+
+  bp->with_angmom_limiter =
+      parser_get_param_int(params, "SPINJETAGN:with_angmom_limiter");
+  if (bp->with_angmom_limiter)
+    bp->alpha_visc = parser_get_param_float(params, "SPINJETAGN:viscous_alpha");
+
+  bp->f_Edd =
+      parser_get_param_float(params, "SPINJETAGN:max_eddington_fraction");
+  bp->f_Edd_recording = parser_get_param_float(
+      params, "SPINJETAGN:eddington_fraction_for_recording");
+
+  /*  Booth Schaye (2009) Parameters */
+  bp->with_boost_factor =
+      parser_get_param_int(params, "SPINJETAGN:with_boost_factor");
+
+  if (bp->with_boost_factor) {
+    bp->boost_alpha = parser_get_param_float(params, "SPINJETAGN:boost_alpha");
+
+    bp->boost_beta = parser_get_param_float(params, "SPINJETAGN:boost_beta");
+
+    /* Load the density in cgs and convert to internal units */
+    bp->boost_n_h_star =
+        parser_get_param_float(params, "SPINJETAGN:boost_n_h_star_cm3") /
+        units_cgs_conversion_factor(us, UNIT_CONV_NUMBER_DENSITY);
+  }
+
+  bp->use_nibbling = parser_get_param_int(params, "SPINJETAGN:use_nibbling");
+  if (bp->use_nibbling) {
+    bp->min_gas_mass_for_nibbling = parser_get_param_float(
+        params, "SPINJETAGN:min_gas_mass_for_nibbling_Msun");
+    bp->min_gas_mass_for_nibbling *= phys_const->const_solar_mass;
+  }
+
+  if ((bp->min_gas_mass_for_nibbling < 1e-5 * bp->subgrid_seed_mass) ||
+      (bp->min_gas_mass_for_nibbling > 1e5 * bp->subgrid_seed_mass)) {
+    error(
+        "The BH seeding mass and minimal gas mass for nibbling differ by more "
+        "than 10^5. That is probably indicating a typo in the parameter file.");
+  }
+
+  /* Feedback parameters ---------------------------------- */
+
+  char temp[PARSER_MAX_LINE_SIZE];
+  parser_get_param_string(params, "SPINJETAGN:AGN_feedback_model", temp);
+  if (strcmp(temp, "Isotropic") == 0)
+    bp->feedback_model = AGN_isotropic_model;
+  else if (strcmp(temp, "MinimumDistance") == 0)
+    bp->feedback_model = AGN_minimum_distance_model;
+  else
+    error(
+        "The AGN feedback model must be either MinimumDistance or Isotropic,"
+        " not %s",
+        temp);
+
+  bp->AGN_deterministic = parser_get_opt_param_int(
+      params, "SPINJETAGN:AGN_use_deterministic_feedback", 1);
+  bp->epsilon_f =
+      parser_get_param_float(params, "SPINJETAGN:coupling_efficiency");
+  bp->AGN_delta_T_desired =
+      parser_get_param_float(params, "SPINJETAGN:AGN_delta_T_K");
+  /* Check that it makes sense. */
+  if (bp->AGN_delta_T_desired <= 0.f)
+    error("The AGN heating temperature delta T must be > 0 K, not %.5e K.",
+          bp->AGN_delta_T_desired);
+
+  bp->AGN_delta_T_desired /=
+      units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE);
+
+  bp->num_ngbs_to_heat =
+      parser_get_param_float(params, "SPINJETAGN:AGN_num_ngb_to_heat");
+
+  /* Reposition parameters --------------------------------- */
+
+  bp->max_reposition_mass =
+      parser_get_param_float(params, "SPINJETAGN:max_reposition_mass_Msun");
+
+  /* Convert to internal units */
+  bp->max_reposition_mass *= phys_const->const_solar_mass;
+
+  bp->max_reposition_distance_ratio = parser_get_param_float(
+      params, "SPINJETAGN:max_reposition_distance_ratio");
+
+  bp->with_reposition_velocity_threshold = parser_get_param_int(
+      params, "SPINJETAGN:with_reposition_velocity_threshold");
+
+  if (bp->with_reposition_velocity_threshold) {
+    bp->max_reposition_velocity_ratio = parser_get_param_float(
+        params, "SPINJETAGN:max_reposition_velocity_ratio");
+
+    /* Prevent nonsensical input */
+    if (bp->max_reposition_velocity_ratio <= 0)
+      error("max_reposition_velocity_ratio must be positive, not %f.",
+            bp->max_reposition_velocity_ratio);
+
+    bp->min_reposition_velocity_threshold = parser_get_param_float(
+        params, "SPINJETAGN:min_reposition_velocity_threshold_km_p_s");
+    /* Convert from km/s to internal units */
+    bp->min_reposition_velocity_threshold *=
+        (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs));
+  }
+
+  bp->set_reposition_speed =
+      parser_get_param_int(params, "SPINJETAGN:set_reposition_speed");
+
+  if (bp->set_reposition_speed) {
+    bp->reposition_coefficient_upsilon = parser_get_param_float(
+        params, "SPINJETAGN:reposition_coefficient_upsilon");
+
+    /* Prevent the user from making silly wishes */
+    if (bp->reposition_coefficient_upsilon <= 0)
+      error(
+          "reposition_coefficient_upsilon must be positive, not %f "
+          "km/s/M_sun.",
+          bp->reposition_coefficient_upsilon);
+
+    /* Convert from km/s to internal units */
+    bp->reposition_coefficient_upsilon *=
+        (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs));
+
+    bp->reposition_exponent_xi = parser_get_opt_param_float(
+        params, "SPINJETAGN:reposition_exponent_xi", 1.0);
+  }
+
+  bp->correct_bh_potential_for_repositioning =
+      parser_get_param_int(params, "SPINJETAGN:with_potential_correction");
+
+  /* Merger parameters ------------------------------------- */
+
+  bp->minor_merger_threshold =
+      parser_get_param_float(params, "SPINJETAGN:threshold_minor_merger");
+
+  bp->major_merger_threshold =
+      parser_get_param_float(params, "SPINJETAGN:threshold_major_merger");
+
+  char temp2[PARSER_MAX_LINE_SIZE];
+  parser_get_param_string(params, "SPINJETAGN:merger_threshold_type", temp2);
+  if (strcmp(temp2, "CircularVelocity") == 0)
+    bp->merger_threshold_type = BH_mergers_circular_velocity;
+  else if (strcmp(temp2, "EscapeVelocity") == 0)
+    bp->merger_threshold_type = BH_mergers_escape_velocity;
+  else if (strcmp(temp2, "DynamicalEscapeVelocity") == 0)
+    bp->merger_threshold_type = BH_mergers_dynamical_escape_velocity;
+  else
+    error(
+        "The BH merger model must be either CircularVelocity, EscapeVelocity, "
+        "or DynamicalEscapeVelocity, not %s",
+        temp2);
+
+  bp->max_merging_distance_ratio =
+      parser_get_param_float(params, "SPINJETAGN:merger_max_distance_ratio");
+
+  /* Common conversion factors ----------------------------- */
+
+  /* Calculate temperature to internal energy conversion factor (all internal
+   * units) */
+  const double k_B = phys_const->const_boltzmann_k;
+  const double m_p = phys_const->const_proton_mass;
+  const double mu = hydro_props->mu_ionised;
+  bp->temp_to_u_factor = k_B / (mu * hydro_gamma_minus_one * m_p);
+
+  /* ---- Black hole time-step properties ------------------ */
+
+  const double yr_in_cgs = 365.25 * 24. * 3600.;
+  bp->time_step_min =
+      parser_get_param_float(params, "SPINJETAGN:minimum_timestep_yr") *
+      yr_in_cgs / units_cgs_conversion_factor(us, UNIT_CONV_TIME);
+
+  /* ---- Black hole accretion disk physics  ------------------ */
+
+  /* The viscosisty parameter of the subgrid accretion disks.*/
+  bp->alpha_acc = parser_get_param_float(params, "SPINJETAGN:alpha_acc");
+
+  /* Various factors and powers of alpha, the subgrid accretion disc
+     viscosity, that appear in subgrid accretion disk equations. We
+     precompute them here. */
+  bp->alpha_acc_2 = bp->alpha_acc * bp->alpha_acc;
+  bp->alpha_acc_2_inv = 1. / bp->alpha_acc_2;
+  bp->alpha_factor_01 = pow(bp->alpha_acc, 0.1);
+  bp->alpha_factor_02 = pow(bp->alpha_acc, 0.2);
+  bp->alpha_factor_08 = pow(bp->alpha_acc, 0.8);
+  bp->alpha_factor_08_inv = 1. / bp->alpha_factor_08;
+  bp->alpha_factor_08_inv_10 = pow(bp->alpha_acc * 10., -0.8);
+  bp->alpha_factor_0549 = pow(bp->alpha_acc, 0.549);
+  bp->alpha_factor_06222 = pow(bp->alpha_acc * 10., 0.6222);
+
+  if ((bp->alpha_acc <= 0.) || (bp->alpha_acc > 1.)) {
+    error(
+        "The alpha viscosity parameter of accretion disks must be between 0. "
+        " and 1., not %f",
+        bp->alpha_acc);
+  }
+
+  /* Calculate the critical transition accretion rate between the thick and
+     thin disk regimes. */
+  float mdot_crit_LHAF = 0.2 * bp->alpha_acc_2;
+  float mdot_crit_thin = 0.07 * bp->alpha_acc;
+  bp->mdot_crit_ADAF = (mdot_crit_LHAF + mdot_crit_thin) * 0.5;
+
+  /* Calculate the gas-to-total pressure ratio as based on simulations
+     (see Yuan & Narayan 2014) */
+  bp->beta_acc = 1. / (1. + 2. * bp->alpha_acc);
+  bp->beta_acc_inv = 1. / bp->beta_acc;
+
+  /* Calculate the critical accretion rate between two thick disk regimes as
+     in Mahadevan (1997). */
+  bp->edd_crit_thick = 2. * bp->delta_ADAF * bp->alpha_acc_2 *
+                       (1. - bp->beta_acc) * bp->beta_acc_inv;
+
+  /* Calculate the adiabatic index based on how strong the magnetic fields are
+    (see Esin 1997) */
+  bp->gamma_acc = (8. - 3. * bp->beta_acc) / (6. - 3. * bp->beta_acc);
+
+  /* Calculate numerical factors of the ADAF (thick disk) as in
+     Narayan & Yi (1995) */
+  bp->eps_ADAF = (1.6667 - bp->gamma_acc) / (bp->gamma_acc - 1.);
+  bp->cs_0_ADAF = sqrtf(2. / (5. + 2. * bp->eps_ADAF));
+  bp->v_0_ADAF = 3. / (5. + 2. * bp->eps_ADAF);
+  bp->omega_0_ADAF = sqrtf(2. * bp->eps_ADAF / (5. + 2. * bp->eps_ADAF));
+
+  /* Instead of using the Narayan & Yi value, we set to 0.3 based on numerous
+     GRMHD simulations (see e.g. Narayan et al. 2021) */
+  bp->h_0_ADAF = 0.3;
+  bp->h_0_ADAF_2 = 0.09;
+
+  bp->delta_ADAF = parser_get_param_float(params, "SPINJETAGN:delta_ADAF");
+
+  if (bp->delta_ADAF <= 0.) {
+    error(
+        "The delta electron heating parameter of thick accretion disks "
+        " must be > 0. not %f",
+        bp->delta_ADAF);
+  }
+
+  bp->gamma_SD = sqrtf(5.);
+  bp->gamma_SD_inv = 1. / bp->gamma_SD;
+
+  /* Formula taken from Lodato et al. (2010) */
+  bp->xi_TD = 2. * (1. + 7. * bp->alpha_acc_2) / (4. + bp->alpha_acc_2) /
+              bp->alpha_acc_2;
+
+  char temp3[PARSER_MAX_LINE_SIZE];
+  parser_get_param_string(params, "SPINJETAGN:TD_region", temp3);
+  if (strcmp(temp3, "B") == 0)
+    bp->TD_region = TD_region_B;
+  else if (strcmp(temp3, "C") == 0)
+    bp->TD_region = TD_region_C;
+  else
+    error("The choice of thin disc region must be B or C, not %s", temp3);
+
+  /* ---- Jet feedback - related parameters ---------- */
+  bp->include_jets = parser_get_param_int(params, "SPINJETAGN:include_jets");
+
+  if ((bp->include_jets != 0) && (bp->include_jets != 1)) {
+    error("The include_jets parameter must be either 0 or 1, not %d",
+          bp->include_jets);
+  }
+
+  bp->turn_off_radiative_feedback =
+      parser_get_param_int(params, "SPINJETAGN:turn_off_radiative_feedback");
+
+  if ((bp->turn_off_radiative_feedback != 0) &&
+      (bp->turn_off_radiative_feedback != 1)) {
+    error(
+        "The turn_off_radiative_feedback parameter must be either 0 or 1, "
+        " not %d",
+        bp->turn_off_radiative_feedback);
+  }
+
+  if ((bp->turn_off_radiative_feedback) && (bp->include_jets == 0)) {
+    error(
+        "The turn_off_radiative_feedback parameter and include_jet parameters"
+        "cannot at the same time be 1 and 0, respectively. In other words, at"
+        "least one of the two feedback modes must be turned on.");
+  }
+
+  bp->turn_off_secondary_feedback =
+      parser_get_param_int(params, "SPINJETAGN:turn_off_secondary_feedback");
+
+  if ((bp->turn_off_secondary_feedback != 0) &&
+      (bp->turn_off_secondary_feedback != 1)) {
+    error(
+        "The turn_off_secondary_feedback parameter must be either 0 or 1, "
+        "not %d",
+        bp->turn_off_secondary_feedback);
+  }
+
+  bp->include_slim_disk =
+      parser_get_param_int(params, "SPINJETAGN:include_slim_disk");
+
+  if ((bp->include_slim_disk != 0) && (bp->include_slim_disk != 1)) {
+    error(
+        "The include_slim_disk parameter must be either 0 or 1, "
+        "not %d",
+        bp->include_slim_disk);
+  }
+
+  bp->include_GRMHD_spindown =
+      parser_get_param_int(params, "SPINJETAGN:include_GRMHD_spindown");
+
+  if ((bp->include_GRMHD_spindown != 0) && (bp->include_GRMHD_spindown != 1)) {
+    error(
+        "The include_GRMHD_spindown parameter must be either 0 or 1, "
+        "not %d",
+        bp->include_GRMHD_spindown);
+  }
+
+  bp->include_ADIOS_suppression =
+      parser_get_param_int(params, "SPINJETAGN:include_ADIOS_suppression");
+
+  if ((bp->include_ADIOS_suppression != 0) &&
+      (bp->include_ADIOS_suppression != 1)) {
+    error(
+        "The include_ADIOS_suppression parameter must be either 0 or 1, "
+        "not %d",
+        bp->include_ADIOS_suppression);
+  }
+
+  bp->ADIOS_R_in = parser_get_param_float(params, "SPINJETAGN:ADIOS_R_in");
+
+  if (bp->ADIOS_R_in <= 1.) {
+    error(
+        "The ADIOS_R_in parameter must be > 1, "
+        "not %f",
+        bp->ADIOS_R_in);
+  }
+
+  bp->ADIOS_s = parser_get_param_float(params, "SPINJETAGN:ADIOS_s");
+
+  if ((bp->ADIOS_s < 0.) || (bp->ADIOS_s > 1.)) {
+    error(
+        "The ADIOS_s parameter must be between 0 and 1, "
+        "not %f",
+        bp->ADIOS_s);
+  }
+
+  bp->TD_SD_eps_r_threshold =
+      parser_get_param_float(params, "SPINJETAGN:TD_SD_eps_r_threshold");
+
+  if ((bp->TD_SD_eps_r_threshold <= 0.) || (bp->TD_SD_eps_r_threshold >= 1.)) {
+    error(
+        "The TD_SD_eps_r_threshold parameter governing the transition between"
+        "thin and slim disk must be between 0. and 1., not %f",
+        bp->TD_SD_eps_r_threshold);
+  }
+
+  bp->N_jet = parser_get_param_float(params, "SPINJETAGN:N_jet");
+
+  if (bp->N_jet % 2 != 0) {
+    error("The N_jet parameter must be divisible by two, not %d", bp->N_jet);
+  }
+
+  char temp4[PARSER_MAX_LINE_SIZE];
+  parser_get_param_string(params, "SPINJETAGN:AGN_jet_velocity_model", temp4);
+  if (strcmp(temp4, "Constant") == 0) {
+    bp->AGN_jet_velocity_model = AGN_jet_velocity_constant;
+
+    bp->v_jet = parser_get_param_float(params, "SPINJETAGN:v_jet_km_p_s");
+
+    /* Convert to internal units */
+    bp->v_jet *= (1e5 / (us->UnitLength_in_cgs / us->UnitTime_in_cgs));
+
+    if (bp->v_jet <= 0.)
+      error("The v_jet parameter must be > 0., not %f", bp->v_jet);
+
+  } else if (strcmp(temp4, "BlackHoleMass") == 0) {
+    bp->AGN_jet_velocity_model = AGN_jet_velocity_BH_mass;
+
+    bp->v_jet_BH_mass_scaling_reference_mass = parser_get_param_float(
+        params, "SPINJETAGN:v_jet_BH_mass_scaling_reference_mass_Msun");
+    bp->v_jet_BH_mass_scaling_reference_mass *= phys_const->const_solar_mass;
+    bp->v_jet_BH_mass_scaling_slope = parser_get_param_float(
+        params, "SPINJETAGN:v_jet_BH_mass_scaling_slope");
+
+    bp->v_jet_min =
+        parser_get_param_float(params, "SPINJETAGN:v_jet_min_km_p_s");
+
+    bp->v_jet_cs_ratio =
+        parser_get_param_float(params, "SPINJETAGN:v_jet_cs_ratio");
+
+  } else if (strcmp(temp4, "HaloMass") == 0) {
+    error(
+        "The scaling of jet velocities with halo mass is currently not "
+        "supported.");
+  } else {
+    error(
+        "The AGN jet velocity model must be Constant, BlackHoleMass or"
+        " HaloMass, not %s",
+        temp4);
+  }
+
+  bp->opening_angle =
+      parser_get_param_float(params, "SPINJETAGN:opening_angle_in_degrees");
+  bp->opening_angle = bp->opening_angle * M_PI / 180.;
+
+  bp->jet_h_r_slope =
+      parser_get_param_float(params, "SPINJETAGN:jet_h_r_slope");
+
+  bp->eps_f_jet = parser_get_param_float(params, "SPINJETAGN:eps_f_jet");
+
+  if ((bp->eps_f_jet <= 0.) || (bp->eps_f_jet > 1.)) {
+    error(
+        "The eps_f_jet corresponding to the jet coupling efficiency "
+        "must be between 0. and 1., not %f",
+        bp->eps_f_jet);
+  }
+
+  bp->fix_jet_efficiency =
+      parser_get_param_int(params, "SPINJETAGN:fix_jet_efficiency");
+
+  if ((bp->fix_jet_efficiency != 0) && (bp->fix_jet_efficiency != 1)) {
+    error(
+        "The fix_jet_efficiency parameter must be either 0 or 1, "
+        "not %d",
+        bp->fix_jet_efficiency);
+  }
+
+  bp->jet_efficiency =
+      parser_get_param_float(params, "SPINJETAGN:jet_efficiency");
+
+  if (bp->jet_efficiency <= 0.) {
+    error(
+        "The jet_efficiency corresponding to the jet efficiency "
+        "must be larger than 0., not %f",
+        bp->jet_efficiency);
+  }
+
+  bp->fix_radiative_efficiency =
+      parser_get_param_int(params, "SPINJETAGN:fix_radiative_efficiency");
+
+  if ((bp->fix_radiative_efficiency != 0) &&
+      (bp->fix_radiative_efficiency != 1)) {
+    error(
+        "The fix_radiative_efficiency parameter must be either 0 or 1, "
+        "not %d",
+        bp->fix_radiative_efficiency);
+  }
+
+  bp->radiative_efficiency =
+      parser_get_param_float(params, "SPINJETAGN:radiative_efficiency");
+
+  if ((bp->radiative_efficiency <= 0.) || (bp->radiative_efficiency > 1.)) {
+    error(
+        "The radiative_efficiency corresponding to the radiative efficiency "
+        "must be between 0. and 1., not %f",
+        bp->radiative_efficiency);
+  }
+
+  char temp5[60];
+  parser_get_param_string(params, "SPINJETAGN:AGN_jet_feedback_model", temp5);
+  if (strcmp(temp5, "MinimumDistance") == 0)
+    bp->jet_feedback_model = AGN_jet_minimum_distance_model;
+  else if (strcmp(temp5, "MaximumDistance") == 0)
+    bp->jet_feedback_model = AGN_jet_maximum_distance_model;
+  else if (strcmp(temp5, "SpinAxis") == 0)
+    bp->jet_feedback_model = AGN_jet_spin_axis_model;
+  else if (strcmp(temp5, "MinimumDensity") == 0)
+    bp->jet_feedback_model = AGN_jet_minimum_density_model;
+  else
+    error(
+        "The AGN feedback model must be MinimumDistance, MaximumDistance, "
+        "SpinAxis or MinimumDensity, not %s",
+        temp5);
+}
+
+/**
+ * @brief Write a black_holes_props struct to the given FILE as a stream of
+ * bytes.
+ *
+ * @param props the black hole properties struct
+ * @param stream the file stream
+ */
+INLINE static void black_holes_struct_dump(
+    const struct black_holes_props *props, FILE *stream) {
+  restart_write_blocks((void *)props, sizeof(struct black_holes_props), 1,
+                       stream, "black_holes props", "black holes props");
+}
+
+/**
+ * @brief Restore a black_holes_props struct from the given FILE as a stream of
+ * bytes.
+ *
+ * @param props the black hole properties struct
+ * @param stream the file stream
+ */
+INLINE static void black_holes_struct_restore(
+    const struct black_holes_props *props, FILE *stream) {
+  restart_read_blocks((void *)props, sizeof(struct black_holes_props), 1,
+                      stream, NULL, "black holes props");
+}
+
+#endif /* SWIFT_SPIN_JET_BLACK_HOLES_PROPERTIES_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_spin.h b/src/black_holes/SPIN_JET/black_holes_spin.h
new file mode 100644
index 0000000000000000000000000000000000000000..d1289686422944c165c9d49feaa79334d3d93fa3
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_spin.h
@@ -0,0 +1,1042 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2022 Filip Husko (filip.husko@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#ifndef SWIFT_SPIN_JET_BLACK_HOLES_SPIN_H
+#define SWIFT_SPIN_JET_BLACK_HOLES_SPIN_H
+
+/* Standard headers */
+#include <float.h>
+
+/* Local includes */
+#include "black_holes_properties.h"
+#include "black_holes_struct.h"
+#include "inline.h"
+#include "physical_constants.h"
+
+/**
+ * @brief Compute the radius of the horizon of a BH particle in gravitational
+ * units.
+ *
+ * @param a Black hole spin, -1 < a < 1.
+ */
+__attribute__((always_inline)) INLINE static float r_hor(float a) {
+  return 1. + sqrtf((1. - a) * (1. + a));
+}
+
+/**
+ * @brief Compute the radius of the innermost stable circular orbit of a
+ * BH particle in gravitational units.
+ *
+ * The expression is given in Appendix B of Fiacconi et al. (2018) or eqn. 4 in
+ * Griffin et al. (2019).
+ *
+ * @param a Black hole spin, -1 < a < 1.
+ */
+__attribute__((always_inline)) INLINE static float r_isco(float a) {
+  const float Z1 = 1. + (cbrtf((1. + fabsf(a)) * (1. - a * a)) +
+                         cbrtf((1. - fabsf(a)) * (1. - a * a)));
+  const float Z2 = sqrtf(3. * a * a + Z1 * Z1);
+
+  const float R_ISCO =
+      3. + Z2 - a / fabsf(a) * sqrtf((3. - Z1) * (3. + Z1 + 2. * Z2));
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (Z1 > 3.) {
+    error(
+        "Something went wrong with calculation of Z1 factor for r_isco of"
+        " black holes. Z1 is %f instead of Z1 > 3.",
+        Z1);
+  }
+
+  if ((3. + Z1 + 2. * Z2) < 0.) {
+    error(
+        "Something went wrong with calculation of (3. + Z1 + 2. * Z2 ) "
+        "factor for r_isco of black holes. (3. + Z1 + 2. * Z2 ) is %f instead "
+        "of"
+        " (3. + Z1 + 2. * Z2 ) > 0.",
+        3. + Z1 + 2. * Z2);
+  }
+
+  if (R_ISCO < 1.) {
+    error(
+        "Something went wrong with calculation of R_ISCO of black holes. "
+        "R_ISCO is %f instead >= 1.",
+        R_ISCO);
+  }
+#endif
+
+  return R_ISCO;
+}
+
+/**
+ * @brief Compute the magnitude of the angular momentum of the black hole
+ * given its spin.
+ *
+ * @param a Black hole spin magnitude, 0 < a < 1.
+ * @param constants Physical constants (in internal units).
+ */
+__attribute__((always_inline)) INLINE static float j_BH(
+    struct bpart* bp, const struct phys_const* constants) {
+
+  const float J_BH =
+      fabs(bp->subgrid_mass * bp->subgrid_mass * bp->spin *
+           constants->const_newton_G / constants->const_speed_light_c);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (J_BH <= 0.) {
+    error(
+        "Something went wrong with calculation of j_BH of black holes. "
+        " J_BH is %f instead of J_BH > 0.",
+        J_BH);
+  }
+#endif
+
+  return J_BH;
+}
+
+/**
+ * @brief Compute the gravitational radius of a black hole.
+ *
+ * @param a Black hole mass.
+ * @param constants Physical constants (in internal units).
+ */
+__attribute__((always_inline)) INLINE static float R_gravitational(
+    float mass, const struct phys_const* constants) {
+
+  const float r_G =
+      mass * constants->const_newton_G /
+      (constants->const_speed_light_c * constants->const_speed_light_c);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (r_G <= 0.) {
+    error(
+        "Something went wrong with calculation of R_G of black holes. "
+        " R_G is %f instead of R_G > 0.",
+        r_G);
+  }
+#endif
+
+  return r_G;
+}
+
+/**
+ * @brief Compute the warp radius of a black hole particle.
+ *
+ * The result depends on bp->accretion_mode (thick disk, thin disk or
+ * slim disk). For the thick disk and slim disk, the radius is calculated
+ * from Lubow et al. (2002), eqn. 22 with x=1. The result will be different
+ * only due to different aspect ratios H/R=h_0.
+ *
+ * For the thin disk, the result depends on props->TD_region (B - region b from
+ * Shakura & Sunyaev 1973, C - region c from Shakura & Sunyaev 1973). The warp
+ * radii are taken as eqns. 11 from Griffin et al. (2019) and A8 from Fiacconi
+ * et al. (2018), respectively.
+ *
+ * For the thin disk we also have to include the possibility that the self-
+ * gravity radius is smaller than the warp radius. In this case r_warp=r_sg
+ * because the disk cannot be larger than the self-gravity radius, and the
+ * entire disk is warped. The sg radius is taken as eqns. 16 in Griffin et al.
+ * (2019) and A6 in Fiacconi et al. (2018), respectively.
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float r_warp(
+    struct bpart* bp, const struct phys_const* constants,
+    const struct black_holes_props* props) {
+
+  /* Define placeholder variable for the result */
+  float Rw = -1.;
+
+  /* Gravitational radius */
+  const float R_G = R_gravitational(bp->subgrid_mass, constants);
+
+  /* Start branching depending on which accretion mode the BH is in */
+  if (bp->accretion_mode == BH_thick_disc) {
+
+    /* Eqn. 22 from Lubow et al. (2002) with H/R=h_0_ADAF (thick disk) */
+    const float base = 15.36 * fabsf(bp->spin) / props->h_0_ADAF_2;
+    Rw = R_G * powf(base, 0.4);
+  } else if (bp->accretion_mode == BH_slim_disc) {
+
+    /* Eqn. 22 from Lubow et al. (2002) with H/R=1/gamma_SD (slim disk) */
+    const float base = 15.36 * fabsf(bp->spin) * props->gamma_SD;
+    Rw = R_G * powf(base, 0.4);
+  } else if (bp->accretion_mode == BH_thin_disc) {
+
+    /* Start branching depending on which region of the thin disk we wish to
+       base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973,
+       or TD_region=C: region c) */
+    if (props->TD_region == TD_region_B) {
+
+      /* Calculate different factors in eqn. 11 (Griffin et al. 2019) for warp
+          radius of region b in Shakura & Sunyaev (1973) */
+      float mass_factor =
+          powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), 0.2);
+      float edd_factor = powf(bp->eddington_fraction, 0.4);
+
+      /* Gather the factors and finalize calculation */
+      const float base = mass_factor * fabsf(bp->spin) /
+                         (props->xi_TD * props->alpha_factor_08 * edd_factor);
+      const float rw = 3410. * 2. * R_G * powf(base, 0.625);
+
+      /* Self-gravity radius in region b: eqn. 16 in Griffin et al. */
+      mass_factor =
+          powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), -0.961);
+      edd_factor = powf(bp->eddington_fraction, -0.353);
+
+      const float rs = 4790. * 2. * R_G * mass_factor *
+                       props->alpha_factor_0549 * edd_factor;
+
+      /* Take the minimum */
+      Rw = min(rs, rw);
+    }
+
+    if (props->TD_region == TD_region_C) {
+
+      /* Calculate different factors in eqn. A8 (Fiacconi et al. 2018) */
+      float mass_factor =
+          powf(bp->subgrid_mass / (1e6 * constants->const_solar_mass), 0.2);
+      float edd_factor = powf(bp->eddington_fraction, 0.3);
+
+      /* Gather the factors and finalize calculation */
+      const float base = mass_factor * fabsf(bp->spin) /
+                         (props->xi_TD * props->alpha_factor_02 * edd_factor);
+      const float rw = 1553. * 2. * R_G * powf(base, 0.5714);
+
+      /* Repeat the same for self-gravity radius - eqn. A6 in F2018 */
+      mass_factor =
+          powf(bp->subgrid_mass / (1e6 * constants->const_solar_mass), -1.1556);
+      edd_factor = powf(bp->eddington_fraction, -0.48889);
+
+      const float rs = 1.2 * 100000. * 2. * R_G * mass_factor *
+                       props->alpha_factor_06222 * edd_factor;
+
+      /* Take the minimum */
+      Rw = min(rs, rw);
+    }
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (Rw < 0.) {
+    error(
+        "Something went wrong with calculation of Rw of black holes. "
+        " Rw is %f instead of Rw >= 0.",
+        Rw);
+  }
+#endif
+
+  return Rw;
+}
+
+/**
+ * @brief Compute the warp mass of a black hole particle.
+ *
+ * Calculated as the integral of the surface density of the disk up to R_warp.
+ * The result again depends on type of accretion mode, both due to different
+ * R_warp and different surface densities.
+ *
+ * The surface densities for the thick and slim disk take the same form
+ * (eqn. 2.3 in Narayan & Yi 1995 for rho, and then sigma = rho * 2H =
+ * dot(M_BH) / (2pi * R * abs(v_r))). They differ due to different radial
+ * radial velocities in the disks: v_r = -alpha * v_0 * v_K (with v_K
+ * the Keplerian velocity). These differences are encoded in the numerical
+ * constant v_0, which depends on alpha in Narayan & Yi for the thick disk,
+ * and is roughly constant for the slim disk (Wang & Zhou 1999).
+ *
+ * For the thin disk the surface densities are more complex, and again depend
+ * on which region of the disk is chosen to be modelled (region b or c from
+ * Shakura & Sunyaev 1973). Sigma for region b is given by eqn. 7 in Griffin
+ * et al. (2019) and for region c, it is not given explicitly but can be
+ * calculated based on Appendix A in Fiacconi et al. (2018).
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float m_warp(
+    struct bpart* bp, const struct phys_const* constants,
+    const struct black_holes_props* props) {
+
+  /* Define placeholder variable for the result */
+  float Mw = -1.;
+
+  /* Gravitational radius */
+  const float R_G = R_gravitational(bp->subgrid_mass, constants);
+
+  /* Start branching depending on which accretion mode the BH is in */
+  if ((bp->accretion_mode == BH_thick_disc) ||
+      (bp->accretion_mode == BH_slim_disc)) {
+
+    /* Define v_0, the only factor which differs between thick and slim
+       disc */
+    float v_0;
+    if (bp->accretion_mode == BH_thick_disc) {
+      v_0 = props->v_0_ADAF;
+    } else {
+      v_0 = props->gamma_SD_inv;
+    }
+
+    /* Final result based on eqn. 2.3 in Narayan & Yi 1995*/
+    Mw = 2. * bp->accretion_rate /
+         (3. * props->alpha_acc * v_0 *
+          sqrtf(bp->subgrid_mass * constants->const_newton_G)) *
+         powf(r_warp(bp, constants, props), 1.5);
+  } else {
+
+    /* Start branching depending on which region of the thin disk we wish to
+       base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973,
+       or TD_region=C: region c) */
+    if (props->TD_region == TD_region_B) {
+
+      /* Calculate different factors that appear in result for M_warp */
+      const float mass_factor =
+          powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), 2.2);
+      const float edd_factor = powf(bp->eddington_fraction, 0.6);
+      const float R_factor =
+          powf(r_warp(bp, constants, props) / (2. * R_G), 1.4);
+
+      /* Gather factors and finalize calculation */
+      Mw = constants->const_solar_mass * 1.35 * mass_factor *
+           props->alpha_factor_08_inv * edd_factor * R_factor;
+    }
+    if (props->TD_region == TD_region_C) {
+
+      /* Same as above but for region c of disk */
+      const float mass_factor =
+          powf(bp->subgrid_mass / (1e6 * constants->const_solar_mass), 2.2);
+      const float edd_factor = powf(bp->eddington_fraction, 0.7);
+      const float R_factor =
+          powf(r_warp(bp, constants, props) / (2. * R_G), 1.25);
+
+      Mw = constants->const_solar_mass * 0.01 * mass_factor *
+           props->alpha_factor_08_inv_10 * edd_factor * R_factor;
+    }
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (Mw < 0.) {
+    error(
+        "Something went wrong with calculation of Mw of black holes. "
+        " Mw is %f instead of Mw >= 0.",
+        Mw);
+  }
+#endif
+
+  return Mw;
+}
+
+/**
+ * @brief Compute the warp angular momentum of a black hole particle.
+ *
+ * Calculated as the integral of the surface density times the specific
+ * angular momentum of the disk up to R_warp. The result depends on type
+ * of accretion mode, due to different R_warp, surface densities and spec.
+ * ang. momenta of the disks.
+ *
+ * The surface densities are the same as for M_warp. For the thin disk, the
+ * spec. ang. mom. is L(R) = R * v_K(R), because orbits are perfectly circular.
+ * For the thick and slim disk, this is replaced by L(R) = Omega_0 * R * v_K(R)
+ * , with Omega_0 a numerical constant between 0 and 1 which encodes the fact
+ * that rotation is slower in the two disks. The values for Omega_0 are given
+ * in Narayan & Yi (1995) and Wang & Zhou (1999) for the thick and slim disk,
+ * respectively.
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float j_warp(
+    struct bpart* bp, const struct phys_const* constants,
+    const struct black_holes_props* props) {
+
+  /* Define placeholder variable for the result */
+  float Jw = -1.;
+
+  /* Start branching depending on which accretion mode the BH is in */
+  if ((bp->accretion_mode == BH_thick_disc) ||
+      (bp->accretion_mode == BH_slim_disc)) {
+
+    /* Get numerical constants for radial and tangential velocities for the
+       thick and slim disk, which factor into the surface density and spec.
+       ang. mom., respectively */
+    float v_0 = 0.;
+    float omega_0 = 0.;
+    if (bp->accretion_mode == BH_thick_disc) {
+      v_0 = props->v_0_ADAF;
+      omega_0 = props->omega_0_ADAF;
+    } else {
+      v_0 = props->gamma_SD_inv;
+      omega_0 = props->gamma_SD_inv;
+    }
+
+    /* Gather factors for the final result  */
+    Jw = 2. * bp->accretion_rate * omega_0 / (2. * props->alpha_acc * v_0) *
+         r_warp(bp, constants, props) * r_warp(bp, constants, props);
+  } else {
+
+    /* Start branching depending on which region of the thin disk we wish to
+       base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973,
+       or TD_region=C: region c). The warp radius can generally be related to
+       the warp mass and radius, if one assumes Keplerian rotation, with the
+       following relation: J_warp = (c+2)/(c+5/2) * M_warp * sqrt(M_BH * G *
+       R_warp), where c is the slope of the surface density profile: sigma~R^c.
+       For region b, c=-3/5 (see Griffin et al. 2019), and for region c, c=-3/4
+       (see Fiacconi et al. 2018). */
+    if (props->TD_region == TD_region_B) {
+      Jw = 0.737 * m_warp(bp, constants, props) *
+           sqrtf(bp->subgrid_mass * constants->const_newton_G *
+                 r_warp(bp, constants, props));
+    }
+    if (props->TD_region == TD_region_C) {
+      Jw = 0.714 * m_warp(bp, constants, props) *
+           sqrtf(bp->subgrid_mass * constants->const_newton_G *
+                 r_warp(bp, constants, props));
+    }
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (Jw < 0.) {
+    error(
+        "Something went wrong with calculation of Jw of black holes. "
+        " Jw is %f instead of Jw >= 0.",
+        Jw);
+  }
+#endif
+
+  return Jw;
+}
+
+/**
+ * @brief Compute the spin-dependant radiative efficiency of a BH particle in
+ * the radiatively efficient (thin disc) regime.
+ *
+ * This is eqn. 3 in Griffin et al. (2019), based on Novikov & Thorne (1973).
+ *
+ * @param a Black hole spin, -1 < a < 1.
+ */
+__attribute__((always_inline)) INLINE static float eps_NT(float a) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (r_isco(a) <= 0.6667) {
+    error(
+        "Something went wrong with calculation of eps_NT of black holes. "
+        " r_isco is %f instead of r_isco > 1.",
+        r_isco(a));
+  }
+#endif
+
+  return 1. - sqrtf(1. - 2. / 3. / r_isco(a));
+}
+
+/**
+ * @brief Compute the spin- and accretion rate-dependant radiative efficiency
+ * of a BH particle in the super-Eddington (slim disk) regime.
+ *
+ * This is eqn. 3 in Madau et al. (2014), which is based on numerical GR
+ * results by Sadowski (2009).
+ *
+ * @param a Black hole spin, -1 < a < 1.
+ * @param m_dot Accretion rate normalized to the Eddington rate.
+ */
+__attribute__((always_inline)) INLINE static float eps_SD(float a, float mdot) {
+  const float B = powf(4.627 - 4.445 * a, -0.5524);
+  const float C = powf(827.3 - 718.1 * a, -0.706);
+  const float A = powf(0.9663 - 0.9292 * a, -0.5693);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (mdot <= 0.) {
+    error(
+        "The calculation of eps_SD was called even though mdot is %f. "
+        " This function should not have been called if the accretion rate is "
+        " not > 0.",
+        mdot);
+  }
+#endif
+
+  return 0.1 / mdot * (0.985 / (B + 1.6 / mdot) + 0.015 / (C + 1.6 / mdot)) * A;
+}
+
+/**
+ * @brief Decide which regime (mode) of accretion the BH particle is in.
+ *
+ * The possible modes are the thick disk, thin disk and slim disk, in
+ * order of increasing accretion rate. The transition from thick to thin disk
+ * is at 0.4*alpha^2, based on current theory (Yuan & Narayan 2014). The
+ * transition from thin to slim disk occurs when the slim disk efficiency
+ * becomes sufficiently weak compared to the thin disk one. We parametrize
+ * this transition as occuring at eps_SD = props->TD_SD_eps_r_threshold *
+ * eps_TD, with props->TD_SD_eps_r_threshold < 1 of order 0.5
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static void decide_mode(
+    struct bpart* bp, const struct black_holes_props* props) {
+  if (bp->eddington_fraction < props->mdot_crit_ADAF) {
+    bp->accretion_mode = BH_thick_disc;
+  } else {
+    if ((eps_SD(bp->spin, bp->eddington_fraction) <
+         props->TD_SD_eps_r_threshold * eps_NT(bp->spin)) &&
+        (props->include_slim_disk)) {
+      bp->accretion_mode = BH_slim_disc;
+    } else {
+      bp->accretion_mode = BH_thin_disc;
+    }
+  }
+
+  /* If we do not include radiative feedback, then we force the disk to be in
+     the thick disk mode */
+  if (props->turn_off_radiative_feedback) {
+    bp->accretion_mode = BH_thick_disc;
+  }
+
+  /* similar for if we do not include jets - we force the disk to be thin */
+  if (props->include_jets == 0) {
+    bp->accretion_mode = BH_thin_disc;
+  }
+}
+
+/**
+ * @brief Compute the aspect ratio of the subgrid accretion disk.
+ *
+ * The result depends on bp->accretion_mode (thick disk, thin disk or
+ * slim disk). For the thick disk and slim disk, the aspect ratio is
+ * a constant, H/R = h_0.
+ *
+ * For the thin disk, the result depends on props->TD_region (B - region b from
+ * Shakura & Sunyaev 1973, C - region c from Shakura & Sunyaev 1973). In region
+ * b, we take H/R as eqn. 8 in Griffin et al. (2019), and in region c H/r is
+ * taken directly as eqn. 2.19 from Shakura & Sunyaev (1973).
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float aspect_ratio(
+    struct bpart* bp, const struct phys_const* constants,
+    const struct black_holes_props* props) {
+
+  /* Define placeholder variable for the result */
+  float h_0 = -1.;
+
+  /* Start branching depending on which accretion mode the BH is in */
+  if ((bp->accretion_mode == BH_thick_disc) ||
+      (bp->accretion_mode == BH_slim_disc)) {
+    if (bp->accretion_mode == BH_thick_disc) {
+      h_0 = props->h_0_ADAF;
+    } else {
+      h_0 = 0.5 * props->gamma_SD_inv;
+    }
+  } else {
+
+    /* Start branching depending on which region of the thin disk we wish to
+       base the model upon (TD_region=B: region b from Shakura & Sunyaev 1973,
+       or TD_region=C: region c). */
+    if (props->TD_region == TD_region_B) {
+
+      /* Compute factors for eqn. 8 in Griffin et al. (2019). */
+      const float mass_factor =
+          powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), -0.1);
+      const float edd_factor = powf(bp->eddington_fraction, 0.2);
+      const float R_G = R_gravitational(bp->subgrid_mass, constants);
+      const float R_factor =
+          powf(r_warp(bp, constants, props) / (2. * R_G), 0.05);
+
+      /* Gather factors and finalize calculation. */
+      h_0 = 1.25 * 0.001 * mass_factor * props->alpha_factor_01 * edd_factor *
+            R_factor;
+    }
+    if (props->TD_region == TD_region_C) {
+
+      /* Compute factors for eqn. 2.19 in Shakura & Sunyaev (1973). */
+      const float mass_factor =
+          powf(bp->subgrid_mass / (1e8 * constants->const_solar_mass), -0.1);
+      const float edd_factor = powf(bp->eddington_fraction, 0.15);
+      const float R_G = R_gravitational(bp->subgrid_mass, constants);
+      const float R_factor =
+          powf(r_warp(bp, constants, props) / (2. * R_G), 0.125);
+
+      /* Gather factors and finalize calculation. */
+      h_0 = 1.15 * 0.001 * mass_factor * props->alpha_factor_01 * edd_factor *
+            R_factor;
+    }
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (h_0 <= 0.) {
+    error(
+        "Something went wrong with calculation of h_0 of black holes. "
+        " h_0 is %f instead of h_0 > 0.",
+        h_0);
+  }
+#endif
+
+  return h_0;
+}
+
+/**
+ * @brief Compute the jet efficiency of a BH particle.
+ *
+ * The result depends on bp->accretion_mode (thick disk, thin disk or
+ * slim disk), through the varying H/R aspect ratios.
+ *
+ * The equation implemented is eqn. 9 from Tchekhovskoy et al. (2010), with the
+ * dimensionless magnetic flux phi taken as eqn. 9 from Narayan et al. (2021).
+ *
+ * The dependence on the aspect ratio comes from results in Tchekhovskoy et al.
+ * (2014) and the dependence in classical Blandford & Znajek (1979) jet theory.
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float jet_efficiency(
+    struct bpart* bp, const struct black_holes_props* props) {
+
+  float jet_eff = -1.;
+  if (props->fix_jet_efficiency) {
+    jet_eff = props->jet_efficiency;
+  } else {
+    const float kappa = 0.05;
+    const float horizon_ang_vel =
+        bp->spin / (2. * (1. + sqrtf(1. - bp->spin * bp->spin)));
+    const float phi = -20.2 * bp->spin * bp->spin * bp->spin -
+                      14.9 * bp->spin * bp->spin + 34. * bp->spin + 52.6;
+    jet_eff = kappa * 0.25 * M_1_PI * phi * phi *
+              powf(bp->aspect_ratio * 3.333, props->jet_h_r_slope) *
+              horizon_ang_vel * horizon_ang_vel *
+              (1. + 1.38 * horizon_ang_vel * horizon_ang_vel -
+               9.2 * horizon_ang_vel * horizon_ang_vel * horizon_ang_vel *
+                   horizon_ang_vel);
+  }
+
+  /* Turn off jet feedback if we want to do that */
+  if (props->include_jets == 0) {
+    jet_eff = 0.;
+  }
+
+  /* Turn off jets in thin disk mode if we want to do that */
+  if ((props->turn_off_secondary_feedback) &&
+      (bp->accretion_mode == BH_thin_disc)) {
+    jet_eff = 0.;
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (jet_eff < 0.) {
+    error(
+        "Something went wrong with calculation of jet efficiency of black "
+        "holes. jet_eff is %f instead of jet_eff >= 0.",
+        jet_eff);
+  }
+#endif
+
+  return jet_eff;
+}
+
+/**
+ * @brief Compute the radiative efficiency of a BH particle.
+ *
+ * The result depends on bp->accretion_mode (thick disk, thin disk or
+ * slim disk), since all modes have different radiative physics.
+ *
+ * For the thin disk, we assume the Novikov-Thorne (1973) radiative efficiency
+ * based on general relativity. For the slim disk, we take the fit from Madau
+ * et al. (2014), which is based on numerical GR results by Sadowski (2009).
+ * For the thick disk, we assume radiative efficiencies from Mahadevan et al.
+ * (1997).
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float rad_efficiency(
+    struct bpart* bp, const struct black_holes_props* props) {
+
+  /* Calculate Novikov-Thorne efficiency, which will be needed twice. */
+  const float eps_TD = eps_NT(bp->spin);
+
+  /* Define placeholder variable for the result */
+  float rad_eff = -1;
+
+  if (props->fix_radiative_efficiency) {
+    rad_eff = props->radiative_efficiency;
+  } else {
+
+    /* Start branching depending on which accretion mode the BH is in */
+    if (bp->accretion_mode == BH_thin_disc) {
+
+      /* Assign Novikov-Thorne efficiency to the thin disk. */
+      rad_eff = eps_TD;
+    } else if (bp->accretion_mode == BH_slim_disc) {
+
+      /* Assign Madau 2014 efficiency to the slim disk. */
+      rad_eff = eps_SD(bp->spin, bp->eddington_fraction);
+    } else {
+
+#ifdef SWIFT_DEBUG_CHECKS
+      if (props->beta_acc > 1.) {
+        error(
+            "Something went wrong with calculation of radiative efficiency of "
+            " black holes. beta_acc is %f instead of beta_acc < 1.",
+            props->beta_acc);
+      }
+#endif
+
+      /* Assign Mahadevan 1997 efficiency to the thick disk. */
+      if (bp->eddington_fraction < props->edd_crit_thick) {
+        rad_eff = 4.8 * eps_TD / r_isco(bp->spin) * (1. - props->beta_acc) *
+                  props->delta_ADAF;
+      } else {
+        rad_eff = 2.4 * eps_TD / r_isco(bp->spin) * props->beta_acc *
+                  bp->eddington_fraction * props->alpha_acc_2_inv;
+      }
+    }
+  }
+
+  /* Turn off radiative feedback if we want to do that */
+  if (props->turn_off_radiative_feedback) {
+    rad_eff = 0.;
+  }
+
+  /* Turn off radiation in the thick disk mode if we want to do that */
+  if ((props->turn_off_secondary_feedback) &&
+      (bp->accretion_mode == BH_thick_disc)) {
+    rad_eff = 0.;
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (rad_eff < 0.) {
+    error(
+        "Something went wrong with calculation of radiative efficiency of "
+        " black holes. rad_eff is %f instead of rad_eff >= 0.",
+        rad_eff);
+  }
+#endif
+
+  return rad_eff;
+}
+
+/**
+ * @brief Compute the spec. ang. mom. at the inner radius of a BH particle.
+ *
+ * The result depends on bp->accretion_mode (thick disk, thin disk or
+ * slim disk), since advection-dominated modes (thick and slim disk)
+ * have more radial orbits.
+ *
+ * For the thin disk, we assume that the spec. ang. mom. consumed matches that
+ * of the innermost stable circular orbit (ISCO). For the other two modes, we
+ * assume that the accreted ang. mom. at the event horizon is 45 per cent of
+ * that at the ISCO, based on the fit from Benson & Babul (2009).
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float l_acc(
+    struct bpart* bp, const struct phys_const* constants,
+    const struct black_holes_props* props) {
+
+  /* Define placeholder variable for the result */
+  float L = -1.;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (r_isco(bp->spin) <= 0.6667) {
+    error(
+        "Something went wrong with calculation of l_acc of black holes. "
+        " r_isco is %f instead of r_isco > 1.",
+        r_isco(bp->spin));
+  }
+#endif
+
+  /* Spec. ang. mom. at ISCO */
+  const float L_ISCO = 0.385 * (1. + 2. * sqrtf(3. * r_isco(bp->spin) - 2.));
+
+  /* Branch depending on which accretion mode the BH is in */
+  if ((bp->accretion_mode == BH_thick_disc) ||
+      (bp->accretion_mode == BH_slim_disc)) {
+    L = 0.45 * L_ISCO;
+  } else {
+    L = L_ISCO;
+  }
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (L <= 0.) {
+    error(
+        "Something went wrong with calculation of l_acc of black holes. "
+        " l_acc is %f instead of l_acc > 0.",
+        L);
+  }
+#endif
+
+  return L;
+}
+
+/**
+ * @brief Compute the evolution of the spin of a BH particle.
+ *
+ * The result depends on bp->accretion_mode (thick disk, thin disk or
+ * slim disk), due to differing spec. ang. momenta as well as jet and
+ * radiative efficiencies.
+ *
+ * This equation corresponds to eqn. 2 in Benson & Babul (2009), including
+ * a jet spindown term.
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static float da_dln_mbh_0(
+    struct bpart* bp, const struct phys_const* constants,
+    const struct black_holes_props* props) {
+  const float a = bp->spin;
+
+  if ((a == 0.) || (a < -0.9981) || (a > 0.9981)) {
+    error(
+        "The da_dln_mbh_0 function was called and spin is %f. Spin should "
+        " not be a = 0, a < -0.998 or a > 0.998.",
+        a);
+  }
+
+  float spinup_rate = 0.;
+
+  if (props->include_GRMHD_spindown) {
+    if (bp->accretion_mode == BH_thin_disc) {
+      spinup_rate = l_acc(bp, constants, props) -
+                    2. * a * (1. - rad_efficiency(bp, props));
+    } else {
+      spinup_rate = 0.45 - 12.53 * a - 7.8 * a * a + 9.44 * a * a * a +
+                    5.71 * a * a * a * a - 4.03 * a * a * a * a * a;
+    }
+  } else {
+    spinup_rate =
+        l_acc(bp, constants, props) -
+        2. * a * (1. - rad_efficiency(bp, props)) -
+        sqrtf(1. - a * a) / a *
+            (a * a + (1. + sqrtf(1. - a * a)) * (1. + sqrtf(1. - a * a))) *
+            jet_efficiency(bp, props);
+  }
+
+  return spinup_rate;
+}
+
+/**
+ * @brief Compute the jet kick velocity to be used for jet feedback.
+ *
+ * @param bp The #bpart doing feedback.
+ * @param props Properties of the BH scheme.
+ * @param cosmo The current cosmological model.
+ * @param constants The physical constants (in internal units).
+ */
+__attribute__((always_inline)) INLINE static float black_hole_feedback_dv_jet(
+    const struct bpart* bp, const struct black_holes_props* props,
+    const struct cosmology* cosmo, const struct phys_const* constants) {
+
+  float v_jet = -1.;
+  if (props->AGN_jet_velocity_model == AGN_jet_velocity_BH_mass) {
+
+    /* Assign the halo mass according to an empirical relation given in the
+       parameter file */
+    const float halo_mass =
+        powf(bp->subgrid_mass / props->v_jet_BH_mass_scaling_reference_mass,
+             props->v_jet_BH_mass_scaling_slope);
+
+    /* Get the critical density and virial overdensity at this redshift */
+    const float critical_density =
+        cosmo->critical_density const float overdensity =
+            cosmo->overdensity_BN98;
+
+    /* Gather the previous factors and compute the virial radius, virial
+       velocity and finally the sound speed in the hot gas */
+    const float virial_radius =
+        cbrtf(3. * halo_mass / (4. * M_PI * overdensity * critical_density));
+    const float virial_velocity =
+        sqrtf(bp->group_mass * constants->const_newton_G / virial_radius);
+    const float sound_speed = sqrtf(5. / 3. * 0.5) * virial_velocity;
+
+    /* Return the jet velocity as some factor times the sound speed */
+    v_jet = fmaxf(props->v_jet_min, props->v_jet_cs_ratio * sound_speed);
+
+  } else if (props->AGN_jet_velocity_model == AGN_jet_velocity_constant) {
+    v_jet = props->v_jet;
+  } else {
+    error(
+        "The scaling of jet velocities with halo mass is currently not "
+        "supported.");
+  }
+
+  if (v_jet <= 0.) {
+    error(
+        "The black_hole_feedback_dv_jet returned a value less than 0. which "
+        " is v_jet = %f.",
+        v_jet);
+  }
+
+  return v_jet;
+}
+
+/**
+ * @brief Compute the resultant spin of a black hole merger.
+ *
+ * This implements the fitting formula from Rezzolla et al. (2008).
+ * The effects of gravitational waves are ignored.
+ *
+ * @param bp Pointer to the b-particle data.
+ * @param constants Physical constants (in internal units).
+ * @param props Properties of the black hole scheme.
+ */
+__attribute__((always_inline)) INLINE static void merger_spin_evolve(
+    struct bpart* bpi, const struct bpart* bpj,
+    const struct phys_const* constants) {
+
+  if ((bpj->subgrid_mass <= 0.) || (bpi->subgrid_mass <= 0.)) {
+    error(
+        "Something went wrong with calculation of spin of a black hole "
+        " merger remnant. The black hole masses are %f and %f, instead of  > "
+        "0.",
+        bpj->subgrid_mass, bpi->subgrid_mass);
+  }
+
+  const float m1 = bpi->subgrid_mass;
+  const float m2 = bpj->subgrid_mass;
+  const float mass_ratio = m2 / m1;
+  const float sym_mass_ratio =
+      mass_ratio / ((mass_ratio + 1.) * (mass_ratio + 1.));
+  const float reduced_mass = m1 * m2 / (m1 + m2);
+
+  const float spin1 = fabsf(bpi->spin);
+  const float spin2 = fabsf(bpj->spin);
+
+  if ((spin1 == 0.) || (spin2 == 0.)) {
+    error(
+        "Something went wrong with calculation of spin of a black hole "
+        " merger remnant. The black hole spins are %f and %f, instead of  > 0.",
+        spin1, spin2);
+  }
+
+  const float spin_vec1[3] = {spin1 * bpi->angular_momentum_direction[0],
+                              spin1 * bpi->angular_momentum_direction[1],
+                              spin1 * bpi->angular_momentum_direction[2]};
+  const float spin_vec2[3] = {spin2 * bpj->angular_momentum_direction[0],
+                              spin2 * bpj->angular_momentum_direction[1],
+                              spin2 * bpj->angular_momentum_direction[2]};
+
+  const float relative_coordinates[3] = {
+      bpj->x[0] - bpi->x[0], bpj->x[1] - bpi->x[1], bpj->x[2] - bpi->x[2]};
+  const float relative_velocities[3] = {
+      bpj->v[0] - bpi->v[0], bpj->v[1] - bpi->v[1], bpj->v[2] - bpi->v[2]};
+
+  float orbital_angular_momentum[3] = {
+      reduced_mass * (relative_coordinates[1] * relative_velocities[2] -
+                      relative_coordinates[2] * relative_velocities[1]),
+      reduced_mass * (relative_coordinates[2] * relative_velocities[0] -
+                      relative_coordinates[0] * relative_velocities[2]),
+      reduced_mass * (relative_coordinates[0] * relative_velocities[1] -
+                      relative_coordinates[1] * relative_velocities[0])};
+
+  const float orbital_angular_momentum_magnitude =
+      sqrtf(orbital_angular_momentum[0] * orbital_angular_momentum[0] +
+            orbital_angular_momentum[1] * orbital_angular_momentum[1] +
+            orbital_angular_momentum[2] * orbital_angular_momentum[2]);
+
+  if (orbital_angular_momentum_magnitude > 0.) {
+    orbital_angular_momentum[0] =
+        orbital_angular_momentum[0] / orbital_angular_momentum_magnitude;
+    orbital_angular_momentum[1] =
+        orbital_angular_momentum[1] / orbital_angular_momentum_magnitude;
+    orbital_angular_momentum[2] =
+        orbital_angular_momentum[2] / orbital_angular_momentum_magnitude;
+  } else {
+    orbital_angular_momentum[0] = 0.;
+    orbital_angular_momentum[1] = 0.;
+    orbital_angular_momentum[2] = 0.;
+  }
+
+  const float angle_0 =
+      (spin_vec1[0] * spin_vec2[0] + spin_vec1[1] * spin_vec2[1] +
+       spin_vec1[2] * spin_vec2[2]) /
+      (spin1 * spin2);
+  const float angle_1 = (spin_vec1[0] * orbital_angular_momentum[0] +
+                         spin_vec1[1] * orbital_angular_momentum[1] +
+                         spin_vec1[2] * orbital_angular_momentum[2]) /
+                        spin1;
+  const float angle_2 = (spin_vec2[0] * orbital_angular_momentum[0] +
+                         spin_vec2[1] * orbital_angular_momentum[1] +
+                         spin_vec2[2] * orbital_angular_momentum[2]) /
+                        spin2;
+
+  const float l =
+      -0.129 / (1. + mass_ratio * mass_ratio) * 1. /
+          (1. + mass_ratio * mass_ratio) *
+          (spin1 * spin1 +
+           spin2 * spin2 * mass_ratio * mass_ratio * mass_ratio * mass_ratio +
+           2. * spin1 * spin2 * mass_ratio * mass_ratio * angle_0) +
+      ((-0.384 * sym_mass_ratio - 0.686) / (1. + mass_ratio * mass_ratio)) *
+          (spin1 * angle_1 + spin2 * mass_ratio * mass_ratio * angle_2) +
+      3.464 - 3.454 * sym_mass_ratio + 2.353 * sym_mass_ratio * sym_mass_ratio;
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (l < 0.) {
+    error(
+        "Something went wrong with calculation of spin of a black hole "
+        " merger remnant. The l factor is %f, instead of  >= 0.",
+        l);
+  }
+#endif
+
+  float final_spin[3] = {
+      1. / (1. + mass_ratio) / (1. + mass_ratio) *
+          (spin_vec1[0] + mass_ratio * mass_ratio * spin_vec2[0] +
+           mass_ratio * l * orbital_angular_momentum[0]),
+      1. / (1. + mass_ratio) / (1. + mass_ratio) *
+          (spin_vec1[1] + mass_ratio * mass_ratio * spin_vec2[1] +
+           mass_ratio * l * orbital_angular_momentum[1]),
+      1. / (1. + mass_ratio) / (1. + mass_ratio) *
+          (spin_vec1[2] + mass_ratio * mass_ratio * spin_vec2[2] +
+           mass_ratio * l * orbital_angular_momentum[2])};
+  const float final_spin_magnitude =
+      sqrtf(final_spin[0] * final_spin[0] + final_spin[1] * final_spin[1] +
+            final_spin[2] * final_spin[2]);
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (final_spin_magnitude <= 0.) {
+    error(
+        "Something went wrong with calculation of spin of a black hole "
+        " merger remnant. The final spin magnitude is %f, instead of > 0.",
+        final_spin_magnitude);
+  }
+#endif
+
+  final_spin[0] = final_spin[0] / final_spin_magnitude;
+  final_spin[1] = final_spin[1] / final_spin_magnitude;
+  final_spin[2] = final_spin[2] / final_spin_magnitude;
+
+  bpi->spin = min(final_spin_magnitude, 0.998);
+  if (fabsf(bpi->spin) < 0.001) {
+    bpi->spin = 0.001;
+  }
+
+  bpi->angular_momentum_direction[0] = final_spin[0];
+  bpi->angular_momentum_direction[1] = final_spin[1];
+  bpi->angular_momentum_direction[2] = final_spin[2];
+}
+
+#endif /* SWIFT_SPIN_JET_BLACK_HOLES_SPIN_H */
diff --git a/src/black_holes/SPIN_JET/black_holes_struct.h b/src/black_holes/SPIN_JET/black_holes_struct.h
new file mode 100644
index 0000000000000000000000000000000000000000..3ddd08fcc609daf5074ebc93312c1cb2e8859794
--- /dev/null
+++ b/src/black_holes/SPIN_JET/black_holes_struct.h
@@ -0,0 +1,134 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2020 Matthieu Schaller (schaller@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
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_BLACK_HOLES_STRUCT_SPIN_JET_H
+#define SWIFT_BLACK_HOLES_STRUCT_SPIN_JET_H
+
+/* Standard headers */
+#include <float.h>
+
+/* Local includes */
+#include "inline.h"
+
+/**
+ * @brief Black holes-related fields carried by each *gas* particle.
+ */
+struct black_holes_part_data {
+
+  /*! ID of the black-hole that will swallow this #part. */
+  long long swallow_id;
+
+  /*! Gravitational potential of the particle (for repositioning) */
+  float potential;
+};
+
+/**
+ * @brief Black holes-related fields carried by each *BH* particle.
+ */
+struct black_holes_bpart_data {
+
+  /*! ID of the black-hole that will swallow this #bpart. */
+  long long swallow_id;
+
+  /*! Mass of the black-hole that will swallow this #bpart. */
+  float swallow_mass;
+};
+
+/**
+ * @brief Update a given #part's BH data field to mark the particle has
+ * not yet been swallowed.
+ *
+ * @param p_data The #part's #black_holes_part_data structure.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_mark_part_as_not_swallowed(struct black_holes_part_data* p_data) {
+
+  p_data->swallow_id = -1;
+}
+
+/**
+ * @brief Reset the particle-carried potential at the start of a time-step.
+ *
+ * @param p_data The #part's black hole data.
+ */
+__attribute__((always_inline)) INLINE static void black_holes_init_potential(
+    struct black_holes_part_data* p_data) {
+  p_data->potential = FLT_MAX;
+}
+
+/**
+ * @brief Update a given #part's BH data field to mark the particle has
+ * having been been swallowed.
+ *
+ * @param p_data The #part's #black_holes_part_data structure.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_mark_part_as_swallowed(struct black_holes_part_data* p_data) {
+
+  p_data->swallow_id = -2;
+}
+
+/**
+ * @brief Return the ID of the BH that should swallow this #part.
+ *
+ * @param p_data The #part's #black_holes_part_data structure.
+ */
+__attribute__((always_inline)) INLINE static long long
+black_holes_get_part_swallow_id(struct black_holes_part_data* p_data) {
+
+  return p_data->swallow_id;
+}
+
+/**
+ * @brief Update a given #bpart's BH data field to mark the particle has
+ * not yet been swallowed.
+ *
+ * @param p_data The #bpart's #black_holes_bpart_data structure.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_mark_bpart_as_not_swallowed(struct black_holes_bpart_data* p_data) {
+
+  p_data->swallow_id = -1;
+  p_data->swallow_mass = 0.f;
+}
+
+/**
+ * @brief Update a given #bpart's BH data field to mark the particle has
+ * having been been swallowed.
+ *
+ * @param p_data The #bpart's #black_holes_bpart_data structure.
+ */
+__attribute__((always_inline)) INLINE static void
+black_holes_mark_bpart_as_merged(struct black_holes_bpart_data* p_data) {
+
+  p_data->swallow_id = -2;
+  p_data->swallow_mass = -1.f;
+}
+
+/**
+ * @brief Return the ID of the BH that should swallow this #bpart.
+ *
+ * @param p_data The #bpart's #black_holes_bpart_data structure.
+ */
+__attribute__((always_inline)) INLINE static long long
+black_holes_get_bpart_swallow_id(struct black_holes_bpart_data* p_data) {
+
+  return p_data->swallow_id;
+}
+
+#endif /* SWIFT_BLACK_HOLES_STRUCT_SPIN_JET_H */
diff --git a/src/black_holes_debug.h b/src/black_holes_debug.h
index 03e3dcc5b182c91c44278f5bdd9c75c05dec2aca..1a00ff2c5da1b4d9cb29b49601cdbbffea71f7bd 100644
--- a/src/black_holes_debug.h
+++ b/src/black_holes_debug.h
@@ -20,13 +20,15 @@
 #define SWIFT_BLACK_HOLES_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right black holes definition */
 #if defined(BLACK_HOLES_NONE)
 #include "./black_holes/Default/black_holes_debug.h"
 #elif defined(BLACK_HOLES_EAGLE)
 #include "./black_holes/EAGLE/black_holes_debug.h"
+#elif defined(BLACK_HOLES_SPIN_JET)
+#include "./black_holes/SPIN_JET/black_holes_debug.h"
 #else
 #error "Invalid choice of BH model"
 #endif
diff --git a/src/black_holes_io.h b/src/black_holes_io.h
index 45208703b500313fffb8cffd070cae9cff70b4b8..0507cef106dd74c18c32146cc3117625dea4ce3c 100644
--- a/src/black_holes_io.h
+++ b/src/black_holes_io.h
@@ -19,7 +19,7 @@
 #ifndef SWIFT_BLACK_HOLES_IO_H
 #define SWIFT_BLACK_HOLES_IO_H
 
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "engine.h"
@@ -29,6 +29,8 @@
 #include "./black_holes/Default/black_holes_io.h"
 #elif defined(BLACK_HOLES_EAGLE)
 #include "./black_holes/EAGLE/black_holes_io.h"
+#elif defined(BLACK_HOLES_SPIN_JET)
+#include "./black_holes/SPIN_JET/black_holes_io.h"
 #else
 #error "Invalid choice of BH model"
 #endif
diff --git a/src/black_holes_properties.h b/src/black_holes_properties.h
index d8fd7189becb63f6e5e7c4fee826f7066e56dfe0..f0f7ae660ebbb56cb7949e517e9f334a137e53c3 100644
--- a/src/black_holes_properties.h
+++ b/src/black_holes_properties.h
@@ -20,13 +20,15 @@
 #define SWIFT_BLACK_HOLES_PROPERTIES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct black_holes model */
 #if defined(BLACK_HOLES_NONE)
 #include "./black_holes/Default/black_holes_properties.h"
 #elif defined(BLACK_HOLES_EAGLE)
 #include "./black_holes/EAGLE/black_holes_properties.h"
+#elif defined(BLACK_HOLES_SPIN_JET)
+#include "./black_holes/SPIN_JET/black_holes_properties.h"
 #else
 #error "Invalid choice of black hole model"
 #endif
diff --git a/src/black_holes_struct.h b/src/black_holes_struct.h
index 73cc156420699923a71bd23d1c9d239d987bfd95..09027e83f7a3339130a1c6cd6a92070b1715edb2 100644
--- a/src/black_holes_struct.h
+++ b/src/black_holes_struct.h
@@ -25,7 +25,9 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
+
+/* Local includes */
 #include "inline.h"
 
 /* Import the right black holes definition */
@@ -33,6 +35,8 @@
 #include "./black_holes/Default/black_holes_struct.h"
 #elif defined(BLACK_HOLES_EAGLE)
 #include "./black_holes/EAGLE/black_holes_struct.h"
+#elif defined(BLACK_HOLES_SPIN_JET)
+#include "./black_holes/SPIN_JET/black_holes_struct.h"
 #else
 #error "Invalid choice of black hole model."
 #endif
diff --git a/src/cache.h b/src/cache.h
index 4b32ac92cf01867997b6bca246ecd01d31ae400f..229685a112e074e1b41d0e001f63c3bfced1d79a 100644
--- a/src/cache.h
+++ b/src/cache.h
@@ -20,7 +20,7 @@
 #define SWIFT_CACHE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "align.h"
diff --git a/src/cbrt.h b/src/cbrt.h
index 61f9a9167fba398649719c7fc0f61bc9efd2a277..8ef76a369355cc2b767c861393a2893ca5334fd4 100644
--- a/src/cbrt.h
+++ b/src/cbrt.h
@@ -21,7 +21,7 @@
 #define SWIFT_CBRT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/cell.c b/src/cell.c
index 6c8d12271e1ba0c4931ef7e2f78122674b15f1ca..c8d5dac7271b6e9f557aa4940c30020d367a3040 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -22,7 +22,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
@@ -579,8 +579,8 @@ void cell_clean_links(struct cell *c, void *data) {
   c->hydro.gradient = NULL;
   c->hydro.force = NULL;
   c->hydro.limiter = NULL;
-  c->hydro.rt_gradient = NULL;
-  c->hydro.rt_transport = NULL;
+  c->rt.rt_gradient = NULL;
+  c->rt.rt_transport = NULL;
   c->grav.grav = NULL;
   c->grav.mm = NULL;
   c->stars.density = NULL;
@@ -1253,7 +1253,7 @@ void cell_check_timesteps(const struct cell *c, const integertime_t ti_current,
 
   if (c->hydro.ti_end_min == 0 && c->grav.ti_end_min == 0 &&
       c->stars.ti_end_min == 0 && c->black_holes.ti_end_min == 0 &&
-      c->sinks.ti_end_min == 0 && c->nr_tasks > 0)
+      c->sinks.ti_end_min == 0 && c->rt.ti_rt_end_min == 0 && c->nr_tasks > 0)
     error("Cell without assigned time-step");
 
   if (c->split) {
diff --git a/src/cell.h b/src/cell.h
index b7b6b7d238f0502abb0f300385ab2a6ed0b5e5cd..7c89d24752135832c618af0defce906823169aef 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -24,7 +24,7 @@
 #define SWIFT_CELL_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include <stddef.h>
@@ -36,8 +36,10 @@
 #include "cell_black_holes.h"
 #include "cell_grav.h"
 #include "cell_hydro.h"
+#include "cell_rt.h"
 #include "cell_sinks.h"
 #include "cell_stars.h"
+#include "ghost_stats.h"
 #include "kernel_hydro.h"
 #include "multipole_struct.h"
 #include "part.h"
@@ -210,6 +212,17 @@ struct pcell {
 
   } sinks;
 
+  /*! RT variables */
+  struct {
+
+    /*! Minimal integer end-of-timestep in this cell for RT tasks */
+    integertime_t ti_rt_end_min;
+
+    /*! smallest RT time-step size in this cell */
+    integertime_t ti_rt_min_step_size;
+
+  } rt;
+
   /*! Maximal depth in that part of the tree */
   int maxdepth;
 
@@ -260,6 +273,16 @@ struct pcell_step {
     /*! Maximal distance any #part has travelled since last rebuild */
     float dx_max_part;
   } black_holes;
+
+  struct {
+
+    /*! Minimal integer end-of-timestep in this cell (rt) */
+    integertime_t ti_rt_end_min;
+
+    /*! smallest RT time-step size in this cell */
+    integertime_t ti_rt_min_step_size;
+
+  } rt;
 };
 
 /**
@@ -318,7 +341,10 @@ enum cell_flags {
   cell_flag_do_hydro_sync = (1UL << 17),
   cell_flag_do_hydro_sub_sync = (1UL << 18),
   cell_flag_unskip_self_grav_processed = (1UL << 19),
-  cell_flag_unskip_pair_grav_processed = (1UL << 20)
+  cell_flag_unskip_pair_grav_processed = (1UL << 20),
+  cell_flag_skip_rt_sort = (1UL << 21),    /* skip rt_sort after a RT recv? */
+  cell_flag_do_rt_sub_sort = (1UL << 22),  /* same as hydro_sub_sort for RT */
+  cell_flag_rt_requests_sort = (1UL << 23) /* was this sort requested by RT? */
 };
 
 /**
@@ -384,6 +410,9 @@ struct cell {
   /*! Sink particles variables */
   struct cell_sinks sinks;
 
+  /*! Radiative transfer variables */
+  struct cell_rt rt;
+
 #ifdef WITH_MPI
   /*! MPI variables */
   struct {
@@ -500,6 +529,8 @@ struct cell {
   char subtasks_executed[task_type_count];
 #endif
 
+  struct ghost_stats ghost_statistics;
+
 } SWIFT_STRUCT_ALIGN;
 
 /* Convert cell location to ID. */
@@ -574,7 +605,8 @@ int cell_unskip_stars_tasks(struct cell *c, struct scheduler *s,
                             const int with_star_formation,
                             const int with_star_formation_sink);
 int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s);
-int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s);
+int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s,
+                         const int sub_cycle);
 int cell_unskip_black_holes_tasks(struct cell *c, struct scheduler *s);
 int cell_unskip_gravity_tasks(struct cell *c, struct scheduler *s);
 void cell_drift_part(struct cell *c, const struct engine *e, int force,
@@ -616,6 +648,9 @@ void cell_activate_subcell_black_holes_tasks(struct cell *ci, struct cell *cj,
                                              const int with_timestep_sync);
 void cell_activate_subcell_external_grav_tasks(struct cell *ci,
                                                struct scheduler *s);
+void cell_activate_subcell_rt_tasks(struct cell *ci, struct cell *cj,
+                                    struct scheduler *s, const int sub_cycle);
+void cell_set_no_rt_sort_flag_up(struct cell *c);
 void cell_activate_super_spart_drifts(struct cell *c, struct scheduler *s);
 void cell_activate_super_sink_drifts(struct cell *c, struct scheduler *s);
 void cell_activate_drift_part(struct cell *c, struct scheduler *s);
@@ -624,6 +659,7 @@ void cell_activate_drift_spart(struct cell *c, struct scheduler *s);
 void cell_activate_drift_sink(struct cell *c, struct scheduler *s);
 void cell_activate_drift_bpart(struct cell *c, struct scheduler *s);
 void cell_activate_sync_part(struct cell *c, struct scheduler *s);
+void cell_activate_rt_sorts(struct cell *c, int sid, struct scheduler *s);
 void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s);
 void cell_activate_stars_sorts(struct cell *c, int sid, struct scheduler *s);
 void cell_activate_limiter(struct cell *c, struct scheduler *s);
diff --git a/src/cell_black_holes.h b/src/cell_black_holes.h
index b487a16991c1de57f07c24fd5424225afadd46fd..fb8a779b4b7c33b8db6d59b44f6480307e2fb36c 100644
--- a/src/cell_black_holes.h
+++ b/src/cell_black_holes.h
@@ -22,7 +22,7 @@
 #define SWIFT_CELL_BLACK_HOLES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "lock.h"
diff --git a/src/cell_convert_part.c b/src/cell_convert_part.c
index 2136fd3fbb09c3167e89e6f7f730850bc742bd52..9d22869f712d3fe25bff9b0d74b6b088b1d792b1 100644
--- a/src/cell_convert_part.c
+++ b/src/cell_convert_part.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "cell.h"
@@ -565,6 +565,9 @@ void cell_remove_part(const struct engine *e, struct cell *c, struct part *p,
 
   /* Mark the particle as inhibited */
   p->time_bin = time_bin_inhibited;
+  /* Mark the RT time bin as inhibited as well,
+   * so part_is_rt_active() checks work as intended */
+  p->rt_time_data.time_bin = time_bin_inhibited;
 
   /* Mark the gpart as inhibited and stand-alone */
   if (p->gpart) {
diff --git a/src/cell_drift.c b/src/cell_drift.c
index 46a594c5eab86a2adcba535ed2c5da095d0e4c16..5c8168eb63a08c3085bd2206fe377aeb8ed18391 100644
--- a/src/cell_drift.c
+++ b/src/cell_drift.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "cell.h"
diff --git a/src/cell_grav.h b/src/cell_grav.h
index bd683ecc749ef2f281783264a6f4e151d7a93cc6..53f7a4178fc78626f034ccc8bf8c5dcb7eb6e07a 100644
--- a/src/cell_grav.h
+++ b/src/cell_grav.h
@@ -22,7 +22,7 @@
 #define SWIFT_CELL_GRAV_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "lock.h"
diff --git a/src/cell_hydro.h b/src/cell_hydro.h
index 5a6b4b15e8b13eb2fa2136e2b88f5b7b167bbf9b..39db7bc21934e434583551de2a693da46fc7cacc 100644
--- a/src/cell_hydro.h
+++ b/src/cell_hydro.h
@@ -22,7 +22,7 @@
 #define SWIFT_CELL_HYDRO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "lock.h"
@@ -103,38 +103,6 @@ struct cell_hydro {
     /*! Task for sorting the stars again after a SF event */
     struct task *stars_resort;
 
-#ifdef RT_NONE
-    union {
-#endif
-
-      /*! Radiative transfer ghost in task */
-      struct task *rt_in;
-
-      /*! Radiative transfer ghost1 task (finishes up injection) */
-      struct task *rt_ghost1;
-
-      /*! Task for self/pair gradient step of radiative transfer */
-      struct link *rt_gradient;
-
-      /*! Radiative transfer ghost2 task */
-      struct task *rt_ghost2;
-
-      /*! Task for self/pair transport step of radiative transfer */
-      struct link *rt_transport;
-
-      /*! Radiative transfer transport out task */
-      struct task *rt_transport_out;
-
-      /*! Radiative transfer thermochemistry task */
-      struct task *rt_tchem;
-
-      /*! Radiative transfer ghost out task */
-      struct task *rt_out;
-
-#ifdef RT_NONE
-    };
-#endif
-
     /*! Last (integer) time the cell's part were drifted forward in time. */
     integertime_t ti_old_part;
 
diff --git a/src/cell_lock.c b/src/cell_lock.c
index 7501cab439d8bd959dc94c9a6dd6ba01a5495623..223c06a5e9408d10335078a809cf59a45fd88cbb 100644
--- a/src/cell_lock.c
+++ b/src/cell_lock.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "cell.h"
diff --git a/src/cell_pack.c b/src/cell_pack.c
index 4a6d703cbc93514856625f877900a1eb35087e50..fe29672afcee84e047cda182ab0a3c412d7b870d 100644
--- a/src/cell_pack.c
+++ b/src/cell_pack.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "cell.h"
@@ -51,6 +51,8 @@ int cell_pack(struct cell *restrict c, struct pcell *restrict pc,
   pc->stars.ti_end_min = c->stars.ti_end_min;
   pc->sinks.ti_end_min = c->sinks.ti_end_min;
   pc->black_holes.ti_end_min = c->black_holes.ti_end_min;
+  pc->rt.ti_rt_end_min = c->rt.ti_rt_end_min;
+  pc->rt.ti_rt_min_step_size = c->rt.ti_rt_min_step_size;
 
   pc->hydro.ti_old_part = c->hydro.ti_old_part;
   pc->grav.ti_old_part = c->grav.ti_old_part;
@@ -210,6 +212,8 @@ int cell_unpack(struct pcell *restrict pc, struct cell *restrict c,
   c->stars.ti_end_min = pc->stars.ti_end_min;
   c->black_holes.ti_end_min = pc->black_holes.ti_end_min;
   c->sinks.ti_end_min = pc->sinks.ti_end_min;
+  c->rt.ti_rt_end_min = pc->rt.ti_rt_end_min;
+  c->rt.ti_rt_min_step_size = pc->rt.ti_rt_min_step_size;
 
   c->hydro.ti_old_part = pc->hydro.ti_old_part;
   c->grav.ti_old_part = pc->grav.ti_old_part;
@@ -345,6 +349,8 @@ int cell_pack_end_step(const struct cell *c, struct pcell_step *pcells) {
   /* Pack this cell's data. */
   pcells[0].hydro.ti_end_min = c->hydro.ti_end_min;
   pcells[0].hydro.dx_max_part = c->hydro.dx_max_part;
+  pcells[0].rt.ti_rt_end_min = c->rt.ti_rt_end_min;
+  pcells[0].rt.ti_rt_min_step_size = c->rt.ti_rt_min_step_size;
 
   pcells[0].grav.ti_end_min = c->grav.ti_end_min;
 
@@ -387,6 +393,9 @@ int cell_unpack_end_step(struct cell *c, const struct pcell_step *pcells) {
   c->hydro.ti_end_min = pcells[0].hydro.ti_end_min;
   c->hydro.dx_max_part = pcells[0].hydro.dx_max_part;
 
+  c->rt.ti_rt_end_min = pcells[0].rt.ti_rt_end_min;
+  c->rt.ti_rt_min_step_size = pcells[0].rt.ti_rt_min_step_size;
+
   c->grav.ti_end_min = pcells[0].grav.ti_end_min;
 
   c->stars.ti_end_min = pcells[0].stars.ti_end_min;
diff --git a/src/cell_rt.h b/src/cell_rt.h
new file mode 100644
index 0000000000000000000000000000000000000000..049c74d5c731442a951ff1da5baffaf53fb0c20a
--- /dev/null
+++ b/src/cell_rt.h
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_CELL_RT_H
+#define SWIFT_CELL_RT_H
+
+/* Config parameters. */
+#include <config.h>
+
+/* Local includes. */
+#include "timeline.h"
+
+/**
+ * @brief Radiative transfer related cell variables.
+ */
+struct cell_rt {
+
+  /* If we are not using RT, compact as much of the unecessary variables
+     into an anonymous union to save memory in the cell structure. */
+#ifdef RT_NONE
+  union {
+#endif
+
+    /*! Radiative transfer ghost in task */
+    struct task *rt_in;
+
+    /*! Radiative transfer ghost1 task (finishes up injection) */
+    struct task *rt_ghost1;
+
+    /*! Task for self/pair gradient step of radiative transfer */
+    struct link *rt_gradient;
+
+    /*! Radiative transfer ghost2 task */
+    struct task *rt_ghost2;
+
+    /*! Task for self/pair transport step of radiative transfer */
+    struct link *rt_transport;
+
+    /*! Radiative transfer transport out task */
+    struct task *rt_transport_out;
+
+    /*! Radiative transfer thermochemistry task */
+    struct task *rt_tchem;
+
+    /*! Radiative transfer cell time advancement task */
+    struct task *rt_advance_cell_time;
+
+    /*! Sort a cell after a recv rt gradients */
+    struct task *rt_sorts;
+
+    /*! Collect the cell times from the super to the top level */
+    struct task *rt_collect_times;
+
+    /*! Radiative transfer ghost out task */
+    struct task *rt_out;
+
+    /*! Bit mask of sorts that need to be computed for this cell.
+     * Needed to be able to skip sorting undrifted cells. */
+    uint16_t do_sort;
+
+#ifdef RT_NONE
+  };
+#endif
+
+#ifdef SWIFT_RT_DEBUG_CHECKS
+  /*! has rt_advance_cell_time run on this cell? */
+  int advanced_time;
+#endif
+
+  /*! Minimum end of (integer) time step in this cell for RT tasks. */
+  integertime_t ti_rt_end_min;
+
+  /*! Maximum beginning of (integer) time step in this cell for RT tasks. */
+  integertime_t ti_rt_beg_max;
+
+  /*! Minimum (integer) time step size in this cell for RT tasks. */
+  integertime_t ti_rt_min_step_size;
+
+  /*! Number of #part updated for RT in this cell */
+  int updated;
+};
+
+#endif /* SWIFT_CELL_RT_H */
diff --git a/src/cell_sinks.h b/src/cell_sinks.h
index eb14631badbe680c92f829fe56ba09c18300fb96..ac1d9d592030e83df9c8c48b8a0772e2981c201f 100644
--- a/src/cell_sinks.h
+++ b/src/cell_sinks.h
@@ -22,7 +22,7 @@
 #define SWIFT_CELL_SINKS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "lock.h"
diff --git a/src/cell_split.c b/src/cell_split.c
index 26e40b17b714d3bbbfd2c2c3917bec25b5203a6e..444023bcec7fcdef33539ec8381392c68a4d9ffa 100644
--- a/src/cell_split.c
+++ b/src/cell_split.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "cell.h"
diff --git a/src/cell_stars.h b/src/cell_stars.h
index cb01b0e78f18d40185bfe404f053876eac003466..e1030b7ad41c2c18360ea920986d5b27bae6cded 100644
--- a/src/cell_stars.h
+++ b/src/cell_stars.h
@@ -22,7 +22,7 @@
 #define SWIFT_CELL_STARS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "lock.h"
diff --git a/src/cell_unskip.c b/src/cell_unskip.c
index a4b10ce722d179252ad549d11f1dea023e2dd20a..0043c7e1af5f7b8b849100e709e89d4d18071684 100644
--- a/src/cell_unskip.c
+++ b/src/cell_unskip.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "cell.h"
@@ -594,6 +594,7 @@ void cell_activate_hydro_sorts_up(struct cell *c, struct scheduler *s) {
       error("Trying to activate un-existing c->hydro.sorts");
 #endif
     scheduler_activate(s, c->hydro.sorts);
+    cell_set_flag(c, cell_flag_skip_rt_sort);
     if (c->nodeID == engine_rank) cell_activate_drift_part(c, s);
   } else {
     for (struct cell *parent = c->parent;
@@ -606,6 +607,7 @@ void cell_activate_hydro_sorts_up(struct cell *c, struct scheduler *s) {
           error("Trying to activate un-existing parents->hydro.sorts");
 #endif
         scheduler_activate(s, parent->hydro.sorts);
+        cell_set_flag(parent, cell_flag_skip_rt_sort);
         if (parent->nodeID == engine_rank) cell_activate_drift_part(parent, s);
         break;
       }
@@ -636,6 +638,95 @@ void cell_activate_hydro_sorts(struct cell *c, int sid, struct scheduler *s) {
   }
 }
 
+/**
+ * @brief Activate the sorts up a cell hierarchy. Activate drifts
+ * and hydro sorts on local cells, and rt_sorts on foreign cells.
+ */
+void cell_activate_rt_sorts_up(struct cell *c, struct scheduler *s) {
+
+  cell_set_flag(c, cell_flag_rt_requests_sort);
+
+  if (c == c->hydro.super) {
+#ifdef SWIFT_DEBUG_CHECKS
+    if (c->nodeID == engine_rank && c->hydro.sorts == NULL)
+      error("Trying to activate non-existing c->hydro.sorts");
+    if (c->nodeID != engine_rank && c->rt.rt_sorts == NULL)
+      error("Trying to activate non-existing c->rt.rt_sorts");
+#endif
+    if (c->nodeID == engine_rank) {
+      cell_set_flag(c, cell_flag_skip_rt_sort);
+      scheduler_activate(s, c->hydro.sorts);
+    } else {
+      scheduler_activate(s, c->rt.rt_sorts);
+    }
+  } else {
+
+    for (struct cell *parent = c->parent;
+         parent != NULL && !cell_get_flag(parent, cell_flag_do_rt_sub_sort);
+         parent = parent->parent) {
+
+      /* Need a separate flag for RT sub sorts because RT sorts don't
+       * necessarily activate the hydro sorts tasks, yet the do_hydro_sub_sort
+       * flag is used as an early exit while climbing up the tree. */
+      cell_set_flag(parent, cell_flag_do_rt_sub_sort);
+      cell_set_flag(parent, cell_flag_rt_requests_sort);
+
+      if (parent == c->hydro.super) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+        if (parent->nodeID == engine_rank && parent->hydro.sorts == NULL)
+          error("Trying to activate non-existing parents->hydro.sorts");
+        if (parent->nodeID != engine_rank && parent->rt.rt_sorts == NULL)
+          error("Trying to activate non-existing parents->rt.rt_sorts");
+#endif
+
+        if (parent->nodeID == engine_rank) {
+          /* Mark the progeny to skip the RT sort as well */
+          cell_set_flag(c, cell_flag_skip_rt_sort);
+          scheduler_activate(s, parent->hydro.sorts);
+        } else {
+          scheduler_activate(s, parent->rt.rt_sorts);
+        }
+        break;
+      }
+    }
+  }
+}
+
+/**
+ * @brief Activate the sorts on a given cell, if needed. Activate
+ * hydro sorts on local cells, and rt_sorts on foreign cells.
+ */
+void cell_activate_rt_sorts(struct cell *c, int sid, struct scheduler *s) {
+  /* Do we need to re-sort? */
+  if (c->hydro.dx_max_sort > space_maxreldx * c->dmin) {
+    /* Climb up the tree to active the sorts in that direction */
+    for (struct cell *finger = c; finger != NULL; finger = finger->parent) {
+      if (finger->hydro.requires_sorts) {
+        atomic_or(&finger->hydro.do_sort, finger->hydro.requires_sorts);
+        cell_activate_rt_sorts_up(finger, s);
+      }
+      finger->hydro.sorted = 0;
+    }
+  }
+
+  /* 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_rt_sorts_up(c, s);
+  }
+}
+
+/**
+ * @brief Mark cells up a hierarchy to not run RT sorts.
+ * */
+void cell_set_skip_rt_sort_flag_up(struct cell *c) {
+
+  for (struct cell *finger = c; finger != NULL; finger = finger->parent) {
+    cell_set_flag(finger, cell_flag_skip_rt_sort);
+  }
+}
+
 /**
  * @brief Activate the sorts up a cell hierarchy.
  */
@@ -1431,6 +1522,94 @@ void cell_activate_subcell_external_grav_tasks(struct cell *ci,
   }
 }
 
+/**
+ * @brief Traverse a sub-cell task and activate the sort tasks that are
+ * required by a RT task
+ *
+ * @param ci The first #cell we recurse in.
+ * @param cj The second #cell we recurse in.
+ * @param s The task #scheduler.
+ * @param sub_cycle Are we in a subcycle or not?
+ */
+void cell_activate_subcell_rt_tasks(struct cell *ci, struct cell *cj,
+                                    struct scheduler *s, const int sub_cycle) {
+
+  /* Only do this during real time steps, not during subcycling. */
+  if (sub_cycle) return;
+  const struct engine *e = s->space->e;
+
+  /* Store the current dx_max and h_max values. */
+  ci->hydro.dx_max_part_old = ci->hydro.dx_max_part;
+  ci->hydro.h_max_old = ci->hydro.h_max;
+
+  if (cj != NULL) {
+    cj->hydro.dx_max_part_old = cj->hydro.dx_max_part;
+    cj->hydro.h_max_old = cj->hydro.h_max;
+  }
+
+  const int ci_active = cell_is_rt_active(ci, e);
+  const int cj_active = ((cj != NULL) && cell_is_rt_active(cj, e));
+
+  /* Self interaction? */
+  if (cj == NULL) {
+    /* Do anything? */
+    if (ci->hydro.count == 0 || !ci_active) return;
+
+    /* Recurse? */
+    if (cell_can_recurse_in_self_hydro_task(ci)) {
+      /* Loop over all progenies and pairs of progenies */
+      for (int j = 0; j < 8; j++) {
+        if (ci->progeny[j] != NULL) {
+          cell_activate_subcell_rt_tasks(ci->progeny[j], NULL, s, sub_cycle);
+          for (int k = j + 1; k < 8; k++)
+            if (ci->progeny[k] != NULL)
+              cell_activate_subcell_rt_tasks(ci->progeny[j], ci->progeny[k], s,
+                                             sub_cycle);
+        }
+      }
+    }
+  }
+
+  /* Otherwise, pair interation */
+  else {
+
+    /* Should we even bother? */
+    if (!ci_active && !cj_active) return;
+    if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
+
+    /* Get the orientation of the pair. */
+    double shift[3];
+    const int sid = space_getsid(s->space, &ci, &cj, shift);
+
+    /* recurse? */
+    if (cell_can_recurse_in_pair_hydro_task(ci) &&
+        cell_can_recurse_in_pair_hydro_task(cj)) {
+      const struct cell_split_pair *csp = &cell_split_pairs[sid];
+      for (int k = 0; k < csp->count; k++) {
+        const int pid = csp->pairs[k].pid;
+        const int pjd = csp->pairs[k].pjd;
+        if (ci->progeny[pid] != NULL && cj->progeny[pjd] != NULL)
+          cell_activate_subcell_rt_tasks(ci->progeny[pid], cj->progeny[pjd], s,
+                                         sub_cycle);
+      }
+    }
+
+    /* Otherwise, activate the sorts and drifts. */
+    else if (ci_active || cj_active) {
+
+      /* We are going to interact this pair, so store some values. */
+      atomic_or(&ci->hydro.requires_sorts, 1 << sid);
+      atomic_or(&cj->hydro.requires_sorts, 1 << sid);
+      ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+      cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+
+      /* Do we need to sort the cells? */
+      cell_activate_rt_sorts(ci, sid, s);
+      cell_activate_rt_sorts(cj, sid, s);
+    }
+  }
+}
+
 /**
  * @brief Un-skips all the hydro tasks associated with a given cell and checks
  * if the space needs to be rebuilt.
@@ -1546,6 +1725,26 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
 #endif
           }
         }
+        /* If the local cell is inactive and the remote cell is active, we
+         * still need to receive stuff to be able to do the force interaction
+         * on this node as well. */
+        else if (ci_active) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+          /* NOTE: (yuyttenh, 09/2022) Since the particle communications send
+           * over whole particles currently, just activating the gradient
+           * send/recieve should be enough for now. The remote active
+           * particles are only needed for the sorts and the flux exchange on
+           * the node of the inactive cell, so sending over the xv and
+           * gradient suffices. If at any point the commutications change, we
+           * should probably also send over the rho separately. */
+          scheduler_activate_recv(s, ci->mpi.recv, task_subtype_xv);
+#ifndef EXTRA_HYDRO_LOOP
+          scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rho);
+#else
+          scheduler_activate_recv(s, ci->mpi.recv, task_subtype_gradient);
+#endif
+#endif
+        }
 
         /* If the foreign cell is active, we want its particles for the limiter
          */
@@ -1575,6 +1774,24 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
 #endif
           }
         }
+        /* If the foreign cell is inactive, but the local cell is active,
+         * we still need to send stuff to be able to do the force interaction
+         * on both nodes */
+        else if (cj_active) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+          /* See NOTE on line 1542 */
+          scheduler_activate_send(s, cj->mpi.send, task_subtype_xv, 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);
+#ifndef EXTRA_HYDRO_LOOP
+          scheduler_activate_send(s, cj->mpi.send, task_subtype_rho, ci_nodeID);
+#else
+          scheduler_activate_send(s, cj->mpi.send, task_subtype_gradient,
+                                  ci_nodeID);
+#endif
+#endif
+        }
 
         /* If the local cell is active, send its particles for the limiting. */
         if (cj_active && with_timestep_limiter) {
@@ -1607,6 +1824,20 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
 #endif
           }
         }
+        /* If the local cell is inactive and the remote cell is active, we
+         * still need to receive stuff to be able to do the force interaction
+         * on this node as well. */
+        else if (cj_active) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+          /* See NOTE on line 1542. */
+          scheduler_activate_recv(s, cj->mpi.recv, task_subtype_xv);
+#ifndef EXTRA_HYDRO_LOOP
+          scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rho);
+#else
+          scheduler_activate_recv(s, cj->mpi.recv, task_subtype_gradient);
+#endif
+#endif
+        }
 
         /* If the foreign cell is active, we want its particles for the limiter
          */
@@ -1637,6 +1868,24 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
 #endif
           }
         }
+        /* If the foreign cell is inactive, but the local cell is active,
+         * we still need to send stuff to be able to do the force interaction
+         * on both nodes */
+        else if (ci_active) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+          /* See NOTE on line 1542. */
+          scheduler_activate_send(s, ci->mpi.send, task_subtype_xv, 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);
+#ifndef EXTRA_HYDRO_LOOP
+          scheduler_activate_send(s, ci->mpi.send, task_subtype_rho, cj_nodeID);
+#else
+          scheduler_activate_send(s, ci->mpi.send, task_subtype_gradient,
+                                  cj_nodeID);
+#endif
+#endif
+        }
 
         /* If the local cell is active, send its particles for the limiting. */
         if (ci_active && with_timestep_limiter) {
@@ -1662,7 +1911,8 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
   }
 
   /* Unskip all the other task types. */
-  if (c->nodeID == nodeID && cell_is_active_hydro(c, e)) {
+  int c_active = cell_is_active_hydro(c, e);
+  if (c->nodeID == nodeID && c_active) {
     for (struct link *l = c->hydro.gradient; l != NULL; l = l->next) {
       scheduler_activate(s, l->t);
     }
@@ -1695,6 +1945,58 @@ int cell_unskip_hydro_tasks(struct cell *c, struct scheduler *s) {
       cell_activate_star_formation_sink_tasks(c->top, s, with_feedback);
     }
   }
+  /* Additionally unskip force interactions between inactive local cell and
+   * active remote cell. (The cell unskip will only be called for active cells,
+   * so, we have to do this now, from the active remote cell). */
+  else if (c->nodeID != nodeID && c_active) {
+#if defined(MPI_SYMMETRIC_FORCE_INTERACTION) && defined(WITH_MPI)
+    for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
+      struct task *t = l->t;
+      if (t->type != task_type_pair && t->type != task_type_sub_pair) continue;
+
+      struct cell *ci = l->t->ci;
+      struct cell *cj = l->t->cj;
+
+      const int ci_active = cell_is_active_hydro(ci, e);
+      const int cj_active = cell_is_active_hydro(cj, e);
+      const int ci_nodeID = ci->nodeID;
+      const int cj_nodeID = cj->nodeID;
+      if ((!ci_active && ci_nodeID == nodeID && cj_active &&
+           cj_nodeID != nodeID) ||
+          (!cj_active && cj_nodeID == nodeID && ci_active &&
+           ci_nodeID != nodeID)) {
+        scheduler_activate(s, l->t);
+
+        if (t->type == task_type_pair) {
+          /* Store some values. */
+          atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+          atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+          ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+          cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+
+          /* Activate the drift tasks. */
+          if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
+          if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
+
+          /* Activate the limiter tasks. */
+          if (ci_nodeID == nodeID && with_timestep_limiter)
+            cell_activate_limiter(ci, s);
+          if (cj_nodeID == nodeID && with_timestep_limiter)
+            cell_activate_limiter(cj, s);
+
+          /* Check the sorts and activate them if needed. */
+          cell_activate_hydro_sorts(ci, t->flags, s);
+          cell_activate_hydro_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_hydro_tasks(ci, cj, s, with_timestep_limiter);
+        }
+      }
+    }
+#endif
+  }
 
   return rebuild;
 }
@@ -2763,18 +3065,25 @@ int cell_unskip_sinks_tasks(struct cell *c, struct scheduler *s) {
  *
  * @param c the #cell.
  * @param s the #scheduler.
+ * @param sub_cycle 1 if this is unskipping during an RT subcycle, 0 if normal
+ * unskip
  *
  * @return 1 If the space needs rebuilding. 0 otherwise.
  */
-int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
+int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s,
+                         const int sub_cycle) {
 
-  /* Note: we only get this far if engine_policy_rt is flagged. */
+  /* Do we have work here? */
+  if (c->hydro.count == 0) return 0;
 
   struct engine *e = s->space->e;
   const int nodeID = e->nodeID;
   int rebuild = 0; /* TODO: implement rebuild conditions? */
 
-  for (struct link *l = c->hydro.rt_gradient; l != NULL; l = l->next) {
+  /* Note: we only get this far if engine_policy_rt is flagged. */
+  if (!(e->policy & engine_policy_rt)) error("Unskipping RT tasks without RT");
+
+  for (struct link *l = c->rt.rt_gradient; l != NULL; l = l->next) {
 
     struct task *t = l->t;
     struct cell *ci = t->ci;
@@ -2786,32 +3095,36 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
     const int ci_nodeID = nodeID;
     const int cj_nodeID = nodeID;
 #endif
-    const int ci_active = cell_is_active_hydro(ci, e);
-    const int cj_active = (cj != NULL) && cell_is_active_hydro(cj, e);
+    const int ci_active = cell_is_rt_active(ci, e);
+    const int cj_active = (cj != NULL) && cell_is_rt_active(cj, e);
 
+    /* Only activate tasks that involve a local active cell. */
     if ((ci_active && ci_nodeID == nodeID) ||
         (cj_active && cj_nodeID == nodeID)) {
       scheduler_activate(s, t);
 
-      /* Activate hydro drift */
-      if (t->type == task_type_self) {
-        if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
-      }
-
-      else if (t->type == task_type_pair || t->type == task_type_sub_pair) {
+      if (!sub_cycle) {
+        /* Activate sorts only during main/normal steps. */
+        if (t->type == task_type_pair) {
+          atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+          atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+          ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+          cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
 
-        atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
-        atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
-        ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
-        cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+          /* Check the sorts and activate them if needed. */
+          cell_activate_rt_sorts(ci, t->flags, s);
+          cell_activate_rt_sorts(cj, t->flags, s);
+        }
 
-        /* Activate the drift tasks. */
-        if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
-        if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
+        /* Store current values of dx_max and h_max. */
+        else if (t->type == task_type_sub_self) {
+          cell_activate_subcell_rt_tasks(ci, NULL, s, sub_cycle);
+        }
 
-        /* Check the sorts and activate them if needed. */
-        cell_activate_hydro_sorts(ci, t->flags, s);
-        cell_activate_hydro_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_rt_tasks(ci, cj, s, sub_cycle);
+        }
       }
     }
 
@@ -2822,13 +3135,20 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
 
       /* 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_recv(s, ci->mpi.recv, task_subtype_rt_gradient);
+          if (sub_cycle) {
+            /* If we're in a sub-cycle, then there should be no sorts. But since
+             * hydro sorts won't be active then, the RT sorts would run. Make
+             * sure the cells are also marked to skip the RT sorts, otherwise
+             * the 'sorted' flags will be wrongly set after a recv rt_gradient.
+             * The recv tasks might also run on a higher level than the current
+             * cell, so walk all the way up. */
+            cell_set_skip_rt_sort_flag_up(ci);
+          }
 
-          /* We only need updates later on if the other cell is active as well
-           */
+          /* We only need updates later on if the other cell is active too */
           if (ci_active) {
             scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_transport);
           }
@@ -2840,10 +3160,6 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
           scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_gradient,
                                   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 (cj_active) {
             scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_transport,
                                     ci_nodeID);
@@ -2855,9 +3171,12 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
         /* If the local cell is active, receive data from the foreign cell. */
         if (ci_active) {
           scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_gradient);
+          if (sub_cycle) {
+            /* No RT sorts during sub-cycling */
+            cell_set_skip_rt_sort_flag_up(cj);
+          }
 
-          /* We only need updates later on if the other cell is active as well
-           */
+          /* We only need updates later on if the other cell is active too */
           if (cj_active) {
             scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_transport);
           }
@@ -2869,10 +3188,6 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
           scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_gradient,
                                   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);
-
           if (ci_active) {
             scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_transport,
                                     cj_nodeID);
@@ -2883,9 +3198,7 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
     }
   }
 
-  for (struct link *l = c->hydro.rt_transport; l != NULL; l = l->next) {
-    /* I assume that all hydro related subcell unskipping/activation necessary
-     * here is being done in the hydro part of cell_unskip */
+  for (struct link *l = c->rt.rt_transport; l != NULL; l = l->next) {
 
     struct task *t = l->t;
     struct cell *ci = t->ci;
@@ -2898,8 +3211,8 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
     const int cj_nodeID = nodeID;
 #endif
 
-    const int ci_active = cell_is_active_hydro(ci, e);
-    const int cj_active = ((cj != NULL) && cell_is_active_hydro(cj, e));
+    const int ci_active = cell_is_rt_active(ci, e);
+    const int cj_active = ((cj != NULL) && cell_is_rt_active(cj, e));
 
     if ((ci_active && ci_nodeID == nodeID) ||
         (cj_active && cj_nodeID == nodeID)) {
@@ -2910,23 +3223,32 @@ int cell_unskip_rt_tasks(struct cell *c, struct scheduler *s) {
         /* Activate transport_out for each cell that is part of
          * a pair/sub_pair task as to not miss any dependencies */
         if (ci_nodeID == nodeID)
-          scheduler_activate(s, ci->hydro.super->hydro.rt_transport_out);
+          scheduler_activate(s, ci->hydro.super->rt.rt_transport_out);
         if (cj_nodeID == nodeID)
-          scheduler_activate(s, cj->hydro.super->hydro.rt_transport_out);
+          scheduler_activate(s, cj->hydro.super->rt.rt_transport_out);
       }
     }
   }
 
   /* Unskip all the other task types */
-
-  if (cell_is_active_hydro(c, e)) {
-    if (c->hydro.rt_in != NULL) scheduler_activate(s, c->hydro.rt_in);
-    if (c->hydro.rt_ghost1 != NULL) scheduler_activate(s, c->hydro.rt_ghost1);
-    if (c->hydro.rt_ghost2 != NULL) scheduler_activate(s, c->hydro.rt_ghost2);
-    if (c->hydro.rt_transport_out != NULL)
-      scheduler_activate(s, c->hydro.rt_transport_out);
-    if (c->hydro.rt_tchem != NULL) scheduler_activate(s, c->hydro.rt_tchem);
-    if (c->hydro.rt_out != NULL) scheduler_activate(s, c->hydro.rt_out);
+  if (cell_is_rt_active(c, e)) {
+    if (c->nodeID == nodeID) {
+      if (c->rt.rt_in != NULL) scheduler_activate(s, c->rt.rt_in);
+      if (c->rt.rt_ghost1 != NULL) scheduler_activate(s, c->rt.rt_ghost1);
+      if (c->rt.rt_ghost2 != NULL) scheduler_activate(s, c->rt.rt_ghost2);
+      if (c->rt.rt_transport_out != NULL)
+        scheduler_activate(s, c->rt.rt_transport_out);
+      if (c->rt.rt_tchem != NULL) scheduler_activate(s, c->rt.rt_tchem);
+      if (c->rt.rt_out != NULL) scheduler_activate(s, c->rt.rt_out);
+    }
+    /* The rt_advance_cell_time tasks also run on foreign cells */
+    if (c->super != NULL && c->super->rt.rt_advance_cell_time != NULL)
+      scheduler_activate(s, c->super->rt.rt_advance_cell_time);
+    /* The rt_collect_times tasks replace the timestep_collect tasks
+     * during sub-cycles, so we only activate it when sub-cycling. */
+    if (c->rt.rt_collect_times != NULL && sub_cycle)
+      scheduler_activate(s, c->rt.rt_collect_times);
   }
+
   return rebuild;
 }
diff --git a/src/chemistry.c b/src/chemistry.c
index 78ef309af9937bfdd524aca1ff02d291555aa72b..fb10f4be7e781d24f1c8aee4b363829e94a156ee 100644
--- a/src/chemistry.c
+++ b/src/chemistry.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "chemistry.h"
diff --git a/src/chemistry.h b/src/chemistry.h
index a2ba6ad1b326ea95a7da611e3cf46b150bd90aa9..ba303b4d4d90f6d6822a68285ac84eeb3bb9f96b 100644
--- a/src/chemistry.h
+++ b/src/chemistry.h
@@ -25,7 +25,9 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "chemistry_struct.h"
 
 /* Import the right chemistry definition */
diff --git a/src/chemistry_csds.h b/src/chemistry_csds.h
index ea22d0ef93460b563230622c27df5c10eba1749b..5866340191f3eb3c8c96e30dcb8db11e55dc9ce5 100644
--- a/src/chemistry_csds.h
+++ b/src/chemistry_csds.h
@@ -20,7 +20,7 @@
 #define SWIFT_CHEMISTRY_CSDS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "align.h"
diff --git a/src/chemistry_debug.h b/src/chemistry_debug.h
index 85989898ed80e341befc31e817bea1aaea6a1719..0d08c8561fe9344c1a83e49e92bd4167f5cb2aad 100644
--- a/src/chemistry_debug.h
+++ b/src/chemistry_debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_CHEMISTRY_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right chemistry definition */
 #if defined(CHEMISTRY_NONE)
diff --git a/src/chemistry_io.h b/src/chemistry_io.h
index 148d51d20b1d506f2e33ac0b4f8bdbffe85cc404..cecbd789bcd007d0d2cb1f3ff8bf3d36066ce062 100644
--- a/src/chemistry_io.h
+++ b/src/chemistry_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_CHEMISTRY_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right functions */
 #if defined(CHEMISTRY_NONE)
diff --git a/src/chemistry_struct.h b/src/chemistry_struct.h
index eb9400591eae6aa9dc4651648c14c5133eaea17f..51572af3c7f45818fd3862bbc49e8518c5de951d 100644
--- a/src/chemistry_struct.h
+++ b/src/chemistry_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right chemistry definition */
 #if defined(CHEMISTRY_NONE)
diff --git a/src/clocks.c b/src/clocks.c
index 257e5460edb7bc0264af4a6f5fb3526a4b7d19da..9144830572a833d89521b84ded3a54039daa00e7 100644
--- a/src/clocks.c
+++ b/src/clocks.c
@@ -26,7 +26,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers. */
 #include <limits.h>
diff --git a/src/clocks.h b/src/clocks.h
index a1957560f7adf2cf529e0b84d065eed8401cd7ce..e39d8e8195439f37dff477a62401f023b755078d 100644
--- a/src/clocks.h
+++ b/src/clocks.h
@@ -20,7 +20,7 @@
 #define SWIFT_CLOCKS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* System includes. */
 #include <sys/times.h>
diff --git a/src/collectgroup.c b/src/collectgroup.c
index d7530bb451094b956d672b42ae878e79430f48d9..f876ccb37f86322617756bba75c638a8909eba27 100644
--- a/src/collectgroup.c
+++ b/src/collectgroup.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
@@ -40,11 +40,13 @@ struct mpicollectgroup1 {
   long long updated, g_updated, s_updated, sink_updated, b_updated;
   long long inhibited, g_inhibited, s_inhibited, sink_inhibited, b_inhibited;
   integertime_t ti_hydro_end_min;
+  integertime_t ti_rt_end_min;
   integertime_t ti_gravity_end_min;
   integertime_t ti_stars_end_min;
   integertime_t ti_sinks_end_min;
   integertime_t ti_black_holes_end_min;
   integertime_t ti_hydro_beg_max;
+  integertime_t ti_rt_beg_max;
   integertime_t ti_gravity_beg_max;
   integertime_t ti_stars_beg_max;
   integertime_t ti_sinks_beg_max;
@@ -99,6 +101,8 @@ void collectgroup1_apply(const struct collectgroup1 *grp1, struct engine *e) {
 
   e->ti_hydro_end_min = grp1->ti_hydro_end_min;
   e->ti_hydro_beg_max = grp1->ti_hydro_beg_max;
+  e->ti_rt_end_min = grp1->ti_rt_end_min;
+  e->ti_rt_beg_max = grp1->ti_rt_beg_max;
   e->ti_gravity_end_min = grp1->ti_gravity_end_min;
   e->ti_gravity_beg_max = grp1->ti_gravity_beg_max;
   e->ti_stars_end_min = grp1->ti_stars_end_min;
@@ -204,7 +208,8 @@ void collectgroup1_init(
     size_t s_updated, size_t sink_updated, size_t b_updated, size_t inhibited,
     size_t g_inhibited, size_t s_inhibited, size_t sink_inhibited,
     size_t b_inhibited, integertime_t ti_hydro_end_min,
-    integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min,
+    integertime_t ti_hydro_beg_max, integertime_t ti_rt_end_min,
+    integertime_t ti_rt_beg_max, integertime_t ti_gravity_end_min,
     integertime_t ti_gravity_beg_max, integertime_t ti_stars_end_min,
     integertime_t ti_stars_beg_max, integertime_t ti_sinks_end_min,
     integertime_t ti_sinks_beg_max, integertime_t ti_black_holes_end_min,
@@ -225,6 +230,8 @@ void collectgroup1_init(
   grp1->sink_inhibited = sink_inhibited;
   grp1->ti_hydro_end_min = ti_hydro_end_min;
   grp1->ti_hydro_beg_max = ti_hydro_beg_max;
+  grp1->ti_rt_end_min = ti_rt_end_min;
+  grp1->ti_rt_beg_max = ti_rt_beg_max;
   grp1->ti_gravity_end_min = ti_gravity_end_min;
   grp1->ti_gravity_beg_max = ti_gravity_beg_max;
   grp1->ti_stars_end_min = ti_stars_end_min;
@@ -271,11 +278,13 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) {
   mpigrp11.sink_inhibited = grp1->sink_inhibited;
   mpigrp11.b_inhibited = grp1->b_inhibited;
   mpigrp11.ti_hydro_end_min = grp1->ti_hydro_end_min;
+  mpigrp11.ti_rt_end_min = grp1->ti_rt_end_min;
   mpigrp11.ti_gravity_end_min = grp1->ti_gravity_end_min;
   mpigrp11.ti_stars_end_min = grp1->ti_stars_end_min;
   mpigrp11.ti_sinks_end_min = grp1->ti_sinks_end_min;
   mpigrp11.ti_black_holes_end_min = grp1->ti_black_holes_end_min;
   mpigrp11.ti_hydro_beg_max = grp1->ti_hydro_beg_max;
+  mpigrp11.ti_rt_beg_max = grp1->ti_rt_beg_max;
   mpigrp11.ti_gravity_beg_max = grp1->ti_gravity_beg_max;
   mpigrp11.ti_stars_beg_max = grp1->ti_stars_beg_max;
   mpigrp11.ti_sinks_beg_max = grp1->ti_sinks_beg_max;
@@ -309,11 +318,13 @@ void collectgroup1_reduce(struct collectgroup1 *grp1) {
   grp1->sink_inhibited = mpigrp12.sink_inhibited;
   grp1->b_inhibited = mpigrp12.b_inhibited;
   grp1->ti_hydro_end_min = mpigrp12.ti_hydro_end_min;
+  grp1->ti_rt_end_min = mpigrp12.ti_rt_end_min;
   grp1->ti_gravity_end_min = mpigrp12.ti_gravity_end_min;
   grp1->ti_stars_end_min = mpigrp12.ti_stars_end_min;
   grp1->ti_sinks_end_min = mpigrp12.ti_sinks_end_min;
   grp1->ti_black_holes_end_min = mpigrp12.ti_black_holes_end_min;
   grp1->ti_hydro_beg_max = mpigrp12.ti_hydro_beg_max;
+  grp1->ti_rt_beg_max = mpigrp12.ti_rt_beg_max;
   grp1->ti_gravity_beg_max = mpigrp12.ti_gravity_beg_max;
   grp1->ti_stars_beg_max = mpigrp12.ti_stars_beg_max;
   grp1->ti_sinks_beg_max = mpigrp12.ti_sinks_beg_max;
@@ -362,6 +373,8 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11,
   /* Minimum end time. */
   mpigrp11->ti_hydro_end_min =
       min(mpigrp11->ti_hydro_end_min, mpigrp12->ti_hydro_end_min);
+  mpigrp11->ti_rt_end_min =
+      min(mpigrp11->ti_rt_end_min, mpigrp12->ti_rt_end_min);
   mpigrp11->ti_gravity_end_min =
       min(mpigrp11->ti_gravity_end_min, mpigrp12->ti_gravity_end_min);
   mpigrp11->ti_stars_end_min =
@@ -374,6 +387,8 @@ static void doreduce1(struct mpicollectgroup1 *mpigrp11,
   /* Maximum beg time. */
   mpigrp11->ti_hydro_beg_max =
       max(mpigrp11->ti_hydro_beg_max, mpigrp12->ti_hydro_beg_max);
+  mpigrp11->ti_rt_beg_max =
+      max(mpigrp11->ti_rt_beg_max, mpigrp12->ti_rt_beg_max);
   mpigrp11->ti_gravity_beg_max =
       max(mpigrp11->ti_gravity_beg_max, mpigrp12->ti_gravity_beg_max);
   mpigrp11->ti_stars_beg_max =
diff --git a/src/collectgroup.h b/src/collectgroup.h
index 8c70085664c265ff38341598c4d9c4a60ca816e6..68815dab0f98035f5bfc4b3616e00da96fa124a4 100644
--- a/src/collectgroup.h
+++ b/src/collectgroup.h
@@ -20,7 +20,7 @@
 #define SWIFT_COLLECTGROUP_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers. */
 #include <stddef.h>
@@ -46,6 +46,7 @@ struct collectgroup1 {
 
   /* Times for the time-step */
   integertime_t ti_hydro_end_min, ti_hydro_beg_max;
+  integertime_t ti_rt_end_min, ti_rt_beg_max;
   integertime_t ti_gravity_end_min, ti_gravity_beg_max;
   integertime_t ti_stars_end_min, ti_stars_beg_max;
   integertime_t ti_black_holes_end_min, ti_black_holes_beg_max;
@@ -83,7 +84,8 @@ void collectgroup1_init(
     size_t s_updated, size_t b_updated, size_t sink_updated, size_t inhibited,
     size_t g_inhibited, size_t s_inhibited, size_t sink_inhibited,
     size_t b_inhibited, integertime_t ti_hydro_end_min,
-    integertime_t ti_hydro_beg_max, integertime_t ti_gravity_end_min,
+    integertime_t ti_hydro_beg_max, integertime_t ti_rt_end_min,
+    integertime_t ti_rt_beg_max, integertime_t ti_gravity_end_min,
     integertime_t ti_gravity_beg_max, integertime_t ti_stars_end_min,
     integertime_t ti_stars_beg_max, integertime_t ti_sinks_end_min,
     integertime_t ti_sinks_beg_max, integertime_t ti_black_holes_end_min,
diff --git a/src/common_io.c b/src/common_io.c
index 3b41f3d538e288e50061fcdda8c51dd5d1a623f1..d07d5780251e412c05b98ada95701b651e2bd94e 100644
--- a/src/common_io.c
+++ b/src/common_io.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "common_io.h"
diff --git a/src/common_io.h b/src/common_io.h
index 920cf90de831e6c1eed6d71fd382020426af8f48..f04ad62d3b725298060ed34783c64e03e62182a4 100644
--- a/src/common_io.h
+++ b/src/common_io.h
@@ -21,7 +21,7 @@
 #define SWIFT_COMMON_IO_H
 
 /* Config parameters. */
-#include "config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "part_type.h"
diff --git a/src/common_io_cells.c b/src/common_io_cells.c
index d714270cacbfbe7ae077b25b745e53b8354f2fdd..79ba20cbae504ea637409ab7db01a3b03656869d 100644
--- a/src/common_io_cells.c
+++ b/src/common_io_cells.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "common_io.h"
diff --git a/src/common_io_copy.c b/src/common_io_copy.c
index 6dbbe827e5263d101dd8ca0974cdcf4dc375b0dc..2be7cb6e4aacfc6796d6592ae1c8f8abfaf36ac2 100644
--- a/src/common_io_copy.c
+++ b/src/common_io_copy.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "common_io.h"
diff --git a/src/common_io_fields.c b/src/common_io_fields.c
index 3a6a826b210775bf2687538682a842fa54f174e5..c85bb674759e9742a6fa5937a424088d51717d77 100644
--- a/src/common_io_fields.c
+++ b/src/common_io_fields.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "common_io.h"
diff --git a/src/const.h b/src/const.h
index 4bfbaa1ca7c9f258a737ede8a14cf208d79219ac..429fe2275a518df5c57402ab1f7276e2df39be1e 100644
--- a/src/const.h
+++ b/src/const.h
@@ -102,4 +102,8 @@
 #define SOURCETERMS_NONE
 //#define SOURCETERMS_SN_FEEDBACK
 
+/* GRACKLE doesn't really like exact zeroes, so use something
+ * comparatively small instead. */
+#define RT_GEAR_TINY_MASS_FRACTION 1.e-20
+
 #endif /* SWIFT_CONST_H */
diff --git a/src/cooling.c b/src/cooling.c
index c53ddccf505a95c12e10f246b19b26dbff88124f..665f0a8360030b23c1a2f2f6147997399b2c0d58 100644
--- a/src/cooling.c
+++ b/src/cooling.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "cooling.h"
diff --git a/src/cooling.h b/src/cooling.h
index 1f5a47a7349750649f7791d6643788242b995f9e..747de00f6fff0bd7795ee0164331a466726e53d2 100644
--- a/src/cooling.h
+++ b/src/cooling.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "parser.h"
diff --git a/src/cooling/COLIBRE/cooling.c b/src/cooling/COLIBRE/cooling.c
index 7a529aaa43f52b794ce8cd2b1f55659427c410df..5adfe42ef668dc0090429431bbfcea040b119ba8 100644
--- a/src/cooling/COLIBRE/cooling.c
+++ b/src/cooling/COLIBRE/cooling.c
@@ -22,7 +22,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/cooling/COLIBRE/cooling_io.h b/src/cooling/COLIBRE/cooling_io.h
index 9e23a4dfd0449878745e1504657c78997d9451d0..ddeb56b851f81ed062768134970762746b577e2f 100644
--- a/src/cooling/COLIBRE/cooling_io.h
+++ b/src/cooling/COLIBRE/cooling_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_COOLING_COLIBRE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/cooling/COLIBRE/cooling_rates.h b/src/cooling/COLIBRE/cooling_rates.h
index 12eba57e361d0683bc64c9c048a4f5b2ca5d0a30..c70b02d9c57f265df289e5263241130c36e2fe66 100644
--- a/src/cooling/COLIBRE/cooling_rates.h
+++ b/src/cooling/COLIBRE/cooling_rates.h
@@ -20,7 +20,7 @@
 #define SWIFT_COLIBRE_COOLING_RATES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "chemistry_struct.h"
diff --git a/src/cooling/COLIBRE/cooling_subgrid.h b/src/cooling/COLIBRE/cooling_subgrid.h
index 86868a272e5ec07a4076b02ca21fd51efe24b296..ed50efebd5a3daf36a1a5bd4e338cdede231efd7 100644
--- a/src/cooling/COLIBRE/cooling_subgrid.h
+++ b/src/cooling/COLIBRE/cooling_subgrid.h
@@ -20,7 +20,7 @@
 #define SWIFT_COLIBRE_COOLING_SUBGRID_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "cooling.h"
diff --git a/src/cooling/COLIBRE/cooling_tables.c b/src/cooling/COLIBRE/cooling_tables.c
index 7904e34f72a4cc4bbe331d606ffcc4d231065b9d..bce5946f99fa70b18f56e5ab4fffa97821bb1fb3 100644
--- a/src/cooling/COLIBRE/cooling_tables.c
+++ b/src/cooling/COLIBRE/cooling_tables.c
@@ -23,7 +23,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This file's header */
 #include "cooling_tables.h"
diff --git a/src/cooling/COLIBRE/cooling_tables.h b/src/cooling/COLIBRE/cooling_tables.h
index f65309b65874fdb6360429dc9aea9c51e736fd5d..3eb70ee999645d2844a151c21aab7ac6a2909180 100644
--- a/src/cooling/COLIBRE/cooling_tables.h
+++ b/src/cooling/COLIBRE/cooling_tables.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "config.h"
+#include <config.h>
 
 /*! Number of different bins along the temperature axis of the tables */
 #define colibre_cooling_N_temperature 86
diff --git a/src/cooling/COLIBRE/interpolate.h b/src/cooling/COLIBRE/interpolate.h
index 9d198afea8516a4a74def91ee7a89946627148cf..4f0c8c7b887261f14ec5ffd43aa1f1ea3a3b00cf 100644
--- a/src/cooling/COLIBRE/interpolate.h
+++ b/src/cooling/COLIBRE/interpolate.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "align.h"
diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c
index 16d743cd59dd458ab4b1216836e84499667c822f..aaa88c629871acf49da9799d5a4ab0e81d549cf7 100644
--- a/src/cooling/EAGLE/cooling.c
+++ b/src/cooling/EAGLE/cooling.c
@@ -22,7 +22,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h
index 4078132825a5d489efd9c66fd882a93d2a0c6e66..46a2d3ef14996520888784824ef7cdbaa32ed48c 100644
--- a/src/cooling/EAGLE/cooling_io.h
+++ b/src/cooling/EAGLE/cooling_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_COOLING_EAGLE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/cooling/EAGLE/cooling_rates.h b/src/cooling/EAGLE/cooling_rates.h
index 2dfb2780ac0d142be78e6708c3691c9de6004d6d..132badb8a25696f72b8211052294605978e12ed9 100644
--- a/src/cooling/EAGLE/cooling_rates.h
+++ b/src/cooling/EAGLE/cooling_rates.h
@@ -21,7 +21,7 @@
 #ifndef SWIFT_EAGLE_COOLING_RATES_H
 #define SWIFT_EAGLE_COOLING_RATES_H
 
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "cooling_tables.h"
diff --git a/src/cooling/EAGLE/cooling_tables.c b/src/cooling/EAGLE/cooling_tables.c
index 1cab6fccfc534fee58d6c90f0f9f6985dde7f542..0b32ce20d9b557f0b9873fbe52850a45dd3f200f 100644
--- a/src/cooling/EAGLE/cooling_tables.c
+++ b/src/cooling/EAGLE/cooling_tables.c
@@ -23,8 +23,9 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <hdf5.h>
 #include <math.h>
 #include <stdlib.h>
diff --git a/src/cooling/EAGLE/cooling_tables.h b/src/cooling/EAGLE/cooling_tables.h
index ecac1259cf107c2605bc8b80c62aad576e58c71e..c492fa962d4401c06a5be19496559323e3a68691 100644
--- a/src/cooling/EAGLE/cooling_tables.h
+++ b/src/cooling/EAGLE/cooling_tables.h
@@ -25,7 +25,9 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "cooling_struct.h"
 
 /*! Number of different bins along the redhsift axis of the tables */
diff --git a/src/cooling/EAGLE/interpolate.h b/src/cooling/EAGLE/interpolate.h
index bfff3c229fb66013ec81f70a82a17d293ab336e7..080c881525e5584a5c76964f7adce0c27b9f3ae8 100644
--- a/src/cooling/EAGLE/interpolate.h
+++ b/src/cooling/EAGLE/interpolate.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "align.h"
diff --git a/src/cooling/QLA/cooling.c b/src/cooling/QLA/cooling.c
index ca9eb6770c55f4e641c5fa835c040b56e813a8d3..60e50ce2f26f3caf1b24d5cb2412d06aa58ab357 100644
--- a/src/cooling/QLA/cooling.c
+++ b/src/cooling/QLA/cooling.c
@@ -22,7 +22,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/cooling/QLA/cooling_io.h b/src/cooling/QLA/cooling_io.h
index 7c6ea88539977fd720b412f085eb57ca9b5db701..5f4352f5c6a75d800763fabfeecb13beb10ed612 100644
--- a/src/cooling/QLA/cooling_io.h
+++ b/src/cooling/QLA/cooling_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_COOLING_QLA_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/cooling/QLA/cooling_rates.h b/src/cooling/QLA/cooling_rates.h
index b668694be66b7e551e8c4c0e5fb0016574021b94..0a2d544ae69a47bf8ae94d12801b18bd9e24c63d 100644
--- a/src/cooling/QLA/cooling_rates.h
+++ b/src/cooling/QLA/cooling_rates.h
@@ -20,7 +20,7 @@
 #define SWIFT_QLA_COOLING_RATES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "chemistry_struct.h"
diff --git a/src/cooling/QLA/cooling_tables.c b/src/cooling/QLA/cooling_tables.c
index ff84e9e1926311bf9e069655adf050c480522813..728df70232a5d1f44b45b0c969463b1307986d95 100644
--- a/src/cooling/QLA/cooling_tables.c
+++ b/src/cooling/QLA/cooling_tables.c
@@ -23,7 +23,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This file's header */
 #include "cooling_tables.h"
diff --git a/src/cooling/QLA/cooling_tables.h b/src/cooling/QLA/cooling_tables.h
index 06610abcefbf8ee15621a142bbaec39af59e57f1..6f8121ce1b9d03e07e5e3f7243f3ef0987734a31 100644
--- a/src/cooling/QLA/cooling_tables.h
+++ b/src/cooling/QLA/cooling_tables.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "config.h"
+#include <config.h>
 
 /*! Number of different bins along the temperature axis of the tables */
 #define qla_cooling_N_temperature 86
diff --git a/src/cooling/QLA/interpolate.h b/src/cooling/QLA/interpolate.h
index b1cacae696393cdfc84e72a7a811362ab4fcc9e4..2444e31fb4becf824c1f4ec3caef0469414f3a7e 100644
--- a/src/cooling/QLA/interpolate.h
+++ b/src/cooling/QLA/interpolate.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "align.h"
diff --git a/src/cooling/QLA_EAGLE/cooling.c b/src/cooling/QLA_EAGLE/cooling.c
index bab618ebf4471454d16f4302bc7b2e2b1128e336..eacc7d02c5af41de9d84ea45acaf0d52e60c2d75 100644
--- a/src/cooling/QLA_EAGLE/cooling.c
+++ b/src/cooling/QLA_EAGLE/cooling.c
@@ -22,7 +22,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/cooling/QLA_EAGLE/cooling_io.h b/src/cooling/QLA_EAGLE/cooling_io.h
index 171d0e742a9d5d2c57a44b39506a450d62b3fefc..fdaae31ede74213ef5feae81de5a8db3cef14e5a 100644
--- a/src/cooling/QLA_EAGLE/cooling_io.h
+++ b/src/cooling/QLA_EAGLE/cooling_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_COOLING_QLA_EAGLE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/cooling/QLA_EAGLE/cooling_rates.h b/src/cooling/QLA_EAGLE/cooling_rates.h
index 4dbb5014caacff3657330e8d44e2af98053a5ab9..c71fa4b31a5941a42fa0eafd00fbe15be00f3d48 100644
--- a/src/cooling/QLA_EAGLE/cooling_rates.h
+++ b/src/cooling/QLA_EAGLE/cooling_rates.h
@@ -21,7 +21,7 @@
 #ifndef SWIFT_QLA_EAGLE_COOLING_RATES_H
 #define SWIFT_QLA_EAGLE_COOLING_RATES_H
 
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "cooling_tables.h"
diff --git a/src/cooling/QLA_EAGLE/cooling_tables.c b/src/cooling/QLA_EAGLE/cooling_tables.c
index 1e97822d0bcdd9ffe08e350623eb2027a830c57e..5ee735a0f2d4d7dd6177ce6fab0b41875a8386bc 100644
--- a/src/cooling/QLA_EAGLE/cooling_tables.c
+++ b/src/cooling/QLA_EAGLE/cooling_tables.c
@@ -23,8 +23,9 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <hdf5.h>
 #include <math.h>
 #include <stdlib.h>
diff --git a/src/cooling/QLA_EAGLE/cooling_tables.h b/src/cooling/QLA_EAGLE/cooling_tables.h
index 2f406d610c0d00980ba39e02cf08ef75556d287f..3759088c74055fde8fbf1e2ce9a0458da1968386 100644
--- a/src/cooling/QLA_EAGLE/cooling_tables.h
+++ b/src/cooling/QLA_EAGLE/cooling_tables.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "config.h"
+#include <config.h>
 
 /*! Number of different bins along the redhsift axis of the tables */
 #define qla_eagle_cooling_N_redshifts 49
diff --git a/src/cooling/QLA_EAGLE/interpolate.h b/src/cooling/QLA_EAGLE/interpolate.h
index 98b49405f830e8531e1fa4c048391a1bc90adf72..d8811beed11ed402be764d95bfebf29a26d640e7 100644
--- a/src/cooling/QLA_EAGLE/interpolate.h
+++ b/src/cooling/QLA_EAGLE/interpolate.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "align.h"
diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h
index 0307b26ee0d1496f916cad11ef0c7de08c5a6213..4076ac07ce6cbc434510da3b47469a05668f2c9e 100644
--- a/src/cooling/const_du/cooling.h
+++ b/src/cooling/const_du/cooling.h
@@ -34,7 +34,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/cooling/const_du/cooling_io.h b/src/cooling/const_du/cooling_io.h
index bfc93c683af8ce3599266993f851563669e7f523..2bc09ec7c16aa073c7b7ba03a04f290467f03307 100644
--- a/src/cooling/const_du/cooling_io.h
+++ b/src/cooling/const_du/cooling_io.h
@@ -31,7 +31,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h
index 84f214b6adb94c7702703bd23723f3e5b2f0d415..b09e6610d58b5ae531b232b7539afb32082d658a 100644
--- a/src/cooling/const_lambda/cooling.h
+++ b/src/cooling/const_lambda/cooling.h
@@ -29,7 +29,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h
index 047a1b8c853f949509cc553a88f6f63d8e190ec4..0efba80c294b732e2e4e59d824143b879757e37d 100644
--- a/src/cooling/const_lambda/cooling_io.h
+++ b/src/cooling/const_lambda/cooling_io.h
@@ -29,7 +29,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/cooling/grackle/cooling.c b/src/cooling/grackle/cooling.c
index eef6d499856594992583d709a4d69817a2f1c687..48e2072a8f23d7e95cddccd8fa9cfc455479eaea 100644
--- a/src/cooling/grackle/cooling.c
+++ b/src/cooling/grackle/cooling.c
@@ -21,7 +21,7 @@
  * @brief Cooling using the GRACKLE 3.1.1 library.
  */
 
-#include "../config.h"
+#include <config.h>
 
 /* Include header */
 #include "cooling.h"
diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h
index 787408e6846181eb7abbb646b31ed8f0c78ba5c2..69128aa3577c072b1c2633cabef820acdb21d486 100644
--- a/src/cooling/none/cooling.h
+++ b/src/cooling/none/cooling.h
@@ -23,7 +23,7 @@
  * @file src/cooling/none/cooling.h
  * @brief Empty infrastructure for the cases without cooling function
  */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/cooling/none/cooling_io.h b/src/cooling/none/cooling_io.h
index bea30ce716af37a9cf5550f7e7964a8e1aca4cb2..a0962787f88b4e1ec15fd009dd487d89b131904a 100644
--- a/src/cooling/none/cooling_io.h
+++ b/src/cooling/none/cooling_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_COOLING_NONE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/cooling_debug.h b/src/cooling_debug.h
index 61912155b668d1014468257422e7e0f464d2b1e6..50d213eaf97393d77abe3fd5aabdafb53694a9e5 100644
--- a/src/cooling_debug.h
+++ b/src/cooling_debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_COOLING_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right cooling definition */
 #if defined(COOLING_NONE)
diff --git a/src/cooling_io.h b/src/cooling_io.h
index 69153775d2bdce057c3c8d9825332b2c26896163..ed08fe3371b562b1722b8f24d9f6e7e7dbde82d3 100644
--- a/src/cooling_io.h
+++ b/src/cooling_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_COOLING_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the i/o routines of the right cooling definition */
 #if defined(COOLING_NONE)
diff --git a/src/cooling_properties.h b/src/cooling_properties.h
index a807ae1eb5dcc27a639ed9187565874e52725056..ba399d1e9c0e3ab7aa149736b9bc878b661d078b 100644
--- a/src/cooling_properties.h
+++ b/src/cooling_properties.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right cooling definition */
 #if defined(COOLING_NONE)
diff --git a/src/cooling_struct.h b/src/cooling_struct.h
index de69723e766626e3d2558c1ae2c727b7e29f6b66..c7962624cc862abe2594d273d2faf57127d86a18 100644
--- a/src/cooling_struct.h
+++ b/src/cooling_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right cooling definition */
 #if defined(COOLING_NONE)
diff --git a/src/cosmology.c b/src/cosmology.c
index 285dfe39cb2c1b6a553fd50c5a5d410fdb0faf82..d7dc6a3f9fae48fb72d17d098e88b4b2057eac8d 100644
--- a/src/cosmology.c
+++ b/src/cosmology.c
@@ -220,9 +220,18 @@ void cosmology_update(struct cosmology *c, const struct phys_const *phys_const,
   /* Mean density */
   c->mean_density = c->critical_density_0 * c->a3_inv;
 
+  /* Mean matter density */
+  c->mean_density_Omega_m = c->mean_density * Omega_m;
+
   /* Mean baryonic density */
   c->mean_density_Omega_b = c->mean_density * c->Omega_b;
 
+  /* Over-density threshold for virialization
+   * Fitting function from Bryan & Norman, 1998, ApJ, 495, 1, 80-99
+   * Equation 6. */
+  const double x = Omega_m * c->a3_inv / (E_z * E_z) - 1.;
+  c->overdensity_BN98 = 18. * M_PI * M_PI + 82. * x - 39 * x * x;
+
   /* Time-step conversion factor */
   c->time_step_factor = c->H;
 
@@ -1050,7 +1059,9 @@ void cosmology_init_no_cosmo(struct cosmology *c) {
   c->critical_density = 0.;
   c->critical_density_0 = 0.;
   c->mean_density = 0.;
+  c->mean_density_Omega_m = 0;
   c->mean_density_Omega_b = 0;
+  c->overdensity_BN98 = 0.;
   c->T_CMB_0 = 0.;
   c->T_CMB_0_K = 0.;
 
@@ -1513,6 +1524,14 @@ void cosmology_write_model(hid_t h_grp, const struct cosmology *c) {
   io_write_attribute_d(h_grp, "Scale-factor", c->a);
   io_write_attribute_d(h_grp, "Critical density [internal units]",
                        c->critical_density);
+  io_write_attribute_d(h_grp,
+                       "Critical density at redshift zero [internal units]",
+                       c->critical_density_0);
+  io_write_attribute_d(h_grp, "Mean matter density [internal units]",
+                       c->mean_density_Omega_m);
+  io_write_attribute_d(h_grp, "Mean baryonic density [internal units]",
+                       c->mean_density_Omega_b);
+  io_write_attribute_d(h_grp, "Virial overdensity (BN98)", c->overdensity_BN98);
 }
 #endif
 
diff --git a/src/cosmology.h b/src/cosmology.h
index 4274bbd1e35482c2929e7894620a116c75886723..a5fa2f32e41dfd5c4c9d1c141f83ecad489302d7 100644
--- a/src/cosmology.h
+++ b/src/cosmology.h
@@ -20,7 +20,9 @@
 #define SWIFT_COSMOLOGY_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "parser.h"
 #include "physical_constants.h"
 #include "timeline.h"
@@ -81,10 +83,18 @@ struct cosmology {
   /*! The mean density at the current redshift (in internal physical units) */
   double mean_density;
 
+  /*! The mean matter density at the current redshift (in internal physical
+   * units) */
+  double mean_density_Omega_m;
+
   /*! The mean baryonic density at the current redshift (in internal physical
    * units) */
   double mean_density_Omega_b;
 
+  /*! Over-density for virialised haloes at the current redshift
+   * from the Bryan & Norman 1998 fit */
+  double overdensity_BN98;
+
   /*! Conversion factor from internal time-step size to cosmological step */
   double time_step_factor;
 
diff --git a/src/csds.c b/src/csds.c
index cec056b299587eb95ca95e71b06abf0025042895..0dbd8e4221d8edc8b561436f460cd273ff7d34c7 100644
--- a/src/csds.c
+++ b/src/csds.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef HAVE_POSIX_FALLOCATE /* Are we on a sensible platform? */
 #ifdef WITH_CSDS
diff --git a/src/csds.h b/src/csds.h
index ac929dc9a62e9b0a5ecbf2d05f50f4b1ea15be74..57682faf3d2f81e87d615116f15264a27a015111 100644
--- a/src/csds.h
+++ b/src/csds.h
@@ -20,7 +20,7 @@
 #ifndef SWIFT_CSDS_H
 #define SWIFT_CSDS_H
 
-#include "../config.h"
+#include <config.h>
 
 #ifdef WITH_CSDS
 
diff --git a/src/csds_io.c b/src/csds_io.c
index 63da5c51f74eb1b410e28e59393325020f0ed20a..08308ff0154006ffce57afc03f12d4cc9d6c3d39 100644
--- a/src/csds_io.c
+++ b/src/csds_io.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(WITH_CSDS)
 
diff --git a/src/csds_io.h b/src/csds_io.h
index 213320a4eca845a416a99b1a6bcfb9eb7da72559..3f7de3ef12d8284e02b911124752af91e58989ce 100644
--- a/src/csds_io.h
+++ b/src/csds_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_CSDS_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef WITH_CSDS
 
diff --git a/src/cycle.h b/src/cycle.h
index 0ba1277893fe7be7d8b62cdcb9a8873d5709eb60..73b2e3aa485283103dbfa670a7efdb39701cf4f5 100644
--- a/src/cycle.h
+++ b/src/cycle.h
@@ -42,7 +42,7 @@
 
    (In order to use some of the OS-dependent timer routines like
    Solaris' gethrtime, you need to paste the autoconf snippet below
-   into your configure.ac file and #include "config.h" before cycle.h,
+   into your configure.ac file and #include <config.h> before cycle.h,
    or define the relevant macros manually if you are not using autoconf.)
 */
 
@@ -81,16 +81,8 @@ intrinsic.])], [rtc_ok=no])
 /***************************************************************************/
 
 #include <stdint.h>
-#if TIME_WITH_SYS_TIME
 #include <sys/time.h>
 #include <time.h>
-#else
-#if HAVE_SYS_TIME_H
-#include <sys/time.h>
-#else
-#include <time.h>
-#endif
-#endif
 
 #define INLINE_ELAPSED(INL)                       \
   static INL double elapsed(ticks t1, ticks t0) { \
diff --git a/src/debug.h b/src/debug.h
index 57170841eb2e7ed6ebd04c5b008530401cd391a7..6c8d3198294c150e6651db3212da38e219a3eff2 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "cell.h"
diff --git a/src/dimension.h b/src/dimension.h
index df1c6200e34c99a79c099529270318da43cb1e41..f44c95731b6b9fc2b87749a3d1a8934334681274 100644
--- a/src/dimension.h
+++ b/src/dimension.h
@@ -26,7 +26,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/distributed_io.c b/src/distributed_io.c
index b2a5cb5120828c46f2565aedc4e4f9685f25fe15..79ae4eb4f19c0801621a2cbd2c6af94f1b262846 100644
--- a/src/distributed_io.c
+++ b/src/distributed_io.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && defined(WITH_MPI)
 
@@ -571,6 +571,8 @@ void write_virtual_file(struct engine* e, const char* fileName_base,
                      swift_type_count);
   io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT,
                      numParticlesHighWord, swift_type_count);
+  io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total,
+                     swift_type_count);
   double MassTable[swift_type_count] = {0};
   io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count);
   io_write_attribute(h_grp, "InitialMassTable", DOUBLE,
@@ -633,7 +635,8 @@ void write_virtual_file(struct engine* e, const char* fileName_base,
     if (h_err < 0) error("Error while creating alias for particle group.\n");
 
     /* Write the number of particles as an attribute */
-    io_write_attribute_l(h_grp, "NumberOfParticles", N_total[ptype]);
+    io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]);
+    io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]);
 
     int num_fields = 0;
     struct io_props list[100];
@@ -1053,6 +1056,8 @@ void write_output_distributed(struct engine* e,
                      swift_type_count);
   io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT,
                      numParticlesHighWord, swift_type_count);
+  io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total,
+                     swift_type_count);
   double MassTable[swift_type_count] = {0};
   io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count);
   io_write_attribute(h_grp, "InitialMassTable", DOUBLE,
@@ -1148,7 +1153,8 @@ void write_output_distributed(struct engine* e,
     if (h_err < 0) error("Error while creating alias for particle group.\n");
 
     /* Write the number of particles as an attribute */
-    io_write_attribute_l(h_grp, "NumberOfParticles", N[ptype]);
+    io_write_attribute_ll(h_grp, "NumberOfParticles", N[ptype]);
+    io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]);
 
     int num_fields = 0;
     struct io_props list[100];
diff --git a/src/distributed_io.h b/src/distributed_io.h
index 41b2740ecede0b919a738c55350e2c62c731a604..0a73129ab7ab6c5a4502b4dcfeab9b72b47558f8 100644
--- a/src/distributed_io.h
+++ b/src/distributed_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_DISTRIBUTED_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && defined(WITH_MPI)
 
diff --git a/src/drift.h b/src/drift.h
index a0645c4ba6a9950bf59f23eac99926da32e2b0cf..4f7d6768b78a7c2547df9598abec9338f1d7017e 100644
--- a/src/drift.h
+++ b/src/drift.h
@@ -20,7 +20,7 @@
 #define SWIFT_DRIFT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "black_holes.h"
@@ -35,6 +35,7 @@
 #include "lightcone/lightcone_replications.h"
 #include "mhd.h"
 #include "part.h"
+#include "rt.h"
 #include "sink.h"
 #include "stars.h"
 
@@ -198,8 +199,10 @@ __attribute__((always_inline)) INLINE static void drift_part(
   }
 
   /* Predict the values of the extra fields */
-  hydro_predict_extra(p, xp, dt_drift, dt_therm, cosmo, hydro_props, floor);
+  hydro_predict_extra(p, xp, dt_drift, dt_therm, dt_kick_grav, cosmo,
+                      hydro_props, floor);
   mhd_predict_extra(p, xp, dt_drift, dt_therm, cosmo, hydro_props, floor);
+  rt_predict_extra(p, xp, dt_drift);
 
   /* Compute offsets since last cell construction */
   for (int k = 0; k < 3; k++) {
diff --git a/src/engine.c b/src/engine.c
index 91fe46d7a989079875bd8456771f6c6fd085b55a..4dfe5e6c1900ed2e705bff22433c3550a11c1e00 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -23,7 +23,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
@@ -439,10 +439,11 @@ void engine_repartition_trigger(struct engine *e) {
         fprintf(timelog, "# %d mean times: %f %f %f\n", e->step, umean, smean,
                 tmean);
         if (abs_trigger > 1.f) abs_trigger = 0.f; /* Not relevant. */
+        double systime = smean > 0. ? (smaxtime - smintime) / smean : 0.;
+        double ttime = tmean > 0. ? (tmaxtime - tmintime) / tmean : 0.;
         fprintf(timelog,
                 "# %d balance: %f, expected: %f (sys: %f, total: %f)\n",
-                e->step, balance, abs_trigger, (smaxtime - smintime) / smean,
-                (tmaxtime - tmintime) / tmean);
+                e->step, balance, abs_trigger, systime, ttime);
 
         fclose(timelog);
 
@@ -1143,11 +1144,12 @@ int engine_estimate_nr_tasks(const struct engine *e) {
     /* gradient: 1 self + 13 pairs                   |   14
      * transport: 1 self + 13 pairs                  | + 14
      * implicits: in + out, transport_out            | +  3
-     * others: ghost1, ghost2, thermochemistry       | +  3
+     * others: ghost1, ghost2, tchem, cell advance   | +  4
+     * sort, collect_times                           | +  2
      * 2 extra space                                 | +  2 */
-    n1 += 36;
+    n1 += 39;
 #ifdef WITH_MPI
-    n1 += 4; /* TODO: check this */
+    n1 += 2;
 #endif
   }
 
@@ -1662,6 +1664,7 @@ void engine_skip_force_and_kick(struct engine *e) {
         t->type == task_type_bh_swallow_ghost3 || t->type == task_type_bh_in ||
         t->type == task_type_bh_out || t->type == task_type_rt_ghost1 ||
         t->type == task_type_rt_ghost2 || t->type == task_type_rt_tchem ||
+        t->type == task_type_rt_advance_cell_time ||
         t->type == task_type_neutrino_weight || t->type == task_type_csds ||
         t->subtype == task_subtype_force ||
         t->subtype == task_subtype_limiter ||
@@ -1810,6 +1813,89 @@ void engine_get_max_ids(struct engine *e) {
 #endif
 }
 
+/**
+ * @brief Run the radiative transfer sub-cycles outside the
+ * regular time-steps.
+ *
+ * @param e The #engine
+ **/
+void engine_run_rt_sub_cycles(struct engine *e) {
+
+  /* Do we have work to do? */
+  if (!(e->policy & engine_policy_rt)) return;
+  if (e->max_nr_rt_subcycles <= 1) return;
+
+  /* Get the subcycling step */
+  const integertime_t rt_step_size = e->ti_rt_end_min - e->ti_current;
+  if (rt_step_size == 0) {
+    /* When we arrive at the final step, the rt_step_size can be == 0 */
+    if (!engine_is_done(e)) error("Got rt_step_size = 0");
+    return;
+  }
+
+  /* At this point, the non-RT ti_end_min is up-to-date. Use that and
+   * the time of the previous regular step to get how many subcycles
+   * we need. */
+  const int nr_rt_cycles = (e->ti_end_min - e->ti_current) / rt_step_size;
+  /* You can't check here that the number of cycles is exactly the number
+   * you fixed it to be. E.g. stars or gravity may reduce the time step
+   * sizes for some main steps such that they coincide with the RT bins,
+   * yielding effectively no subcycles. (At least for low numbers.) */
+
+  /* Get some time variables for printouts. Don't update the ones in the
+   * engine like in the regular step, or the outputs in the regular steps
+   * will be wrong. */
+  /* think cosmology one day: needs adapting here */
+  if (e->policy & engine_policy_cosmology)
+    error("Can't run RT subcycling with cosmology yet");
+  const double dt_subcycle = rt_step_size * e->time_base;
+  double time = e->ti_current_subcycle * e->time_base + e->time_begin;
+
+  /* Collect and print info before it's gone */
+  engine_collect_end_of_sub_cycle(e);
+  if (e->nodeID == 0) {
+    printf(
+        "  %6d cycle   0 (during regular tasks) dt=%14e "
+        "min/max active bin=%2d/%2d rt_updates=%18lld\n",
+        e->step, dt_subcycle, e->min_active_bin_subcycle,
+        e->max_active_bin_subcycle, e->rt_updates);
+  }
+
+  /* Note: zeroth sub-cycle already happened during the regular tasks,
+   * so we need to do one less than that. */
+  for (int sub_cycle = 1; sub_cycle < nr_rt_cycles; ++sub_cycle) {
+
+    e->rt_updates = 0ll;
+    integertime_t ti_subcycle_old = e->ti_current_subcycle;
+    e->ti_current_subcycle = e->ti_current + sub_cycle * rt_step_size;
+    e->max_active_bin_subcycle = get_max_active_bin(e->ti_current_subcycle);
+    e->min_active_bin_subcycle =
+        get_min_active_bin(e->ti_current_subcycle, ti_subcycle_old);
+    /* think cosmology one day: needs adapting here */
+    if (e->policy & engine_policy_cosmology)
+      error("Can't run RT subcycling with cosmology yet");
+    time = e->ti_current_subcycle * e->time_base + e->time_begin;
+
+    /* Do the actual work now. */
+    engine_unskip_rt_sub_cycle(e);
+    engine_launch(e, "cycles");
+
+    /* Collect number of updates and print */
+    engine_collect_end_of_sub_cycle(e);
+
+    if (e->nodeID == 0) {
+      printf(
+          "  %6d cycle %3d time=%13.6e     dt=%14e "
+          "min/max active bin=%2d/%2d rt_updates=%18lld\n",
+          e->step, sub_cycle, time, dt_subcycle, e->min_active_bin_subcycle,
+          e->max_active_bin_subcycle, e->rt_updates);
+    }
+  }
+
+  /* Once we're done, clean up after ourselves */
+  e->rt_updates = 0ll;
+}
+
 /**
  * @brief Initialises the particles and set them in a state ready to move
  *forward in time.
@@ -2024,12 +2110,6 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
   engine_launch(e, "tasks");
   TIMER_TOC2(timer_runners);
 
-  /* Initialise additional RT data now that time bins are set */
-#ifdef SWIFT_RT_DEBUG_CHECKS
-  if (e->policy & engine_policy_rt)
-    space_convert_rt_quantities_after_zeroth_step(e->s, e->verbose);
-#endif
-
 #ifdef SWIFT_HYDRO_DENSITY_CHECKS
   /* Run the brute-force hydro calculation for some parts */
   if (e->policy & engine_policy_hydro)
@@ -2171,6 +2251,9 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
     }
   }
 
+  /* Run the RT sub-cycles now. */
+  engine_run_rt_sub_cycles(e);
+
   clocks_gettime(&time2);
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -2193,6 +2276,14 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
   e->force_checks_snapshot_flag = 0;
 #endif
 
+#ifdef SWIFT_RT_DEBUG_CHECKS
+  /* if we're running the debug RT scheme, do some checks after every step,
+   * and reset debugging flags now. */
+  if (e->policy & engine_policy_rt) {
+    rt_debugging_checks_end_of_step(e, e->verbose);
+  }
+#endif
+
   if (e->verbose) message("took %.3f %s.", e->wallclock_time, clocks_getunit());
 }
 
@@ -2200,8 +2291,9 @@ void engine_init_particles(struct engine *e, int flag_entropy_ICs,
  * @brief Let the #engine loose to compute the forces.
  *
  * @param e The #engine.
+ * @return Should the run stop after this step?
  */
-void engine_step(struct engine *e) {
+int engine_step(struct engine *e) {
 
   TIMER_TIC2;
 
@@ -2269,8 +2361,11 @@ void engine_step(struct engine *e) {
               clocks_from_ticks(getticks() - tic_files), clocks_getunit());
   }
 
-  /* We need some cells to exist but not the whole task stuff. */
+  /* When restarting, we may have had some i/o to do on the step
+   * where we decided to stop. We have to do this now.
+   * We need some cells to exist but not the whole task stuff. */
   if (e->restarting) space_rebuild(e->s, 0, e->gravity_properties, e->verbose);
+  if (e->restarting) engine_io(e);
 
   /* Move forward in time */
   e->ti_old = e->ti_current;
@@ -2281,6 +2376,12 @@ void engine_step(struct engine *e) {
   engine_current_step = e->step;
   e->step_props = engine_step_prop_none;
 
+  /* RT sub-cycling related time updates */
+  e->max_active_bin_subcycle = get_max_active_bin(e->ti_end_min);
+  e->min_active_bin_subcycle =
+      get_min_active_bin(e->ti_end_min, e->ti_current_subcycle);
+  e->ti_current_subcycle = e->ti_end_min;
+
   /* When restarting, move everyone to the current time. */
   if (e->restarting) engine_drift_all(e, /*drift_mpole=*/1);
 
@@ -2341,6 +2442,7 @@ void engine_step(struct engine *e) {
     }
   }
 
+  /* Trigger a rebuild if we reached a gravity mesh step? */
   if ((e->policy & engine_policy_self_gravity) && e->s->periodic &&
       e->mesh->ti_end_mesh_next == e->ti_current)
     e->forcerebuild = 1;
@@ -2353,6 +2455,37 @@ void engine_step(struct engine *e) {
     }
   }
 
+  /* Trigger a tree-rebuild if the fraction of active gparts is large enough */
+  if ((e->policy & engine_policy_self_gravity) && !e->forcerebuild &&
+      e->gravity_properties->rebuild_active_fraction <= 1.0f) {
+
+    ticks tic = getticks();
+
+    /* Count the number of active particles */
+    size_t nr_gparts = e->s->nr_gparts;
+    size_t nr_active_gparts = 0;
+    for (size_t i = 0; i < nr_gparts; ++i) {
+      struct gpart *gp = &e->s->gparts[i];
+      if (gpart_is_active(gp, e)) nr_active_gparts++;
+    }
+
+    long long total_nr_active_gparts = nr_active_gparts;
+#ifdef WITH_MPI
+    MPI_Allreduce(MPI_IN_PLACE, &total_nr_active_gparts, 1, MPI_LONG_LONG_INT,
+                  MPI_SUM, MPI_COMM_WORLD);
+#endif
+
+    if (e->verbose)
+      message("Counting active gparts took %.3f %s.",
+              clocks_from_ticks(getticks() - tic), clocks_getunit());
+
+    /* Trigger the tree-rebuild? */
+    if (((double)total_nr_active_gparts >
+         ((double)e->total_nr_gparts) *
+             e->gravity_properties->rebuild_active_fraction))
+      e->forcerebuild = 1;
+  }
+
 #ifdef WITH_CSDS
   if (e->policy & engine_policy_csds) {
     /* Mark the current time step in the particle csds file. */
@@ -2390,15 +2523,13 @@ void engine_step(struct engine *e) {
   /* Prepare the tasks to be launched, rebuild or repartition if needed. */
   const int drifted_all = engine_prepare(e);
 
+  /* Dump local cells and active particle counts. */
+  // dumpCells("cells", 1, 0, 0, 0, e->s, e->nodeID, e->step);
+
 #ifdef SWIFT_DEBUG_CHECKS
   /* Print the number of active tasks */
   if (e->verbose) engine_print_task_counts(e);
-#endif
 
-    /* Dump local cells and active particle counts. */
-    // dumpCells("cells", 1, 0, 0, 0, e->s, e->nodeID, e->step);
-
-#ifdef SWIFT_DEBUG_CHECKS
   /* Check that we have the correct total mass in the top-level multipoles */
   long long num_gpart_mpole = 0;
   if (e->policy & engine_policy_self_gravity) {
@@ -2454,7 +2585,7 @@ void engine_step(struct engine *e) {
   }
 #endif
 
-  /* Re-compute the mesh forces */
+  /* Re-compute the mesh forces? */
   if ((e->policy & engine_policy_self_gravity) && e->s->periodic &&
       e->mesh->ti_end_mesh_next == e->ti_current) {
 
@@ -2489,6 +2620,11 @@ void engine_step(struct engine *e) {
       e->step % e->sched.frequency_task_levels == 0)
     scheduler_write_task_level(&e->sched, e->step);
 
+  /* we have to reset the ghost histograms here and not in engine_launch,
+     because engine_launch is re-used for the limiter and sync (and we don't
+     want to lose the data from the tasks) */
+  space_reset_ghost_histograms(e->s);
+
   /* Start all the tasks. */
   TIMER_TIC;
   engine_launch(e, "tasks");
@@ -2583,6 +2719,9 @@ void engine_step(struct engine *e) {
     error("Obtained a time-step of size 0");
 #endif
 
+  /* Run the RT sub-cycling now. */
+  engine_run_rt_sub_cycles(e);
+
 #ifdef WITH_CSDS
   if (e->policy & engine_policy_csds && e->verbose)
     message("The CSDS currently uses %f GB of storage",
@@ -2603,17 +2742,30 @@ void engine_step(struct engine *e) {
 #endif
 
   /* Create a restart file if needed. */
-  engine_dump_restarts(e, 0, e->restart_onexit && engine_is_done(e));
+  const int force_stop =
+      engine_dump_restarts(e, 0, e->restart_onexit && engine_is_done(e));
 
-  engine_check_for_dumps(e);
+  /* Is there any form of i/o this step?
+   *
+   * Note that if the run was forced to stop, we do not dump,
+   * we will do so when the run is restarted*/
+  if (!force_stop) engine_io(e);
 
-#if defined(SWIFT_RT_DEBUG_CHECKS)
+#ifdef SWIFT_RT_DEBUG_CHECKS
   /* if we're running the debug RT scheme, do some checks after every step.
    * Do this after the output so we can safely reset debugging checks now. */
   if (e->policy & engine_policy_rt)
     rt_debugging_checks_end_of_step(e, e->verbose);
 #endif
 
+#ifdef SWIFT_RT_DEBUG_CHECKS
+  /* if we're running the debug RT scheme, do some checks after every step.
+   * Do this after the output so we can safely reset debugging flags now. */
+  if (e->policy & engine_policy_rt) {
+    rt_debugging_checks_end_of_step(e, e->verbose);
+  }
+#endif
+
   TIMER_TOC2(timer_step);
 
   clocks_gettime(&time2);
@@ -2621,6 +2773,8 @@ void engine_step(struct engine *e) {
 
   /* Time in ticks at the end of this step. */
   e->toc_step = getticks();
+
+  return force_stop;
 }
 
 /**
@@ -2991,6 +3145,9 @@ void engine_init(
   e->time_end = 0.;
   e->max_active_bin = num_time_bins;
   e->min_active_bin = 1;
+  e->ti_current_subcycle = 0;
+  e->max_active_bin_subcycle = num_time_bins;
+  e->min_active_bin_subcycle = 1;
   e->internal_units = internal_units;
   e->output_list_snapshots = NULL;
   e->a_first_snapshot =
@@ -3046,6 +3203,8 @@ void engine_init(
   e->ps_output_count = 0;
   e->dt_min = parser_get_param_double(params, "TimeIntegration:dt_min");
   e->dt_max = parser_get_param_double(params, "TimeIntegration:dt_max");
+  e->max_nr_rt_subcycles = parser_get_opt_param_int(
+      params, "TimeIntegration:max_nr_rt_subcycles", /*default=*/0);
   e->dt_max_RMS_displacement = FLT_MAX;
   e->max_RMS_displacement_factor = parser_get_opt_param_double(
       params, "TimeIntegration:max_dt_RMS_factor", 0.25);
diff --git a/src/engine.h b/src/engine.h
index ba1498df6a3755138b5e8ce75632dcfd5d1090ac..5fa8db8508b351276b446ee8ff0055636bd93f52 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -25,7 +25,7 @@
 #define SWIFT_ENGINE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
@@ -196,6 +196,14 @@ struct engine {
   /* The lowest active bin at this time */
   timebin_t min_active_bin;
 
+  /* RT sub-cycling counterparts for timestepping vars */
+  integertime_t ti_current_subcycle;
+  timebin_t max_active_bin_subcycle;
+  timebin_t min_active_bin_subcycle;
+
+  /* Maximal number of radiative transfer sub-cycles per hydro step */
+  int max_nr_rt_subcycles;
+
   /* Time step */
   double time_step;
 
@@ -212,6 +220,12 @@ struct engine {
   /* Maximal hydro ti_beg for the next time-step */
   integertime_t ti_hydro_beg_max;
 
+  /* Minimal rt ti_end for the next time-step */
+  integertime_t ti_rt_end_min;
+
+  /* Maximal rt ti_beg for the next time-step */
+  integertime_t ti_rt_beg_max;
+
   /* Minimal gravity ti_end for the next time-step */
   integertime_t ti_gravity_end_min;
 
@@ -255,7 +269,7 @@ struct engine {
   integertime_t ti_beg_max;
 
   /* Number of particles updated in the previous step */
-  long long updates, g_updates, s_updates, b_updates, sink_updates;
+  long long updates, g_updates, s_updates, b_updates, sink_updates, rt_updates;
 
   /* Number of updates since the last rebuild */
   long long updates_since_rebuild;
@@ -560,9 +574,29 @@ struct engine {
   /* Do we free the foreign data before rebuilding the tree? */
   int free_foreign_when_rebuilding;
 
+  /* Name of the restart file directory. */
+  const char *restart_dir;
+
   /* Name of the restart file. */
   const char *restart_file;
 
+  /* Flag whether we should resubmit on this step? */
+  int resubmit;
+
+  /* Do we want to run the resubmission command once a run reaches the time
+   * limit? */
+  int resubmit_after_max_hours;
+
+  /* What command should we run to resubmit at the end? */
+  char resubmit_command[PARSER_MAX_LINE_SIZE];
+
+  /* How often to check for the stop file and dump restarts and exit the
+   * application. */
+  int restart_stop_steps;
+
+  /* Get the maximal wall-clock time of this run */
+  float restart_max_hours_runtime;
+
   /* Ticks between restart dumps. */
   ticks restart_dt;
 
@@ -634,13 +668,15 @@ void engine_compute_next_los_time(struct engine *e);
 void engine_compute_next_ps_time(struct engine *e);
 void engine_recompute_displacement_constraint(struct engine *e);
 void engine_unskip(struct engine *e);
+void engine_unskip_rt_sub_cycle(struct engine *e);
 void engine_drift_all(struct engine *e, const int drift_mpoles);
 void engine_drift_top_multipoles(struct engine *e);
 void engine_reconstruct_multipoles(struct engine *e);
 void engine_allocate_foreign_particles(struct engine *e, const int fof);
 void engine_print_stats(struct engine *e);
-void engine_check_for_dumps(struct engine *e);
+void engine_io(struct engine *e);
 void engine_collect_end_of_step(struct engine *e, int apply);
+void engine_collect_end_of_sub_cycle(struct engine *e);
 void engine_dump_snapshot(struct engine *e);
 void engine_run_on_dump(struct engine *e);
 void engine_init_output_lists(struct engine *e, struct swift_params *params,
@@ -672,13 +708,14 @@ void engine_init(
 void engine_config(int restart, int fof, struct engine *e,
                    struct swift_params *params, int nr_nodes, int nodeID,
                    int nr_task_threads, int nr_pool_threads, int with_aff,
-                   int verbose, const char *restart_file,
-                   struct repartition *reparttype);
+                   int verbose, const char *restart_dir,
+                   const char *restart_file, struct repartition *reparttype);
 void engine_launch(struct engine *e, const char *call);
 int engine_prepare(struct engine *e);
+void engine_run_rt_sub_cycles(struct engine *e);
 void engine_init_particles(struct engine *e, int flag_entropy_ICs,
                            int clean_h_values);
-void engine_step(struct engine *e);
+int engine_step(struct engine *e);
 void engine_split(struct engine *e, struct partition *initial_partition);
 void engine_exchange_strays(struct engine *e, const size_t offset_parts,
                             const int *ind_part, size_t *Npart,
@@ -724,6 +761,6 @@ void engine_numa_policies(int rank, int verbose);
 /* Struct dump/restore support. */
 void engine_struct_dump(struct engine *e, FILE *stream);
 void engine_struct_restore(struct engine *e, FILE *stream);
-void engine_dump_restarts(struct engine *e, int drifted_all, int final_step);
+int engine_dump_restarts(struct engine *e, int drifted_all, int force);
 
 #endif /* SWIFT_ENGINE_H */
diff --git a/src/engine_collect_end_of_step.c b/src/engine_collect_end_of_step.c
index ce81bb15912ea9cba9a591e2ada0de0b950037bd..edca57333c939646e7e04fd22bf2ff2798e544b3 100644
--- a/src/engine_collect_end_of_step.c
+++ b/src/engine_collect_end_of_step.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "engine.h"
@@ -39,6 +39,7 @@ struct end_of_step_data {
   size_t updated, g_updated, s_updated, sink_updated, b_updated;
   size_t inhibited, g_inhibited, s_inhibited, sink_inhibited, b_inhibited;
   integertime_t ti_hydro_end_min, ti_hydro_beg_max;
+  integertime_t ti_rt_end_min, ti_rt_beg_max;
   integertime_t ti_gravity_end_min, ti_gravity_beg_max;
   integertime_t ti_stars_end_min, ti_stars_beg_max;
   integertime_t ti_sinks_end_min, ti_sinks_beg_max;
@@ -75,6 +76,7 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements,
   size_t updated = 0, g_updated = 0, s_updated = 0, sink_updated = 0,
          b_updated = 0;
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0;
+  integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0;
   integertime_t ti_gravity_end_min = max_nr_timesteps, ti_gravity_beg_max = 0;
   integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_beg_max = 0;
   integertime_t ti_sinks_end_min = max_nr_timesteps, ti_sinks_beg_max = 0;
@@ -98,6 +100,10 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements,
         ti_hydro_end_min = min(ti_hydro_end_min, c->hydro.ti_end_min);
       ti_hydro_beg_max = max(ti_hydro_beg_max, c->hydro.ti_beg_max);
 
+      if (c->rt.ti_rt_end_min > e->ti_current)
+        ti_rt_end_min = min(c->rt.ti_rt_end_min, ti_rt_end_min);
+      ti_rt_beg_max = max(c->rt.ti_rt_beg_max, ti_rt_beg_max);
+
       if (c->grav.ti_end_min > e->ti_current)
         ti_gravity_end_min = min(ti_gravity_end_min, c->grav.ti_end_min);
       ti_gravity_beg_max = max(ti_gravity_beg_max, c->grav.ti_beg_max);
@@ -156,6 +162,10 @@ void engine_collect_end_of_step_mapper(void *map_data, int num_elements,
       data->ti_hydro_end_min = min(ti_hydro_end_min, data->ti_hydro_end_min);
     data->ti_hydro_beg_max = max(ti_hydro_beg_max, data->ti_hydro_beg_max);
 
+    if (ti_rt_end_min > e->ti_current)
+      data->ti_rt_end_min = min(ti_rt_end_min, data->ti_rt_end_min);
+    data->ti_rt_beg_max = max(ti_rt_beg_max, data->ti_rt_beg_max);
+
     if (ti_gravity_end_min > e->ti_current)
       data->ti_gravity_end_min =
           min(ti_gravity_end_min, data->ti_gravity_end_min);
@@ -205,6 +215,7 @@ void engine_collect_end_of_step(struct engine *e, int apply) {
   data.updated = 0, data.g_updated = 0, data.s_updated = 0, data.b_updated = 0;
   data.sink_updated = 0;
   data.ti_hydro_end_min = max_nr_timesteps, data.ti_hydro_beg_max = 0;
+  data.ti_rt_end_min = max_nr_timesteps, data.ti_rt_beg_max = 0;
   data.ti_gravity_end_min = max_nr_timesteps, data.ti_gravity_beg_max = 0;
   data.ti_stars_end_min = max_nr_timesteps, data.ti_stars_beg_max = 0;
   data.ti_sinks_end_min = max_nr_timesteps, data.ti_sinks_beg_max = 0;
@@ -249,9 +260,10 @@ void engine_collect_end_of_step(struct engine *e, int apply) {
       &e->collect_group1, data.updated, data.g_updated, data.s_updated,
       data.sink_updated, data.b_updated, data.inhibited, data.g_inhibited,
       data.s_inhibited, data.sink_inhibited, data.b_inhibited,
-      data.ti_hydro_end_min, data.ti_hydro_beg_max, data.ti_gravity_end_min,
-      data.ti_gravity_beg_max, data.ti_stars_end_min, data.ti_stars_beg_max,
-      data.ti_sinks_end_min, data.ti_sinks_beg_max, data.ti_black_holes_end_min,
+      data.ti_hydro_end_min, data.ti_hydro_beg_max, data.ti_rt_end_min,
+      data.ti_rt_beg_max, data.ti_gravity_end_min, data.ti_gravity_beg_max,
+      data.ti_stars_end_min, data.ti_stars_beg_max, data.ti_sinks_end_min,
+      data.ti_sinks_beg_max, data.ti_black_holes_end_min,
       data.ti_black_holes_beg_max, e->forcerebuild, e->s->tot_cells,
       e->sched.nr_tasks, (float)e->sched.nr_tasks / (float)e->s->tot_cells,
       data.sfh, data.runtime, data.flush_lightcone_maps, data.deadtime,
@@ -340,3 +352,70 @@ void engine_collect_end_of_step(struct engine *e, int apply) {
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
 }
+
+/**
+ * @brief Mapping function to collect the data from the end of the sub-cycle
+ *
+ * @param map_data The list of cells with tasks on this node.
+ * @param num_elements The number of elements in the list this thread will work
+ * on.
+ * @param extra_data The #engine.
+ */
+void engine_collect_end_of_sub_cycle_mapper(void *map_data, int num_elements,
+                                            void *extra_data) {
+
+  struct engine *e = (struct engine *)extra_data;
+  struct space *s = e->s;
+  int *local_cells = (int *)map_data;
+
+  /* Local collectible */
+  long long rt_updated = 0LL;
+
+  for (int ind = 0; ind < num_elements; ind++) {
+    struct cell *c = &s->cells_top[local_cells[ind]];
+
+    if (c->hydro.count > 0) {
+
+      /* Aggregate data */
+      rt_updated += c->rt.updated;
+
+      /* Collected, so clear for next time. */
+      c->rt.updated = 0;
+    }
+  }
+
+  /* write back to the global data. */
+  atomic_add(&e->rt_updates, rt_updated);
+}
+
+/**
+ * @brief Collects additional data at the end of a subcycle.
+ * This function does not collect any data relevant to the
+ * time-steps or time integration.
+ *
+ * @param e The #engine.
+ */
+void engine_collect_end_of_sub_cycle(struct engine *e) {
+
+  const ticks tic = getticks();
+  struct space *s = e->s;
+
+  /* Collect information from the local top-level cells */
+  threadpool_map(&e->threadpool, engine_collect_end_of_sub_cycle_mapper,
+                 s->local_cells_top, s->nr_local_cells, sizeof(int),
+                 threadpool_auto_chunk_size, e);
+
+  /* Aggregate collective data from the different nodes for this step. */
+#ifdef WITH_MPI
+  long long rt_updates_tot = 0ll;
+  int test = MPI_Reduce(&e->rt_updates, &rt_updates_tot, 1, MPI_LONG_LONG,
+                        MPI_SUM, 0, MPI_COMM_WORLD);
+  if (test != MPI_SUCCESS) error("MPI reduce failed");
+  /* Overwrite only on rank 0. */
+  if (e->nodeID == 0) e->rt_updates = rt_updates_tot;
+#endif
+
+  if (e->verbose)
+    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
+            clocks_getunit());
+}
diff --git a/src/engine_config.c b/src/engine_config.c
index 7cb43050c40ce9b6a5b5f26b050825a23dae62ca..b444c9a8ed357eb3e88728f6e2ff18e8bc049e20 100644
--- a/src/engine_config.c
+++ b/src/engine_config.c
@@ -20,8 +20,9 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -162,8 +163,8 @@ static void engine_dumper_init(struct engine *e) {
 void engine_config(int restart, int fof, struct engine *e,
                    struct swift_params *params, int nr_nodes, int nodeID,
                    int nr_task_threads, int nr_pool_threads, int with_aff,
-                   int verbose, const char *restart_file,
-                   struct repartition *reparttype) {
+                   int verbose, const char *restart_dir,
+                   const char *restart_file, struct repartition *reparttype) {
 
   struct clocks_time tic, toc;
   if (nodeID == 0) clocks_gettime(&tic);
@@ -187,7 +188,9 @@ void engine_config(int restart, int fof, struct engine *e,
   e->verbose = verbose;
   e->wallclock_time = 0.f;
   e->restart_dump = 0;
+  e->restart_dir = restart_dir;
   e->restart_file = restart_file;
+  e->resubmit = 0;
   e->restart_next = 0;
   e->restart_dt = 0;
   e->run_fof = 0;
@@ -215,6 +218,21 @@ void engine_config(int restart, int fof, struct engine *e,
   /* Welcome message */
   if (e->nodeID == 0) message("Running simulation '%s'.", e->run_name);
 
+  /* Check-pointing properties */
+
+  e->restart_stop_steps =
+      parser_get_opt_param_int(params, "Restarts:stop_steps", 100);
+
+  e->restart_max_hours_runtime =
+      parser_get_opt_param_float(params, "Restarts:max_run_time", FLT_MAX);
+
+  e->resubmit_after_max_hours =
+      parser_get_opt_param_int(params, "Restarts:resubmit_on_exit", 0);
+
+  if (e->resubmit_after_max_hours)
+    parser_get_param_string(params, "Restarts:resubmit_command",
+                            e->resubmit_command);
+
   /* Get the number of queues */
   int nr_queues =
       parser_get_opt_param_int(params, "Scheduler:nr_queues", e->nr_threads);
@@ -528,6 +546,30 @@ void engine_config(int restart, int fof, struct engine *e,
     if (e->policy & engine_policy_stars)
       if (e->nodeID == 0) stars_props_print(e->stars_properties);
 
+    /* Print information about the RT scheme */
+    if (e->policy & engine_policy_rt) {
+      rt_props_print(e->rt_props);
+      if (e->nodeID == 0) {
+        if (e->max_nr_rt_subcycles <= 1)
+          message("WARNING: running without RT sub-cycling.");
+        else {
+          /* Make sure max_nr_rt_subcycles is an acceptable power of 2 */
+          timebin_t power_subcycles = 0;
+          while ((e->max_nr_rt_subcycles > (1 << power_subcycles)) &&
+                 power_subcycles < num_time_bins)
+            ++power_subcycles;
+          if (power_subcycles == num_time_bins)
+            error("TimeIntegration:max_nr_rt_subcycles=%d too big",
+                  e->max_nr_rt_subcycles);
+          if ((1 << power_subcycles) > e->max_nr_rt_subcycles)
+            error("TimeIntegration:max_nr_rt_subcycles=%d not a power of 2",
+                  e->max_nr_rt_subcycles);
+          message("Running up to %d RT sub-cycles per hydro step.",
+                  e->max_nr_rt_subcycles);
+        }
+      }
+    }
+
     /* Check we have sensible time bounds */
     if (e->time_begin >= e->time_end)
       error(
diff --git a/src/engine_drift.c b/src/engine_drift.c
index cce538ab320d8659e7ce9b5ca2510c970676892f..dabcb6456adb22993c671da7feec0ce77ea6a314 100644
--- a/src/engine_drift.c
+++ b/src/engine_drift.c
@@ -23,7 +23,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "engine.h"
@@ -356,16 +356,14 @@ void engine_drift_all(struct engine *e, const int drift_mpoles) {
 
   const ticks tic = getticks();
 
-#ifdef SWIFT_DEBUG_CHECKS
-  if (e->nodeID == 0) {
+  if (e->nodeID == 0 && e->verbose) {
     if (e->policy & engine_policy_cosmology)
-      message("Drifting all to a=%e",
+      message("Drifting all to a=%15.12e",
               exp(e->ti_current * e->time_base) * e->cosmology->a_begin);
     else
-      message("Drifting all to t=%e",
+      message("Drifting all to t=%15.12e",
               e->ti_current * e->time_base + e->time_begin);
   }
-#endif
 
 #ifdef WITH_LIGHTCONE
   /* Determine which periodic replications could contribute to the lightcone
diff --git a/src/engine_fof.c b/src/engine_fof.c
index 4d4fc98b5eee0cb90fd84940a5fc0b6d03898ebc..7cda72195d7cd10fee4b4102bb4c8569cbf9f87f 100644
--- a/src/engine_fof.c
+++ b/src/engine_fof.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "engine.h"
diff --git a/src/engine_io.c b/src/engine_io.c
index 90b74e97e850fa49003b21fb8f6f54ff30f72209..5b934ce3502e4b0fa9cd1d202c464b88282fd138 100644
--- a/src/engine_io.c
+++ b/src/engine_io.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
@@ -50,20 +50,33 @@
  * @param e the engine.
  * @param drifted_all true if a drift_all has just been performed.
  * @param force force a dump, if dumping is enabled.
+ * @return Do we want to stop the run altogether?
  */
-void engine_dump_restarts(struct engine *e, int drifted_all, int force) {
+int engine_dump_restarts(struct engine *e, const int drifted_all,
+                         const int force) {
+
+  /* Are any of the conditions to fully stop a run met? */
+  const int end_run_time = e->runtime > e->restart_max_hours_runtime;
+  const int stop_file = (e->step % e->restart_stop_steps == 0 &&
+                         restart_stop_now(e->restart_dir, 0));
+
+  /* Exit run when told to */
+  const int exit_run = (end_run_time || stop_file);
 
   if (e->restart_dump) {
     ticks tic = getticks();
 
+    const int check_point_time = tic > e->restart_next;
+
     /* Dump when the time has arrived, or we are told to. */
-    int dump = ((tic > e->restart_next) || force);
+    int dump = (check_point_time || end_run_time || force || stop_file);
 
 #ifdef WITH_MPI
     /* Synchronize this action from rank 0 (ticks may differ between
      * machines). */
     MPI_Bcast(&dump, 1, MPI_INT, 0, MPI_COMM_WORLD);
 #endif
+
     if (dump) {
 
       if (e->nodeID == 0) {
@@ -123,6 +136,12 @@ void engine_dump_restarts(struct engine *e, int drifted_all, int force) {
       e->step_props |= engine_step_prop_restarts;
     }
   }
+
+  /* If we stopped by reaching the time limit, flag that we need to
+   * run the resubmission command */
+  if (end_run_time && e->resubmit_after_max_hours) e->resubmit = 1;
+
+  return exit_run;
 }
 
 /**
@@ -249,7 +268,7 @@ void engine_run_on_dump(struct engine *e) {
  *
  * @param e The #engine.
  */
-void engine_check_for_dumps(struct engine *e) {
+void engine_io(struct engine *e) {
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const int with_stf = (e->policy & engine_policy_structure_finding);
   const int with_los = (e->policy & engine_policy_line_of_sight);
diff --git a/src/engine_maketasks.c b/src/engine_maketasks.c
index 3b5d4e415c145da56a78690aea7ffc3d670e7029..35833fadc0a31c4871eb040e12ae70bf1c9a55d2 100644
--- a/src/engine_maketasks.c
+++ b/src/engine_maketasks.c
@@ -23,7 +23,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdlib.h>
@@ -131,17 +131,23 @@ void engine_addtasks_send_gravity(struct engine *e, struct cell *ci,
  * @param t_gradient The send_gradient #task, if already created.
  * @param t_prep1 The send_prep1 #task, if it has already been created.
  * @param t_limiter The send_limiter #task, if it has already been created.
+ * @param t_rt_gradient The send_rt_gradient #task, if it has already been
+ * created.
+ * @param t_rt_transport The send_rt_transport #task, if it has already been
  * @param with_feedback Are we running with stellar feedback?
  * @param with_limiter Are we running with the time-step limiter?
  * @param with_sync Are we running with time-step synchronization?
+ * @param with_rt Are we running with radiative transfer?
  */
 void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
                                 struct cell *cj, struct task *t_xv,
                                 struct task *t_rho, struct task *t_gradient,
                                 struct task *t_prep1, struct task *t_limiter,
                                 struct task *t_pack_limiter,
+                                struct task *t_rt_gradient,
+                                struct task *t_rt_transport,
                                 const int with_feedback, const int with_limiter,
-                                const int with_sync) {
+                                const int with_sync, const int with_rt) {
 
 #ifdef WITH_MPI
   struct link *l = NULL;
@@ -170,10 +176,12 @@ 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);
+      scheduler_addunlock(s, t_xv, t_rho);
 
 #ifdef EXTRA_HYDRO_LOOP
       t_gradient = scheduler_addtask(s, task_type_send, task_subtype_gradient,
                                      ci->mpi.tag, 0, ci, cj);
+      scheduler_addunlock(s, t_rho, t_gradient);
 #endif
 
       if (with_limiter) {
@@ -192,6 +200,17 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
       }
 #endif
 
+      if (with_rt) {
+        /* Add the RT sends */
+        t_rt_gradient =
+            scheduler_addtask(s, task_type_send, task_subtype_rt_gradient,
+                              ci->mpi.tag, 0, ci, cj);
+
+        t_rt_transport =
+            scheduler_addtask(s, task_type_send, task_subtype_rt_transport,
+                              ci->mpi.tag, 0, ci, cj);
+      }
+
 #ifdef EXTRA_HYDRO_LOOP
 
       scheduler_addunlock(s, t_gradient, ci->hydro.super->hydro.end_force);
@@ -236,7 +255,39 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
         scheduler_addunlock(s, t_prep1, ci->hydro.super->stars.prep2_ghost);
       }
 #endif
-    }
+
+      if (with_rt) {
+        /* Don't send the transport stuff before the gradient stuff */
+        scheduler_addunlock(s, t_rt_gradient, t_rt_transport);
+
+        /* The send_gradient task depends on the cell's ghost1 task. */
+        scheduler_addunlock(s, ci->hydro.super->rt.rt_ghost1, t_rt_gradient);
+
+        /* The send_transport task depends on the cell's ghost2 task. */
+        scheduler_addunlock(s, ci->hydro.super->rt.rt_ghost2, t_rt_transport);
+
+        /* Safety measure: collect dependencies and make sure data is sent
+         * before modifying it */
+        scheduler_addunlock(s, t_rt_gradient, ci->hydro.super->rt.rt_ghost2);
+
+        /* Safety measure: collect dependencies and make sure data is sent
+         * before modifying it */
+        scheduler_addunlock(s, t_rt_transport,
+                            ci->hydro.super->rt.rt_transport_out);
+
+        /* Drift before you send. Especially intended to cover inactive cells
+         * being sent. */
+        scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_gradient);
+        scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_transport);
+
+        /* Make sure the gradient sends don't run before the xv is finished.
+         * This can occur when a cell itself is inactive for both hydro and
+         * RT, but needs to be sent over for some other cell's pair task.
+         * The rt_gradient - xv dependency is special because when received,
+         * these two tasks will/may activate the sorts.*/
+        scheduler_addunlock(s, t_xv, t_rt_gradient);
+      }
+    } /* if t_xv == NULL */
 
     /* Add them to the local cell. */
     engine_addlink(e, &ci->mpi.send, t_xv);
@@ -251,6 +302,12 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
 #ifdef EXTRA_STAR_LOOPS
     if (with_feedback) engine_addlink(e, &ci->mpi.send, t_prep1);
 #endif
+
+    if (with_rt) {
+      /* Add them to the local cell. */
+      engine_addlink(e, &ci->mpi.send, t_rt_gradient);
+      engine_addlink(e, &ci->mpi.send, t_rt_transport);
+    }
   }
 
   /* Recurse? */
@@ -259,7 +316,8 @@ void engine_addtasks_send_hydro(struct engine *e, struct cell *ci,
       if (ci->progeny[k] != NULL)
         engine_addtasks_send_hydro(
             e, ci->progeny[k], cj, t_xv, t_rho, t_gradient, t_prep1, t_limiter,
-            t_pack_limiter, with_feedback, with_limiter, with_sync);
+            t_pack_limiter, t_rt_gradient, t_rt_transport, with_feedback,
+            with_limiter, with_sync, with_rt);
 
 #else
   error("SWIFT was not compiled with MPI support.");
@@ -483,87 +541,6 @@ void engine_addtasks_send_black_holes(struct engine *e, struct cell *ci,
 #endif
 }
 
-/**
- * @brief Add send tasks for the RT 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_rt_gradient The send_rt_gradient #task, if it has already been
- * created.
- * @param t_rt_transport The send_rt_transport #task, if it has already been
- * created.
- */
-void engine_addtasks_send_rt(struct engine *e, struct cell *ci, struct cell *cj,
-                             struct task *t_rt_gradient,
-                             struct task *t_rt_transport) {
-
-#ifdef WITH_MPI
-  struct link *l = NULL;
-  struct scheduler *s = &e->sched;
-  const int nodeID = cj->nodeID;
-
-  /* Early abort (are we below the level where tasks are)? */
-  if (!cell_get_flag(ci, cell_flag_has_tasks)) return;
-
-  /* Check if any of the density tasks are for the target node. */
-  for (l = ci->hydro.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) {
-
-    /* Create the tasks and their dependencies? */
-    if (t_rt_gradient == NULL) {
-
-      /* Make sure this cell is tagged. */
-      cell_ensure_tagged(ci);
-
-      t_rt_gradient = scheduler_addtask(
-          s, task_type_send, task_subtype_rt_gradient, ci->mpi.tag, 0, ci, cj);
-      t_rt_transport = scheduler_addtask(
-          s, task_type_send, task_subtype_rt_transport, ci->mpi.tag, 0, ci, cj);
-
-      /* The send_gradient task depends on the cell's ghost1 task. */
-      scheduler_addunlock(s, ci->hydro.super->hydro.rt_ghost1, t_rt_gradient);
-
-      /* The send_transport task depends on the cell's ghost2 task. */
-      scheduler_addunlock(s, ci->hydro.super->hydro.rt_ghost2, t_rt_transport);
-
-      /* Safety measure: collect dependencies and make sure data is sent before
-       * modifying it */
-      scheduler_addunlock(s, t_rt_gradient, ci->hydro.super->hydro.rt_ghost2);
-
-      /* Safety measure: collect dependencies and make sure data is sent before
-       * modifying it */
-      scheduler_addunlock(s, t_rt_transport,
-                          ci->hydro.super->hydro.rt_transport_out);
-
-      /* Drift before you send. Especially intended to cover inactive cells
-       * being sent. */
-      scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_gradient);
-      scheduler_addunlock(s, ci->hydro.super->hydro.drift, t_rt_transport);
-    }
-
-    /* Add them to the local cell. */
-    engine_addlink(e, &ci->mpi.send, t_rt_gradient);
-    engine_addlink(e, &ci->mpi.send, t_rt_transport);
-  }
-
-  /* Recurse? */
-  if (ci->split)
-    for (int k = 0; k < 8; k++)
-      if (ci->progeny[k] != NULL)
-        engine_addtasks_send_rt(e, ci->progeny[k], cj, t_rt_gradient,
-                                t_rt_transport);
-
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
 /**
  * @brief Add recv tasks for hydro pairs to a hierarchy of cells.
  *
@@ -576,18 +553,28 @@ void engine_addtasks_send_rt(struct engine *e, struct cell *ci, struct cell *cj,
  * @param t_limiter The recv_limiter #task, if it has already been created.
  * @param t_unpack_limiter The unpack_limiter #task, if it has already been
  * created.
+ * @param t_rt_gradient The recv_rt_gradient #task, if it has already been
+ * created.
+ * @param t_rt_transport The recv_rt_transport #task, if it has already been
+ * created.
+ * @param t_rt_advance_cell_time The rt_advance_cell_time #task, if it has
+ * already been created
+ * @param t_rt_sorts The rt_sort #task, if it has already been created.
  * @param tend The top-level time-step communication #task.
  * @param with_feedback Are we running with stellar feedback?
  * @param with_black_holes Are we running with black holes?
  * @param with_limiter Are we running with the time-step limiter?
  * @param with_sync Are we running with time-step synchronization?
+ * @param with_rt Are we running with radiative transfer?
  */
 void engine_addtasks_recv_hydro(
     struct engine *e, struct cell *c, struct task *t_xv, struct task *t_rho,
     struct task *t_gradient, struct task *t_prep1, struct task *t_limiter,
-    struct task *t_unpack_limiter, struct task *const tend,
-    const int with_feedback, const int with_black_holes, const int with_limiter,
-    const int with_sync) {
+    struct task *t_unpack_limiter, struct task *t_rt_gradient,
+    struct task *t_rt_transport, struct task *t_rt_advance_cell_time,
+    struct task *t_rt_sorts, struct task *const tend, const int with_feedback,
+    const int with_black_holes, const int with_limiter, const int with_sync,
+    const int with_rt) {
 
 #ifdef WITH_MPI
   struct scheduler *s = &e->sched;
@@ -614,6 +601,8 @@ void engine_addtasks_recv_hydro(
 #ifdef EXTRA_HYDRO_LOOP
     t_gradient = scheduler_addtask(s, task_type_recv, task_subtype_gradient,
                                    c->mpi.tag, 0, c, NULL);
+    scheduler_addunlock(s, t_xv, t_gradient);
+    scheduler_addunlock(s, t_rho, t_gradient);
 #endif
 
     if (with_limiter) {
@@ -631,6 +620,81 @@ void engine_addtasks_recv_hydro(
                                   c->mpi.tag, 0, c, NULL);
     }
 #endif
+
+    if (with_rt) {
+      /* Create the tasks. */
+      t_rt_gradient = scheduler_addtask(
+          s, task_type_recv, task_subtype_rt_gradient, c->mpi.tag, 0, c, NULL);
+      t_rt_transport = scheduler_addtask(
+          s, task_type_recv, task_subtype_rt_transport, c->mpi.tag, 0, c, NULL);
+      /* Also create the rt_advance_cell_time tasks for the foreign cells
+       * for the sub-cycling. */
+#ifdef SWIFT_RT_DEBUG_CHECKS
+      if (c->super == NULL)
+        error("trying to add rt_advance_cell_time above super level...");
+#endif
+      t_rt_advance_cell_time =
+          scheduler_addtask(s, task_type_rt_advance_cell_time,
+                            task_subtype_none, 0, 0, c->super, NULL);
+
+      c->super->rt.rt_advance_cell_time = t_rt_advance_cell_time;
+      /* Create the RT collect times task at the top level, if it hasn't
+       * already. */
+      if (c->top->rt.rt_collect_times == NULL)
+        c->top->rt.rt_collect_times =
+            scheduler_addtask(s, task_type_rt_collect_times, task_subtype_none,
+                              0, 0, c->top, NULL);
+      /* Don't run collect times before you run advance cell time */
+      scheduler_addunlock(s, t_rt_advance_cell_time,
+                          c->top->rt.rt_collect_times);
+
+      /* Make sure we sort after receiving RT data. The hydro sorts may or may
+       * not be active. Blocking them with dependencies deadlocks with MPI. So
+       * add a new sort task instead, which will just do nothing if the cell is
+       * already sorted. */
+      t_rt_sorts = scheduler_addtask(s, task_type_rt_sort, task_subtype_none, 0,
+                                     0, c, NULL);
+      c->rt.rt_sorts = t_rt_sorts;
+      if (c->hydro.sorts != NULL) {
+        /* Copy task flags. While these should always be empty for sorts, better
+         * be safe than spend hours looking for this. */
+        t_rt_sorts->flags = c->hydro.sorts->flags;
+        /* Make sure the normal hydro sorts run before the RT sorts run. */
+        scheduler_addunlock(s, c->hydro.sorts, t_rt_sorts);
+        /* Don't run gradients on unsorted cells. */
+        scheduler_addunlock(s, c->hydro.sorts, t_rt_gradient);
+      }
+
+      /* Make sure the second receive doesn't get enqueued before the first one
+       * is done */
+      scheduler_addunlock(s, t_rt_gradient, t_rt_sorts);
+      scheduler_addunlock(s, t_rt_gradient, t_rt_transport);
+      /* Avoid situation where we receive while the sort hasn't finished yet. */
+      scheduler_addunlock(s, t_rt_sorts, t_rt_transport);
+      /* If one or both recv tasks are active, make sure the
+       * rt_advance_cell_time tasks doesn't run before them */
+      scheduler_addunlock(s, t_rt_gradient, t_rt_advance_cell_time);
+      scheduler_addunlock(s, t_rt_transport, t_rt_advance_cell_time);
+      /* Make sure the gradient recv don't run before the xv is finished.
+       * This can occur when a cell itself is inactive for both hydro and
+       * RT, but needs to be sent over for some other cell's pair task.
+       * For active cells, you must make sure that t_rho and t_gradient have
+       * been received first. As there is no guarantee which message will
+       * arrive first, you might overwrite data otherwise. */
+
+      scheduler_addunlock(s, t_xv, t_rt_gradient);
+      scheduler_addunlock(s, t_rho, t_rt_gradient);
+#ifdef EXTRA_HYDRO_LOOP
+      scheduler_addunlock(s, t_gradient, t_rt_gradient);
+#endif
+
+      /* In normal steps, tend mustn't run before rt_advance_cell_time or the
+       * cell's ti_rt_end_min will be updated wrongly. In sub-cycles, we don't
+       * have the tend tasks, so there's no worry about that. (Them missing is
+       * the reason we need the rt_advanced_cell_time to complete the sub-cycles
+       * in the first place) */
+      scheduler_addunlock(s, t_rt_advance_cell_time, tend);
+    }
   }
 
   if (t_xv != NULL) {
@@ -651,6 +715,9 @@ void engine_addtasks_recv_hydro(
     if (c->hydro.sorts != NULL) {
       scheduler_addunlock(s, t_xv, c->hydro.sorts);
       scheduler_addunlock(s, c->hydro.sorts, t_rho);
+#if defined(MPI_SYMMETRIC_FORCE_INTERACTION) && defined(EXTRA_HYDRO_LOOP)
+      scheduler_addunlock(s, c->hydro.sorts, t_gradient);
+#endif
     }
 
     for (struct link *l = c->hydro.density; l != NULL; l = l->next) {
@@ -710,16 +777,48 @@ void engine_addtasks_recv_hydro(
         scheduler_addunlock(s, t_rho, l->t);
       }
     }
+
+    if (with_rt) {
+      engine_addlink(e, &c->mpi.recv, t_rt_gradient);
+      engine_addlink(e, &c->mpi.recv, t_rt_transport);
+
+      /* RT recvs mustn't run before hydro force has completed. */
+      for (struct link *l = c->hydro.force; l != NULL; l = l->next) {
+        scheduler_addunlock(s, l->t, t_rt_gradient);
+      }
+
+      for (struct link *l = c->rt.rt_gradient; l != NULL; l = l->next) {
+        /* RT gradient tasks mustn't run before we receive necessary data */
+        scheduler_addunlock(s, t_rt_gradient, l->t);
+        /* Don't run gradient tasks without sorting */
+        scheduler_addunlock(s, t_rt_sorts, l->t);
+        /* Don't update local particles before gradient tasks are finished */
+        scheduler_addunlock(s, l->t, t_rt_transport);
+      }
+
+      for (struct link *l = c->rt.rt_transport; l != NULL; l = l->next) {
+        /* RT transport tasks (iact, not comm tasks!!) mustn't run before we
+         * receive necessary data */
+        scheduler_addunlock(s, t_rt_transport, l->t);
+        /* add dependency for the timestep communication tasks. In cases where
+         * RT is inactive, rt_advance_cell_time won't run, so we need to make
+         * sure we don't receive data before we're done with all the work. */
+        scheduler_addunlock(s, l->t, tend);
+        /* advance cell time mustn't run before transport is done */
+        scheduler_addunlock(s, l->t, t_rt_advance_cell_time);
+      }
+    }
   }
 
   /* Recurse? */
   if (c->split)
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL)
-        engine_addtasks_recv_hydro(e, c->progeny[k], t_xv, t_rho, t_gradient,
-                                   t_prep1, t_limiter, t_unpack_limiter, tend,
-                                   with_feedback, with_black_holes,
-                                   with_limiter, with_sync);
+        engine_addtasks_recv_hydro(
+            e, c->progeny[k], t_xv, t_rho, t_gradient, t_prep1, t_limiter,
+            t_unpack_limiter, t_rt_gradient, t_rt_transport,
+            t_rt_advance_cell_time, t_rt_sorts, tend, with_feedback,
+            with_black_holes, with_limiter, with_sync, with_rt);
 
 #else
   error("SWIFT was not compiled with MPI support.");
@@ -1004,78 +1103,6 @@ void engine_addtasks_recv_gravity(struct engine *e, struct cell *c,
 #endif
 }
 
-/**
- * @brief Add recv tasks for RT pairs to a hierarchy of cells.
- *
- * @param e The #engine.
- * @param c The foreign #cell.
- * @param t_rt_gradient The recv_rt_gradient #task, if it has already been
- * created.
- * @param t_rt_transport The recv_rt_transport #task, if it has already been
- * created.
- * @param tend The top-level time-step communication #task.
- */
-void engine_addtasks_recv_rt(struct engine *e, struct cell *c,
-                             struct task *t_rt_gradient,
-                             struct task *t_rt_transport, struct task *tend) {
-
-#ifdef WITH_MPI
-  struct scheduler *s = &e->sched;
-
-  /* Early abort (are we below the level where tasks are)? */
-  if (!cell_get_flag(c, cell_flag_has_tasks)) return;
-
-  /* Have we reached a level where there are any hydro tasks ? */
-  if (t_rt_gradient == NULL && c->hydro.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_rt_gradient = scheduler_addtask(
-        s, task_type_recv, task_subtype_rt_gradient, c->mpi.tag, 0, c, NULL);
-    t_rt_transport = scheduler_addtask(
-        s, task_type_recv, task_subtype_rt_transport, c->mpi.tag, 0, c, NULL);
-
-    /* Make sure the second receive doens't get enqueued before the first one is
-     * done */
-    scheduler_addunlock(s, t_rt_gradient, t_rt_transport);
-  }
-
-  if (t_rt_gradient != NULL) {
-    engine_addlink(e, &c->mpi.recv, t_rt_gradient);
-    engine_addlink(e, &c->mpi.recv, t_rt_transport);
-
-    if (c->hydro.sorts != NULL) {
-      scheduler_addunlock(s, c->hydro.sorts, t_rt_transport);
-    }
-
-    for (struct link *l = c->hydro.rt_gradient; l != NULL; l = l->next) {
-      /* RT gradient tasks mustn't run before we receive necessary data */
-      scheduler_addunlock(s, t_rt_gradient, l->t);
-      scheduler_addunlock(s, l->t, t_rt_transport);
-    }
-
-    for (struct link *l = c->hydro.rt_transport; l != NULL; l = l->next) {
-      /* RT transport tasks mustn't run before we receive necessary data */
-      scheduler_addunlock(s, t_rt_transport, l->t);
-      /* add dependency for the timestep communication tasks */
-      scheduler_addunlock(s, l->t, tend);
-    }
-  }
-  /* Recurse? */
-  if (c->split)
-    for (int k = 0; k < 8; k++)
-      if (c->progeny[k] != NULL)
-        engine_addtasks_recv_rt(e, c->progeny[k], t_rt_gradient, t_rt_transport,
-                                tend);
-#else
-  error("SWIFT was not compiled with MPI support.");
-#endif
-}
-
 /**
  * @brief Generate the hydro hierarchical tasks for a hierarchy of cells -
  * i.e. all the O(Npart) tasks -- timestep version
@@ -1098,6 +1125,7 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) {
   const int with_timestep_limiter =
       (e->policy & engine_policy_timestep_limiter);
   const int with_timestep_sync = (e->policy & engine_policy_timestep_sync);
+  const int with_rt = (e->policy & engine_policy_rt);
 #ifdef WITH_CSDS
   const int with_csds = e->policy & engine_policy_csds;
 #endif
@@ -1130,6 +1158,11 @@ void engine_make_hierarchical_tasks_common(struct engine *e, struct cell *c) {
       c->sinks.sink_formation = scheduler_addtask(
           s, task_type_sink_formation, task_subtype_none, 0, 0, c, NULL);
     }
+
+    if (with_rt) {
+      c->rt.rt_collect_times = scheduler_addtask(
+          s, task_type_rt_collect_times, task_subtype_none, 0, 0, c, NULL);
+    }
   }
 
   /* Are we in a super-cell ? */
@@ -1634,48 +1667,70 @@ void engine_make_hierarchical_tasks_hydro(struct engine *e, struct cell *c,
       /* Radiative Transfer */
       if (with_rt) {
         /* RT ghost in task */
-        c->hydro.rt_in =
+        c->rt.rt_in =
             scheduler_addtask(s, task_type_rt_in, task_subtype_none, 0,
                               /* implicit= */ 1, c, NULL);
-        scheduler_addunlock(s, c->super->kick2, c->hydro.rt_in);
+        scheduler_addunlock(s, c->super->kick2, c->rt.rt_in);
         /* Star formation */
         if (c->top->hydro.count > 0 && with_star_formation)
-          scheduler_addunlock(s, c->top->hydro.star_formation, c->hydro.rt_in);
+          scheduler_addunlock(s, c->top->hydro.star_formation, c->rt.rt_in);
         /* Star formation from sinks */
         if (with_star_formation_sink &&
             (c->top->hydro.count > 0 || c->top->sinks.count > 0))
           scheduler_addunlock(s, c->top->sinks.star_formation_sink,
-                              c->hydro.rt_in);
+                              c->rt.rt_in);
         if (with_feedback)
-          scheduler_addunlock(s, c->stars.stars_out, c->hydro.rt_in);
+          scheduler_addunlock(s, c->stars.stars_out, c->rt.rt_in);
         /* TODO: check/add dependencies from Loic's new sink SF tasks */
 
         /* RT ghost out task */
-        c->hydro.rt_out =
+        c->rt.rt_out =
             scheduler_addtask(s, task_type_rt_out, task_subtype_none, 0,
                               /* implicit= */ 1, c, NULL);
-        scheduler_addunlock(s, c->hydro.rt_out, c->super->timestep);
+        scheduler_addunlock(s, c->rt.rt_out, c->super->timestep);
+
+        /* In cases where nothing but RT is active, don't allow the timestep
+         * collect to run before we've finished */
+        scheduler_addunlock(s, c->rt.rt_out, c->super->timestep_collect);
 
         /* non-implicit ghost 1 */
-        c->hydro.rt_ghost1 = scheduler_addtask(
-            s, task_type_rt_ghost1, task_subtype_none, 0, 0, c, NULL);
-        scheduler_addunlock(s, c->hydro.rt_in, c->hydro.rt_ghost1);
+        c->rt.rt_ghost1 = scheduler_addtask(s, task_type_rt_ghost1,
+                                            task_subtype_none, 0, 0, c, NULL);
+        scheduler_addunlock(s, c->rt.rt_in, c->rt.rt_ghost1);
 
         /* non-implicit ghost 2 */
-        c->hydro.rt_ghost2 = scheduler_addtask(
-            s, task_type_rt_ghost2, task_subtype_none, 0, 0, c, NULL);
+        c->rt.rt_ghost2 = scheduler_addtask(s, task_type_rt_ghost2,
+                                            task_subtype_none, 0, 0, c, NULL);
 
         /* implicit transport out */
-        c->hydro.rt_transport_out =
+        c->rt.rt_transport_out =
             scheduler_addtask(s, task_type_rt_transport_out, task_subtype_none,
                               0, /*implicit= */ 1, c, NULL);
 
-        /* non-implicit ghost 2 */
-        c->hydro.rt_tchem = scheduler_addtask(s, task_type_rt_tchem,
-                                              task_subtype_none, 0, 0, c, NULL);
+        /* thermochemistry */
+        c->rt.rt_tchem = scheduler_addtask(s, task_type_rt_tchem,
+                                           task_subtype_none, 0, 0, c, NULL);
+
+        /* Advance cell time for subcycling */
+        /* We need to make sure that rt_advance_cell_time is at the same level
+         * as the timestep task, not below. Otherwise, the updated cell times
+         * won't propagate up the hierarchy enough, and the cell_is_rt_active
+         * will return bogus results. Note that c->super is not necessarily
+         * c->hydro.super in general. */
+        /* Create the task only once ! */
+        if (c->super->rt.rt_advance_cell_time == NULL) {
+          c->super->rt.rt_advance_cell_time =
+              scheduler_addtask(s, task_type_rt_advance_cell_time,
+                                task_subtype_none, 0, 0, c->super, NULL);
+          /* Don't run the rt_collect_times before the rt_advance_cell_time */
+          scheduler_addunlock(s, c->super->rt.rt_advance_cell_time,
+                              c->top->rt.rt_collect_times);
+        }
 
-        scheduler_addunlock(s, c->hydro.rt_transport_out, c->hydro.rt_tchem);
-        scheduler_addunlock(s, c->hydro.rt_tchem, c->hydro.rt_out);
+        scheduler_addunlock(s, c->rt.rt_transport_out, c->rt.rt_tchem);
+        scheduler_addunlock(s, c->rt.rt_tchem,
+                            c->super->rt.rt_advance_cell_time);
+        scheduler_addunlock(s, c->super->rt.rt_advance_cell_time, c->rt.rt_out);
       }
 
       /* Subgrid tasks: black hole feedback */
@@ -1787,7 +1842,7 @@ void engine_make_self_gravity_tasks_mapper(void *map_data, int num_elements,
 
   /* Convert the maximal search distance to a number of cells
    * Define a lower and upper delta in case things are not symmetric */
-  const int delta = (int)(sqrt(3) * distance / cells[0].width[0]) + 1;
+  const int delta = max((int)(sqrt(3) * distance / cells[0].width[0]) + 1, 2);
   int delta_m = delta;
   int delta_p = delta;
 
@@ -2638,8 +2693,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
         engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback);
       }
       if (with_rt) {
-        engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient);
-        engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport);
+        engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
+        engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
       }
 
 #ifdef EXTRA_HYDRO_LOOP
@@ -2783,14 +2838,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
 
       if (with_rt) {
         scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient);
-        scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1,
+        scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
                             t_rt_gradient);
         scheduler_addunlock(sched, t_rt_gradient,
-                            ci->hydro.super->hydro.rt_ghost2);
-        scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2,
+                            ci->hydro.super->rt.rt_ghost2);
+        scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
                             t_rt_transport);
         scheduler_addunlock(sched, t_rt_transport,
-                            ci->hydro.super->hydro.rt_transport_out);
+                            ci->hydro.super->rt.rt_transport_out);
       }
     }
 
@@ -2818,6 +2873,19 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
       t_force = scheduler_addtask(sched, task_type_pair, task_subtype_force,
                                   flags, 0, ci, cj);
 
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+      /* The order of operations for an inactive local cell interacting
+       * with an active foreign cell is not guaranteed because the density
+       * (and gradient) iact loops don't exist in that case. So we need
+       * an explicit dependency here to have sorted cells. */
+
+      /* Make all force tasks depend on the sorts */
+      scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_force);
+      if (ci->hydro.super != cj->hydro.super) {
+        scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_force);
+      }
+#endif
+
       /* and the task for the time-step limiter */
       if (with_timestep_limiter) {
         t_limiter = scheduler_addtask(sched, task_type_pair,
@@ -2917,10 +2985,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
         engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback);
       }
       if (with_rt) {
-        engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient);
-        engine_addlink(e, &cj->hydro.rt_gradient, t_rt_gradient);
-        engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport);
-        engine_addlink(e, &cj->hydro.rt_transport, t_rt_transport);
+        engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
+        engine_addlink(e, &cj->rt.rt_gradient, t_rt_gradient);
+        engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
+        engine_addlink(e, &cj->rt.rt_transport, t_rt_transport);
       }
 
 #ifdef EXTRA_HYDRO_LOOP
@@ -3107,14 +3175,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
         if (with_rt) {
           scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
                               t_rt_gradient);
-          scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1,
+          scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
                               t_rt_gradient);
           scheduler_addunlock(sched, t_rt_gradient,
-                              ci->hydro.super->hydro.rt_ghost2);
-          scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2,
+                              ci->hydro.super->rt.rt_ghost2);
+          scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
                               t_rt_transport);
           scheduler_addunlock(sched, t_rt_transport,
-                              ci->hydro.super->hydro.rt_transport_out);
+                              ci->hydro.super->rt.rt_transport_out);
         }
 
       } else /*(ci->nodeID != nodeID) */ {
@@ -3251,14 +3319,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
           if (with_rt) {
             scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
                                 t_rt_gradient);
-            scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost1,
+            scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost1,
                                 t_rt_gradient);
             scheduler_addunlock(sched, t_rt_gradient,
-                                cj->hydro.super->hydro.rt_ghost2);
-            scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost2,
+                                cj->hydro.super->rt.rt_ghost2);
+            scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost2,
                                 t_rt_transport);
             scheduler_addunlock(sched, t_rt_transport,
-                                cj->hydro.super->hydro.rt_transport_out);
+                                cj->hydro.super->rt.rt_transport_out);
           }
 
           if (with_timestep_limiter) {
@@ -3315,6 +3383,7 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
       /* Start by constructing the task for the second hydro loop */
       t_force = scheduler_addtask(sched, task_type_sub_self, task_subtype_force,
                                   flags, 0, ci, NULL);
+
       /* and the task for the time-step limiter */
       if (with_timestep_limiter) {
         t_limiter = scheduler_addtask(sched, task_type_sub_self,
@@ -3410,8 +3479,8 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
         engine_addlink(e, &ci->black_holes.feedback, t_bh_feedback);
       }
       if (with_rt) {
-        engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient);
-        engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport);
+        engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
+        engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
       }
 
 #ifdef EXTRA_HYDRO_LOOP
@@ -3561,14 +3630,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
       if (with_rt) {
         scheduler_addunlock(sched, ci->hydro.super->hydro.drift, t_rt_gradient);
         scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_rt_gradient);
-        scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1,
+        scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
                             t_rt_gradient);
         scheduler_addunlock(sched, t_rt_gradient,
-                            ci->hydro.super->hydro.rt_ghost2);
-        scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2,
+                            ci->hydro.super->rt.rt_ghost2);
+        scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
                             t_rt_transport);
         scheduler_addunlock(sched, t_rt_transport,
-                            ci->hydro.super->hydro.rt_transport_out);
+                            ci->hydro.super->rt.rt_transport_out);
       }
     }
 
@@ -3596,6 +3665,20 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
       /* New task for the force */
       t_force = scheduler_addtask(sched, task_type_sub_pair, task_subtype_force,
                                   flags, 0, ci, cj);
+
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+      /* The order of operations for an inactive local cell interacting
+       * with an active foreign cell is not guaranteed because the density
+       * (and gradient) iact loops don't exist in that case. So we need
+       * an explicit dependency here to have sorted cells. */
+
+      /* Make all force tasks depend on the sorts */
+      scheduler_addunlock(sched, ci->hydro.super->hydro.sorts, t_force);
+      if (ci->hydro.super != cj->hydro.super) {
+        scheduler_addunlock(sched, cj->hydro.super->hydro.sorts, t_force);
+      }
+#endif
+
       /* and the task for the time-step limiter */
       if (with_timestep_limiter) {
         t_limiter = scheduler_addtask(sched, task_type_sub_pair,
@@ -3706,10 +3789,10 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
         engine_addlink(e, &cj->black_holes.feedback, t_bh_feedback);
       }
       if (with_rt) {
-        engine_addlink(e, &ci->hydro.rt_gradient, t_rt_gradient);
-        engine_addlink(e, &cj->hydro.rt_gradient, t_rt_gradient);
-        engine_addlink(e, &ci->hydro.rt_transport, t_rt_transport);
-        engine_addlink(e, &cj->hydro.rt_transport, t_rt_transport);
+        engine_addlink(e, &ci->rt.rt_gradient, t_rt_gradient);
+        engine_addlink(e, &cj->rt.rt_gradient, t_rt_gradient);
+        engine_addlink(e, &ci->rt.rt_transport, t_rt_transport);
+        engine_addlink(e, &cj->rt.rt_transport, t_rt_transport);
       }
 
 #ifdef EXTRA_HYDRO_LOOP
@@ -3895,14 +3978,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
         if (with_rt) {
           scheduler_addunlock(sched, ci->hydro.super->hydro.drift,
                               t_rt_gradient);
-          scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost1,
+          scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost1,
                               t_rt_gradient);
           scheduler_addunlock(sched, t_rt_gradient,
-                              ci->hydro.super->hydro.rt_ghost2);
-          scheduler_addunlock(sched, ci->hydro.super->hydro.rt_ghost2,
+                              ci->hydro.super->rt.rt_ghost2);
+          scheduler_addunlock(sched, ci->hydro.super->rt.rt_ghost2,
                               t_rt_transport);
           scheduler_addunlock(sched, t_rt_transport,
-                              ci->hydro.super->hydro.rt_transport_out);
+                              ci->hydro.super->rt.rt_transport_out);
         }
       } else /* ci->nodeID != nodeID */ {
 
@@ -4037,14 +4120,14 @@ void engine_make_extra_hydroloop_tasks_mapper(void *map_data, int num_elements,
           if (with_rt) {
             scheduler_addunlock(sched, cj->hydro.super->hydro.drift,
                                 t_rt_gradient);
-            scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost1,
+            scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost1,
                                 t_rt_gradient);
             scheduler_addunlock(sched, t_rt_gradient,
-                                cj->hydro.super->hydro.rt_ghost2);
-            scheduler_addunlock(sched, cj->hydro.super->hydro.rt_ghost2,
+                                cj->hydro.super->rt.rt_ghost2);
+            scheduler_addunlock(sched, cj->hydro.super->rt.rt_ghost2,
                                 t_rt_transport);
             scheduler_addunlock(sched, t_rt_transport,
-                                cj->hydro.super->hydro.rt_transport_out);
+                                cj->hydro.super->rt.rt_transport_out);
           }
 
           if (with_timestep_limiter) {
@@ -4238,6 +4321,7 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements,
   const int with_limiter = (e->policy & engine_policy_timestep_limiter);
   const int with_feedback = (e->policy & engine_policy_feedback);
   const int with_sync = (e->policy & engine_policy_timestep_sync);
+  const int with_rt = (e->policy & engine_policy_rt);
   struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -4252,12 +4336,29 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements,
     const int type = cell_type_pairs[k].type;
 
 #ifdef WITH_MPI
+
     if (!cell_is_empty(ci)) {
       /* Add the timestep exchange task */
       struct task *tend = scheduler_addtask(
           &e->sched, task_type_send, task_subtype_tend, ci->mpi.tag, 0, ci, cj);
       scheduler_addunlock(&e->sched, ci->timestep_collect, tend);
       engine_addlink(e, &ci->mpi.send, tend);
+
+      if (with_rt && (type & proxy_cell_type_hydro)) {
+
+        /* If we're running with RT subcycling, we need to ensure that nothing
+         * is sent before the advance cell time task has finished. This may
+         * overwrite the correct cell times, particularly so when we're sending
+         * over data for non-RT tasks, e.g. for gravity pair tasks. */
+        if (ci->super->rt.rt_advance_cell_time != NULL) {
+          scheduler_addunlock(&e->sched, ci->super->rt.rt_advance_cell_time,
+                              tend);
+#ifdef SWIFT_RT_DEBUG_CHECKS
+        } else {
+          error("Got local super cell without rt_advance_cell_time task");
+#endif
+        }
+      }
     }
 #endif
 
@@ -4268,7 +4369,9 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements,
                                  /*t_rho=*/NULL, /*t_gradient=*/NULL,
                                  /*t_prep1=*/NULL,
                                  /*t_limiter=*/NULL, /*t_pack_limiter=*/NULL,
-                                 with_feedback, with_limiter, with_sync);
+                                 /*t_rt_gradient=*/NULL,
+                                 /*t_rt_transport=*/NULL, with_feedback,
+                                 with_limiter, with_sync, with_rt);
 
     /* Add the send tasks for the cells in the proxy that have a stars
      * connection. */
@@ -4291,10 +4394,6 @@ void engine_addtasks_send_mapper(void *map_data, int num_elements,
     if ((e->policy & engine_policy_self_gravity) &&
         (type & proxy_cell_type_gravity))
       engine_addtasks_send_gravity(e, ci, cj, /*t_grav=*/NULL);
-
-    if ((e->policy & engine_policy_rt) && (type & proxy_cell_type_hydro))
-      engine_addtasks_send_rt(e, ci, cj, /*t_rt_gradient=*/NULL,
-                              /*t_rt_transport=*/NULL);
   }
 }
 
@@ -4307,6 +4406,7 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements,
   const int with_feedback = (e->policy & engine_policy_feedback);
   const int with_black_holes = (e->policy & engine_policy_black_holes);
   const int with_sync = (e->policy & engine_policy_timestep_sync);
+  const int with_rt = (e->policy & engine_policy_rt);
   struct cell_type_pair *cell_type_pairs = (struct cell_type_pair *)map_data;
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -4331,13 +4431,14 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements,
 
     /* Add the recv tasks for the cells in the proxy that have a hydro
      * connection. */
-    if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro))
-      engine_addtasks_recv_hydro(e, ci, /*t_xv=*/NULL, /*t_rho=*/NULL,
-                                 /*t_gradient=*/NULL,
-                                 /*t_prep1=*/NULL,
-                                 /*t_limiter=*/NULL, /*t_unpack_limiter=*/NULL,
-                                 tend, with_feedback, with_black_holes,
-                                 with_limiter, with_sync);
+    if ((e->policy & engine_policy_hydro) && (type & proxy_cell_type_hydro)) {
+      engine_addtasks_recv_hydro(
+          e, ci, /*t_xv=*/NULL, /*t_rho=*/NULL, /*t_gradient=*/NULL,
+          /*t_prep1=*/NULL, /*t_limiter=*/NULL, /*t_unpack_limiter=*/NULL,
+          /*t_rt_gradient=*/NULL, /*t_rt_transport=*/NULL,
+          /*t_rt_advance_cell_time=*/NULL, /*t_rt_sorts=*/NULL, tend,
+          with_feedback, with_black_holes, with_limiter, with_sync, with_rt);
+    }
 
     /* Add the recv tasks for the cells in the proxy that have a stars
      * connection. */
@@ -4360,12 +4461,6 @@ void engine_addtasks_recv_mapper(void *map_data, int num_elements,
     if ((e->policy & engine_policy_self_gravity) &&
         (type & proxy_cell_type_gravity))
       engine_addtasks_recv_gravity(e, ci, /*t_grav=*/NULL, tend);
-
-    /* Add the recv tasks for the cells in the proxy that have an RT
-     * connection. */
-    if ((e->policy & engine_policy_rt) && (type & proxy_cell_type_hydro))
-      engine_addtasks_recv_rt(e, ci, /*t_rt_gradient=*/NULL,
-                              /*t_rt_transport=*/NULL, tend);
   }
 }
 
diff --git a/src/engine_marktasks.c b/src/engine_marktasks.c
index f153c9481aa5cec54b827ce30ab29a2c3f3fc00e..0f816cdeb317a1a4f9b7fd4ea919b3d4e92b2b67 100644
--- a/src/engine_marktasks.c
+++ b/src/engine_marktasks.c
@@ -23,7 +23,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdlib.h>
@@ -94,6 +94,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 #ifdef SWIFT_DEBUG_CHECKS
       if (ci->nodeID != nodeID) error("Non-local self task found");
 #endif
+
       const int ci_active_hydro = cell_is_active_hydro(ci, e);
       const int ci_active_gravity = cell_is_active_gravity(ci, e);
       const int ci_active_black_holes = cell_is_active_black_holes(ci, e);
@@ -101,6 +102,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
           cell_is_active_sinks(ci, e) || ci_active_hydro;
       const int ci_active_stars = cell_need_activating_stars(
           ci, e, with_star_formation, with_star_formation_sink);
+      const int ci_active_rt = cell_is_rt_active(ci, e);
 
       /* Activate the hydro drift */
       if (t_type == task_type_self && t_subtype == task_subtype_density) {
@@ -346,9 +348,21 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
       }
 
       /* Activate RT tasks */
-      else if (t_subtype == task_subtype_rt_gradient ||
-               t_subtype == task_subtype_rt_transport) {
-        if (ci_active_hydro) scheduler_activate(s, t);
+      else if (t_type == task_type_self &&
+               t_subtype == task_subtype_rt_gradient) {
+        if (ci_active_rt) scheduler_activate(s, t);
+      }
+
+      else if (t_type == task_type_sub_self &&
+               t_subtype == task_subtype_rt_gradient) {
+        if (ci_active_rt) {
+          scheduler_activate(s, t);
+          cell_activate_subcell_rt_tasks(ci, NULL, s, /*sub_cycle=*/0);
+        }
+      }
+
+      else if (t_subtype == task_subtype_rt_transport) {
+        if (ci_active_rt) scheduler_activate(s, t);
       }
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -412,6 +426,9 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
       const int cj_active_stars = cell_need_activating_stars(
           cj, e, with_star_formation, with_star_formation_sink);
 
+      const int ci_active_rt = cell_is_rt_active(ci, e);
+      const int cj_active_rt = cell_is_rt_active(cj, e);
+
       /* Only activate tasks that involve a local active cell. */
       if ((t_subtype == task_subtype_density ||
            t_subtype == task_subtype_gradient ||
@@ -792,16 +809,33 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 
       /* RT gradient and transport tasks */
       else if (t_subtype == task_subtype_rt_gradient) {
+
         /* We only want to activate the task if the cell is active and is
            going to update some gas on the *local* node */
 
-        if ((ci_nodeID == nodeID && ci_active_hydro) ||
-            (cj_nodeID == nodeID && cj_active_hydro)) {
+        if ((ci_nodeID == nodeID && ci_active_rt) ||
+            (cj_nodeID == nodeID && cj_active_rt)) {
 
-          /* The gradient and transport task subtypes mirror the hydro tasks.
-           * Therefore all the (subcell) sorts and drifts should already have
-           * been activated properly in the hydro part of the activation. */
           scheduler_activate(s, t);
+
+          /* Set the correct sorting flags */
+          if (t_type == task_type_pair) {
+
+            /* Store some values. */
+            atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+            atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+            ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+            cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+
+            /* Check the sorts and activate them if needed. */
+            cell_activate_rt_sorts(ci, t->flags, s);
+            cell_activate_rt_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_rt_tasks(ci, cj, s, /*sub_cycle=*/0);
+          }
         }
       }
 
@@ -809,8 +843,8 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
         /* We only want to activate the task if the cell is active and is
            going to update some gas on the *local* node */
 
-        if ((ci_nodeID == nodeID && ci_active_hydro) ||
-            (cj_nodeID == nodeID && cj_active_hydro)) {
+        if ((ci_nodeID == nodeID && ci_active_rt) ||
+            (cj_nodeID == nodeID && cj_active_rt)) {
 
           /* The gradient and transport task subtypes mirror the hydro tasks.
            * Therefore all the (subcell) sorts and drifts should already have
@@ -821,11 +855,55 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
             /* Activate transport_out for each cell that is part of
              * a pair/sub_pair task as to not miss any dependencies */
             if (ci_nodeID == nodeID)
-              scheduler_activate(s, ci->hydro.super->hydro.rt_transport_out);
+              scheduler_activate(s, ci->hydro.super->rt.rt_transport_out);
             if (cj_nodeID == nodeID)
-              scheduler_activate(s, cj->hydro.super->hydro.rt_transport_out);
+              scheduler_activate(s, cj->hydro.super->rt.rt_transport_out);
+          }
+        }
+      }
+
+      /* Pair tasks between inactive local cells and active remote cells. */
+      if ((ci_nodeID != nodeID && cj_nodeID == nodeID && ci_active_hydro &&
+           !cj_active_hydro) ||
+          (ci_nodeID == nodeID && cj_nodeID != nodeID && !ci_active_hydro &&
+           cj_active_hydro)) {
+
+#if defined(WITH_MPI) && defined(MPI_SYMMETRIC_FORCE_INTERACTION)
+        if (t_subtype == task_subtype_force) {
+
+          scheduler_activate(s, t);
+
+          /* Set the correct sorting flags */
+          if (t_type == task_type_pair) {
+            /* Store some values. */
+            atomic_or(&ci->hydro.requires_sorts, 1 << t->flags);
+            atomic_or(&cj->hydro.requires_sorts, 1 << t->flags);
+            ci->hydro.dx_max_sort_old = ci->hydro.dx_max_sort;
+            cj->hydro.dx_max_sort_old = cj->hydro.dx_max_sort;
+
+            /* Activate the hydro drift tasks. */
+            if (ci_nodeID == nodeID) cell_activate_drift_part(ci, s);
+            if (cj_nodeID == nodeID) cell_activate_drift_part(cj, s);
+
+            /* And the limiter */
+            if (ci_nodeID == nodeID && with_timestep_limiter)
+              cell_activate_limiter(ci, s);
+            if (cj_nodeID == nodeID && with_timestep_limiter)
+              cell_activate_limiter(cj, s);
+
+            /* Check the sorts and activate them if needed. */
+            cell_activate_hydro_sorts(ci, t->flags, s);
+            cell_activate_hydro_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_hydro_tasks(t->ci, t->cj, s,
+                                              with_timestep_limiter);
           }
         }
+#endif
       }
 
       /* Only interested in density tasks as of here. */
@@ -860,6 +938,26 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 #endif
             }
           }
+          /* If the local cell is inactive and the remote cell is active, we
+           * still need to receive stuff to be able to do the force interaction
+           * on this node as well. */
+          else if (ci_active_hydro) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+            /* NOTE: (yuyttenh, 09/2022) Since the particle communications send
+             * over whole particles currently, just activating the gradient
+             * send/recieve should be enough for now. The remote active
+             * particles are only needed for the sorts and the flux exchange on
+             * the node of the inactive cell, so sending over the xv and
+             * gradient suffices. If at any point the commutications change, we
+             * should probably also send over the rho separately. */
+            scheduler_activate_recv(s, ci->mpi.recv, task_subtype_xv);
+#ifndef EXTRA_HYDRO_LOOP
+            scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rho);
+#else
+            scheduler_activate_recv(s, ci->mpi.recv, task_subtype_gradient);
+#endif
+#endif
+          }
 
           /* If the foreign cell is active, we want its particles for the
            * limiter */
@@ -889,6 +987,27 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 #endif
             }
           }
+          /* If the foreign cell is inactive, but the local cell is active,
+           * we still need to send stuff to be able to do the force interaction
+           * on both nodes */
+          else if (cj_active_hydro) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+            /* See NOTE on line 867 */
+            struct link *l = scheduler_activate_send(
+                s, cj->mpi.send, task_subtype_xv, ci_nodeID);
+            /* Drift the cell which will be sent at the level at which it is
+             * sent, i.e. drift the cell specified in the send task (l->t)
+             * itself. */
+            cell_activate_drift_part(l->t->ci, s);
+#ifndef EXTRA_HYDRO_LOOP
+            scheduler_activate_send(s, cj->mpi.send, task_subtype_rho,
+                                    ci_nodeID);
+#else
+            scheduler_activate_send(s, cj->mpi.send, task_subtype_gradient,
+                                    ci_nodeID);
+#endif
+#endif
+          }
 
           /* If the local cell is active, send its particles for the limiting.
            */
@@ -924,6 +1043,20 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 #endif
             }
           }
+          /* If the local cell is inactive and the remote cell is active, we
+           * still need to receive stuff to be able to do the force interaction
+           * on this node as well. */
+          else if (cj_active_hydro) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+            /* See NOTE on line 867. */
+            scheduler_activate_recv(s, cj->mpi.recv, task_subtype_xv);
+#ifndef EXTRA_HYDRO_LOOP
+            scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rho);
+#else
+            scheduler_activate_recv(s, cj->mpi.recv, task_subtype_gradient);
+#endif
+#endif
+          }
 
           /* If the foreign cell is active, we want its particles for the
            * limiter */
@@ -955,6 +1088,27 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 #endif
             }
           }
+          /* If the foreign cell is inactive, but the local cell is active,
+           * we still need to send stuff to be able to do the force interaction
+           * on both nodes */
+          else if (ci_active_hydro) {
+#ifdef MPI_SYMMETRIC_FORCE_INTERACTION
+            /* See NOTE on line 867. */
+            struct link *l = scheduler_activate_send(
+                s, ci->mpi.send, task_subtype_xv, cj_nodeID);
+            /* Drift the cell which will be sent at the level at which it is
+             * sent, i.e. drift the cell specified in the send task (l->t)
+             * itself. */
+            cell_activate_drift_part(l->t->ci, s);
+#ifndef EXTRA_HYDRO_LOOP
+            scheduler_activate_send(s, ci->mpi.send, task_subtype_rho,
+                                    cj_nodeID);
+#else
+            scheduler_activate_send(s, ci->mpi.send, task_subtype_gradient,
+                                    cj_nodeID);
+#endif
+#endif
+          }
 
           /* If the local cell is active, send its particles for the limiting.
            */
@@ -1224,28 +1378,25 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
         if (ci_nodeID != nodeID) {
 
           /* If the local cell is active, receive data from the foreign cell. */
-          if (cj_active_hydro) {
+          if (cj_active_rt) {
+
             scheduler_activate_recv(s, ci->mpi.recv, task_subtype_rt_gradient);
 
-            if (ci_active_hydro) {
-              /* We only need updates later on if the other cell is active as
-               * well */
+            if (ci_active_rt) {
+              /* We only need updates later on if the other cell is active too
+               */
               scheduler_activate_recv(s, ci->mpi.recv,
                                       task_subtype_rt_transport);
             }
           }
 
           /* Is the foreign cell active and will need stuff from us? */
-          if (ci_active_hydro) {
+          if (ci_active_rt) {
 
             scheduler_activate_send(s, cj->mpi.send, task_subtype_rt_gradient,
                                     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 (cj_active_hydro) {
+            if (cj_active_rt) {
               scheduler_activate_send(s, cj->mpi.send,
                                       task_subtype_rt_transport, ci_nodeID);
             }
@@ -1254,30 +1405,27 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
         } else if (cj_nodeID != nodeID) {
 
           /* If the local cell is active, receive data from the foreign cell. */
-          if (ci_active_hydro) {
+          if (ci_active_rt) {
+
             scheduler_activate_recv(s, cj->mpi.recv, task_subtype_rt_gradient);
 
-            if (cj_active_hydro) {
-              /* We only need updates later on if the other cell is active as
-               * well */
+            if (cj_active_rt) {
+              /* We only need updates later on if the other cell is active too
+               */
               scheduler_activate_recv(s, cj->mpi.recv,
                                       task_subtype_rt_transport);
             }
           }
 
           /* Is the foreign cell active and will need stuff from us? */
-          if (cj_active_hydro) {
+          if (cj_active_rt) {
 
             scheduler_activate_send(s, ci->mpi.send, task_subtype_rt_gradient,
                                     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);
-
-            if (ci_active_hydro) {
-              /* We only need updates later on if the other cell is active as
-               * well */
+            if (ci_active_rt) {
+              /* We only need updates later on if the other cell is active too
+               */
               scheduler_activate_send(s, ci->mpi.send,
                                       task_subtype_rt_transport, cj_nodeID);
             }
@@ -1445,7 +1593,7 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 
     /* Radiative transfer implicit tasks */
     else if (t->type == task_type_rt_in) {
-      if (cell_is_active_hydro(t->ci, e) ||
+      if (cell_is_rt_active(t->ci, e) ||
           cell_need_activating_stars(t->ci, e, with_star_formation,
                                      with_star_formation_sink))
         scheduler_activate(s, t);
@@ -1453,8 +1601,12 @@ void engine_marktasks_mapper(void *map_data, int num_elements,
 
     else if (t->type == task_type_rt_ghost1 || t->type == task_type_rt_ghost2 ||
              t->type == task_type_rt_transport_out ||
-             t->type == task_type_rt_tchem || t->type == task_type_rt_out) {
-      if (cell_is_active_hydro(t->ci, e)) scheduler_activate(s, t);
+             t->type == task_type_rt_tchem ||
+             t->type == task_type_rt_advance_cell_time ||
+             t->type == task_type_rt_out) {
+      if (cell_is_rt_active(t->ci, e)) scheduler_activate(s, t);
+      /* Note that rt_collect_times never needs to be active on main steps,
+       * which is always what follows engine_marktasks().*/
     }
 
     /* Subgrid tasks: sink formation */
diff --git a/src/engine_proxy.c b/src/engine_proxy.c
index 7df4f5e2d80a4e6e1ca723d9566f0aec25a83c34..cdff12c867bd1de24451fb310c1fb42cb27a5431 100644
--- a/src/engine_proxy.c
+++ b/src/engine_proxy.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
diff --git a/src/engine_redistribute.c b/src/engine_redistribute.c
index f6331dc00727c1a13553f72f5d706295363a5e74..0d37a4d2f84135d4df8d7b10237267f2f278fbdd 100644
--- a/src/engine_redistribute.c
+++ b/src/engine_redistribute.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "engine.h"
diff --git a/src/engine_split_particles.c b/src/engine_split_particles.c
index 08a82032408921f7e6d540ae33f9dac7a6896422..f20684f206632d79602377f0ed9101732677329f 100644
--- a/src/engine_split_particles.c
+++ b/src/engine_split_particles.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "engine.h"
diff --git a/src/engine_strays.c b/src/engine_strays.c
index e80e2b597f280d346816b7efd717164993053c89..d55909c73e08c5b594d6ab30662f3b6e64d92a25 100644
--- a/src/engine_strays.c
+++ b/src/engine_strays.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
diff --git a/src/engine_unskip.c b/src/engine_unskip.c
index 7f81d34b52a3ce6e9c3b4cfe5c3b9a96e88edb35..30fc66aa16a7e87911a16e149aa6d1c64c1793ce 100644
--- a/src/engine_unskip.c
+++ b/src/engine_unskip.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "engine.h"
@@ -85,8 +85,10 @@ static void engine_do_unskip_hydro(struct cell *c, struct engine *e) {
   /* Ignore empty cells. */
   if (c->hydro.count == 0) return;
 
+#ifndef MPI_SYMMETRIC_FORCE_INTERACTION
   /* Skip inactive cells. */
   if (!cell_is_active_hydro(c, e)) return;
+#endif
 
   /* Recurse */
   if (c->split) {
@@ -247,8 +249,10 @@ static void engine_do_unskip_gravity(struct cell *c, struct engine *e) {
  *
  * @param c The cell.
  * @param e The engine.
+ * @param sub_cycle 1 if this is a call for a sub cycle, 0 otherwise
  */
-static void engine_do_unskip_rt(struct cell *c, struct engine *e) {
+static void engine_do_unskip_rt(struct cell *c, struct engine *e,
+                                const int sub_cycle) {
 
   /* Note: we only get this far if engine_policy_rt is flagged. */
 #ifdef SWIFT_DEBUG_CHECKS
@@ -260,20 +264,20 @@ static void engine_do_unskip_rt(struct cell *c, struct engine *e) {
   if (!cell_get_flag(c, cell_flag_has_tasks)) return;
 
   /* Do we have work to do? */
-  if (!cell_is_active_hydro(c, e)) return;
+  if (c->hydro.count == 0) return;
+  if (!cell_is_rt_active(c, e)) return;
 
   /* Recurse */
   if (c->split) {
     for (int k = 0; k < 8; k++) {
       if (c->progeny[k] != NULL) {
-        struct cell *cp = c->progeny[k];
-        engine_do_unskip_rt(cp, e);
+        engine_do_unskip_rt(c->progeny[k], e, sub_cycle);
       }
     }
   }
 
   /* Unskip any active tasks. */
-  const int forcerebuild = cell_unskip_rt_tasks(c, &e->sched);
+  const int forcerebuild = cell_unskip_rt_tasks(c, &e->sched, sub_cycle);
   if (forcerebuild) atomic_inc(&e->forcerebuild);
 }
 
@@ -364,7 +368,7 @@ void engine_do_unskip_mapper(void *map_data, int num_elements,
         if (!(e->policy & engine_policy_rt))
           error("Trying to unskip radiative transfer tasks in a non-rt run!");
 #endif
-        engine_do_unskip_rt(c, e);
+        engine_do_unskip_rt(c, e, /*sub_cycle=*/0);
         break;
       default:
 #ifdef SWIFT_DEBUG_CHECKS
@@ -418,7 +422,7 @@ void engine_unskip(struct engine *e) {
         (with_stars && c->nodeID == nodeID && cell_is_active_stars(c, e)) ||
         (with_sinks && cell_is_active_sinks(c, e)) ||
         (with_black_holes && cell_is_active_black_holes(c, e)) ||
-        (with_rt && cell_is_active_hydro(c, e))) {
+        (with_rt && cell_is_rt_active(c, e))) {
 
       if (num_active_cells != k)
         memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int));
@@ -509,3 +513,59 @@ void engine_unskip(struct engine *e) {
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
 }
+
+void engine_do_unskip_sub_cycle_mapper(void *map_data, int num_elements,
+                                       void *extra_data) {
+
+  struct engine *e = (struct engine *)extra_data;
+  struct cell *const cells_top = e->s->cells_top;
+
+  /* The current chunk of active cells */
+  const int *const local_cells = (int *)map_data;
+
+  /* Loop over this thread's chunk of cells to unskip */
+  for (int ind = 0; ind < num_elements; ind++) {
+
+    /* Handle on the cell */
+    struct cell *const c = &cells_top[local_cells[ind]];
+
+    engine_do_unskip_rt(c, e, /*sub_cycle=*/1);
+  }
+}
+
+/**
+ * @brief Unskip all the RT tasks that are active during this sub-cycle.
+ *
+ * @param e The #engine.
+ */
+void engine_unskip_rt_sub_cycle(struct engine *e) {
+
+  const ticks tic = getticks();
+  struct space *s = e->s;
+  const int with_rt = e->policy & engine_policy_rt;
+
+  if (!with_rt) error("Unskipping sub-cycles when running without RT!");
+
+  int *local_cells = e->s->local_cells_with_tasks_top;
+  int num_active_cells = 0;
+  for (int k = 0; k < s->nr_local_cells_with_tasks; k++) {
+    struct cell *c = &s->cells_top[local_cells[k]];
+
+    if (c->hydro.count == 0) continue;
+
+    if (cell_is_rt_active(c, e)) {
+
+      if (num_active_cells != k)
+        memswap(&local_cells[k], &local_cells[num_active_cells], sizeof(int));
+      num_active_cells += 1;
+    }
+  }
+
+  /* Activate all the regular tasks */
+  threadpool_map(&e->threadpool, engine_do_unskip_sub_cycle_mapper, local_cells,
+                 num_active_cells, sizeof(int), /*chunk=*/1, e);
+
+  if (e->verbose)
+    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
+            clocks_getunit());
+}
diff --git a/src/entropy_floor.h b/src/entropy_floor.h
index 58e87cf811687396178bddfbdb0eab2b89ff865f..08c934db241c0ab9abe3bea53acad0508e41f312 100644
--- a/src/entropy_floor.h
+++ b/src/entropy_floor.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "common_io.h"
diff --git a/src/equation_of_state.h b/src/equation_of_state.h
index bf4a467a4e1b3babc1113bd8534a79e8d8157aec..769e3dbb6888f52efc16d52858b26ed0d8150515 100644
--- a/src/equation_of_state.h
+++ b/src/equation_of_state.h
@@ -27,7 +27,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right functions */
 #if defined(EOS_IDEAL_GAS)
diff --git a/src/error.h b/src/error.h
index 4c5775066e18161696276a5c20775d3b646752bd..8ab8e2064574ac5c5cacd19509a20f538926d8a5 100644
--- a/src/error.h
+++ b/src/error.h
@@ -22,7 +22,7 @@
 #define SWIFT_ERROR_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/exchange_structs.c b/src/exchange_structs.c
index e51d45d3d66565983adea86206e2c32b918ef435..6b4045d19896e009bd852c2caf3fb01c9bf8c7c6 100644
--- a/src/exchange_structs.c
+++ b/src/exchange_structs.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers */
 #include <limits.h>
diff --git a/src/exp.h b/src/exp.h
index 470876a23293bcf80c121f8868511f88fb971be4..cd3967b3e8bd83b323f1cc494a1a5f0943e5009e 100644
--- a/src/exp.h
+++ b/src/exp.h
@@ -20,7 +20,7 @@
 #define SWIFT_OPTIMIZED_EXP_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/exp10.h b/src/exp10.h
index 2b102cfa984d56d5f36fe5af144d8ea712cbbab6..d55faf35a10323f40d2b4f2743db17e179a43c75 100644
--- a/src/exp10.h
+++ b/src/exp10.h
@@ -20,7 +20,7 @@
 #define SWIFT_EXP10_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/extra_io.h b/src/extra_io.h
index b7fa650a1d3fb3a92015f3370b35be2199c4e1a1..65471eb115c81881679ad39671b2febd7707f73c 100644
--- a/src/extra_io.h
+++ b/src/extra_io.h
@@ -21,7 +21,7 @@
 #define SWIFT_EXTRA_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the i/o routines the user asked for */
 #if defined(EXTRA_IO_EAGLE)
diff --git a/src/extra_io/EAGLE/extra_lightcone_map_types.c b/src/extra_io/EAGLE/extra_lightcone_map_types.c
index c0e73d2876a5048308eaf02439e0220d51a26a0e..73278919e5c49ce5d0c50e3eae749e54c8eec484 100644
--- a/src/extra_io/EAGLE/extra_lightcone_map_types.c
+++ b/src/extra_io/EAGLE/extra_lightcone_map_types.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "black_holes.h"
diff --git a/src/feedback.h b/src/feedback.h
index 4992784d9f469af2ef62b678c557d617b5c4f126..2cf74b61c7a21abf3355edf8818ea4441313c184 100644
--- a/src/feedback.h
+++ b/src/feedback.h
@@ -20,7 +20,7 @@
 #define SWIFT_FEEDBACK_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct feedback model */
 #if defined(FEEDBACK_NONE)
diff --git a/src/feedback/EAGLE_kinetic/feedback_properties.h b/src/feedback/EAGLE_kinetic/feedback_properties.h
index 33d0fcf71ece8676962faa3f5755cb78f6458ada..210d7aa0f9dfe2a681a0b0523ba2ac6b37165570 100644
--- a/src/feedback/EAGLE_kinetic/feedback_properties.h
+++ b/src/feedback/EAGLE_kinetic/feedback_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_EAGLE_FEEDBACK_KINETIC_PROPERTIES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "chemistry.h"
diff --git a/src/feedback/EAGLE_thermal/feedback_properties.h b/src/feedback/EAGLE_thermal/feedback_properties.h
index 09d926af24cc88f1b01a9b6edf4a846d0b93b8cb..5e1e38b11f322aa858631bfc6c1c138d65c8297b 100644
--- a/src/feedback/EAGLE_thermal/feedback_properties.h
+++ b/src/feedback/EAGLE_thermal/feedback_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_EAGLE_FEEDBACK_PROPERTIES_THERMAL_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "chemistry.h"
diff --git a/src/feedback_debug.h b/src/feedback_debug.h
index 7e73d5c73c2fd9b2462754cb1ecfa3b2797e47b9..86806baed9360687c2038a299ec8d3d2c456144f 100644
--- a/src/feedback_debug.h
+++ b/src/feedback_debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_FEEDBACK_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right feedback definition */
 #if defined(FEEDBACK_NONE)
diff --git a/src/feedback_properties.h b/src/feedback_properties.h
index d91e9795669b79bf196d4848a5c70eab5bf0022e..1e561611b3982b559f908835c48e8381797fda9b 100644
--- a/src/feedback_properties.h
+++ b/src/feedback_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_FEEDBACK_PROPERTIES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct feedback model */
 #if defined(FEEDBACK_NONE)
diff --git a/src/feedback_struct.h b/src/feedback_struct.h
index cf796bef72ddb2f0501ee39fc598fb0a462f9cc6..c0568e93b8e6365a36b2252eabf081e2b1d25978 100644
--- a/src/feedback_struct.h
+++ b/src/feedback_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right feedback definition */
 #if defined(FEEDBACK_NONE)
diff --git a/src/fof.c b/src/fof.c
index d66d1bb25e4922b44e16a47b97a1138dc60b89a0..de69efda987a01a804839216186ce0e4c9f8230d 100644
--- a/src/fof.c
+++ b/src/fof.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef WITH_FOF
 
@@ -2213,7 +2213,8 @@ void fof_seed_black_holes(const struct fof_props *props,
 #endif
 
       /* Copy over all the gas properties that we want */
-      black_holes_create_from_gas(bp, bh_props, constants, cosmo, p, xp);
+      black_holes_create_from_gas(bp, bh_props, constants, cosmo, p, xp,
+                                  s->e->ti_current);
 
       /* Move to the next BH slot */
       k++;
diff --git a/src/fof.h b/src/fof.h
index 2a7a886bf651afed2defbaae1c581469927a3108..bff0c0a3d0f0933866b7d3949ebf5b5f6bdc8b30 100644
--- a/src/fof.h
+++ b/src/fof.h
@@ -20,7 +20,7 @@
 #define SWIFT_FOF_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "align.h"
diff --git a/src/fof_catalogue_io.c b/src/fof_catalogue_io.c
index eebfe032595f40293e8c50c6c59be145a2437d8e..e7410a0b1707ad83e0a86bccc4a684c813432687 100644
--- a/src/fof_catalogue_io.c
+++ b/src/fof_catalogue_io.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef HAVE_HDF5
 
diff --git a/src/fof_catalogue_io.h b/src/fof_catalogue_io.h
index 345c03ae4eca661c60550a6101780d423f2d3045..8162a52a596af37a9f582a28c01a95901ea1deb6 100644
--- a/src/fof_catalogue_io.h
+++ b/src/fof_catalogue_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_FOF_CATALOGUE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef WITH_FOF
 
diff --git a/src/fof_io.h b/src/fof_io.h
index 24edefed3f0e04d40b9d6a1d444c8942426e4f41..90b0c13d14950559fc76e72430a69047bbbb3d3b 100644
--- a/src/fof_io.h
+++ b/src/fof_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_FOF_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef WITH_FOF
 
diff --git a/src/fof_struct.h b/src/fof_struct.h
index 38022b56db7d273291e2fa783c48adb7887f516f..ea788b5bf60d68d73a2a10eaf01875e3479f107e 100644
--- a/src/fof_struct.h
+++ b/src/fof_struct.h
@@ -20,7 +20,7 @@
 #define SWIFT_FOF_STRUCT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef WITH_FOF
 
diff --git a/src/ghost_stats.c b/src/ghost_stats.c
new file mode 100644
index 0000000000000000000000000000000000000000..883579d9f54580f01104fa6994dfa9e641acb5e4
--- /dev/null
+++ b/src/ghost_stats.c
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2021 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#include "ghost_stats.h"
+
+#include "cell.h"
+#include "space.h"
+
+#ifdef SWIFT_GHOST_STATS
+
+/**
+ * @brief Resets all the individual cell ghost counters to 0.
+ *
+ * @param c The #cell to reset.
+ */
+void cell_reset_ghost_histograms(struct cell *c) {
+
+  ghost_stats_reset_entries(&c->ghost_statistics);
+
+  /* recurse */
+  for (int k = 0; k < 8; ++k)
+    if (c->progeny[k] != NULL) cell_reset_ghost_histograms(c->progeny[k]);
+}
+
+/**
+ * @brief Write the ghost statistics for the given cell to the given file,
+ * using the given cell ID.
+ *
+ * This function recurses into the progenies. They all get different cell IDs
+ * by shifting the parent cell ID by 3 bits. Note that this does not
+ * guarantee unique cell IDs (but it is good enough to distinguish parents
+ * from their progeny).
+ *
+ * @param f File to write to.
+ * @param c Cell to write.
+ * @param cellID ID used to distinguish this cell from its progeny and
+ * immediate neigbours (not guaranteed to be unique!).
+ */
+void cell_write_ghost_stats(FILE *f, const struct cell *c,
+                            const long long cellID) {
+  if (c == NULL) return;
+
+  ghost_stats_write_cell_stats(f, &c->ghost_statistics, cellID);
+
+  /* Write children */
+  const long long pID = cellID << 3;
+  for (int i = 0; i < 8; i++) {
+    cell_write_ghost_stats(f, c->progeny[i], pID + i);
+  }
+}
+
+/**
+ * @brief Reset the ghost histograms for all top level cells in the space.
+ *
+ * @param s Space.
+ */
+void space_reset_ghost_histograms(struct space *s) {
+  for (int i = 0; i < s->nr_cells; ++i) {
+    cell_reset_ghost_histograms(&s->cells_top[i]);
+  }
+}
+
+/**
+ * @brief Write the ghost statistics for all the top level cells in the space
+ * to a file.
+ *
+ * The file name is composed as follows:
+ *   ghost_stats_%04i_%04i.txt
+ * where the first counter is an argument to this function (intended to be the
+ * step counter), and the second counter is the rank that does the write.
+ *
+ * @param s Space.
+ * @param j First counter in the output file name.
+ */
+void space_write_ghost_stats(const struct space *s, int j) {
+  /* Open file */
+  char filename[200];
+  sprintf(filename, "ghost_stats_%04i_%04i.txt", j, engine_rank);
+  FILE *f = fopen(filename, "w");
+  if (f == NULL) error("Error opening ghost stats file.");
+
+  /* Write header */
+  ghost_stats_write_header(f);
+
+  /* Write all the top level cells (and their children) */
+  for (int i = 0; i < s->nr_cells; i++) {
+    struct cell *c = &s->cells_top[i];
+    if (c->nodeID == engine_rank) cell_write_ghost_stats(f, c, i);
+  }
+
+  /* Cleanup */
+  fclose(f);
+}
+
+#endif
diff --git a/src/ghost_stats.h b/src/ghost_stats.h
new file mode 100644
index 0000000000000000000000000000000000000000..23fbcc288c935292b14dcf6ee4fdcbe60ac366ee
--- /dev/null
+++ b/src/ghost_stats.h
@@ -0,0 +1,500 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2021 Bert Vandenbroucke (bert.vandenbroucke@gmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+#ifndef SWIFT_GHOST_STATS_H
+#define SWIFT_GHOST_STATS_H
+
+/* Config parameters. */
+#include "minmax.h"
+#include "part.h"
+
+#include <config.h>
+#include <float.h>
+#include <stdio.h>
+
+#ifdef SWIFT_GHOST_STATS
+
+/*****************************
+ * ghost_stats functionality *
+ *****************************/
+
+/**
+ * @brief Entry for a single iteration in the ghost statistics table.
+ */
+struct ghost_stats_entry {
+  /*! Number of particles processed during the iteration. */
+  int count;
+  /*! Number of particles in the iteration that had no neighbours. */
+  int count_no_ngb;
+  /*! Minimum initial smoothing length of the particles at the start of the
+   *  iteration. */
+  float hmin;
+  /*! Maximum initial smoothing length of the particles at the start of the
+   *  iteration. */
+  float hmax;
+  /*! Sum of the initial smoothing lengths, useful to compute averages in
+   *  post-processing. */
+  double hsum;
+  /*! Sum of the initial smoothing lengths squared, useful to compute variances
+   *  and standard deviations in post-processing. */
+  double hsum2;
+};
+
+/**
+ * @brief Ghost statistics stored in a cell.
+ */
+struct ghost_stats {
+  /* Hydro ghost statistics. */
+  struct ghost_stats_entry hydro[SWIFT_GHOST_STATS + 1];
+  /* Stars ghost statistics. */
+  struct ghost_stats_entry stars[SWIFT_GHOST_STATS + 1];
+  /* Black holes ghost statistics. */
+  struct ghost_stats_entry black_holes[SWIFT_GHOST_STATS + 1];
+};
+
+/* ghost_stats_entry struct functions */
+
+/**
+ * @brief Reset the given ghost stats bin.
+ *
+ * @param bin Ghost stats bin to reset.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_reset_entry(
+    struct ghost_stats_entry *restrict bin) {
+
+  bin->count = 0;
+  bin->count_no_ngb = 0;
+  bin->hmin = FLT_MAX;
+  bin->hmax = 0.0f;
+  bin->hsum = 0.;
+  bin->hsum2 = 0.;
+}
+
+/**
+ * @brief Write the given ghost stats bin to the given file.
+ *
+ * @param f File to write to.
+ * @param bin Ghost stats bin to write.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_print_entry(
+    FILE *f, const struct ghost_stats_entry *restrict bin) {
+
+  if (bin->count > 0) {
+    fprintf(f, "\t%i\t%i\t%g\t%g\t%g\t%g", bin->count, bin->count_no_ngb,
+            bin->hmin, bin->hmax, bin->hsum, bin->hsum2);
+  } else {
+    fprintf(f, "\t0\t0\t0\t0\t0\t0");
+  }
+}
+
+/* ghost_stats struct functions */
+
+/**
+ * @brief Reset all the entries in the ghost_stats struct.
+ *
+ * @param gstats Ghost stats struct.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_reset_entries(
+    struct ghost_stats *restrict gstats) {
+
+  for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) {
+    ghost_stats_reset_entry(&gstats->hydro[b]);
+  }
+  for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) {
+    ghost_stats_reset_entry(&gstats->stars[b]);
+  }
+  for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) {
+    ghost_stats_reset_entry(&gstats->black_holes[b]);
+  }
+}
+
+/**
+ * @brief Account for the star particles that are still under consideration at
+ * the start of a ghost decision loop (so after the neighbour loop but before
+ * the smoothing length is updated).
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param iteration_number Iteration number in the high-level ghost iteration
+ * scheme.
+ * @param scount Number of star particles still under consideration (smoothing
+ * length has not converged yet or will do so during this iteration).
+ * @param sparts Star particle array.
+ * @param sid Indices of active star particles in the array.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_account_for_stars(
+    struct ghost_stats *restrict gstats, int iteration_number, int scount,
+    struct spart *restrict sparts, int *sid) {
+
+  /* accumulate in highest bin, so we can spot out-of-bounds values */
+  int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1);
+  struct ghost_stats_entry *restrict sbin = &gstats->stars[binidx];
+  sbin->count += scount;
+  for (int i = 0; i < scount; i++) {
+    const float hi = sparts[sid[i]].h;
+    sbin->hmin = min(sbin->hmin, hi);
+    sbin->hmax = max(sbin->hmax, hi);
+    sbin->hsum += hi;
+    sbin->hsum2 += hi * hi;
+  }
+}
+
+/**
+ * @brief Account for the properties of the a converged star particle.
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param sp Star particle that has a converged smoothing length.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_converged_star(
+    struct ghost_stats *restrict gstats, struct spart *restrict sp) {
+
+  struct ghost_stats_entry *restrict sbin = &gstats->stars[SWIFT_GHOST_STATS];
+  ++sbin->count;
+  const float hi = sp->h;
+  sbin->hmin = min(sbin->hmin, hi);
+  sbin->hmax = max(sbin->hmax, hi);
+  sbin->hsum += hi;
+  sbin->hsum2 += hi * hi;
+}
+
+/**
+ * @brief Register the occurrence of a "no neighbour" event during the current
+ * star iteration step.
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param iteration_number Number of the current iteration.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_star_iteration(struct ghost_stats *restrict gstats,
+                                  int iteration_number) {
+
+  int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1);
+  struct ghost_stats_entry *restrict sbin = &gstats->stars[binidx];
+  ++sbin->count_no_ngb;
+}
+
+/**
+ * @brief Register the occurrence of a converged star particle without
+ * neighbours.
+ *
+ * @param gstats Ghost stats struct to update.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_star_converged(struct ghost_stats *restrict gstats) {
+
+  struct ghost_stats_entry *restrict sbin = &gstats->stars[SWIFT_GHOST_STATS];
+  ++sbin->count_no_ngb;
+}
+
+/**
+ * @brief Account for the black hole particles that are still under
+ * consideration at the start of a ghost decision loop (so after the neighbour
+ * loop but before the smoothing length is updated).
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param iteration_number Iteration number in the high-level ghost iteration
+ * scheme.
+ * @param bcount Number of black hole particles still under consideration
+ * (smoothing length has not converged yet or will do so during this iteration).
+ * @param bparts Black hole particle array.
+ * @param sid Indices of active black hole particles in the array.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_account_for_black_holes(struct ghost_stats *restrict gstats,
+                                    int iteration_number, int bcount,
+                                    struct bpart *restrict bparts, int *sid) {
+
+  /* accumulate in highest bin, so we can spot out-of-bounds values */
+  int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1);
+  struct ghost_stats_entry *restrict bbin = &gstats->black_holes[binidx];
+  bbin->count += bcount;
+  for (int i = 0; i < bcount; i++) {
+    const float hi = bparts[sid[i]].h;
+    bbin->hmin = min(bbin->hmin, hi);
+    bbin->hmax = max(bbin->hmax, hi);
+    bbin->hsum += hi;
+    bbin->hsum2 += hi * hi;
+  }
+}
+
+/**
+ * @brief Account for the properties of the a converged black hole particle.
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param bp Black hole particle that has a converged smoothing length.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_converged_black_hole(struct ghost_stats *restrict gstats,
+                                 struct bpart *restrict bp) {
+
+  struct ghost_stats_entry *restrict bbin =
+      &gstats->black_holes[SWIFT_GHOST_STATS];
+  ++bbin->count;
+  const float hi = bp->h;
+  bbin->hmin = min(bbin->hmin, hi);
+  bbin->hmax = max(bbin->hmax, hi);
+  bbin->hsum += hi;
+  bbin->hsum2 += hi * hi;
+}
+
+/**
+ * @brief Register the occurrence of a "no neighbour" event during the current
+ * black hole iteration step.
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param iteration_number Number of the current iteration.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_black_hole_iteration(struct ghost_stats *restrict gstats,
+                                        int iteration_number) {
+
+  int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1);
+  struct ghost_stats_entry *restrict bbin = &gstats->black_holes[binidx];
+  ++bbin->count_no_ngb;
+}
+
+/**
+ * @brief Register the occurrence of a converged black hole particle without
+ * neighbours.
+ *
+ * @param gstats Ghost stats struct to update.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_black_hole_converged(struct ghost_stats *restrict gstats) {
+
+  struct ghost_stats_entry *restrict bbin =
+      &gstats->black_holes[SWIFT_GHOST_STATS];
+  ++bbin->count_no_ngb;
+}
+
+/**
+ * @brief Account for the gas particles that are still under consideration at
+ * the start of a ghost decision loop (so after the neighbour loop but before
+ * the smoothing length is updated).
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param iteration_number Iteration number in the high-level ghost iteration
+ * scheme.
+ * @param count Number of gas particles still under consideration (smoothing
+ * length has not converged yet or will do so during this iteration).
+ * @param parts Gas particle array.
+ * @param sid Indices of active gas particles in the array.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_account_for_hydro(
+    struct ghost_stats *restrict gstats, int iteration_number, int count,
+    struct part *restrict parts, int *pid) {
+
+  /* accumulate in highest bin, so we can spot out-of-bounds values */
+  int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1);
+  struct ghost_stats_entry *restrict hbin = &gstats->hydro[binidx];
+  hbin->count += count;
+  for (int i = 0; i < count; i++) {
+    const float hi = parts[pid[i]].h;
+    hbin->hmin = min(hbin->hmin, hi);
+    hbin->hmax = max(hbin->hmax, hi);
+    hbin->hsum += hi;
+    hbin->hsum2 += hi * hi;
+  }
+}
+
+/**
+ * @brief Account for the properties of the a converged gas particle.
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param p Gas particle that has a converged smoothing length.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_converged_hydro(
+    struct ghost_stats *restrict gstats, struct part *restrict p) {
+
+  struct ghost_stats_entry *restrict hbin = &gstats->hydro[SWIFT_GHOST_STATS];
+  ++hbin->count;
+  const float hi = p->h;
+  hbin->hmin = min(hbin->hmin, hi);
+  hbin->hmax = max(hbin->hmax, hi);
+  hbin->hsum += hi;
+  hbin->hsum2 += hi * hi;
+}
+
+/**
+ * @brief Register the occurrence of a "no neighbour" event during the current
+ * hydro iteration step.
+ *
+ * @param gstats Ghost stats struct to update.
+ * @param iteration_number Number of the current iteration.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_hydro_iteration(struct ghost_stats *restrict gstats,
+                                   int iteration_number) {
+
+  int binidx = min(iteration_number, SWIFT_GHOST_STATS - 1);
+  struct ghost_stats_entry *restrict hbin = &gstats->hydro[binidx];
+  ++hbin->count_no_ngb;
+}
+
+/**
+ * @brief Register the occurrence of a converged gas particle without
+ * neighbours.
+ *
+ * @param gstats Ghost stats struct to update.
+ */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_hydro_converged(struct ghost_stats *restrict gstats) {
+
+  struct ghost_stats_entry *restrict hbin = &gstats->hydro[SWIFT_GHOST_STATS];
+  ++hbin->count_no_ngb;
+}
+
+/**
+ * @brief Write the header of a ghost statistics file.
+ *
+ * @param f File to write to.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_write_header(
+    FILE *f) {
+
+  fprintf(f, "# Ghost statistics\n");
+  fprintf(f, "# Values listed in blocks per particle type\n");
+  fprintf(f, "# Order of types: hydro, stars, black holes\n");
+  fprintf(f, "# Number of blocks per type: %i\n", SWIFT_GHOST_STATS + 1);
+  fprintf(f, "# Number of values per block: 6\n");
+  fprintf(f, "# Last block contains converged values\n");
+  fprintf(f, "# Fields per block:\n");
+  fprintf(f, "#  - count: i4\n");
+  fprintf(f, "#  - no neighbour count: i4\n");
+  fprintf(f, "#  - min h: f4\n");
+  fprintf(f, "#  - max h: f4\n");
+  fprintf(f, "#  - sum h: f8\n");
+  fprintf(f, "#  - sum h^2: f8\n");
+  fprintf(f, "# First column is cellID\n");
+  fprintf(f, "# Cells with no values are omitted\n");
+}
+
+/**
+ * @brief Write the ghost statistics for the given cell to the given file.
+ *
+ * @param f File to write to.
+ * @param gstats Ghost statistics to write.
+ * @param cellID Cell ID to use to identify the cell.
+ */
+__attribute__((always_inline)) INLINE static void ghost_stats_write_cell_stats(
+    FILE *f, const struct ghost_stats *restrict gstats,
+    const long long cellID) {
+
+  if (gstats->hydro[0].count + gstats->stars[0].count > 0) {
+    fprintf(f, "%lld", cellID);
+    for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) {
+      ghost_stats_print_entry(f, &gstats->hydro[b]);
+    }
+    for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) {
+      ghost_stats_print_entry(f, &gstats->stars[b]);
+    }
+    for (int b = 0; b < SWIFT_GHOST_STATS + 1; ++b) {
+      ghost_stats_print_entry(f, &gstats->black_holes[b]);
+    }
+    fprintf(f, "\n");
+  }
+}
+
+/******************
+ * cell interface *
+ ******************/
+
+struct cell;
+
+void cell_reset_ghost_histograms(struct cell *c);
+void cell_write_ghost_stats(FILE *f, const struct cell *c,
+                            const long long cellID);
+/*******************
+ * space interface *
+ *******************/
+
+struct space;
+
+void space_reset_ghost_histograms(struct space *s);
+void space_write_ghost_stats(const struct space *s, int j);
+
+#else
+
+struct ghost_stats {};
+
+/* stars */
+__attribute__((always_inline)) INLINE static void ghost_stats_account_for_stars(
+    struct ghost_stats *restrict gstats, int iteration_number, int scount,
+    struct spart *restrict sparts, int *sid) {}
+
+__attribute__((always_inline)) INLINE static void ghost_stats_converged_star(
+    struct ghost_stats *restrict gstats, struct spart *restrict sp) {}
+
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_star_iteration(struct ghost_stats *restrict gstats,
+                                  int iteration_number) {}
+
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_star_converged(struct ghost_stats *restrict gstats) {}
+
+/* black holes */
+__attribute__((always_inline)) INLINE static void
+ghost_stats_account_for_black_holes(struct ghost_stats *restrict gstats,
+                                    int iteration_number, int bcount,
+                                    struct bpart *restrict bparts, int *sid) {}
+
+__attribute__((always_inline)) INLINE static void
+ghost_stats_converged_black_hole(struct ghost_stats *restrict gstats,
+                                 struct bpart *restrict bp) {}
+
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_black_hole_iteration(struct ghost_stats *restrict gstats,
+                                        int iteration_number) {}
+
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_black_hole_converged(struct ghost_stats *restrict gstats) {}
+
+/* hydro */
+__attribute__((always_inline)) INLINE static void ghost_stats_account_for_hydro(
+    struct ghost_stats *restrict gstats, int iteration_number, int count,
+    struct part *restrict parts, int *pid) {}
+
+__attribute__((always_inline)) INLINE static void ghost_stats_converged_hydro(
+    struct ghost_stats *restrict gstats, struct part *restrict p) {}
+
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_hydro_iteration(struct ghost_stats *restrict gstats,
+                                   int iteration_number) {}
+
+__attribute__((always_inline)) INLINE static void
+ghost_stats_no_ngb_hydro_converged(struct ghost_stats *restrict gstats) {}
+
+/// cell interface
+
+struct cell;
+
+__attribute__((always_inline)) INLINE static void cell_reset_ghost_histograms(
+    struct cell *c) {}
+
+/// space interface
+
+struct space;
+
+__attribute__((always_inline)) INLINE static void space_reset_ghost_histograms(
+    struct space *s) {}
+
+__attribute__((always_inline)) INLINE static void space_write_ghost_stats(
+    const struct space *s, int j) {}
+#endif
+
+#endif /* SWIFT_GHOST_STATS */
diff --git a/src/gravity.c b/src/gravity.c
index 81e0296035d52837a42c5b47852057fe3a99af23..ae2efbe3f68beda4af906a95d90af8ee25bf8092 100644
--- a/src/gravity.c
+++ b/src/gravity.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/gravity.h b/src/gravity.h
index c3f65a15cab534cfafa00b1c9aeffe7b8419d4a4..7f10fefe6edf9dd012ec861a9e9683fdc5daa5ae 100644
--- a/src/gravity.h
+++ b/src/gravity.h
@@ -20,7 +20,7 @@
 #define SWIFT_GRAVITY_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "const.h"
diff --git a/src/gravity_cache.h b/src/gravity_cache.h
index 618374ea75d154cbd147662dbf3b3cc82c593ad6..d8b33de3c56d4656819ca4c94beb017212e840d1 100644
--- a/src/gravity_cache.h
+++ b/src/gravity_cache.h
@@ -20,7 +20,7 @@
 #define SWIFT_GRAVITY_CACHE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "accumulate.h"
diff --git a/src/gravity_csds.h b/src/gravity_csds.h
index 85746d445bc089816def74d64e51987ac36c3aae..9b99d0f723488ba82b5e69994b9b9c2f51dc535e 100644
--- a/src/gravity_csds.h
+++ b/src/gravity_csds.h
@@ -20,7 +20,7 @@
 #define SWIFT_GRAVITY_CSDS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "./const.h"
diff --git a/src/gravity_derivatives.h b/src/gravity_derivatives.h
index f504c2ab43d94bc1a57f8578722a5ea8d6dcb6b4..4129098631eb1c34435937fcf8f1075e4fec750b 100644
--- a/src/gravity_derivatives.h
+++ b/src/gravity_derivatives.h
@@ -28,7 +28,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/gravity_iact.h b/src/gravity_iact.h
index a56e750cff9bc9e139e38597a6398eb5a6983c91..b5025b0df991556d1006014ef44af01f4f3074dd 100644
--- a/src/gravity_iact.h
+++ b/src/gravity_iact.h
@@ -20,7 +20,7 @@
 #define SWIFT_GRAVITY_IACT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "const.h"
diff --git a/src/gravity_io.h b/src/gravity_io.h
index 1271c48984e9bdc0c7cdf29e716cbedfe871f506..02af251c2150c4088475171bee960a04d7ebf4a1 100644
--- a/src/gravity_io.h
+++ b/src/gravity_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_GRAVITY_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "./const.h"
diff --git a/src/gravity_properties.c b/src/gravity_properties.c
index 6cffb1f395730369470266267354d6edc818735c..9dc3805615f5676304ae913429f39877e94664d7 100644
--- a/src/gravity_properties.c
+++ b/src/gravity_properties.c
@@ -39,6 +39,7 @@
 #define gravity_props_default_r_cut_max 4.5f
 #define gravity_props_default_r_cut_min 0.1f
 #define gravity_props_default_rebuild_frequency 0.01f
+#define gravity_props_default_rebuild_active_fraction 1.01f  // > 1 means never
 #define gravity_props_default_distributed_mesh 0
 
 void gravity_props_init(struct gravity_props *p, struct swift_params *params,
@@ -55,6 +56,10 @@ void gravity_props_init(struct gravity_props *p, struct swift_params *params,
       parser_get_opt_param_float(params, "Gravity:rebuild_frequency",
                                  gravity_props_default_rebuild_frequency);
 
+  p->rebuild_active_fraction =
+      parser_get_opt_param_float(params, "Gravity:rebuild_active_fraction",
+                                 gravity_props_default_rebuild_active_fraction);
+
   if (p->rebuild_frequency < 0.f || p->rebuild_frequency > 1.f)
     error("Invalid tree rebuild frequency. Must be in [0., 1.]");
 
diff --git a/src/gravity_properties.h b/src/gravity_properties.h
index 016c318f46e34b5e68ae7d3aa1ebbb1aab928b46..9fd4d089ffe572bfd9e8569a864b1e7fff6def54 100644
--- a/src/gravity_properties.h
+++ b/src/gravity_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_GRAVITY_PROPERTIES
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5)
 #include <hdf5.h>
@@ -110,6 +110,9 @@ struct gravity_props {
   /*! Frequency of tree-rebuild in units of #gpart updates. */
   float rebuild_frequency;
 
+  /*! Fraction of active #gparts needed to trigger a tree-rebuild */
+  float rebuild_active_fraction;
+
   /*! Time integration dimensionless multiplier */
   float eta;
 
diff --git a/src/gravity_softened_derivatives.h b/src/gravity_softened_derivatives.h
index 4232688f1714c3b1fa71d8a4cb45bfd00d0dd8e9..1645efcde81e316a806ceac5442d49a10c80918c 100644
--- a/src/gravity_softened_derivatives.h
+++ b/src/gravity_softened_derivatives.h
@@ -28,7 +28,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/hdf5_object_to_blob.c b/src/hdf5_object_to_blob.c
index 67d7a0608db94ad8badcacdecb77281394f91917..943cb0f982106a6c42e9232f86d2779675402c01 100644
--- a/src/hdf5_object_to_blob.c
+++ b/src/hdf5_object_to_blob.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/hdf5_object_to_blob.h b/src/hdf5_object_to_blob.h
index 733a623eb2d806b37e6d38332b0c126489441bc8..30e83ae4a23a474e989fea91bbe226e088682bde 100644
--- a/src/hdf5_object_to_blob.h
+++ b/src/hdf5_object_to_blob.h
@@ -21,7 +21,7 @@
 #define SWIFT_HDF5_OBJECT_TO_BLOB_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef HAVE_HDF5
 
diff --git a/src/hydro.c b/src/hydro.c
index 8f948d1df6fa2c09be610f65b08a75841efb06e6..ae4cfd9742a2426ceaf46fd19984d10a9f3dd74c 100644
--- a/src/hydro.c
+++ b/src/hydro.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/hydro.h b/src/hydro.h
index 7dfeef18ced81b45ef9c5839d74a07eeb78e57e0..1f96d1f3031033c85d5915ecb3ca1d0959738d70 100644
--- a/src/hydro.h
+++ b/src/hydro.h
@@ -20,7 +20,7 @@
 #define SWIFT_HYDRO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "const.h"
diff --git a/src/hydro/AnarchyPU/hydro.h b/src/hydro/AnarchyPU/hydro.h
index cdc955a398262250902df1ee71fa0683bcf56952..2b3b3d73686bf6392a8b7f43aad81be12a0c2fd5 100644
--- a/src/hydro/AnarchyPU/hydro.h
+++ b/src/hydro/AnarchyPU/hydro.h
@@ -776,11 +776,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   /* Here we need to update the artificial viscosity */
 
@@ -924,13 +925,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -1020,6 +1022,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -1028,7 +1031,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/AnarchyPU/hydro_parameters.h b/src/hydro/AnarchyPU/hydro_parameters.h
index e5decff1c47c50fd9c6c14b44c0624560630e4ab..0fe3b61f78e213bd5d4ca2ba5e08c12c31b34bca 100644
--- a/src/hydro/AnarchyPU/hydro_parameters.h
+++ b/src/hydro/AnarchyPU/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_ANARCHY_PU_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/AnarchyPU/hydro_part.h b/src/hydro/AnarchyPU/hydro_part.h
index b8dead7e5003676047021a06e88824273d76bee8..7cfa3f1cce1a637bf020e0bcda1c36977e2e2304 100644
--- a/src/hydro/AnarchyPU/hydro_part.h
+++ b/src/hydro/AnarchyPU/hydro_part.h
@@ -229,6 +229,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Gadget2/hydro.h b/src/hydro/Gadget2/hydro.h
index 289582a7ec7ea08430d7e6fbe2db3a8d832c4857..724d416b6a8b1d532bc1771a477954388e1f02c6 100644
--- a/src/hydro/Gadget2/hydro.h
+++ b/src/hydro/Gadget2/hydro.h
@@ -641,11 +641,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps;
 
@@ -782,13 +783,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -871,6 +873,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon
  * @param dt_therm The time-step for this kick (for thermodynamic quantities)
  * @param dt_grav The time-step for this kick (for gravity forces)
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro forces)
  * @param dt_kick_corr The time-step for this kick (for correction of the kick)
  * @param cosmo The cosmological model.
@@ -879,7 +882,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/Gadget2/hydro_parameters.h b/src/hydro/Gadget2/hydro_parameters.h
index 3d8c9a15b68abdf9ca25f6a12ea9c20668b0f872..1278fede39a599ffc9c35e4fe2a21ad6e229e4c6 100644
--- a/src/hydro/Gadget2/hydro_parameters.h
+++ b/src/hydro/Gadget2/hydro_parameters.h
@@ -21,7 +21,7 @@
 #define SWIFT_GADGET2_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h
index d5bf88312c61aafd6f3dc81e2f7990d80e167e94..a2ad80a1da696a458467288586e59bcd59576ab9 100644
--- a/src/hydro/Gadget2/hydro_part.h
+++ b/src/hydro/Gadget2/hydro_part.h
@@ -189,6 +189,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Gasoline/hydro.h b/src/hydro/Gasoline/hydro.h
index 281b1892f9f4bb4562ee17b2b14acb40f565f097..e9cad33611181eeb41aee1ce4d23a67807c23bfb 100644
--- a/src/hydro/Gasoline/hydro.h
+++ b/src/hydro/Gasoline/hydro.h
@@ -755,11 +755,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
   /* Here we need to update the artificial viscosity alpha */
   const float d_shock_indicator_dt =
       dt_alpha == 0.f ? 0.f
@@ -877,13 +878,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
   /* Predict the internal energy */
@@ -962,6 +964,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -970,7 +973,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
   /* Integrate the internal energy forward in time */
diff --git a/src/hydro/Gasoline/hydro_parameters.h b/src/hydro/Gasoline/hydro_parameters.h
index 49f927097cca029dcff9022adc2a9fb46b857808..1e1cd26ae944ef50a7145bcd08d82c2236cff4c4 100644
--- a/src/hydro/Gasoline/hydro_parameters.h
+++ b/src/hydro/Gasoline/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_GASOLINE_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Gasoline/hydro_part.h b/src/hydro/Gasoline/hydro_part.h
index d2de6f8c772c366e2edba5afc3cdad23480fda56..39ebe556ee8eee33eb156bc39a30a050f1b4911e 100644
--- a/src/hydro/Gasoline/hydro_part.h
+++ b/src/hydro/Gasoline/hydro_part.h
@@ -237,6 +237,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Gizmo/MFM/hydro_flux.h b/src/hydro/Gizmo/MFM/hydro_flux.h
index a0d6155d267b4237cf243067d03b82e273c0d0a6..68b590e9b68d036072dbaeeceb5069e48fbd2b8c 100644
--- a/src/hydro/Gizmo/MFM/hydro_flux.h
+++ b/src/hydro/Gizmo/MFM/hydro_flux.h
@@ -22,11 +22,11 @@
 #include "riemann.h"
 
 /**
- * @brief Reset the fluxes for the given particle.
+ * @brief Reset the hydrodynamical fluxes for the given particle.
  *
  * @param p Particle.
  */
-__attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes(
+__attribute__((always_inline)) INLINE static void hydro_part_reset_hydro_fluxes(
     struct part* restrict p) {
 
   p->flux.momentum[0] = 0.0f;
@@ -35,6 +35,16 @@ __attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes(
   p->flux.energy = 0.0f;
 }
 
+/**
+ * @brief Reset the gravity fluxes for the given particle.
+ *
+ * Does nothing, since MFM does not have gravity fluxes.
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_part_reset_gravity_fluxes(struct part* restrict p) {}
+
 /**
  * @brief Get the fluxes for the given particle.
  *
@@ -80,19 +90,21 @@ __attribute__((always_inline)) INLINE static void hydro_compute_flux(
  * @param p Particle.
  * @param fluxes Fluxes accross the interface.
  * @param dx Distance between the particles that share the interface.
+ * @param dt Time step for the flux exchange.
  */
 __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left(
-    struct part* restrict p, const float* fluxes, const float* dx) {
+    struct part* restrict p, const float* fluxes, const float* dx,
+    const float dt) {
 
-  p->flux.momentum[0] -= fluxes[1];
-  p->flux.momentum[1] -= fluxes[2];
-  p->flux.momentum[2] -= fluxes[3];
-  p->flux.energy -= fluxes[4];
+  p->flux.momentum[0] -= fluxes[1] * dt;
+  p->flux.momentum[1] -= fluxes[2] * dt;
+  p->flux.momentum[2] -= fluxes[3] * dt;
+  p->flux.energy -= fluxes[4] * dt;
 
 #ifndef GIZMO_TOTAL_ENERGY
-  p->flux.energy += fluxes[1] * p->v[0];
-  p->flux.energy += fluxes[2] * p->v[1];
-  p->flux.energy += fluxes[3] * p->v[2];
+  p->flux.energy += fluxes[1] * p->v[0] * dt;
+  p->flux.energy += fluxes[2] * p->v[1] * dt;
+  p->flux.energy += fluxes[3] * p->v[2] * dt;
 #endif
 }
 
@@ -103,20 +115,21 @@ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left(
  * @param p Particle.
  * @param fluxes Fluxes accross the interface.
  * @param dx Distance between the particles that share the interface.
+ * @param dt Time step for the flux exchange.
  */
 __attribute__((always_inline)) INLINE static void
 hydro_part_update_fluxes_right(struct part* restrict p, const float* fluxes,
-                               const float* dx) {
+                               const float* dx, const float dt) {
 
-  p->flux.momentum[0] += fluxes[1];
-  p->flux.momentum[1] += fluxes[2];
-  p->flux.momentum[2] += fluxes[3];
-  p->flux.energy += fluxes[4];
+  p->flux.momentum[0] += fluxes[1] * dt;
+  p->flux.momentum[1] += fluxes[2] * dt;
+  p->flux.momentum[2] += fluxes[3] * dt;
+  p->flux.energy += fluxes[4] * dt;
 
 #ifndef GIZMO_TOTAL_ENERGY
-  p->flux.energy -= fluxes[1] * p->v[0];
-  p->flux.energy -= fluxes[2] * p->v[1];
-  p->flux.energy -= fluxes[3] * p->v[2];
+  p->flux.energy -= fluxes[1] * p->v[0] * dt;
+  p->flux.energy -= fluxes[2] * p->v[1] * dt;
+  p->flux.energy -= fluxes[3] * p->v[2] * dt;
 #endif
 }
 
@@ -137,35 +150,46 @@ hydro_gizmo_mfv_density_drift_term(const float mass_flux, const float dt,
 /**
  * @brief Add the gravitational contribution to the fluid velocity drift.
  *
- * This method does nothing, since this is Gizmo MFM.
+ * This just needs to reset the particle velocity, which was incorrectly
+ * drifted, since this is Gizmo MFM.
  *
+ * @param v (drifted) particle velocity.
  * @param fluid_v Fluid velocity.
- * @param v (Undrifted) particle velocity.
- * @param v_full (Drifted) particle velocity.
+ * @param v_full (Undrifted) particle velocity.
+ * @param a_grav Gravitational acceleration.
+ * @param dt_kick_grav Time-step to kick the particle gravitationally.
  */
 __attribute__((always_inline)) INLINE static void
-hydro_gizmo_mfv_extra_velocity_drift(float* fluid_v, const float* v,
-                                     const float* v_full) {}
+hydro_gizmo_mfv_extra_velocity_drift(float* v, const float* restrict fluid_v,
+                                     const float* restrict v_full,
+                                     const float* restrict a_grav,
+                                     float dt_kick_grav) {
+  /* Just reset particle velocity */
+  v[0] = v_full[0];
+  v[1] = v_full[1];
+  v[2] = v_full[2];
+}
 
 /**
  * @brief Get the term required to update the MFV energy due to the change in
  * gravitational energy.
  *
  * @param dt_kick_corr Time step for the potential energy correction.
- * @param dt_grav Time step for the (optional) kinetic energy correction.
  * @param p Particle.
  * @param momentum Momentum of the particle, explicitly requested so that it is
  * clear from the code that the momentum needs to be updated after the call to
  * this function.
  * @param a_grav Gravitational acceleration.
+ * @param grav_kick_factor Gravitational kick factor
+ * (a_grav * dt + a_grav_mesh * dt_mesh)
  * @return 0, since this is Gizmo MFM.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_gizmo_mfv_gravity_energy_update_term(const float dt_kick_corr,
-                                           const float dt_grav,
                                            const struct part* restrict p,
                                            const float* momentum,
-                                           const float* a_grav) {
+                                           const float* a_grav,
+                                           const float* grav_kick_factor) {
 
   return 0.0f;
 }
diff --git a/src/hydro/Gizmo/MFM/hydro_part.h b/src/hydro/Gizmo/MFM/hydro_part.h
index d89bb4ecffa136bc6e2bcee3b030c0ce824657ee..509dd79ad661317714425bfc93939264f68c5b33 100644
--- a/src/hydro/Gizmo/MFM/hydro_part.h
+++ b/src/hydro/Gizmo/MFM/hydro_part.h
@@ -84,19 +84,6 @@ struct part {
     } limiter;
 
     struct {
-      /* Fluxes. */
-      struct {
-
-        /* No mass flux, since it is always zero. */
-
-        /* Momentum flux. */
-        float momentum[3];
-
-        /* Energy flux. */
-        float energy;
-
-      } flux;
-
       /* Variables used for timestep calculation. */
       struct {
 
@@ -120,6 +107,21 @@ struct part {
     };
   };
 
+  /* Fluxes. */
+  struct {
+    /* No mass flux, since it is always zero. */
+
+    /* Momentum flux. */
+    float momentum[3];
+
+    /* Energy flux. */
+    float energy;
+
+    /* Particle time step. Used to compute time-integrated fluxes. */
+    float dt;
+
+  } flux;
+
   /* Gradients of the primitive variables. */
   struct {
 
@@ -178,6 +180,12 @@ struct part {
   /*! Sink information (e.g. swallowing ID) */
   struct sink_part_data sink_data;
 
+  /*! Additional Radiative Transfer Data */
+  struct rt_part_data rt_data;
+
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Gizmo/MFV/hydro_flux.h b/src/hydro/Gizmo/MFV/hydro_flux.h
index 59174ffabcd302f2f32b404d0996d070b3f8322f..bab922bd10f40f18ac870d4a47e8fc8d925c220a 100644
--- a/src/hydro/Gizmo/MFV/hydro_flux.h
+++ b/src/hydro/Gizmo/MFV/hydro_flux.h
@@ -22,11 +22,11 @@
 #include "riemann.h"
 
 /**
- * @brief Reset the fluxes for the given particle.
+ * @brief Reset the hydrodynamical fluxes for the given particle.
  *
  * @param p Particle.
  */
-__attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes(
+__attribute__((always_inline)) INLINE static void hydro_part_reset_hydro_fluxes(
     struct part* restrict p) {
 
   p->flux.mass = 0.0f;
@@ -34,6 +34,15 @@ __attribute__((always_inline)) INLINE static void hydro_part_reset_fluxes(
   p->flux.momentum[1] = 0.0f;
   p->flux.momentum[2] = 0.0f;
   p->flux.energy = 0.0f;
+}
+
+/**
+ * @brief Reset the gravity fluxes for the given particle.
+ *
+ * @param p Particle.
+ */
+__attribute__((always_inline)) INLINE static void
+hydro_part_reset_gravity_fluxes(struct part* restrict p) {
 
   p->gravity.mflux[0] = 0.0f;
   p->gravity.mflux[1] = 0.0f;
@@ -87,28 +96,30 @@ __attribute__((always_inline)) INLINE static void hydro_compute_flux(
  * @param p Particle.
  * @param fluxes Fluxes accross the interface.
  * @param dx Distance between the particles that share the interface.
+ * @param dt Time step for the flux exchange.
  */
 __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left(
-    struct part* restrict p, const float* fluxes, const float* dx) {
+    struct part* restrict p, const float* fluxes, const float* dx,
+    const float dt) {
 
   p->gravity.mflux[0] += fluxes[0] * dx[0];
   p->gravity.mflux[1] += fluxes[0] * dx[1];
   p->gravity.mflux[2] += fluxes[0] * dx[2];
 
-  p->flux.mass -= fluxes[0];
-  p->flux.momentum[0] -= fluxes[1];
-  p->flux.momentum[1] -= fluxes[2];
-  p->flux.momentum[2] -= fluxes[3];
-  p->flux.energy -= fluxes[4];
+  p->flux.mass -= fluxes[0] * dt;
+  p->flux.momentum[0] -= fluxes[1] * dt;
+  p->flux.momentum[1] -= fluxes[2] * dt;
+  p->flux.momentum[2] -= fluxes[3] * dt;
+  p->flux.energy -= fluxes[4] * dt;
 
 #ifndef GIZMO_TOTAL_ENERGY
   const float ekin =
       0.5f * (p->fluid_v[0] * p->fluid_v[0] + p->fluid_v[1] * p->fluid_v[1] +
               p->fluid_v[2] * p->fluid_v[2]);
-  p->flux.energy += fluxes[1] * p->fluid_v[0];
-  p->flux.energy += fluxes[2] * p->fluid_v[1];
-  p->flux.energy += fluxes[3] * p->fluid_v[2];
-  p->flux.energy -= fluxes[0] * ekin;
+  p->flux.energy += fluxes[1] * p->fluid_v[0] * dt;
+  p->flux.energy += fluxes[2] * p->fluid_v[1] * dt;
+  p->flux.energy += fluxes[3] * p->fluid_v[2] * dt;
+  p->flux.energy -= fluxes[0] * ekin * dt;
 #endif
 }
 
@@ -119,29 +130,30 @@ __attribute__((always_inline)) INLINE static void hydro_part_update_fluxes_left(
  * @param p Particle.
  * @param fluxes Fluxes accross the interface.
  * @param dx Distance between the particles that share the interface.
+ * @param dt Time step for the flux exchange.
  */
 __attribute__((always_inline)) INLINE static void
 hydro_part_update_fluxes_right(struct part* restrict p, const float* fluxes,
-                               const float* dx) {
+                               const float* dx, const float dt) {
 
   p->gravity.mflux[0] += fluxes[0] * dx[0];
   p->gravity.mflux[1] += fluxes[0] * dx[1];
   p->gravity.mflux[2] += fluxes[0] * dx[2];
 
-  p->flux.mass += fluxes[0];
-  p->flux.momentum[0] += fluxes[1];
-  p->flux.momentum[1] += fluxes[2];
-  p->flux.momentum[2] += fluxes[3];
-  p->flux.energy += fluxes[4];
+  p->flux.mass += fluxes[0] * dt;
+  p->flux.momentum[0] += fluxes[1] * dt;
+  p->flux.momentum[1] += fluxes[2] * dt;
+  p->flux.momentum[2] += fluxes[3] * dt;
+  p->flux.energy += fluxes[4] * dt;
 
 #ifndef GIZMO_TOTAL_ENERGY
   const float ekin =
       0.5f * (p->fluid_v[0] * p->fluid_v[0] + p->fluid_v[1] * p->fluid_v[1] +
               p->fluid_v[2] * p->fluid_v[2]);
-  p->flux.energy -= fluxes[1] * p->fluid_v[0];
-  p->flux.energy -= fluxes[2] * p->fluid_v[1];
-  p->flux.energy -= fluxes[3] * p->fluid_v[2];
-  p->flux.energy += fluxes[0] * ekin;
+  p->flux.energy -= fluxes[1] * p->fluid_v[0] * dt;
+  p->flux.energy -= fluxes[2] * p->fluid_v[1] * dt;
+  p->flux.energy -= fluxes[3] * p->fluid_v[2] * dt;
+  p->flux.energy += fluxes[0] * ekin * dt;
 #endif
 }
 
@@ -167,17 +179,27 @@ hydro_gizmo_mfv_density_drift_term(const float mass_flux, const float dt,
 /**
  * @brief Add the gravitational contribution to the fluid velocity drift.
  *
+ * @param v (drifted) particle velocity.
  * @param fluid_v Fluid velocity.
- * @param v (Undrifted) particle velocity.
- * @param v_full (Drifted) particle velocity.
+ * @param v_full (Undrifted) particle velocity.
+ * @param a_grav Gravitational acceleration.
+ * @param dt_kick_grav Time-step to kick the particle gravitationally.
  */
 __attribute__((always_inline)) INLINE static void
-hydro_gizmo_mfv_extra_velocity_drift(float* fluid_v, const float* v,
-                                     const float* v_full) {
+hydro_gizmo_mfv_extra_velocity_drift(float* restrict v, float* restrict fluid_v,
+                                     const float* restrict v_full,
+                                     const float* restrict a_grav,
+                                     float dt_kick_grav) {
+
+  /* Reset particle velocity */
+  v[0] = v_full[0];
+  v[1] = v_full[1];
+  v[2] = v_full[2];
 
-  fluid_v[0] += v[0] - v_full[0];
-  fluid_v[1] += v[1] - v_full[1];
-  fluid_v[2] += v[2] - v_full[2];
+  /* Drift fluid velocity */
+  fluid_v[0] += a_grav[0] * dt_kick_grav;
+  fluid_v[1] += a_grav[1] * dt_kick_grav;
+  fluid_v[2] += a_grav[2] * dt_kick_grav;
 }
 
 /**
@@ -185,28 +207,29 @@ hydro_gizmo_mfv_extra_velocity_drift(float* fluid_v, const float* v,
  * gravitational energy.
  *
  * @param dt_kick_corr Time step for the potential energy correction.
- * @param dt_grav Time step for the (optional) kinetic energy correction.
  * @param p Particle.
  * @param momentum Momentum of the particle, explicitly requested so that it is
  * clear from the code that the momentum needs to be updated after the call to
  * this function.
  * @param a_grav Gravitational acceleration.
+ * @param grav_kick_factor Gravitational kick factor
+ * (a_grav * dt + a_grav_mesh * dt_mesh)
  * @return Term used to update the energy variable.
  */
 __attribute__((always_inline)) INLINE static float
 hydro_gizmo_mfv_gravity_energy_update_term(const float dt_kick_corr,
-                                           const float dt_grav,
                                            const struct part* restrict p,
                                            const float* momentum,
-                                           const float* a_grav) {
+                                           const float* a_grav,
+                                           const float* grav_kick_factor) {
 
   float dE =
       -0.5f * dt_kick_corr *
       (p->gravity.mflux[0] * a_grav[0] + p->gravity.mflux[1] * a_grav[1] +
        p->gravity.mflux[2] * a_grav[2]);
 #if defined(GIZMO_TOTAL_ENERGY)
-  dE += dt_grav * (momentum[0] * a_grav[0] + momentum[1] * a_grav[1] +
-                   momentum[2] * a_grav[2]);
+  dE += momentum[0] * grav_kick_factor[0] + momentum[1] * grav_kick_factor[1] +
+        momentum[2] * grav_kick_factor[2];
 #endif
   return dE;
 }
@@ -220,7 +243,7 @@ hydro_gizmo_mfv_gravity_energy_update_term(const float dt_kick_corr,
  */
 __attribute__((always_inline)) INLINE static float
 hydro_gizmo_mfv_mass_update_term(const float mass_flux, const float dt) {
-  return mass_flux * dt;
+  return mass_flux;
 }
 
 /**
diff --git a/src/hydro/Gizmo/MFV/hydro_part.h b/src/hydro/Gizmo/MFV/hydro_part.h
index 97003d2bcda3782a68b354d4c940f4dc0cc7b10c..e64b605f2d240a5dbda0266dcdc5c8277f994d55 100644
--- a/src/hydro/Gizmo/MFV/hydro_part.h
+++ b/src/hydro/Gizmo/MFV/hydro_part.h
@@ -106,6 +106,9 @@ struct part {
     /* Energy flux. */
     float energy;
 
+    /* Particle time step. Used to compute time-integrated fluxes. */
+    float dt;
+
   } flux;
 
   /* Geometrical quantities used for hydro. */
@@ -182,6 +185,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Gizmo/hydro.h b/src/hydro/Gizmo/hydro.h
index 57abc862a7175b2641233a472e14e52758b4069f..7e3ce039b0d0b5daaa14bc0e3789cdaffb19ec4e 100644
--- a/src/hydro/Gizmo/hydro.h
+++ b/src/hydro/Gizmo/hydro.h
@@ -75,9 +75,10 @@ __attribute__((always_inline)) INLINE static float hydro_compute_timestep(
   vrel[0] = W[1] - xp->v_full[0];
   vrel[1] = W[2] - xp->v_full[1];
   vrel[2] = W[3] - xp->v_full[2];
+  const float rhoinv = (W[0] > 0.0f) ? 1.0f / W[0] : 0.0f;
   float vmax =
       sqrtf(vrel[0] * vrel[0] + vrel[1] * vrel[1] + vrel[2] * vrel[2]) +
-      sqrtf(hydro_gamma * W[4] / W[0]);
+      sqrtf(hydro_gamma * W[4] * rhoinv);
   vmax = max(vmax, p->timestepvars.vmax);
 
   const float psize = cosmo->a * cosmo->a *
@@ -311,9 +312,9 @@ __attribute__((always_inline)) INLINE static void hydro_end_density(
 
   /* compute primitive variables */
   /* eqns (3)-(5) */
-  const float Q[5] = {p->conserved.mass, p->conserved.momentum[0],
-                      p->conserved.momentum[1], p->conserved.momentum[2],
-                      p->conserved.energy};
+  float Q[5] = {p->conserved.mass, p->conserved.momentum[0],
+                p->conserved.momentum[1], p->conserved.momentum[2],
+                p->conserved.energy};
 
 #ifdef SWIFT_DEBUG_CHECKS
   if (Q[0] < 0.) {
@@ -477,13 +478,15 @@ __attribute__((always_inline)) INLINE static void hydro_end_gradient(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part* restrict p, struct xpart* restrict xp,
     const struct cosmology* cosmo, const struct hydro_props* hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
-  hydro_part_reset_fluxes(p);
+  hydro_part_reset_gravity_fluxes(p);
+  p->flux.dt = dt_therm;
 }
 
 /**
@@ -547,7 +550,8 @@ __attribute__((always_inline)) INLINE static void hydro_convert_quantities(
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part* p, struct xpart* xp, float dt_drift, float dt_therm,
-    const struct cosmology* cosmo, const struct hydro_props* hydro_props,
+    float dt_kick_grav, const struct cosmology* cosmo,
+    const struct hydro_props* hydro_props,
     const struct entropy_floor_properties* floor_props) {
 
   /* skip the drift if we are using Lloyd's algorithm */
@@ -578,36 +582,39 @@ __attribute__((always_inline)) INLINE static void hydro_predict_extra(
 
   float W[5];
   hydro_part_get_primitive_variables(p, W);
-  float flux[5];
-  hydro_part_get_fluxes(p, flux);
-
-  W[0] +=
-      hydro_gizmo_mfv_density_drift_term(flux[0], dt_therm, p->geometry.volume);
-
-  if (p->conserved.mass > 0.0f) {
-    const float m_inv = 1.0f / p->conserved.mass;
-
-    W[1] += flux[1] * dt_therm * m_inv;
-    W[2] += flux[2] * dt_therm * m_inv;
-    W[3] += flux[3] * dt_therm * m_inv;
-
-#if !defined(EOS_ISOTHERMAL_GAS)
-#ifdef GIZMO_TOTAL_ENERGY
-    const float Etot = p->conserved.energy + flux[4] * dt_therm;
-    const float v2 = (W[1] * W[1] + W[2] * W[2] + W[3] * W[3]);
-    const float u = (Etot * m_inv - 0.5f * v2);
-#else
-    const float u = (p->conserved.energy + flux[4] * dt_therm) * m_inv;
-#endif
-    W[4] = hydro_gamma_minus_one * u * W[0];
-#endif
+  float gradrho[3], gradvx[3], gradvy[3], gradvz[3], gradP[3];
+  hydro_part_get_gradients(p, gradrho, gradvx, gradvy, gradvz, gradP);
+
+  const float divv = gradvx[0] + gradvy[1] + gradvz[2];
+
+  float Wprime[5];
+  Wprime[0] = W[0] - dt_therm * (W[0] * divv + W[1] * gradrho[0] +
+                                 W[2] * gradrho[1] + W[3] * gradrho[2]);
+  if (W[0] != 0.0f) {
+    const float rhoinv = 1.0f / W[0];
+    Wprime[1] = W[1] - dt_therm * (W[1] * divv + rhoinv * gradP[0]);
+    Wprime[2] = W[2] - dt_therm * (W[2] * divv + rhoinv * gradP[1]);
+    Wprime[3] = W[3] - dt_therm * (W[3] * divv + rhoinv * gradP[2]);
+  } else {
+    Wprime[1] = 0.0f;
+    Wprime[2] = 0.0f;
+    Wprime[3] = 0.0f;
   }
+  Wprime[4] = W[4] - dt_therm * (hydro_gamma * W[4] * divv + W[1] * gradP[0] +
+                                 W[2] * gradP[1] + W[3] * gradP[2]);
+
+  W[0] = Wprime[0];
+  W[1] = Wprime[1];
+  W[2] = Wprime[2];
+  W[3] = Wprime[3];
+  W[4] = Wprime[4];
 
   // MATTHIEU: Apply the entropy floor here.
 
   /* add the gravitational contribution to the fluid velocity drift */
   /* (MFV only) */
-  hydro_gizmo_mfv_extra_velocity_drift(&W[1], p->v, xp->v_full);
+  hydro_gizmo_mfv_extra_velocity_drift(p->v, p->fluid_v, xp->v_full, xp->a_grav,
+                                       dt_kick_grav);
 
   gizmo_check_physical_quantities("density", "pressure", W[0], W[1], W[2], W[3],
                                   W[4]);
@@ -653,8 +660,8 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part* p, struct xpart* xp, float dt_therm, float dt_grav,
-    float dt_hydro, float dt_kick_corr, const struct cosmology* cosmo,
-    const struct hydro_props* hydro_props,
+    float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
+    const struct cosmology* cosmo, const struct hydro_props* hydro_props,
     const struct entropy_floor_properties* floor_props) {
 
   /* Add gravity. We only do this if we have gravity activated. */
@@ -662,39 +669,67 @@ __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     /* Retrieve the current value of the gravitational acceleration from the
        gpart. We are only allowed to do this because this is the kick. We still
        need to check whether gpart exists though.*/
-    float a_grav[3];
+    float a_grav[3], grav_kick_factor[3];
 
-    a_grav[0] = p->gpart->a_grav[0];
-    a_grav[1] = p->gpart->a_grav[1];
-    a_grav[2] = p->gpart->a_grav[2];
+    a_grav[0] = p->gpart->a_grav[0] + p->gpart->a_grav_mesh[0];
+    a_grav[1] = p->gpart->a_grav[1] + p->gpart->a_grav_mesh[1];
+    a_grav[2] = p->gpart->a_grav[2] + p->gpart->a_grav_mesh[2];
 
-    p->conserved.energy += hydro_gizmo_mfv_gravity_energy_update_term(
-        dt_kick_corr, dt_grav, p, p->conserved.momentum, a_grav);
+    grav_kick_factor[0] = dt_grav * p->gpart->a_grav[0];
+    grav_kick_factor[1] = dt_grav * p->gpart->a_grav[1];
+    grav_kick_factor[2] = dt_grav * p->gpart->a_grav[2];
+    if (dt_grav_mesh != 0) {
+      grav_kick_factor[0] += dt_grav_mesh * p->gpart->a_grav_mesh[0];
+      grav_kick_factor[1] += dt_grav_mesh * p->gpart->a_grav_mesh[1];
+      grav_kick_factor[2] += dt_grav_mesh * p->gpart->a_grav_mesh[2];
+    }
 
     /* Kick the momentum for half a time step */
     /* Note that this also affects the particle movement, as the velocity for
        the particles is set after this. */
-    p->conserved.momentum[0] += p->conserved.mass * a_grav[0] * dt_grav;
-    p->conserved.momentum[1] += p->conserved.mass * a_grav[1] * dt_grav;
-    p->conserved.momentum[2] += p->conserved.mass * a_grav[2] * dt_grav;
+    p->conserved.momentum[0] += p->conserved.mass * grav_kick_factor[0];
+    p->conserved.momentum[1] += p->conserved.mass * grav_kick_factor[1];
+    p->conserved.momentum[2] += p->conserved.mass * grav_kick_factor[2];
+
+    p->conserved.energy += hydro_gizmo_mfv_gravity_energy_update_term(
+        dt_kick_corr, p, p->conserved.momentum, a_grav, grav_kick_factor);
   }
 
-  float flux[5];
-  hydro_part_get_fluxes(p, flux);
+  if (p->flux.dt > 0.0f) {
+    float flux[5];
+    hydro_part_get_fluxes(p, flux);
 
-  /* Update conserved variables. */
-  p->conserved.mass += hydro_gizmo_mfv_mass_update_term(flux[0], dt_therm);
-  p->conserved.momentum[0] += flux[1] * dt_therm;
-  p->conserved.momentum[1] += flux[2] * dt_therm;
-  p->conserved.momentum[2] += flux[3] * dt_therm;
+    /* Update conserved variables. */
+    p->conserved.mass += hydro_gizmo_mfv_mass_update_term(flux[0], dt_therm);
+    p->conserved.momentum[0] += flux[1];
+    p->conserved.momentum[1] += flux[2];
+    p->conserved.momentum[2] += flux[3];
 #if defined(EOS_ISOTHERMAL_GAS)
-  /* We use the EoS equation in a sneaky way here just to get the constant u */
-  p->conserved.energy =
-      p->conserved.mass * gas_internal_energy_from_entropy(0.0f, 0.0f);
+    /* We use the EoS equation in a sneaky way here just to get the constant u
+     */
+    p->conserved.energy =
+        p->conserved.mass * gas_internal_energy_from_entropy(0.0f, 0.0f);
 #else
-  p->conserved.energy += flux[4] * dt_therm;
+    p->conserved.energy += flux[4];
 #endif
 
+    /* reset the fluxes, so that they do not get used again in kick1 */
+    hydro_part_reset_hydro_fluxes(p);
+    /* invalidate the particle time step. It is considered to be inactive until
+       dt is set again in hydro_prepare_force() */
+    p->flux.dt = -1.0f;
+  } else if (p->flux.dt == 0.0f) {
+    /* something tricky happens at the beginning of the simulation: the flux
+       exchange is done for all particles, but using a time step of 0. This
+       in itself is not a problem. However, it causes some issues with the
+       initialisation of flux.dt for inactive particles, since this value will
+       remain 0 until the particle is active again, and its flux.dt is set to
+       the actual time step in hydro_prepare_force(). We have to make sure it
+       is properly set to -1 here, so that inactive particles are indeed found
+       to be inactive during the flux loop. */
+    p->flux.dt = -1.0f;
+  }
+
 #ifndef HYDRO_GAMMA_5_3
 
   const float Pcorr = (dt_hydro - dt_therm) * p->geometry.volume;
diff --git a/src/hydro/Gizmo/hydro_iact.h b/src/hydro/Gizmo/hydro_iact.h
index ec4d213fe427ab07d044c2d7469684f5d1d7bed6..3d96473879f78b79e2ee56bef32f449cef915894 100644
--- a/src/hydro/Gizmo/hydro_iact.h
+++ b/src/hydro/Gizmo/hydro_iact.h
@@ -406,15 +406,19 @@ __attribute__((always_inline)) INLINE static void runner_iact_fluxes_common(
   float totflux[5];
   hydro_compute_flux(Wi, Wj, n_unit, vij, Anorm, totflux);
 
-  hydro_part_update_fluxes_left(pi, totflux, dx);
-
-  /* Note that this used to be much more complicated in early implementations of
-   * the GIZMO scheme, as we wanted manifest conservation of conserved variables
-   * and had to do symmetric flux exchanges. Now we don't care about manifest
-   * conservation anymore and just assume the current fluxes are representative
-   * for the flux over the entire time step. */
-  if (mode == 1) {
-    hydro_part_update_fluxes_right(pj, totflux, dx);
+  /* get the time step for the flux exchange. This is always the smallest time
+     step among the two particles */
+  const float mindt =
+      (pj->flux.dt > 0.0f) ? fminf(pi->flux.dt, pj->flux.dt) : pi->flux.dt;
+
+  hydro_part_update_fluxes_left(pi, totflux, dx, mindt);
+
+  /* Unlike in SPH schemes, we do need to update inactive neighbours, so that
+     the fluxes are always exchanged symmetrically. Thanks to our sneaky use
+     of flux.dt, we can detect inactive neighbours through their negative
+     time step. */
+  if (mode == 1 || (pj->flux.dt < 0.0f)) {
+    hydro_part_update_fluxes_right(pj, totflux, dx, mindt);
   }
 
   /* If we're working with RT, we need to pay additional attention to the
diff --git a/src/hydro/Gizmo/hydro_io.h b/src/hydro/Gizmo/hydro_io.h
index 4b96e20336eaac422b42fc18361e83296a91f8ac..c56db563e02d31cfdf8b99b99a9ade21d65c1796 100644
--- a/src/hydro/Gizmo/hydro_io.h
+++ b/src/hydro/Gizmo/hydro_io.h
@@ -103,13 +103,16 @@ INLINE static void convert_Etot(const struct engine* e, const struct part* p,
 #ifdef GIZMO_TOTAL_ENERGY
   ret[0] = p->conserved.energy;
 #else
-  float momentum2;
+  if (p->conserved.mass > 0.0f) {
+    const float momentum2 =
+        p->conserved.momentum[0] * p->conserved.momentum[0] +
+        p->conserved.momentum[1] * p->conserved.momentum[1] +
+        p->conserved.momentum[2] * p->conserved.momentum[2];
 
-  momentum2 = p->conserved.momentum[0] * p->conserved.momentum[0] +
-              p->conserved.momentum[1] * p->conserved.momentum[1] +
-              p->conserved.momentum[2] * p->conserved.momentum[2];
-
-  ret[0] = p->conserved.energy + 0.5f * momentum2 / p->conserved.mass;
+    ret[0] = p->conserved.energy + 0.5f * momentum2 / p->conserved.mass;
+  } else {
+    ret[0] = 0.0f;
+  }
 #endif
 }
 
diff --git a/src/hydro/Gizmo/hydro_parameters.h b/src/hydro/Gizmo/hydro_parameters.h
index 7996dd3fc965c54fc984775371029e01b00f9670..06b304df54b1484c552f379029c3327a36b49be1 100644
--- a/src/hydro/Gizmo/hydro_parameters.h
+++ b/src/hydro/Gizmo/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_GIZMO_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Gizmo/hydro_slope_limiters_face.h b/src/hydro/Gizmo/hydro_slope_limiters_face.h
index b9d3b8b24424fc3837f2dfdc0c90c387030a26f8..779abf7eef88e5dbea1a3fe7600470b7e9bfeb89 100644
--- a/src/hydro/Gizmo/hydro_slope_limiters_face.h
+++ b/src/hydro/Gizmo/hydro_slope_limiters_face.h
@@ -59,13 +59,15 @@ hydro_slope_limit_face_quantity(float phi_i, float phi_j, float phi_mid0,
   if (same_signf(phimax + delta1, phimax)) {
     phiplus = phimax + delta1;
   } else {
-    phiplus = phimax / (1.0f + delta1 / (fabsf(phimax) + FLT_MIN));
+    phiplus =
+        (phimax != 0.0f) ? phimax / (1.0f + delta1 / fabsf(phimax)) : 0.0f;
   }
 
   if (same_signf(phimin - delta1, phimin)) {
     phiminus = phimin - delta1;
   } else {
-    phiminus = phimin / (1.0f + delta1 / (fabsf(phimin) + FLT_MIN));
+    phiminus =
+        (phimin != 0.0f) ? phimin / (1.0f + delta1 / fabsf(phimin)) : 0.0f;
   }
 
   if (phi_i < phi_j) {
diff --git a/src/hydro/Minimal/hydro.h b/src/hydro/Minimal/hydro.h
index d1471fe30a25f3c169541c49e1b86eda990ab193..b984c13dc88ba1ee54cad1c86ee52059a443464f 100644
--- a/src/hydro/Minimal/hydro.h
+++ b/src/hydro/Minimal/hydro.h
@@ -662,11 +662,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps;
 
@@ -801,13 +802,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -883,6 +885,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -891,7 +894,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/Minimal/hydro_parameters.h b/src/hydro/Minimal/hydro_parameters.h
index 3731724d32508fb8c0edd31058ab0516e81bb8f3..74d3236f5a0d1fc095865f45e1db51973ad229c8 100644
--- a/src/hydro/Minimal/hydro_parameters.h
+++ b/src/hydro/Minimal/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_MINIMAL_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h
index a3d12ad665d0a183458b1dc1e04ee0e8cab45837..6709bb5e236bfd1f3ab1cffb6ecdc925115a9375 100644
--- a/src/hydro/Minimal/hydro_part.h
+++ b/src/hydro/Minimal/hydro_part.h
@@ -207,6 +207,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/None/hydro.h b/src/hydro/None/hydro.h
index ef605d1894c5a26fe6dd005f178abdfc318b75e1..4e7dd9ad6b6ad96e0f5170a93c3733e836fe70c1 100644
--- a/src/hydro/None/hydro.h
+++ b/src/hydro/None/hydro.h
@@ -577,11 +577,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {}
+    const float dt_alpha, const float dt_therm) {}
 
 /**
  * @brief Reset acceleration fields of a particle
@@ -619,13 +620,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {}
 
@@ -654,6 +656,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -662,7 +665,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {}
 
diff --git a/src/hydro/None/hydro_parameters.h b/src/hydro/None/hydro_parameters.h
index 6e6163a9408161cf652cf8303fbc270a8aaab160..6470173d35c24e3d9ad230da9e3ab9190df1d941 100644
--- a/src/hydro/None/hydro_parameters.h
+++ b/src/hydro/None/hydro_parameters.h
@@ -20,7 +20,7 @@
 #define SWIFT_NONE_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/None/hydro_part.h b/src/hydro/None/hydro_part.h
index c6270a53cdf743a3d8f131e74b42dac5615aab89..589c874e9716bb17c97d14da6d6e202ccffa6439 100644
--- a/src/hydro/None/hydro_part.h
+++ b/src/hydro/None/hydro_part.h
@@ -173,6 +173,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Phantom/hydro.h b/src/hydro/Phantom/hydro.h
index f2b32bea92606e40502e617232a70e66fef3f7d0..c998e06971f63007510f51d79a2ff2537689846e 100644
--- a/src/hydro/Phantom/hydro.h
+++ b/src/hydro/Phantom/hydro.h
@@ -741,11 +741,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   /* Here we need to update the artificial viscosity */
 
@@ -866,13 +867,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -949,6 +951,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -957,7 +960,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/Phantom/hydro_iact.h b/src/hydro/Phantom/hydro_iact.h
index 7a82628fe2e823c9524e7bfe2ef56db02a76ec4d..3ae9c98ca5eaf48a93246947ae1d2e811f1419ca 100644
--- a/src/hydro/Phantom/hydro_iact.h
+++ b/src/hydro/Phantom/hydro_iact.h
@@ -333,9 +333,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   const float omega_ij = min(dvdr_Hubble, 0.f);
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
-  /* Compute sound speeds and signal velocity */
-  const float v_sig = pi->force.soundspeed + pj->force.soundspeed -
-                      const_viscosity_beta * mu_ij;
+  /* Compute the signal velocity */
+  const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta);
 
   /* Variable smoothing length term */
   const float f_ij = 1.f - pi->force.f / mj;
@@ -389,7 +388,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha);
   /* wi_dx + wj_dx / 2 is F_ij */
   const float diff_du_term = alpha_diff * v_diff * (pi->u - pj->u) * 0.5f *
-                             (wi_dr * f_ij / pi->rho + wj_dr * f_ji / pi->rho);
+                             (wi_dr * f_ij / rhoi + wj_dr * f_ji / rhoj);
 
   /* Assemble the energy equation term */
   const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term;
@@ -400,8 +399,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_force(
   pj->u_dt += du_dt_j * mi;
 
   /* Get the time derivative for h. */
-  pi->force.h_dt -= mj * dvdr * f_ij * r_inv / rhoj * wi_dr;
-  pj->force.h_dt -= mi * dvdr * f_ji * r_inv / rhoi * wj_dr;
+  pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
+  pj->force.h_dt -= mi * dvdr * r_inv / rhoi * wj_dr;
 }
 
 /**
@@ -466,9 +465,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   const float omega_ij = min(dvdr_Hubble, 0.f);
   const float mu_ij = fac_mu * r_inv * omega_ij; /* This is 0 or negative */
 
-  /* Compute sound speeds and signal velocity */
-  const float v_sig = pi->force.soundspeed + pj->force.soundspeed -
-                      const_viscosity_beta * mu_ij;
+  /* Compute the signal velocity */
+  const float v_sig = signal_velocity(dx, pi, pj, mu_ij, const_viscosity_beta);
 
   /* Variable smoothing length term */
   const float f_ij = 1.f - pi->force.f / mj;
@@ -516,7 +514,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   const float alpha_diff = 0.5f * (pi->diffusion.alpha + pj->diffusion.alpha);
   /* wi_dx + wj_dx / 2 is F_ij */
   const float diff_du_term = alpha_diff * v_diff * (pi->u - pj->u) * 0.5f *
-                             (wi_dr * f_ij / pi->rho + wj_dr * f_ji / pi->rho);
+                             (wi_dr * f_ij / rhoi + wj_dr * f_ji / rhoj);
 
   /* Assemble the energy equation term */
   const float du_dt_i = sph_du_term_i + visc_du_term + diff_du_term;
@@ -525,7 +523,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_force(
   pi->u_dt += du_dt_i * mj;
 
   /* Get the time derivative for h. */
-  pi->force.h_dt -= mj * dvdr * f_ij * r_inv / rhoj * wi_dr;
+  pi->force.h_dt -= mj * dvdr * r_inv / rhoj * wi_dr;
 }
 
 #endif /* SWIFT_PHANTOM_HYDRO_IACT_H */
diff --git a/src/hydro/Phantom/hydro_parameters.h b/src/hydro/Phantom/hydro_parameters.h
index 9ce364373c22835018d4c0e45b039834095e8552..73b110b9c0f8257aef9237b4815d5b2b872aad19 100644
--- a/src/hydro/Phantom/hydro_parameters.h
+++ b/src/hydro/Phantom/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_PHANTOM_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Phantom/hydro_part.h b/src/hydro/Phantom/hydro_part.h
index 9a1dfd80a10a23299f16b08057a9cba45e7151cb..7c65800e3ccca7c069a993de95733163cb3bac45 100644
--- a/src/hydro/Phantom/hydro_part.h
+++ b/src/hydro/Phantom/hydro_part.h
@@ -227,6 +227,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Planetary/hydro.h b/src/hydro/Planetary/hydro.h
index 0c7186a8358854e803a3e54efd406a6d39a3d4b6..daa9c39b19c6225ed96bf8daedda9229341a5c73 100644
--- a/src/hydro/Planetary/hydro.h
+++ b/src/hydro/Planetary/hydro.h
@@ -660,11 +660,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   const float fac_Balsara_eps = cosmo->a_factor_Balsara_eps;
 
@@ -810,13 +811,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The constants used in the scheme
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -886,13 +888,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param p The particle to act upon.
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
+ * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
+ * @param dt_hydro The time-step for this kick (for hydro quantities).
+ * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
  * @param hydro_props The constants used in the scheme
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/Planetary/hydro_parameters.h b/src/hydro/Planetary/hydro_parameters.h
index 437cf7874b03abe85e258a8e65a0a1f35d046e46..292c23eabf967482810856d3b2cdb89dad31eb57 100644
--- a/src/hydro/Planetary/hydro_parameters.h
+++ b/src/hydro/Planetary/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_PLANETARY_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Planetary/hydro_part.h b/src/hydro/Planetary/hydro_part.h
index c82739355db5c82b3124002273c9429b87601c51..ccc7ef3b9996ccd544a5b27af0eaca0f052b69c8 100644
--- a/src/hydro/Planetary/hydro_part.h
+++ b/src/hydro/Planetary/hydro_part.h
@@ -209,6 +209,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h
index 9b8ef78e1979c38b9d771027068898a2a97444e6..01ba818f147516a18a3dc991d160192c823323cc 100644
--- a/src/hydro/PressureEnergy/hydro.h
+++ b/src/hydro/PressureEnergy/hydro.h
@@ -702,11 +702,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   const float fac_B = cosmo->a_factor_Balsara_eps;
 
@@ -825,13 +826,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -921,6 +923,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -929,7 +932,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/PressureEnergy/hydro_parameters.h b/src/hydro/PressureEnergy/hydro_parameters.h
index 1fbdaf9c2b302efdf507a92bbac47ffbd8e01375..e9c282ca14bf663cef24252ad6fa4ba379117c21 100644
--- a/src/hydro/PressureEnergy/hydro_parameters.h
+++ b/src/hydro/PressureEnergy/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_PRESSURE_ENERGY_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/PressureEnergy/hydro_part.h b/src/hydro/PressureEnergy/hydro_part.h
index 5624c92135ea22d42c3fbd4f9cf2e07963f4a242..75d5df1835bde62933b590d0606ec7a60a51a130 100644
--- a/src/hydro/PressureEnergy/hydro_part.h
+++ b/src/hydro/PressureEnergy/hydro_part.h
@@ -214,6 +214,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h
index 167b2aa8581e6d97f1d82257a895778375afdc5e..7afc09d744a53fc9ae59ff7faa223b2f05b00a45 100644
--- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h
+++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro.h
@@ -684,11 +684,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   const float fac_B = cosmo->a_factor_Balsara_eps;
 
@@ -822,13 +823,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -917,6 +919,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -925,7 +928,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h
index d1b0a9f890975a69c53c788fb0c84bb8a7d4ce20..d337c482f9190bae3ca5b016703765c9db8fcb65 100644
--- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h
+++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_PRESSURE_ENERGY_MORRIS_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h
index 40211ed19aeeffe06e8d78814a3dc05b3fcd5bf4..aae8ac721e462c916e003b3086ab127c76a928d7 100644
--- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h
+++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h
@@ -211,6 +211,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/PressureEntropy/hydro.h b/src/hydro/PressureEntropy/hydro.h
index 80c03c8bcf8d0b5b25b0703d4a3d85bba0912810..473ff1aa5645956f090f7cdde113cdebdbca57e5 100644
--- a/src/hydro/PressureEntropy/hydro.h
+++ b/src/hydro/PressureEntropy/hydro.h
@@ -619,11 +619,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   const float fac_mu = cosmo->a_factor_mu;
 
@@ -747,13 +748,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -829,13 +831,17 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param p The particle to act upon
  * @param xp The particle extended data to act upon
  * @param dt_therm The time-step for this kick (for thermodynamic quantities)
+ * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
+ * @param dt_hydro The time-step for this kick (for hydro quantities).
+ * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
  * @param hydro_props The constants used in the scheme
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/PressureEntropy/hydro_parameters.h b/src/hydro/PressureEntropy/hydro_parameters.h
index 286915587ecadfaf77c42025e2578f87f07f9935..8eb336986d809467414f3517d55ad3f06db29220 100644
--- a/src/hydro/PressureEntropy/hydro_parameters.h
+++ b/src/hydro/PressureEntropy/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_PRESSURE_ENTROPY_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h
index 53100336a98a4789cc9d8f33d26c2466faad4d16..8665b70098f98df1dbb81a2a0f3dca34b2a21eeb 100644
--- a/src/hydro/PressureEntropy/hydro_part.h
+++ b/src/hydro/PressureEntropy/hydro_part.h
@@ -187,6 +187,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/SPHENIX/hydro.h b/src/hydro/SPHENIX/hydro.h
index f87d45d6bab6b8cd8028c2cf8d67d21dd485548f..f37d2298f0bc5c61bc65096b6c9a849c11e8a44b 100644
--- a/src/hydro/SPHENIX/hydro.h
+++ b/src/hydro/SPHENIX/hydro.h
@@ -813,11 +813,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part *restrict p, struct xpart *restrict xp,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   /* Here we need to update the artificial viscosity */
 
@@ -994,13 +995,14 @@ __attribute__((always_inline)) INLINE static void hydro_reset_predicted_values(
  * @param xp The extended data of the particle.
  * @param dt_drift The drift time-step for positions.
  * @param dt_therm The drift time-step for thermal quantities.
+ * @param dt_kick_grav The time-step for gravity quantities.
  * @param cosmo The cosmological model.
  * @param hydro_props The properties of the hydro scheme.
  * @param floor_props The properties of the entropy floor.
  */
 __attribute__((always_inline)) INLINE static void hydro_predict_extra(
     struct part *restrict p, const struct xpart *restrict xp, float dt_drift,
-    float dt_therm, const struct cosmology *cosmo,
+    float dt_therm, float dt_kick_grav, const struct cosmology *cosmo,
     const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
@@ -1081,6 +1083,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  * @param xp The particle extended data to act upon.
  * @param dt_therm The time-step for this kick (for thermodynamic quantities).
  * @param dt_grav The time-step for this kick (for gravity quantities).
+ * @param dt_grav_mesh The time-step for this kick (mesh gravity).
  * @param dt_hydro The time-step for this kick (for hydro quantities).
  * @param dt_kick_corr The time-step for this kick (for gravity corrections).
  * @param cosmo The cosmological model.
@@ -1089,7 +1092,7 @@ __attribute__((always_inline)) INLINE static void hydro_end_force(
  */
 __attribute__((always_inline)) INLINE static void hydro_kick_extra(
     struct part *restrict p, struct xpart *restrict xp, float dt_therm,
-    float dt_grav, float dt_hydro, float dt_kick_corr,
+    float dt_grav, float dt_grav_mesh, float dt_hydro, float dt_kick_corr,
     const struct cosmology *cosmo, const struct hydro_props *hydro_props,
     const struct entropy_floor_properties *floor_props) {
 
diff --git a/src/hydro/SPHENIX/hydro_parameters.h b/src/hydro/SPHENIX/hydro_parameters.h
index aeb387784061a59b898d055549c419f251b48167..277be9562dd079d160ca1a9d5ab7208551c1e276 100644
--- a/src/hydro/SPHENIX/hydro_parameters.h
+++ b/src/hydro/SPHENIX/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_SPHENIX_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/SPHENIX/hydro_part.h b/src/hydro/SPHENIX/hydro_part.h
index 682a0dcc3be3febb9f950f86aa5862ef62be199f..bbc3bd717d205e5799bce97561fdf757e421a836 100644
--- a/src/hydro/SPHENIX/hydro_part.h
+++ b/src/hydro/SPHENIX/hydro_part.h
@@ -239,6 +239,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro/Shadowswift/hydro.h b/src/hydro/Shadowswift/hydro.h
index ae1a5cb2cb0e73a5c32ecca285455d8a31e54a27..a4f88d08e97a86461f975ef7c8a9a47db20db9c7 100644
--- a/src/hydro/Shadowswift/hydro.h
+++ b/src/hydro/Shadowswift/hydro.h
@@ -302,11 +302,12 @@ __attribute__((always_inline)) INLINE static void hydro_part_has_no_neighbours(
  * @param hydro_props Hydrodynamic properties.
  * @param dt_alpha The time-step used to evolve non-cosmological quantities such
  *                 as the artificial viscosity.
+ * @param dt_therm The time-step used to evolve hydrodynamical quantities.
  */
 __attribute__((always_inline)) INLINE static void hydro_prepare_force(
     struct part* restrict p, struct xpart* restrict xp,
     const struct cosmology* cosmo, const struct hydro_props* hydro_props,
-    const float dt_alpha) {
+    const float dt_alpha, const float dt_therm) {
 
   /* Initialize time step criterion variables */
   p->timestepvars.vmax = 0.0f;
diff --git a/src/hydro/Shadowswift/hydro_parameters.h b/src/hydro/Shadowswift/hydro_parameters.h
index b67d586cfb2c2e2c8c8175f3db13d34da5c5dd60..4db2a31bbdc08cbc490ffa308105a0a4fc9783f8 100644
--- a/src/hydro/Shadowswift/hydro_parameters.h
+++ b/src/hydro/Shadowswift/hydro_parameters.h
@@ -22,7 +22,7 @@
 #define SWIFT_SHADOWSWIFT_HYDRO_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h
index efecb8bee1e27618bbffbcf47240369658310f2b..397dba20d6b0d05fd473f6c1dc9c519cb8777493 100644
--- a/src/hydro/Shadowswift/hydro_part.h
+++ b/src/hydro/Shadowswift/hydro_part.h
@@ -206,6 +206,9 @@ struct part {
   /*! Additional Radiative Transfer Data */
   struct rt_part_data rt_data;
 
+  /*! RT sub-cycling time stepping data */
+  struct rt_timestepping_data rt_time_data;
+
   /*! Time-step length */
   timebin_t time_bin;
 
diff --git a/src/hydro_csds.h b/src/hydro_csds.h
index ab395cec0bb8def6bcf081ed5f5f488078d21112..96cb374423a438c5c42ea1f6a284c6d06f6815b6 100644
--- a/src/hydro_csds.h
+++ b/src/hydro_csds.h
@@ -20,7 +20,7 @@
 #define SWIFT_HYDRO_CSDS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "align.h"
diff --git a/src/hydro_io.h b/src/hydro_io.h
index f137fa00a1de1b08c68f73a33a053c1dd07d731e..c0f82c2e7b4f52adc870b59f41d12c0e14e84a46 100644
--- a/src/hydro_io.h
+++ b/src/hydro_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_HYDRO_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right functions */
 #if defined(NONE_SPH)
diff --git a/src/hydro_parameters.h b/src/hydro_parameters.h
index f5d221dec00c52dab6f9c66afbfd53d88ebaef72..5d1f3909632ac055b00587c01c276242b7bf76f9 100644
--- a/src/hydro_parameters.h
+++ b/src/hydro_parameters.h
@@ -26,7 +26,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "parser.h"
diff --git a/src/hydro_properties.h b/src/hydro_properties.h
index 30a3f24641d18db5db7ac66cee516b208d9f3c8c..b86329737edaf7964367bf1260eefd0ea63a57b0 100644
--- a/src/hydro_properties.h
+++ b/src/hydro_properties.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5)
 #include <hdf5.h>
diff --git a/src/hydro_space.h b/src/hydro_space.h
index e64b532d16a5b78526dfed22d99558a768d07400..6cda07e789e27583cbbdfd1842d5438683d8516d 100644
--- a/src/hydro_space.h
+++ b/src/hydro_space.h
@@ -19,7 +19,7 @@
 #ifndef SWIFT_HYDRO_SPACE_H
 #define SWIFT_HYDRO_SPACE_H
 
-#include "../config.h"
+#include <config.h>
 
 struct space;
 
diff --git a/src/ic_info.c b/src/ic_info.c
index a8db918f1db781659a51c3ebaa7fe71ef4de2070..65ac006baecf3ae2022ac6e1818a1ed6c6dc9c0a 100644
--- a/src/ic_info.c
+++ b/src/ic_info.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/ic_info.h b/src/ic_info.h
index afcbd9383baed933a84f87c042a4bccd49749daa..1bbe4712c06bd59673a8d1230150b88d822399bd 100644
--- a/src/ic_info.h
+++ b/src/ic_info.h
@@ -21,7 +21,7 @@
 #define SWIFT_IC_INFO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/inline.h b/src/inline.h
index b88240d2b3b4d13d87f75c1a54cec36f02ca4bed..0d5c09481837972e19df38a91ec6949b58c72329 100644
--- a/src/inline.h
+++ b/src/inline.h
@@ -21,7 +21,7 @@
 #define SWIFT_INLINE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /**
  * @brief Defines inline
diff --git a/src/integer_power.h b/src/integer_power.h
index 3aec75fd4f828aedd800512060edccf8d18cb8da..49a917572be9d62a29e3ac01645d0615af7facd1 100644
--- a/src/integer_power.h
+++ b/src/integer_power.h
@@ -20,7 +20,7 @@
 #define SWIFT_INTEGER_POWER_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "error.h"
diff --git a/src/intrinsics.h b/src/intrinsics.h
index 1d1747595c8bde5dbd29eb6c410575b4517806c2..6bcf7461bf1254f356ba7cffb8fa4c7db60364aa 100644
--- a/src/intrinsics.h
+++ b/src/intrinsics.h
@@ -20,7 +20,7 @@
 #define SWIFT_INTRINSICS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/io_compression.c b/src/io_compression.c
index eac0349f3ee33d7145468e0b9235c45916d42677..649674cd6cfb294825e9cf66ce745e6ae3af5d68 100644
--- a/src/io_compression.c
+++ b/src/io_compression.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "io_compression.h"
@@ -37,8 +37,8 @@
 const char* lossy_compression_schemes_names[compression_level_count] = {
     "off",        "on",          "DScale1",   "DScale2",    "DScale3",
     "DScale4",    "DScale5",     "DScale6",   "DMantissa9", "DMantissa13",
-    "FMantissa9", "FMantissa13", "HalfFloat", "BFloat16",   "Nbit36",
-    "Nbit40",     "Nbit44",      "Nbit48",    "Nbit56"};
+    "FMantissa9", "FMantissa13", "HalfFloat", "BFloat16",   "Nbit32",
+    "Nbit36",     "Nbit40",      "Nbit44",    "Nbit48",     "Nbit56"};
 
 /**
  * @brief Returns the lossy compression scheme given its name
@@ -481,13 +481,16 @@ void set_hdf5_lossy_compression(hid_t* h_prop, hid_t* h_type,
   }
 
   else if (comp == compression_write_Nbit_36 ||
+           comp == compression_write_Nbit_32 ||
            comp == compression_write_Nbit_40 ||
            comp == compression_write_Nbit_44 ||
            comp == compression_write_Nbit_48 ||
            comp == compression_write_Nbit_56) {
 
     int n_bits = 0;
-    if (comp == compression_write_Nbit_36)
+    if (comp == compression_write_Nbit_32)
+      n_bits = 32;
+    else if (comp == compression_write_Nbit_36)
       n_bits = 36;
     else if (comp == compression_write_Nbit_40)
       n_bits = 40;
diff --git a/src/io_compression.h b/src/io_compression.h
index dd5a1612789dd53421a9f837e8ea77d19924efeb..5ebc68caea1c91489ea7ce3dea871c8938158acf 100644
--- a/src/io_compression.h
+++ b/src/io_compression.h
@@ -20,7 +20,7 @@
 #define SWIFT_IO_COMPRESSION_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /**
  * @brief Compression levels for snapshot fields
@@ -40,6 +40,7 @@ enum lossy_compression_schemes {
   compression_write_f_mantissa_13, /*!< Conversion to 13-bits mantissa float */
   compression_write_half_float,    /*!< Conversion to IEEE754 half-float */
   compression_write_bfloat_16,     /*!< Conversion to Bfloat16 */
+  compression_write_Nbit_32, /*!< Conversion to 32-bit int (from long long) */
   compression_write_Nbit_36, /*!< Conversion to 36-bit int (from long long) */
   compression_write_Nbit_40, /*!< Conversion to 40-bit int (from long long) */
   compression_write_Nbit_44, /*!< Conversion to 44-bit int (from long long) */
diff --git a/src/io_properties.h b/src/io_properties.h
index 7bd501923d3b3c9cbf1b63e5ac708176219ba1d9..5c69a6ad760e7eb45a1fd5f1eda25a9427f08a4b 100644
--- a/src/io_properties.h
+++ b/src/io_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_IO_PROPERTIES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "common_io.h"
diff --git a/src/kernel_hydro.h b/src/kernel_hydro.h
index 90f25c32095a53d8e009ac56d5283c25b504e2ed..dc0c721e5b17528ad28ebd12a05fa8d8a004420d 100644
--- a/src/kernel_hydro.h
+++ b/src/kernel_hydro.h
@@ -29,7 +29,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/kernel_long_gravity.h b/src/kernel_long_gravity.h
index 60632d6d23819dbb8978ad63f28c0c8c5be60e3d..d81e7692ba96d623da32ec0e464f58119c1b82d7 100644
--- a/src/kernel_long_gravity.h
+++ b/src/kernel_long_gravity.h
@@ -20,7 +20,7 @@
 #define SWIFT_KERNEL_LONG_GRAVITY_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "const.h"
diff --git a/src/kick.h b/src/kick.h
index f1c5acec4b600cc8e4ff5c3199f44bbf1f1bc3ea..d81f8fb5e622d8ffc9a5db36b705c5ca43bb2dd7 100644
--- a/src/kick.h
+++ b/src/kick.h
@@ -20,7 +20,7 @@
 #define SWIFT_KICK_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "black_holes.h"
@@ -271,8 +271,9 @@ __attribute__((always_inline)) INLINE static void kick_part(
    * the particle masses in hydro_kick_extra */
   rt_kick_extra(p, dt_kick_therm, dt_kick_grav, dt_kick_hydro, dt_kick_corr,
                 cosmo, hydro_props);
-  hydro_kick_extra(p, xp, dt_kick_therm, dt_kick_grav, dt_kick_hydro,
-                   dt_kick_corr, cosmo, hydro_props, floor_props);
+  hydro_kick_extra(p, xp, dt_kick_therm, dt_kick_grav, dt_kick_mesh_grav,
+                   dt_kick_hydro, dt_kick_corr, cosmo, hydro_props,
+                   floor_props);
   mhd_kick_extra(p, xp, dt_kick_therm, dt_kick_grav, dt_kick_hydro,
                  dt_kick_corr, cosmo, hydro_props, floor_props);
   if (p->gpart != NULL) gravity_kick_extra(p->gpart, dt_kick_grav);
diff --git a/src/lightcone/lightcone.c b/src/lightcone/lightcone.c
index 649e14a30bea2639c7e8139a5e28b7a79a1ae63a..8c244b0c71a3d3e00bdcf1a8baab16887c1af9ea 100644
--- a/src/lightcone/lightcone.c
+++ b/src/lightcone/lightcone.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <hdf5.h>
diff --git a/src/lightcone/lightcone.h b/src/lightcone/lightcone.h
index 2e5ee59e8ca27f7514a25d2458e38e94e4cb046e..f0628fb85b234edbb6c0daa5cd30bac72732ac85 100644
--- a/src/lightcone/lightcone.h
+++ b/src/lightcone/lightcone.h
@@ -21,7 +21,7 @@
 #define SWIFT_LIGHTCONE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "lightcone/lightcone_map_types.h"
diff --git a/src/lightcone/lightcone_array.c b/src/lightcone/lightcone_array.c
index 2ea71d19a2cb7a626bd9cb45101e67c6703cfff8..f4bf79e56ad4badc226ad7741a0def61dc6bab7b 100644
--- a/src/lightcone/lightcone_array.c
+++ b/src/lightcone/lightcone_array.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/lightcone/lightcone_array.h b/src/lightcone/lightcone_array.h
index c4eb92e9e2d612606876576c7c41402cfe2b87a3..a47932cb797ea5a12b567f3966b93475b405eca1 100644
--- a/src/lightcone/lightcone_array.h
+++ b/src/lightcone/lightcone_array.h
@@ -21,7 +21,7 @@
 #define SWIFT_LIGHTCONE_ARRAY_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "lightcone/lightcone.h"
diff --git a/src/lightcone/lightcone_crossing.h b/src/lightcone/lightcone_crossing.h
index 000eb3e09f57e4e1d90c667b879f5169f44eed64..78777f1ff09b805074110601701c4c85aa6ffc2a 100644
--- a/src/lightcone/lightcone_crossing.h
+++ b/src/lightcone/lightcone_crossing.h
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "cosmology.h"
diff --git a/src/lightcone/lightcone_map.c b/src/lightcone/lightcone_map.c
index 2bad7db6d4224a7402879bc5297aadff2259bb95..0f4ae43b922d8ff146e2bf21bbc07b10190ef98a 100644
--- a/src/lightcone/lightcone_map.c
+++ b/src/lightcone/lightcone_map.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/lightcone/lightcone_map.h b/src/lightcone/lightcone_map.h
index 7b48752d67b37fe02cf98a899fa931dca204f065..5f7b5bbd9225ddc59848ecbe9165c6fdbd3475d8 100644
--- a/src/lightcone/lightcone_map.h
+++ b/src/lightcone/lightcone_map.h
@@ -31,7 +31,7 @@
 #include <math.h>
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* HDF5 */
 #ifdef HAVE_HDF5
diff --git a/src/lightcone/lightcone_map_types.c b/src/lightcone/lightcone_map_types.c
index 717b4e9ba2b5606d0d807bdfe668d3c80dd94681..00b359448bb78caf32f015dc92b7a210ee682470 100644
--- a/src/lightcone/lightcone_map_types.c
+++ b/src/lightcone/lightcone_map_types.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "black_holes.h"
diff --git a/src/lightcone/lightcone_map_types.h b/src/lightcone/lightcone_map_types.h
index 04993aa09724812d62f502ad579a163c907ba5c4..6dc94f935601fb70ce1937c5d6e5313301051fd8 100644
--- a/src/lightcone/lightcone_map_types.h
+++ b/src/lightcone/lightcone_map_types.h
@@ -21,7 +21,7 @@
 #define SWIFT_LIGHTCONE_MAP_TYPES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "io_compression.h"
diff --git a/src/lightcone/lightcone_particle_io.c b/src/lightcone/lightcone_particle_io.c
index 7065bca6403322819fc5f37fad8a758453eb027f..02a2cd5851f0c14585375fbe403b7a182f17a8fa 100644
--- a/src/lightcone/lightcone_particle_io.c
+++ b/src/lightcone/lightcone_particle_io.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <hdf5.h>
diff --git a/src/lightcone/lightcone_particle_io.h b/src/lightcone/lightcone_particle_io.h
index ce21a279632e3a0243bcac9a3f513af87dd6e538..dccde25c7c888ab3d71b2944988b0f5a6fc0446c 100644
--- a/src/lightcone/lightcone_particle_io.h
+++ b/src/lightcone/lightcone_particle_io.h
@@ -21,7 +21,7 @@
 #define SWIFT_LIGHTCONE_PARTICLE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <hdf5.h>
diff --git a/src/lightcone/lightcone_shell.c b/src/lightcone/lightcone_shell.c
index ae1d5b220fff5c3e07114fba5ba2609b1c8924bc..e3ac45885fd57efa2b14a67b1d376ee44f0d1568 100644
--- a/src/lightcone/lightcone_shell.c
+++ b/src/lightcone/lightcone_shell.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/lightcone/lightcone_shell.h b/src/lightcone/lightcone_shell.h
index d14fd8ac4e8914745c2aeba0a9b737f9b49b5fba..e78f6969bc98089a307f4ba6e31c70b0d9cf5bdf 100644
--- a/src/lightcone/lightcone_shell.h
+++ b/src/lightcone/lightcone_shell.h
@@ -24,7 +24,7 @@
 #include <stdio.h>
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "cosmology.h"
diff --git a/src/lightcone/pixel_index.h b/src/lightcone/pixel_index.h
index 85ca09dfee193f1f786a9686a83d2806d235d9c5..84458111eecd9b556685e77e83cb96f625113228 100644
--- a/src/lightcone/pixel_index.h
+++ b/src/lightcone/pixel_index.h
@@ -24,7 +24,7 @@
 #include <stdint.h>
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Type to use for HEALPix pixel indexes */
 typedef int64_t pixel_index_t;
diff --git a/src/lightcone/projected_kernel.c b/src/lightcone/projected_kernel.c
index 69c9d0c30065c60af002e6a188e8de66f8ee6b32..2dddef6c2cab1d32c228d89838a4b216a8df74f5 100644
--- a/src/lightcone/projected_kernel.c
+++ b/src/lightcone/projected_kernel.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "projected_kernel.h"
diff --git a/src/lightcone/projected_kernel.h b/src/lightcone/projected_kernel.h
index 674172c975847ad84aeff039c4f65298d6ebc2ee..3a9334e0d63c79d00e1fc0477f796446e42f68cd 100644
--- a/src/lightcone/projected_kernel.h
+++ b/src/lightcone/projected_kernel.h
@@ -21,7 +21,7 @@
 #define SWIFT_PROJECTED_KERNEL_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "error.h"
diff --git a/src/line_of_sight.c b/src/line_of_sight.c
index 99a530fc1dc4e61d2f08e1a2cf7699dc134899ce..40ddf3632348b42c8afcae118b2ade1a11947e54 100644
--- a/src/line_of_sight.c
+++ b/src/line_of_sight.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
diff --git a/src/line_of_sight.h b/src/line_of_sight.h
index f0c03a432f777ffa3ac1e4558128b89101ab245d..158c0fce6cb348f9dde95da0302b170d3091de79 100644
--- a/src/line_of_sight.h
+++ b/src/line_of_sight.h
@@ -22,7 +22,9 @@
 #define SWIFT_LOS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "engine.h"
 #include "io_properties.h"
 
diff --git a/src/log.h b/src/log.h
index 6bee9643371323ec9984a11af5f7bf3df589f1e5..674377c7692309f01a394635a0d992ecaaca1e64 100644
--- a/src/log.h
+++ b/src/log.h
@@ -20,7 +20,7 @@
 #define SWIFT_OPTIMIZED_LOG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/map.c b/src/map.c
index 3c999f622f10dc66a5f6e945bf4c1cf613c02e16..b4641aad59cfbdd0edacff490ed75e52b177f00e 100644
--- a/src/map.c
+++ b/src/map.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/memswap.h b/src/memswap.h
index adaab7fbd295f25bdbec14b757abc68b228e2e89..12de6d08830bdddee0b2ee0a17635763488a3560 100644
--- a/src/memswap.h
+++ b/src/memswap.h
@@ -20,8 +20,9 @@
 #define SWIFT_MEMSWAP_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <stdint.h>
 
 #ifdef HAVE_IMMINTRIN_H
diff --git a/src/memuse.c b/src/memuse.c
index aeff8ad59590a32a3cd0085f4761adee461ac4e2..29844fda0e323a3496ee2c972e1adbcf2182d1e3 100644
--- a/src/memuse.c
+++ b/src/memuse.c
@@ -24,7 +24,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard includes. */
 #include <stdint.h>
diff --git a/src/memuse.h b/src/memuse.h
index 90dd64e844950e21107c1fc9d15cfc54ed3aa507..b40869c87c1341265a07dae3e7aeea4b3da03c32 100644
--- a/src/memuse.h
+++ b/src/memuse.h
@@ -20,7 +20,7 @@
 #define SWIFT_MEMUSE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include <stdlib.h>
diff --git a/src/memuse_rnodes.c b/src/memuse_rnodes.c
index ae1229e63521dff55e12d2aa37f765095017208b..61973a5a6d645819b01314aa0c18255e773fbc3c 100644
--- a/src/memuse_rnodes.c
+++ b/src/memuse_rnodes.c
@@ -23,7 +23,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard includes. */
 #include <stdint.h>
diff --git a/src/memuse_rnodes.h b/src/memuse_rnodes.h
index e38ae65a127a70c6b2f78f1e68826d8b667d8cf2..09afa499ba7cbd5943e9af2b118375a3be91d6c3 100644
--- a/src/memuse_rnodes.h
+++ b/src/memuse_rnodes.h
@@ -20,7 +20,7 @@
 #define SWIFT_MEMUSE_RNODE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include <inttypes.h>
diff --git a/src/mesh_gravity.c b/src/mesh_gravity.c
index e7a276d0dc2a5a8e92afd72cba3aa04c1a1d18b5..22d80b282bd4e89dc8f3a0af2df73cd495145367 100644
--- a/src/mesh_gravity.c
+++ b/src/mesh_gravity.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef HAVE_FFTW
 #include <fftw3.h>
diff --git a/src/mesh_gravity.h b/src/mesh_gravity.h
index a73626bfcdf4d8c3a892c61173f3a17a8ac8a6d0..70cf5dae075ad7f417ede6af1741bd4f81ae5c2f 100644
--- a/src/mesh_gravity.h
+++ b/src/mesh_gravity.h
@@ -20,7 +20,7 @@
 #define SWIFT_MESH_GRAVITY_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "gravity_properties.h"
diff --git a/src/mesh_gravity_mpi.c b/src/mesh_gravity_mpi.c
index 2401b5ac4dab2030864a36305c3f9b21205012f8..24191f312d581081422156918f4dc766c00278f0 100644
--- a/src/mesh_gravity_mpi.c
+++ b/src/mesh_gravity_mpi.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
diff --git a/src/mesh_gravity_mpi.h b/src/mesh_gravity_mpi.h
index 11e2f9a3a9ad9c19d6defec939a7b0aee9a39f88..33d100ce3accce82846c37252109efea3a4d9820 100644
--- a/src/mesh_gravity_mpi.h
+++ b/src/mesh_gravity_mpi.h
@@ -20,7 +20,7 @@
 #define SWIFT_MESH_GRAVITY_MPI_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Forward declarations */
 struct space;
diff --git a/src/mesh_gravity_patch.c b/src/mesh_gravity_patch.c
index 53036581ca35d4a631904bb6e25f0c4161ee6936..bce169f9901c026ea48994e3c5ecaa51ce8e896e 100644
--- a/src/mesh_gravity_patch.c
+++ b/src/mesh_gravity_patch.c
@@ -18,8 +18,9 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <math.h>
 
 /* This object's header. */
diff --git a/src/mesh_gravity_patch.h b/src/mesh_gravity_patch.h
index cc82175882159e3a29fc523a008da44c27fc2d3c..cbd37bda64d1e12da96f59487a7bc3980f84ddf3 100644
--- a/src/mesh_gravity_patch.h
+++ b/src/mesh_gravity_patch.h
@@ -21,7 +21,7 @@
 #define SWIFT_MESH_GRAVITY_PATCH_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "align.h"
diff --git a/src/mesh_gravity_sort.c b/src/mesh_gravity_sort.c
index 78e315ed817869fba04d7e4019ebc7f2a6752864..25422815745ebd6322f24dbc378ef7d004419560 100644
--- a/src/mesh_gravity_sort.c
+++ b/src/mesh_gravity_sort.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "mesh_gravity_sort.h"
diff --git a/src/mesh_gravity_sort.h b/src/mesh_gravity_sort.h
index 86f77b117062e92de7fce9686d93a91b30735096..fd7816d9ee97fa7d8f865c19ece9147a6d38603b 100644
--- a/src/mesh_gravity_sort.h
+++ b/src/mesh_gravity_sort.h
@@ -20,7 +20,7 @@
 #define SWIFT_MESH_GRAVITY_SORT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard includes */
 #include <stdlib.h>
diff --git a/src/mhd.h b/src/mhd.h
index 8b29a93964e4b7f8e0ce4894ef4f599c45f4351d..913a7e55afb8caa065b8c1a16c57bf50357fd7d2 100644
--- a/src/mhd.h
+++ b/src/mhd.h
@@ -20,7 +20,7 @@
 #define SWIFT_MHD_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "part.h"
diff --git a/src/mhd/None/mhd_parameters.h b/src/mhd/None/mhd_parameters.h
index 0ce1ef83feb08c3e57fd88b1206c40eca4e0b049..8c9dcb3e8da7ef2aa464f66d822a84380c3a8219 100644
--- a/src/mhd/None/mhd_parameters.h
+++ b/src/mhd/None/mhd_parameters.h
@@ -21,7 +21,7 @@
 #define SWIFT_NONE_MHD_PARAMETERS_H
 
 /* Configuration file */
-#include "config.h"
+#include <config.h>
 
 /* Global headers */
 #if defined(HAVE_HDF5)
diff --git a/src/mhd_io.h b/src/mhd_io.h
index 72da130bcc82259a78c7285f03baaeed49d44da1..ce034485eae5eeabdb682c24d0bc6ecfa3a37c62 100644
--- a/src/mhd_io.h
+++ b/src/mhd_io.h
@@ -19,7 +19,7 @@
 #ifndef SWIFT_MHD_IO_H
 #define SWIFT_MHD_IO_H
 
-#include "../config.h"
+#include <config.h>
 
 /* Load the correct MHD model */
 #if defined(NONE_MHD)
diff --git a/src/mhd_struct.h b/src/mhd_struct.h
index 771bd4a41f24e443462a5ea2c2051931ed2dc1b7..ad9c6b2213fccf085d8d2a253b7960ff12823f7b 100644
--- a/src/mhd_struct.h
+++ b/src/mhd_struct.h
@@ -20,7 +20,7 @@
 #define SWIFT_MHD_STRUCT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "part.h"
diff --git a/src/mpiuse.c b/src/mpiuse.c
index b50bdc3458341bbd951012a0bf15047cae64644c..7d00934226e61372f2b10db4214da2957e823709 100644
--- a/src/mpiuse.c
+++ b/src/mpiuse.c
@@ -21,7 +21,7 @@
  *  @brief file of routines to report about MPI tasks used in SWIFT.
  */
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(SWIFT_MPIUSE_REPORTS) && defined(WITH_MPI)
 
diff --git a/src/mpiuse.h b/src/mpiuse.h
index 812af6a78ea98d138f45638e442f100fe23d8596..1bbf3ff81aeae5b468268e3b7980e596d84b4eae 100644
--- a/src/mpiuse.h
+++ b/src/mpiuse.h
@@ -20,7 +20,7 @@
 #define SWIFT_MPIUSE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "cycle.h"
diff --git a/src/multipole.c b/src/multipole.c
index b95b64e0248edeffd3e902df0896b608dfe5f6a6..eddc7274d176907440a878aac96a1516e51a02ab 100644
--- a/src/multipole.c
+++ b/src/multipole.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "multipole.h"
diff --git a/src/multipole.h b/src/multipole.h
index 338ada94261ab231fef9757323715a46b506a844..897623f9515add2e40f0cc544b1e9702ddddf96f 100644
--- a/src/multipole.h
+++ b/src/multipole.h
@@ -21,7 +21,7 @@
 #define SWIFT_MULTIPOLE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/multipole_accept.h b/src/multipole_accept.h
index 22ee31c5639d9591a4040983abeb9c2c2c20171c..bf052c19d1d8774f3e0fade7086e77d3032f6930 100644
--- a/src/multipole_accept.h
+++ b/src/multipole_accept.h
@@ -20,7 +20,7 @@
 #define SWIFT_MULTIPOLE_ACCEPT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "binomial.h"
@@ -194,8 +194,8 @@ __attribute__((nonnull, pure)) INLINE static int gravity_M2L_accept_symmetric(
 /**
  * Compute the distance above which an M2L kernel is allowed to be used.
  *
- * This uses conservative assumptions to garanty that all the possible cell pair
- * interactions that need a direct interaction are below this distance.
+ * This uses conservative assumptions to guarantee that all the possible cell
+ * pair interactions that need a direct interaction are below this distance.
  *
  * @param props The properties of the gravity scheme.
  * @param size The size of the multipoles (here the cell size).
diff --git a/src/multipole_struct.h b/src/multipole_struct.h
index ffd615cd798aa3963491ad31fb86f6af8f9e5d89..6812259e6b42682106558637c189754ea498785c 100644
--- a/src/multipole_struct.h
+++ b/src/multipole_struct.h
@@ -20,7 +20,7 @@
 #define SWIFT_MULTIPOLE_STRUCT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "timeline.h"
diff --git a/src/neutrino.h b/src/neutrino.h
index c42c61cebabc80a9086e8d1e4e43b68f334e6848..27cd04403bd27fb7dec51353c2e19680bf46dc8a 100644
--- a/src/neutrino.h
+++ b/src/neutrino.h
@@ -20,7 +20,7 @@
 #define SWIFT_NEUTRINO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct neutrino model */
 #include "./neutrino/Default/neutrino.h"
diff --git a/src/neutrino/Default/neutrino.h b/src/neutrino/Default/neutrino.h
index e9fad12f18bc34e7aa4407e5ba7907ad9fffcfce..aaccc5a56b3f7cfd384813ff46d63ab05b276b4b 100644
--- a/src/neutrino/Default/neutrino.h
+++ b/src/neutrino/Default/neutrino.h
@@ -20,7 +20,7 @@
 #define SWIFT_DEFAULT_NEUTRINO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "../../engine.h"
diff --git a/src/neutrino/Default/neutrino_response.c b/src/neutrino/Default/neutrino_response.c
index 511b0a43089f6e25dd75f63e3b4402607ccb85fb..4d841a633c7efb598349652ee33bc05279e4c5ee 100644
--- a/src/neutrino/Default/neutrino_response.c
+++ b/src/neutrino/Default/neutrino_response.c
@@ -18,7 +18,9 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "parser.h"
 
 #ifdef HAVE_LIBGSL
diff --git a/src/neutrino_io.h b/src/neutrino_io.h
index f23d169dc5449ce8423f80f8a037ac0a4a2b1fc4..2c28c33670e95a35062822a7eda49f2e7096ecac 100644
--- a/src/neutrino_io.h
+++ b/src/neutrino_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_NEUTRINO_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct neutrino model */
 #include "./neutrino/Default/neutrino_io.h"
diff --git a/src/neutrino_properties.h b/src/neutrino_properties.h
index 756d4e0c6c1a3bebb3e1849c950b5ab20aca76a1..456614d2672ca5d0a3ccd76de0210f96f8a310d4 100644
--- a/src/neutrino_properties.h
+++ b/src/neutrino_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_NEUTRINO_PROPERTIES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct neutrino model */
 #include "./neutrino/Default/neutrino_properties.h"
diff --git a/src/output_list.c b/src/output_list.c
index 1c93bf6c71bf0027d1ef4aaf61886f44a66139f9..806be846a193764c82d8b6e58043e12bd0cc00d1 100644
--- a/src/output_list.c
+++ b/src/output_list.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "output_list.h"
diff --git a/src/output_list.h b/src/output_list.h
index 2fa7394ad4ce69a18ccfb8c52d15c5c5630c875a..27a345a6f9c4d112d72334e4f0d0f0a294225691 100644
--- a/src/output_list.h
+++ b/src/output_list.h
@@ -20,7 +20,7 @@
 #define SWIFT_OUTPUT_LIST_H
 
 /* Config parameters. */
-#include "config.h"
+#include <config.h>
 
 /* Local headers */
 #include "common_io.h"
diff --git a/src/output_options.c b/src/output_options.c
index 214dc9d101f551202350ede6b21a623914353b27..350a4016bf49157bafd6f8e9ed0fb8a5ff5abfc0 100644
--- a/src/output_options.c
+++ b/src/output_options.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 <stdlib.h>
diff --git a/src/parallel_io.c b/src/parallel_io.c
index 2c505c30369cc4c479a2ec8a1c44ba7fa5d5fa5d..5f79ceb077ad11507162ffa8aacd63574e531b3b 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5)
 
@@ -1262,6 +1262,8 @@ void prepare_file(struct engine* e, const char* fileName,
                      swift_type_count);
   io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT,
                      numParticlesHighWord, swift_type_count);
+  io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total,
+                     swift_type_count);
   double MassTable[swift_type_count] = {0};
   io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count);
   io_write_attribute(h_grp, "InitialMassTable", DOUBLE,
@@ -1324,7 +1326,8 @@ void prepare_file(struct engine* e, const char* fileName,
     if (h_err < 0) error("Error while creating alias for particle group.\n");
 
     /* Write the number of particles as an attribute */
-    io_write_attribute_l(h_grp, "NumberOfParticles", N_total[ptype]);
+    io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]);
+    io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]);
 
     int num_fields = 0;
     struct io_props list[100];
diff --git a/src/parallel_io.h b/src/parallel_io.h
index d86c3058c74afb6304f1845e32e2e5d9b670a4e6..aaac00aa4c8b1153fe844e61b9146843834030cf 100644
--- a/src/parallel_io.h
+++ b/src/parallel_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_PARALLEL_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && defined(WITH_MPI) && defined(HAVE_PARALLEL_HDF5)
 
diff --git a/src/parser.c b/src/parser.c
index 1789a2b4194f8aeff71aa4c11075a330ec36fe80..ae684607017f2f6b5476f14172ee370e56edc2b1 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 /* Needs to be included so that strtok returns char * instead of a int *. */
diff --git a/src/parser.h b/src/parser.h
index f29070dc681e767d03fa3fd5ae856b5b1eaeed26..072bbe03b719be025d2ddf04523caa8f13ef28af 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -21,7 +21,7 @@
 #define SWIFT_PARSER_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers */
 #include <stdio.h>
diff --git a/src/part.c b/src/part.c
index a20defa3af40fcf923dfb1ca58f11f260d545195..aec016bea44387afa5b155e7f1330c23625b3892 100644
--- a/src/part.c
+++ b/src/part.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
diff --git a/src/part.h b/src/part.h
index ed1f914e730e62376d785a9432e4718a802a7a20..dce15ece9df269b51f5ceebefa32e3f9b52abc0b 100644
--- a/src/part.h
+++ b/src/part.h
@@ -20,7 +20,7 @@
 #define SWIFT_PART_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers. */
 #include <stddef.h>
@@ -72,6 +72,7 @@ struct threadpool;
 #include "./hydro/Gizmo/hydro_part.h"
 #define hydro_need_extra_init_loop 0
 #define EXTRA_HYDRO_LOOP
+#define MPI_SYMMETRIC_FORCE_INTERACTION
 #elif defined(SHADOWFAX_SPH)
 #include "./hydro/Shadowswift/hydro_part.h"
 #define hydro_need_extra_init_loop 0
@@ -122,6 +123,8 @@ struct threadpool;
 #include "./black_holes/Default/black_holes_part.h"
 #elif defined(BLACK_HOLES_EAGLE)
 #include "./black_holes/EAGLE/black_holes_part.h"
+#elif defined(BLACK_HOLES_SPIN_JET)
+#include "./black_holes/SPIN_JET/black_holes_part.h"
 #else
 #error "Invalid choice of black hole particle"
 #endif
diff --git a/src/partition.c b/src/partition.c
index 3dfa13e9e99dc1ec56dcdc8690bbf0d090f963ae..9563e08fab4fa04108e7d1a4511cef51f6538bf1 100644
--- a/src/partition.c
+++ b/src/partition.c
@@ -28,7 +28,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers. */
 #include <float.h>
@@ -3002,18 +3002,24 @@ static void check_weights(struct task *tasks, int nr_tasks,
   double refsum = 0.0;
   double sum = 0.0;
   if (vweights) {
+    if (engine_rank == 0) message("checking vertex weight consistency");
+    if (ref_weights_v == NULL)
+      error("vertex partition weights are inconsistent");
     for (int k = 0; k < nr_cells; k++) {
       refsum += ref_weights_v[k];
       sum += weights_v[k];
-    } 
-    if (fabs(sum - refsum) > 1.0) 
-      error("vertex partition weights are not consistent (%f!=%f)", sum, refsum);
+    }
+    if (fabs(sum - refsum) > 1.0) {
+      error("vertex partition weights are not consistent (%f!=%f)", sum,
+            refsum);
+    }
   }
-
   if (eweights) {
+    if (engine_rank == 0) message("checking edge weight consistency");
     refsum = 0.0;
     sum = 0.0;
-    for (int k = 0; k < nedges; k++) {
+    if (ref_weights_e == NULL) error("edge partition weights are inconsistent");
+    for (int k = 0; k < 26 * nr_cells; k++) {
       refsum += ref_weights_e[k];
       sum += weights_e[k];
     }
@@ -3021,7 +3027,7 @@ static void check_weights(struct task *tasks, int nr_tasks,
       error("edge partition weights are not consistent (%f!=%f)", sum, refsum);
     }
   }
-  message("partition weights checked successfully");
+  if (engine_rank == 0) message("partition weights checked successfully");
 }
 #endif
 #endif
diff --git a/src/periodic.h b/src/periodic.h
index 509a204acde71292c71694f90f040825bc81aeed..f0e18288a7526c71112923ef4d0cd6055698b967 100644
--- a/src/periodic.h
+++ b/src/periodic.h
@@ -20,7 +20,7 @@
 #define SWIFT_PERIODIC_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "inline.h"
diff --git a/src/physical_constants.c b/src/physical_constants.c
index 84d17005a79d15d09da5779daae006cea19bc0a0..c4f32892309689d13ec7dfeaf6b67b9e9ea4eead 100644
--- a/src/physical_constants.c
+++ b/src/physical_constants.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "physical_constants.h"
diff --git a/src/physical_constants.h b/src/physical_constants.h
index 7a32fd1513415ff7d28c642efb96e224566f2092..2eaa7d98db51b4afd256e0f717bd634382bad394 100644
--- a/src/physical_constants.h
+++ b/src/physical_constants.h
@@ -26,7 +26,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "parser.h"
diff --git a/src/potential.c b/src/potential.c
index 601ec9b4da4b395a5916f6ea780000567d9e7334..beebdcd17a32255756f76eccb9a6771da9ae7c0d 100644
--- a/src/potential.c
+++ b/src/potential.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "potential.h"
diff --git a/src/potential.h b/src/potential.h
index ef80cedb07a159b21e33389f9d8909a03f8005b5..9011eee11123f0d04883a69119ac6ee86aeb354d 100644
--- a/src/potential.h
+++ b/src/potential.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right external potential definition */
 #if defined(EXTERNAL_POTENTIAL_NONE)
diff --git a/src/potential/constant/potential.h b/src/potential/constant/potential.h
index dcc4f3a28b0dbe3b7ed7e36dcfd361c95f75fc09..de0fb726042ae712c4c200168481ed3c4e2c7b4d 100644
--- a/src/potential/constant/potential.h
+++ b/src/potential/constant/potential.h
@@ -20,7 +20,7 @@
 #define SWIFT_POTENTIAL_CONSTANT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/potential/disc_patch/potential.h b/src/potential/disc_patch/potential.h
index b0f237bd8ec0850670187c23996ee5c889eadde3..d8486ed85fe7eae399e7082e051b41d859bf31d7 100644
--- a/src/potential/disc_patch/potential.h
+++ b/src/potential/disc_patch/potential.h
@@ -21,7 +21,7 @@
 #define SWIFT_DISC_PATCH_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/potential/hernquist/potential.h b/src/potential/hernquist/potential.h
index fa1872bbdf711d388067db6cdd230315faadb59c..144181f432f1a754e2934964c6b7b7e4bfda9cf4 100644
--- a/src/potential/hernquist/potential.h
+++ b/src/potential/hernquist/potential.h
@@ -20,7 +20,7 @@
 #define SWIFT_POTENTIAL_HERNQUIST_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
@@ -73,6 +73,9 @@ struct external_potential {
    * time to get the time steps */
   double timestep_mult;
 
+  /*! Inverse of the sqrt of G*M, a common factor */
+  double sqrtgm_inv;
+
   /*! Mode to use 0 for simplest form of potential purely 1 for idealized
    * galaxies */
   int usedisk;
@@ -92,21 +95,18 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep(
     const struct phys_const* restrict phys_const,
     const struct gpart* restrict g) {
 
-  const float G_newton = phys_const->const_newton_G;
-
   /* Calculate the relative potential with respect to the centre of the
    * potential */
   const float dx = g->x[0] - potential->x[0];
   const float dy = g->x[1] - potential->x[1];
   const float dz = g->x[2] - potential->x[2];
 
-  /* calculate the radius  */
+  /* Calculate the radius  */
   const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
-  const float sqrtgm_inv = 1.f / sqrtf(G_newton * potential->mass);
 
   /* Calculate the circular orbital period */
-  const float period = 2.f * M_PI * sqrtf(r) * potential->al *
-                       (1 + r / potential->al) * sqrtgm_inv;
+  const float period =
+      2.f * M_PI * sqrtf(r) * (potential->al + r) * potential->sqrtgm_inv;
 
   /* Time-step as a fraction of the cirecular orbital time */
   const float time_step = potential->timestep_mult * period;
@@ -211,7 +211,7 @@ static INLINE void potential_init_backend(
     potential->x[2] += s->dim[2] / 2.;
   }
 
-  /* check whether we use the more advanced idealized disk setting */
+  /* Check whether we use the more advanced idealized disk setting */
   potential->usedisk = parser_get_opt_param_int(
       parameter_file, "HernquistPotential:idealizeddisk",
       idealized_disk_default);
@@ -269,7 +269,7 @@ static INLINE void potential_init_backend(
     potential->M200 = M200;
     potential->R200 = R200;
 
-    /* get the concentration from the parameter file */
+    /* Get the concentration from the parameter file */
     potential->c = parser_get_param_double(parameter_file,
                                            "HernquistPotential:concentration");
 
@@ -286,7 +286,7 @@ static INLINE void potential_init_backend(
     const double b = 2. * cc_inv * cc_inv * (log(1. + cc) - cc / (1. + cc));
 
     /* Calculate the Hernquist equivalent scale length */
-    potential->al = (b + sqrt(b)) / (1 - b) * R200;
+    potential->al = R200 * (b + sqrt(b)) / (1 - b);
 
     /* Define R200 inv*/
     const double R200_inv = 1. / R200;
@@ -296,9 +296,9 @@ static INLINE void potential_init_backend(
                                 (potential->R200 + potential->al) * R200_inv *
                                 R200_inv * potential->M200;
 
-    /* Depending on the disk mass and and the bulge mass the halo
-     * gets a different mass, because of this we read the fractions
-     * from the parameter file and calculate the absolute mass*/
+    /* Depending on the disk mass and and the bulge mass, the halo
+     * gets a different mass. Because of this, we read the fractions
+     * from the parameter file and calculate the absolute mass */
     const double diskfraction = parser_get_param_double(
         parameter_file, "HernquistPotential:diskfraction");
     const double bulgefraction = parser_get_param_double(
@@ -318,12 +318,14 @@ static INLINE void potential_init_backend(
       parser_get_param_double(parameter_file, "HernquistPotential:epsilon");
   potential->epsilon2 = epsilon * epsilon;
 
-  /* Compute the minimal time-step. */
-  /* This is the circular orbital time at the softened radius */
+  /* Calculate a common factor in the calculation, i.e. 1/sqrt(GM)*/
   const float sqrtgm = sqrtf(phys_const->const_newton_G * potential->mass);
-  potential->mintime = 2.f * sqrtf(epsilon) * potential->al * M_PI *
-                       (1. + epsilon / potential->al) / sqrtgm *
-                       potential->timestep_mult;
+  potential->sqrtgm_inv = 1. / sqrtgm;
+
+  /* Compute the minimal time-step. */
+  /* This is a fraction of the circular orbital time at the softened radius */
+  potential->mintime = potential->timestep_mult * 2.f * sqrtf(epsilon) * M_PI *
+                       (potential->al + epsilon) * potential->sqrtgm_inv;
 }
 
 /**
diff --git a/src/potential/hernquist_sdmh05/potential.h b/src/potential/hernquist_sdmh05/potential.h
index b03e766fb325a5e13b0695dc70f301a47e4d1bf0..8755d24ef939c70c012a1f00fd693e6fc5b8668b 100644
--- a/src/potential/hernquist_sdmh05/potential.h
+++ b/src/potential/hernquist_sdmh05/potential.h
@@ -20,7 +20,7 @@
 #define SWIFT_POTENTIAL_HERNQUIST_SDMH05_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
@@ -63,6 +63,9 @@ struct external_potential {
   /*! Time-step condition pre-factor, is multiplied times the circular orbital
    * time to get the time steps */
   double timestep_mult;
+
+  /* Additional common parameter inverse of sqrt(GM)*/
+  double sqrtgm_inv;
 };
 
 /**
@@ -79,8 +82,6 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep(
     const struct phys_const* restrict phys_const,
     const struct gpart* restrict g) {
 
-  const float G_newton = phys_const->const_newton_G;
-
   /* Calculate the relative potential with respect to the centre of the
    * potential */
   const float dx = g->x[0] - potential->x[0];
@@ -89,11 +90,10 @@ __attribute__((always_inline)) INLINE static float external_gravity_timestep(
 
   /* calculate the radius  */
   const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
-  const float sqrtgm_inv = 1.f / sqrtf(G_newton * potential->mass);
 
   /* Calculate the circular orbital period */
-  const float period = 2.f * M_PI * sqrtf(r) * potential->al *
-                       (1 + r / potential->al) * sqrtgm_inv;
+  const float period =
+      2.f * M_PI * sqrtf(r) * (potential->al + r) * potential->sqrtgm_inv;
 
   /* Time-step as a fraction of the cirecular orbital time */
   const float time_step = potential->timestep_mult * period;
@@ -254,7 +254,7 @@ static INLINE void potential_init_backend(
     /* message("M200 = %g, R200 = %g, V200 = %g", M200, R200, V200); */
     /* message("H0 = %g", H0); */
 
-    /* get the concentration from the parameter file */
+    /* Get the concentration from the parameter file */
     const double concentration = parser_get_param_double(
         parameter_file, "HernquistPotential:concentration");
 
@@ -262,12 +262,12 @@ static INLINE void potential_init_backend(
     const double RS = R200 / concentration;
 
     /* Calculate the Hernquist equivalent scale length */
-    potential->al = RS * sqrt(1. * (log(1. + concentration) -
+    potential->al = RS * sqrt(2. * (log(1. + concentration) -
                                     concentration / (1. + concentration)));
 
-    /* Depending on the disk mass and and the bulge mass the halo
-     * gets a different mass, because of this we read the fractions
-     * from the parameter file and calculate the absolute mass*/
+    /* Depending on the disk mass and the bulge mass, the halo
+     * gets a different mass. Because of this, we read the fractions
+     * from the parameter file and calculate the absolute mass */
     const double diskfraction = parser_get_param_double(
         parameter_file, "HernquistPotential:diskfraction");
     const double bulgefraction = parser_get_param_double(
@@ -287,12 +287,14 @@ static INLINE void potential_init_backend(
       parser_get_param_double(parameter_file, "HernquistPotential:epsilon");
   potential->epsilon2 = epsilon * epsilon;
 
-  /* Compute the minimal time-step. */
-  /* This is the circular orbital time at the softened radius */
+  /* Calculate a common factor in the calculation, i.e. 1/sqrt(GM)*/
   const float sqrtgm = sqrtf(phys_const->const_newton_G * potential->mass);
-  potential->mintime = 2.f * sqrtf(epsilon) * potential->al * M_PI *
-                       (1. + epsilon / potential->al) / sqrtgm *
-                       potential->timestep_mult;
+  potential->sqrtgm_inv = 1. / sqrtgm;
+
+  /* Compute the minimal time-step. */
+  /* This is a fraction of the circular orbital time at the softened radius */
+  potential->mintime = potential->timestep_mult * 2.f * sqrtf(epsilon) * M_PI *
+                       (potential->al + epsilon) * potential->sqrtgm_inv;
 }
 
 /**
@@ -304,7 +306,7 @@ static inline void potential_print_backend(
     const struct external_potential* potential) {
 
   message(
-      "external potential is 'hernquist Springel, Di Matteo & Herquist 2005' "
+      "external potential is 'hernquist Springel, Di Matteo & Hernquist 2005' "
       "with properties are (x,y,z) = (%e, %e, %e), mass = %e scale length = %e "
       ", minimum time = %e timestep multiplier = %e",
       potential->x[0], potential->x[1], potential->x[2], potential->mass,
diff --git a/src/potential/isothermal/potential.h b/src/potential/isothermal/potential.h
index 04cc2ef772359c0645095e6284f011f970a5e767..408561116bcc75c9d947cbb4433c1f28d1ad578d 100644
--- a/src/potential/isothermal/potential.h
+++ b/src/potential/isothermal/potential.h
@@ -22,7 +22,7 @@
 #define SWIFT_POTENTIAL_ISOTHERMAL_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/potential/nfw/potential.h b/src/potential/nfw/potential.h
index e09251cd6298ff99bc54d40d301440955825d152..f3ea96b349696a3a46e693ed32016f34335d8d15 100644
--- a/src/potential/nfw/potential.h
+++ b/src/potential/nfw/potential.h
@@ -21,7 +21,7 @@
 #define SWIFT_POTENTIAL_NFW_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
@@ -204,7 +204,7 @@ external_gravity_get_potential_energy(
   const float term1 = -potential->M_200_times_log_c200_term_inv / r;
   const float term2 = logf(1.0f + r / potential->r_s);
 
-  return term1 * term2;
+  return phys_const->const_newton_G * term1 * term2;
 }
 
 /**
diff --git a/src/potential/nfw_mn/potential.h b/src/potential/nfw_mn/potential.h
index 4b87da912edf9b2aca8e8dfed3c637af11098272..c32ffe9130b7b4623c9ff0d331257449b0ddb2a8 100644
--- a/src/potential/nfw_mn/potential.h
+++ b/src/potential/nfw_mn/potential.h
@@ -21,7 +21,7 @@
 #define SWIFT_POTENTIAL_NFW_MN_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
@@ -216,7 +216,7 @@ external_gravity_get_potential_energy(
   const float mn_term = potential->Rdisk + sqrtf(potential->Zdisk + dz * dz);
   const float mn_pot = -potential->Mdisk / sqrtf(R2 + mn_term * mn_term);
 
-  return term1 * term2 + mn_pot;
+  return phys_const->const_newton_G * (term1 * term2 + mn_pot);
 }
 
 /**
diff --git a/src/potential/none/potential.h b/src/potential/none/potential.h
index a12ff7d0bfe61d6ff86846485968d2a40d546e1a..9e1a83e5a0ba94b8f6f9c66a8fdf6f25a5018436 100644
--- a/src/potential/none/potential.h
+++ b/src/potential/none/potential.h
@@ -20,7 +20,7 @@
 #define SWIFT_POTENTIAL_NONE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/potential/point_mass/potential.h b/src/potential/point_mass/potential.h
index 74e78f07d0911c8b5ae5ca1697f6cc9f28ff4ff3..27f5a08afab229da1126a528c8851c5e16103f16 100644
--- a/src/potential/point_mass/potential.h
+++ b/src/potential/point_mass/potential.h
@@ -21,7 +21,7 @@
 #define SWIFT_POTENTIAL_POINT_MASS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/potential/point_mass_softened/potential.h b/src/potential/point_mass_softened/potential.h
index 6ff7d20033c57dc9e0e106b7b932c9b611d651f7..1e710c9c04ae7d4885aa1101c70fd59c37e4c51b 100644
--- a/src/potential/point_mass_softened/potential.h
+++ b/src/potential/point_mass_softened/potential.h
@@ -22,7 +22,7 @@
 #define SWIFT_POTENTIAL_POINT_MASS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/potential/sine_wave/potential.h b/src/potential/sine_wave/potential.h
index f160652ddcf184227c5a83273be55b529a7b79f6..b759c340afcfc0e41013954e9a7258bfe4848c01 100644
--- a/src/potential/sine_wave/potential.h
+++ b/src/potential/sine_wave/potential.h
@@ -20,7 +20,7 @@
 #define SWIFT_SINE_WAVE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/power_spectrum.c b/src/power_spectrum.c
index 304513662e181b1d6bb4f8c927ccdd9f4e3ef4d7..0b947fee9e65ecb6f55ac044b526bd1bde80f2e7 100644
--- a/src/power_spectrum.c
+++ b/src/power_spectrum.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef WITH_MPI
 #include <mpi.h>
@@ -28,6 +28,8 @@
 #include <fftw3.h>
 #endif
 
+/* Standard headers */
+#include <stdio.h>
 #include <string.h>
 
 /* This object's header. */
@@ -114,6 +116,8 @@ INLINE static const char* get_powtype_filename(const enum power_type type) {
 void power_init(struct power_spectrum_data* p, struct swift_params* params,
                 int nr_threads) {
 
+#ifdef HAVE_FFTW
+
   /* Get power spectrum parameters */
   p->Ngrid = parser_get_opt_param_int(params, "PowerSpectrum:grid_side_length",
                                       power_data_default_grid_side_length);
@@ -223,8 +227,14 @@ void power_init(struct power_spectrum_data* p, struct swift_params* params,
     safe_checkdir("power_spectra", /*create=*/1);
     safe_checkdir("power_spectra/foldings", /*create=*/1);
   }
+
+#else
+  error("Trying to initialize the PS code without FFTW present!");
+#endif
 }
 
+#ifdef HAVE_FFTW
+
 /**
  * @brief Shared information for shot noise to be used by all the threads in the
  * pool.
@@ -464,13 +474,11 @@ __attribute__((always_inline)) INLINE static void TSC_set(
 
   const double lx = 0.5 * (0.5 - dx) * (0.5 - dx); /* left side, dist 1 + dx  */
   const double mx = 0.75 - dx * dx;                /* center, dist |dx|  */
-  const double rx =
-      0.5 * (0.5 + dx) * (0.5 + dx); /* right side, dist 1 - dx  */
+  const double rx = 0.5 * (0.5 + dx) * (0.5 + dx); /* right side, dist 1 - dx */
 
   const double ly = 0.5 * (0.5 - dy) * (0.5 - dy); /* left side, dist 1 + dy  */
   const double my = 0.75 - dy * dy;                /* center, dist |dy|  */
-  const double ry =
-      0.5 * (0.5 + dy) * (0.5 + dy); /* right side, dist 1 - dy  */
+  const double ry = 0.5 * (0.5 + dy) * (0.5 + dy); /* right side, dist 1 - dy */
 
   const double lz = 0.5 * (0.5 - dz) * (0.5 - dz); /* left side, dist 1 + dz  */
   const double mz = 0.75 - dz * dz;                /* center, dist |dz|  */
@@ -1355,9 +1363,12 @@ void power_spectrum(const enum power_type type1, const enum power_type type2,
   pow_data->powgridft = NULL;
 }
 
+#endif /* HAVE_FFTW */
+
 void calc_all_power_spectra(struct power_spectrum_data* pow_data,
                             const struct space* s, struct threadpool* tp,
                             const int verbose) {
+#ifdef HAVE_FFTW
 
   const ticks tic = getticks();
 
@@ -1375,9 +1386,13 @@ void calc_all_power_spectra(struct power_spectrum_data* pow_data,
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
+#else
+  error("Can't use the PS code without FFTW present!");
+#endif /* HAVE_FFTW */
 }
 
 void power_clean(struct power_spectrum_data* pow_data) {
+#ifdef HAVE_FFTW
   fftw_destroy_plan(pow_data->fftplanpow);
   fftw_destroy_plan(pow_data->fftplanpow2);
   free(pow_data->types2);
@@ -1386,6 +1401,9 @@ void power_clean(struct power_spectrum_data* pow_data) {
   // Probably already done for PM at this point
   fftw_cleanup_threads();
 #endif
+#else
+  error("Can't use the PS code without FFTW present!");
+#endif /* HAVE_FFTW */
 }
 
 /**
@@ -1397,12 +1415,14 @@ void power_clean(struct power_spectrum_data* pow_data) {
  */
 void power_spectrum_struct_dump(const struct power_spectrum_data* p,
                                 FILE* stream) {
+#ifdef HAVE_FFTW
   restart_write_blocks((void*)p, sizeof(struct power_spectrum_data), 1, stream,
                        "power spectrum data", "power spectrum data");
   restart_write_blocks(p->types1, p->spectrumcount, sizeof(enum power_type),
                        stream, "power types 1", "power types 1");
   restart_write_blocks(p->types2, p->spectrumcount, sizeof(enum power_type),
                        stream, "power types 2", "power types 2");
+#endif
 }
 
 /**
@@ -1414,6 +1434,7 @@ void power_spectrum_struct_dump(const struct power_spectrum_data* p,
  */
 void power_spectrum_struct_restore(struct power_spectrum_data* p,
                                    FILE* stream) {
+#ifdef HAVE_FFTW
   restart_read_blocks((void*)p, sizeof(struct power_spectrum_data), 1, stream,
                       NULL, "power spectrum data");
   p->types1 =
@@ -1465,4 +1486,5 @@ void power_spectrum_struct_restore(struct power_spectrum_data* p,
   fftw_free(p->powgrid2);
   p->powgrid2 = NULL;
   p->powgridft2 = NULL;
+#endif /* HAVE_FFTW */
 }
diff --git a/src/power_spectrum.h b/src/power_spectrum.h
index c3565ddd3bfb1c33e434774ec1e01e7e409798c5..97b7feefde6225e590a05f0331b607838a5a24bb 100644
--- a/src/power_spectrum.h
+++ b/src/power_spectrum.h
@@ -20,7 +20,7 @@
 #define SWIFT_POWER_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #ifdef HAVE_FFTW
@@ -100,9 +100,6 @@ struct power_spectrum_data {
 
 void power_init(struct power_spectrum_data* p, struct swift_params* params,
                 int nr_threads);
-void power_spectrum(const enum power_type type1, const enum power_type type2,
-                    struct power_spectrum_data* pow_data, const struct space* s,
-                    struct threadpool* tp, const int verbose);
 void calc_all_power_spectra(struct power_spectrum_data* pow_data,
                             const struct space* s, struct threadpool* tp,
                             const int verbose);
diff --git a/src/pressure_floor.h b/src/pressure_floor.h
index 76376129307a2f46ebcde531988cddcb7805ba0f..07f46c6e26434ca0b6acd061ba772c4cc019f2ed 100644
--- a/src/pressure_floor.h
+++ b/src/pressure_floor.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "common_io.h"
diff --git a/src/pressure_floor_debug.h b/src/pressure_floor_debug.h
index b59ddb3368e8281528dcb7cf05a168bdab66ae42..57b4e628b7fe3b0a40b18928d6abfed14032dff8 100644
--- a/src/pressure_floor_debug.h
+++ b/src/pressure_floor_debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_PRESSURE_FLOOR_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right pressure floor definition */
 #if defined(PRESSURE_FLOOR_NONE)
diff --git a/src/pressure_floor_iact.h b/src/pressure_floor_iact.h
index 258bf25a5a8429668c30b1d78532159ff32916b9..0a1ffc56fa77783ccebe3474820df908af90cf33 100644
--- a/src/pressure_floor_iact.h
+++ b/src/pressure_floor_iact.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right pressure floor definition */
 #if defined(PRESSURE_FLOOR_NONE)
diff --git a/src/pressure_floor_struct.h b/src/pressure_floor_struct.h
index e5538b37e03258fab45f9a2cb067f571ff7d16fd..9c3f21cffc244e5952299b4b1592abfef3bb6d74 100644
--- a/src/pressure_floor_struct.h
+++ b/src/pressure_floor_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right pressure floor definition */
 #if defined(PRESSURE_FLOOR_NONE)
diff --git a/src/profiler.c b/src/profiler.c
index 2089729256ac1e4f4a4d77dce858adfbfc5a7cf8..c75caf487de44763b052e2b9fb77de049571473c 100644
--- a/src/profiler.c
+++ b/src/profiler.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/profiler.h b/src/profiler.h
index 911d4747912d64dd46d2cf59369670ff26a92012..54261aa5bd146efcfabc3f0c9ecab74853b565fe 100644
--- a/src/profiler.h
+++ b/src/profiler.h
@@ -20,7 +20,7 @@
 #define SWIFT_PROFILER_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "engine.h"
diff --git a/src/proxy.c b/src/proxy.c
index b5d56db69490aa8d57fcb54c671e7fc290e00f60..4a5ab5d6ced6c282e1d856c3277a0255094aa05a 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/queue.c b/src/queue.c
index 2a6c6cd8796e6fc5cdf0598d1fa6bde177db3ec4..30601667cdc5ad3fde6ff1cfc9468187df429c6c 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdio.h>
diff --git a/src/random.h b/src/random.h
index d2015136aa3e88412517ec667df64edb46a38891..530233b68fb4b32e2961e6112c90f50c778f7e3b 100644
--- a/src/random.h
+++ b/src/random.h
@@ -21,7 +21,7 @@
 #define SWIFT_RANDOM_H
 
 /* Code configuration */
-#include "../config.h"
+#include <config.h>
 
 /* Standard header */
 #include <stdint.h>
@@ -29,6 +29,9 @@
 #include <string.h>
 #include <sys/types.h>
 
+/* Local headers */
+#include "sincos.h"
+
 /**
  * @brief The categories of random number generated.
  *
@@ -40,10 +43,6 @@
  * 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:
- * 193877777
- * 303595777
  */
 enum random_number_type {
   random_number_star_formation = 0LL,
@@ -61,6 +60,8 @@ enum random_number_type {
   random_number_BH_feedback = 1640531371LL,
   random_number_BH_swallow = 4947009007LL,
   random_number_BH_reposition = 59969537LL,
+  random_number_BH_spin = 193877777LL,
+  random_number_BH_kick = 303595777LL,
   random_number_snapshot_sampling = 6561001LL,
   random_number_stellar_winds = 5947309451LL,
   random_number_HII_regions = 8134165677LL,
@@ -290,5 +291,112 @@ INLINE static int random_poisson(const int64_t id, const double lambda,
 
   return k - 1;
 }
+/**
+ * @brief Generates a unit vector within a cone.
+ *
+ * We draw a random unit vector around the unit vector a = (a0, a1, a2),
+ * such that it is uniformly distributed in solid angle from 0 opening
+ * angle (perfectly aligned with the vector a) to a maximum of
+ * opening_angle radians.
+ *
+ * @param id_bh The ID of the (BH) particle which is doing the jet feedback.
+ * @param id_gas The ID of the gas particle being kicked by jet feedback.
+ * @param ti_current The time (on the time-line) for which to generate the
+ *                    random kick direction.
+ * @param type The #random_number_type to generate.
+ * @param opening_angle Opening angle of the cone.
+ * @param a Reference direction that defines the cone.
+ * @param rand_cone_direction Return value.
+ */
+INLINE static void random_direction_in_cone(
+    const int64_t id_bh, const int64_t id_gas, const integertime_t ti_current,
+    const enum random_number_type type, const float opening_angle,
+    const float a[3], float rand_cone_direction[3]) {
+
+  /* We want to draw a random unit vector from a cone around the unit
+   * vector a = (a0, a1, a2). We do this in a frame x'y'z', where the z'
+   * axis is aligned with the a vector, i.e. the a vector is one of its
+   * basis vectors. The choice of the other two axes, x' and y', is
+   * arbitrary. We can use any two unit vectors that are orthogonal to the
+   * a vector. These two vectors can be obtained using cross products.
+   * However, we need to first start from an initial vector that is not
+   * perfectly aligned with a. The choice of this vector is also arbitrary;
+   * we choose a vector that is perfectly aligned with the smallest
+   * component of the spin vector a.  */
+  float init_unit[3] = {0.f, 0.f, 1.f};
+
+  /* Find which of the x, y or z is the smallest components of a. We also
+   * need to take into account the possibility that two of the components
+   * are equal. In this case it doesn't matter which of the two we
+   * choose. */
+  const float a0_abs = fabsf(a[0]);
+  const float a1_abs = fabsf(a[1]);
+  const float a2_abs = fabsf(a[2]);
+  if (((a0_abs < a1_abs) && (a0_abs < a2_abs)) ||
+      ((a0_abs == a1_abs) && (a0_abs < a2_abs))) {
+    init_unit[0] = 1.f;
+  } else if (((a1_abs < a2_abs) && (a1_abs < a0_abs)) ||
+             ((a1_abs == a2_abs) && (a1_abs < a0_abs))) {
+    init_unit[1] = 1.f;
+  } else if (((a2_abs < a0_abs) && (a2_abs < a1_abs)) ||
+             ((a2_abs == a0_abs) && (a2_abs < a1_abs))) {
+    init_unit[2] = 1.f;
+  }
+
+  /* Using this vector and the a vector, we can find the first basis
+   * vector x (alongside a) that will also be orthogonal to a, by using a
+   * cross product, such that x = init_unit cross a. This vector needs to be
+   * normalized to get an actual unit vector. */
+  float basis_vec_x[3];
+  basis_vec_x[0] = init_unit[1] * a[2] - init_unit[2] * a[1];
+  basis_vec_x[1] = init_unit[2] * a[0] - init_unit[0] * a[2];
+  basis_vec_x[2] = init_unit[0] * a[1] - init_unit[1] * a[0];
+  const float basis_vec_x_magn =
+      sqrtf(basis_vec_x[0] * basis_vec_x[0] + basis_vec_x[1] * basis_vec_x[1] +
+            basis_vec_x[2] * basis_vec_x[2]);
+  basis_vec_x[0] = basis_vec_x[0] / basis_vec_x_magn;
+  basis_vec_x[1] = basis_vec_x[1] / basis_vec_x_magn;
+  basis_vec_x[2] = basis_vec_x[2] / basis_vec_x_magn;
+
+  /* The other basis vector, y, follows as x cross a. It is already
+   * normalized since x and a are orthogonal. */
+  const float basis_vec_y[3] = {basis_vec_x[1] * a[2] - basis_vec_x[2] * a[1],
+                                basis_vec_x[2] * a[0] - basis_vec_x[0] * a[2],
+                                basis_vec_x[0] * a[1] - basis_vec_x[1] * a[0]};
+
+  /* Draw a random cosine confined to the range [cos(opening_angle), 1] */
+  const float rand_cos_theta =
+      1.f - (1.f - cosf(opening_angle)) *
+                random_unit_interval(id_bh + id_gas, ti_current, type);
+
+  /* Get the corresponding sine */
+  const float rand_sin_theta =
+      sqrtf(max(0.f, (1.f - rand_cos_theta) * (1.f + rand_cos_theta)));
+
+  /* Get a random equitorial angle from [0, 180] deg */
+  const float rand_phi =
+      ((float)(2. * M_PI)) *
+      random_unit_interval((id_bh + id_gas) * (id_bh + id_gas), ti_current,
+                           type);
+
+  float rand_cos_phi, rand_sin_phi;
+  sincosf(rand_phi, &rand_sin_phi, &rand_cos_phi);
+
+  /* We now calculate the direction of the final vector by picking a random
+   * direction within a cone around a, with basis_vec_x and basis_vec_y
+   * playing the role of x and y unit vectors, and a playing the role of z
+   * unit vector. In other words, in vector notation rand_cone_direction =
+   * sin(theta)cos(phi) * basis_vec_x + sin(theta)sin(phi) * basis_vec_y
+   * + cos(theta) * a . */
+  rand_cone_direction[0] = rand_sin_theta * rand_cos_phi * basis_vec_x[0] +
+                           rand_sin_theta * rand_sin_phi * basis_vec_y[0] +
+                           rand_cos_theta * a[0],
+  rand_cone_direction[1] = rand_sin_theta * rand_cos_phi * basis_vec_x[1] +
+                           rand_sin_theta * rand_sin_phi * basis_vec_y[1] +
+                           rand_cos_theta * a[1],
+  rand_cone_direction[2] = rand_sin_theta * rand_cos_phi * basis_vec_x[2] +
+                           rand_sin_theta * rand_sin_phi * basis_vec_y[2] +
+                           rand_cos_theta * a[2];
+}
 
 #endif /* SWIFT_RANDOM_H */
diff --git a/src/restart.c b/src/restart.c
index 3e0b5e8c4477d359f33decf4271f21904a43406e..9b166a5670f0048761226eace6d947dda5a495de 100644
--- a/src/restart.c
+++ b/src/restart.c
@@ -23,7 +23,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers. */
 #include "engine.h"
diff --git a/src/riemann.h b/src/riemann.h
index aca97de8bb43854565fdf0765af70ecfc548a460..dbc44bc8d25548623c35c13131fdc1dfa89450f2 100644
--- a/src/riemann.h
+++ b/src/riemann.h
@@ -20,7 +20,7 @@
 #define SWIFT_RIEMANN_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(RIEMANN_SOLVER_EXACT)
 
diff --git a/src/riemann/riemann_hllc.h b/src/riemann/riemann_hllc.h
index a34b907ba5c6142755f2c4107d7b456393c12b42..da87dbf1c81c5567600dbe0c9e22377b96f6434a 100644
--- a/src/riemann/riemann_hllc.h
+++ b/src/riemann/riemann_hllc.h
@@ -54,8 +54,8 @@ __attribute__((always_inline)) INLINE static void riemann_solve_for_flux(
   /* STEP 0: obtain velocity in interface frame */
   const float uL = WL[1] * n[0] + WL[2] * n[1] + WL[3] * n[2];
   const float uR = WR[1] * n[0] + WR[2] * n[1] + WR[3] * n[2];
-  const float rhoLinv = 1.0f / WL[0];
-  const float rhoRinv = 1.0f / WR[0];
+  const float rhoLinv = (WL[0] > 0.0f) ? 1.0f / WL[0] : 0.0f;
+  const float rhoRinv = (WR[0] > 0.0f) ? 1.0f / WR[0] : 0.0f;
   const float aL = sqrtf(hydro_gamma * WL[4] * rhoLinv);
   const float aR = sqrtf(hydro_gamma * WR[4] * rhoRinv);
 
diff --git a/src/row_major_id.h b/src/row_major_id.h
index 206e7f2d086edfbb4582d447d47f7c75c3fec9f3..68655cb5421790ad10278f56cf38527c5c767db4 100644
--- a/src/row_major_id.h
+++ b/src/row_major_id.h
@@ -20,7 +20,7 @@
 #define SWIFT_ROW_MAJOR_ID_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "inline.h"
diff --git a/src/rt.h b/src/rt.h
index 8d892da1ede65a241bd768f0069a164c7c8b9c0d..b64506d7bbc46e9c422a7a28659f51199657e17d 100644
--- a/src/rt.h
+++ b/src/rt.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right RT definition */
 #if defined(RT_NONE)
diff --git a/src/rt/GEAR/rt.h b/src/rt/GEAR/rt.h
index 6e51abd6a889476b29327e58a844a6059e456a7d..92d8c3d11e954033963d805c573de9b64c939b20 100644
--- a/src/rt/GEAR/rt.h
+++ b/src/rt/GEAR/rt.h
@@ -24,7 +24,7 @@
 #include "rt_gradients.h"
 #include "rt_properties.h"
 /* #include "rt_slope_limiters_cell.h" [> skipped for now <] */
-#include "rt_stellar_emission_rate.h"
+#include "rt_stellar_emission_model.h"
 #include "rt_thermochemistry.h"
 
 #include <float.h>
@@ -66,14 +66,35 @@ rt_compute_stellar_emission_rate(struct spart* restrict sp, double time,
   if (time == 0.l) {
     /* if function is called before the first actual step, time is still
      * at zero unless specified otherwise in parameter file.*/
-    star_age = dt;
+    /* We're going to need the star age later for more sophistiscated models,
+     * but for now the compiler won't let me get away with keeping this here,
+     * so keep it as a comment. */
+    /* star_age = dt; */
   }
 
+  /* TODO: this is for later, when we use more sophisticated models. */
   /* now get the emission rates */
-  double star_age_begin_of_step = star_age - dt;
-  star_age_begin_of_step = max(0.l, star_age_begin_of_step);
-  rt_set_stellar_emission_rate(sp, star_age_begin_of_step, star_age, rt_props,
-                               phys_const, internal_units);
+  /* double star_age_begin_of_step = star_age - dt; */
+  /* star_age_begin_of_step = max(0.l, star_age_begin_of_step); */
+
+  double emission_this_step[RT_NGROUPS];
+  for (int g = 0; g < RT_NGROUPS; g++) emission_this_step[g] = 0.;
+
+  if (rt_props->stellar_emission_model == rt_stellar_emission_model_const) {
+    rt_get_emission_this_step_const(emission_this_step,
+                                    rt_props->stellar_const_emission_rates, dt);
+  } else if (rt_props->stellar_emission_model ==
+             rt_stellar_emission_model_IlievTest) {
+    rt_get_emission_this_step_IlievTest(
+        emission_this_step, sp->mass, dt, rt_props->photon_number_integral,
+        rt_props->average_photon_energy, phys_const, internal_units);
+  } else {
+    error("Unknown stellar emission rate model %d",
+          rt_props->stellar_emission_model);
+  }
+
+  for (int g = 0; g < RT_NGROUPS; g++)
+    sp->rt_data.emission_this_step[g] = emission_this_step[g];
 }
 
 /**
@@ -87,11 +108,10 @@ __attribute__((always_inline)) INLINE static void rt_init_part(
     struct part* restrict p) {}
 
 /**
- * @brief Reset of the RT hydro particle data not related to the density.
+ * @brief Reset the RT hydro particle data not related to the hydro density.
  * Note: during initalisation (space_init), rt_reset_part and rt_init_part
- * are both called individually. Also, if debugging checks are active, an
- * extra call to rt_reset_part is made in
- * space_convert_rt_quantities_after_zeroth_step().
+ * are both called individually. To reset RT data needed in each RT sub-cycle,
+ * use rt_reset_part_each_subcycle().
  *
  * @param p particle to work on
  * @param cosmo Cosmology.
@@ -103,15 +123,21 @@ __attribute__((always_inline)) INLINE static void rt_reset_part(
   /* reset this here as well as in the rt_debugging_checks_end_of_step()
    * routine to test task dependencies are done right */
   p->rt_data.debug_iact_stars_inject = 0;
+  p->rt_data.debug_nsubcycles = 0;
+  p->rt_data.debug_kicked = 0;
+#endif
+}
 
-  p->rt_data.debug_calls_iact_gradient_interaction = 0;
-  p->rt_data.debug_calls_iact_transport_interaction = 0;
+/**
+ * @brief Reset RT particle data which needs to be reset each sub-cycle.
+ *
+ * @param p the particle to work on
+ */
+__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle(
+    struct part* restrict p) {
 
-  p->rt_data.debug_kicked = 0;
-  p->rt_data.debug_injection_done = 0;
-  p->rt_data.debug_gradients_done = 0;
-  p->rt_data.debug_transport_done = 0;
-  p->rt_data.debug_thermochem_done = 0;
+#ifdef SWIFT_RT_DEBUG_CHECKS
+  rt_debugging_reset_each_subcycle(p);
 #endif
 
   rt_gradients_init(p);
@@ -136,41 +162,13 @@ __attribute__((always_inline)) INLINE static void rt_first_init_part(
   rt_init_part(p);
   rt_reset_part(p, cosmo);
   rt_part_reset_mass_fluxes(p);
+  rt_reset_part_each_subcycle(p);
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
   p->rt_data.debug_radiation_absorbed_tot = 0ULL;
 #endif
 }
 
-/**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the particle density and time step to be known.
- *
- * @param p particle to work on
- * @param rt_props RT properties struct
- * @param cosmo #cosmology data structure.
- */
-__attribute__((always_inline)) INLINE static void
-rt_init_part_after_zeroth_step(struct part* restrict p,
-                               const struct rt_props* rt_props,
-                               const struct cosmology* restrict cosmo) {
-
-#ifdef SWIFT_RT_DEBUG_CHECKS
-  /* If we're running with debugging checks on, reset debugging
-   * counters and flags in particular after the zeroth step so
-   * that the checks work as intended. */
-  rt_init_part(p);
-  rt_reset_part(p, cosmo);
-  /* Since the inject_prep has been moved to the density loop, the
-   * initialization at startup is messing with the total counters for stars
-   * because the density is called, but not the force-and-kick tasks. So reset
-   * the total counters here as well so that they will match the star counters.
-   */
-  p->rt_data.debug_radiation_absorbed_tot = 0ULL;
-#endif
-}
-
 /**
  * @brief Initialisation of the RT density loop related star particle data.
  * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart
@@ -205,9 +203,7 @@ __attribute__((always_inline)) INLINE static void rt_init_spart(
 /**
  * @brief Reset of the RT star particle data not related to the density.
  * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart
- * are both called individually. Also, if debugging checks are active, an
- * extra call to rt_reset_spart is made in
- * space_convert_rt_quantities_after_zeroth_step()
+ * are both called individually.
  *
  * @param sp star particle to work on
  */
@@ -237,39 +233,6 @@ __attribute__((always_inline)) INLINE static void rt_first_init_spart(
 #endif
 }
 
-/**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the star density and time step to be known.
- * @param sp star particle to work on
- * @param time current system time
- * @param star_age age of the star *at the end of the step*
- * @param dt star time step
- * @param rt_props RT properties struct
- * @param phys_const physical constants struct
- * @param internal_units struct holding internal units
- */
-__attribute__((always_inline)) INLINE static void
-rt_init_star_after_zeroth_step(struct spart* restrict sp, double time,
-                               double star_age, double dt,
-                               const struct rt_props* rt_props,
-                               const struct phys_const* phys_const,
-                               const struct unit_system* internal_units) {
-
-#ifdef SWIFT_RT_DEBUG_CHECKS
-  /* If we're running with debugging checks on, reset debugging
-   * counters and flags in particular after the zeroth step so
-   * that the checks work as intended. */
-  rt_init_spart(sp);
-  rt_reset_spart(sp);
-  /* Since the inject_prep has been moved to the density loop, the
-   * initialization at startup is messing with the total counters because
-   * the density is called, but not the force-and-kick tasks. So reset
-   * the total counters here as well. */
-  sp->rt_data.debug_radiation_emitted_tot = 0ULL;
-#endif
-}
-
 /**
  * @brief Split the RT data of a particle into n pieces
  *
@@ -385,9 +348,14 @@ __attribute__((always_inline)) INLINE static float rt_compute_timestep(
   float dt = psize * rt_params.reduced_speed_of_light_inverse *
              rt_props->CFL_condition;
 
-  /* TODO: Add cooling time? */
-  float dt_cool = rt_tchem_get_tchem_time(p, xp, rt_props, cosmo, hydro_props,
-                                          phys_const, us);
+  if (rt_props->skip_thermochemistry) return dt;
+
+  float dt_cool = FLT_MAX;
+  if (rt_props->f_limit_cooling_time > 0.f)
+    /* Note: cooling time may be negative if the gas is being heated */
+    dt_cool = rt_props->f_limit_cooling_time *
+              rt_tchem_get_tchem_time(p, xp, rt_props, cosmo, hydro_props,
+                                      phys_const, us);
 
   return min(dt, fabsf(dt_cool));
 }
@@ -445,9 +413,8 @@ __attribute__((always_inline)) INLINE static void rt_finalise_injection(
     struct part* restrict p, struct rt_props* props) {
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  if (p->rt_data.debug_kicked != 1)
-    error("called rt_ghost1 when particle %lld is unkicked (count=%d)", p->id,
-          p->rt_data.debug_kicked);
+  rt_debug_sequence_check(p, 1, "rt_ghost1/rt_finalise_injection");
+
   p->rt_data.debug_injection_done += 1;
 #endif
 
@@ -468,15 +435,7 @@ __attribute__((always_inline)) INLINE static void rt_end_gradient(
     struct part* restrict p, const struct cosmology* cosmo) {
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  if (p->rt_data.debug_kicked != 1)
-    error("called finalise gradient when particle %lld is unkicked (count=%d)",
-          p->id, p->rt_data.debug_kicked);
-
-  if (p->rt_data.debug_injection_done != 1)
-    error(
-        "Called finalise gradient on particle %lld"
-        "where injection_done count = %d",
-        p->id, p->rt_data.debug_injection_done);
+  rt_debug_sequence_check(p, 2, __func__);
 
   if (p->rt_data.debug_calls_iact_gradient_interaction == 0)
     message(
@@ -502,21 +461,7 @@ __attribute__((always_inline)) INLINE static void rt_finalise_transport(
     const struct cosmology* restrict cosmo) {
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  if (p->rt_data.debug_kicked != 1)
-    error("called finalise transport when particle %lld is unkicked (count=%d)",
-          p->id, p->rt_data.debug_kicked);
-
-  if (p->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do finalise_transport on particle %lld when "
-        "injection_done count is %d",
-        p->id, p->rt_data.debug_injection_done);
-
-  if (p->rt_data.debug_gradients_done != 1)
-    error(
-        "Trying to do finalise_transport on particle %lld when "
-        "gradients_done count is %d",
-        p->id, p->rt_data.debug_gradients_done);
+  rt_debug_sequence_check(p, 3, __func__);
 
   if (p->rt_data.debug_calls_iact_transport_interaction == 0)
     message(
@@ -565,21 +510,7 @@ __attribute__((always_inline)) INLINE static void rt_tchem(
     const struct unit_system* restrict us, const double dt) {
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  if (p->rt_data.debug_kicked != 1)
-    error(
-        "Part %lld trying to do thermochemistry on unkicked particle "
-        "(count=%d)",
-        p->id, p->rt_data.debug_kicked);
-  if (p->rt_data.debug_injection_done != 1)
-    error("Part %lld trying to do thermochemistry when injection_done != 1: %d",
-          p->id, p->rt_data.debug_injection_done);
-  if (p->rt_data.debug_gradients_done != 1)
-    error("Part %lld trying to do thermochemistry when gradients_done != 1: %d",
-          p->id, p->rt_data.debug_gradients_done);
-  if (p->rt_data.debug_transport_done != 1)
-    error("Part %lld trying to do thermochemistry when transport_done != 1: %d",
-          p->id, p->rt_data.debug_transport_done);
-
+  rt_debug_sequence_check(p, 4, __func__);
   p->rt_data.debug_thermochem_done += 1;
 #endif
 
@@ -592,7 +523,7 @@ __attribute__((always_inline)) INLINE static void rt_tchem(
 
 /**
  * @brief Extra operations done during the kick. This needs to be
- * done before the particle mass is updated in the hydro_kick_extra
+ * done before the particle mass is updated in the hydro_kick_extra.
  *
  * @param p Particle to act upon.
  * @param dt_therm Thermal energy time-step @f$\frac{dt}{a^2}@f$.
@@ -612,6 +543,8 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra(
   /* Don't account for timestep_sync backward kicks */
   if (dt_therm >= 0.f && dt_grav >= 0.f && dt_hydro >= 0.f &&
       dt_kick_corr >= 0.f) {
+
+    rt_debug_sequence_check(p, 0, __func__);
     p->rt_data.debug_kicked += 1;
   }
 #endif
@@ -681,6 +614,29 @@ __attribute__((always_inline)) INLINE static void rt_prepare_force(
   rt_part_reset_mass_fluxes(p);
 }
 
+/**
+ * @brief Extra operations to be done during the drift
+ *
+ * @param p Particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param dt_drift The drift time-step for positions.
+ */
+__attribute__((always_inline)) INLINE static void rt_predict_extra(
+    struct part* p, struct xpart* xp, float dt_drift) {
+
+  float dx[3] = {xp->v_full[0] * dt_drift, xp->v_full[1] * dt_drift,
+                 xp->v_full[2] * dt_drift};
+
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    float Unew[4];
+    rt_gradients_predict_drift(p, Unew, g, dx);
+    p->rt_data.radiation[g].energy_density = Unew[0];
+    p->rt_data.radiation[g].flux[0] = Unew[1];
+    p->rt_data.radiation[g].flux[1] = Unew[2];
+    p->rt_data.radiation[g].flux[2] = Unew[3];
+  }
+}
+
 /**
  * @brief Clean the allocated memory inside the RT properties struct.
  *
@@ -693,11 +649,8 @@ __attribute__((always_inline)) INLINE static void rt_clean(
   /* If we were restarting, free-ing manually will lead to
    * segfaults since we didn't malloc the stuff */
   if (!restart) {
-    /* TODO: clean this up in a later MR once grackle is properly
-     * cleaned up and MPI issues are resolved. */
     /* Clean up grackle data. This is a call to a grackle function */
-    /* _free_chemistry_data(&grackle_chemistry_data, */
-    /*                      props->grackle_chemistry_rates); */
+    _free_chemistry_data(&props->grackle_chemistry_data, &grackle_rates);
 
     for (int g = 0; g < RT_NGROUPS; g++) {
       free(props->energy_weighted_cross_sections[g]);
@@ -706,13 +659,6 @@ __attribute__((always_inline)) INLINE static void rt_clean(
     free(props->energy_weighted_cross_sections);
     free(props->number_weighted_cross_sections);
   }
-
-#ifdef SWIFT_RT_DEBUG_CHECKS
-#ifndef WITH_MPI
-  fclose(props->conserved_energy_filep);
-  fclose(props->star_emitted_energy_filep);
-#endif
-#endif
 }
 
 #endif /* SWIFT_RT_GEAR_H */
diff --git a/src/rt/GEAR/rt_debugging.h b/src/rt/GEAR/rt_debugging.h
index 5421dde78016efa4c1a512108d6389429bf46dd6..bc722567cd59e0aa7f1a26a43db4807e1bd3f52f 100644
--- a/src/rt/GEAR/rt_debugging.h
+++ b/src/rt/GEAR/rt_debugging.h
@@ -21,7 +21,9 @@
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
 
+#include "active.h"
 #include "rt_properties.h"
+#include "timeline.h"
 
 /**
  * @file src/rt/GEAR/rt_debugging.h
@@ -29,6 +31,100 @@
  * extra debugging functions.
  */
 
+/**
+ * @brief This resets particle carried quantities after each subcycling
+ * step such that the internal checks are still consistent.
+ * @param p the particle to work on
+ */
+__attribute__((always_inline)) INLINE static void rt_debugging_count_subcycle(
+    struct part *restrict p) {
+  p->rt_data.debug_nsubcycles += 1;
+}
+
+/**
+ * @brief Check that the particle completed the correct number of subcycles.
+ * This is checked in every rt_reset_part, before the subcycling count is reset.
+ * @param p the particle to work on
+ * @param rt_props RT properties struct
+ */
+__attribute__((always_inline)) INLINE static void
+rt_debugging_check_nr_subcycles(struct part *restrict p,
+                                const struct rt_props *rt_props) {
+
+  /* TODO: this check may fail when running with limiter/sync. */
+
+  /* NOTE: we need to do this check somewhere in the hydro tasks.
+   * (1) it needs to be done when all tasks are active and before the
+   * particle hydro time step is changed.
+   * (2) If you do it during RT tasks, it won't properly check how
+   * many sub-cycles you did during a single hydro task.
+   * (3) You can't do it during the timestep task, since between
+   * the hydro and the timestep we already do an RT step. */
+
+  /* skip initialization */
+  if (p->time_bin == 0) return;
+  if (p->rt_time_data.time_bin == 0)
+    error("Got part %lld with RT time bin 0", p->id);
+
+  timebin_t bindiff = p->time_bin - p->rt_time_data.time_bin;
+
+  if (rt_props->debug_max_nr_subcycles <= 1) {
+    /* Running without subcycling. */
+    if (bindiff != 0)
+      error("Running without subcycling but got bindiff=%d for part=%lld",
+            bindiff, p->id);
+    if (p->rt_data.debug_nsubcycles != 1)
+      error("Running without subcycling but got part=%lld subcycle count=%d",
+            p->id, p->rt_data.debug_nsubcycles);
+    return;
+  }
+
+  /* TODO: this assumes that max_nr_subcycles is not an upper limit,
+   * but a fixed number of sub-cycles */
+  timebin_t bindiff_expect = 0;
+
+  while (!(rt_props->debug_max_nr_subcycles & (1 << bindiff_expect)) &&
+         bindiff_expect != num_time_bins)
+    ++bindiff_expect;
+
+  if (bindiff_expect == num_time_bins)
+    error(
+        "Couldn't determine expected time bin difference. Max nr subcycles %d "
+        "bindiff %d",
+        rt_props->debug_max_nr_subcycles, bindiff);
+
+  if (bindiff != bindiff_expect)
+    error("Particle %lld Got bindiff=%d expect=%d; timebins=%d rt=%d", p->id,
+          bindiff, bindiff_expect, p->time_bin, p->rt_time_data.time_bin);
+
+  int subcycles_expect = (1 << bindiff);
+  if (p->rt_data.debug_nsubcycles != subcycles_expect)
+
+    if (p->rt_data.debug_nsubcycles != rt_props->debug_max_nr_subcycles)
+      error(
+          "Particle %lld didn't do the expected amount of subcycles: Expected "
+          "%d, done %d; time bins %d RT: %d",
+          p->id, subcycles_expect, p->rt_data.debug_nsubcycles, p->time_bin,
+          p->rt_time_data.time_bin);
+}
+
+/**
+ * @brief This resets particle carried quantities after each subcycling
+ * step such that the internal checks are still consistent.
+ * @param p the particle to work on
+ */
+__attribute__((always_inline)) INLINE static void
+rt_debugging_reset_each_subcycle(struct part *restrict p) {
+
+  p->rt_data.debug_calls_iact_gradient_interaction = 0;
+  p->rt_data.debug_calls_iact_transport_interaction = 0;
+
+  p->rt_data.debug_injection_done = 0;
+  p->rt_data.debug_gradients_done = 0;
+  p->rt_data.debug_transport_done = 0;
+  p->rt_data.debug_thermochem_done = 0;
+}
+
 /**
  * @brief Debugging checks loop over all star particles after each time step
  */
@@ -41,8 +137,6 @@ static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data,
 
   unsigned long long emission_sum_this_step = 0ULL;
   unsigned long long emission_sum_tot = 0ULL;
-  float emitted_energy[RT_NGROUPS];
-  for (int g = 0; g < RT_NGROUPS; g++) emitted_energy[g] = 0.f;
 
   for (int k = 0; k < scount; k++) {
 
@@ -53,6 +147,9 @@ static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data,
     sp->rt_data.debug_iact_hydro_inject = 0;
     sp->rt_data.debug_iact_hydro_inject_prep = 0;
 
+#ifndef WITH_MPI
+    /* These checks will fail with MPI since we're not
+     * sending data back */
     for (int g = 0; g < RT_NGROUPS; g++) {
       /* also check now that we actually injected the correct
        * amount of energy
@@ -82,8 +179,8 @@ static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data,
                 sp->rt_data.debug_injected_energy[g], diff, diff_weights);
         }
       }
-      emitted_energy[g] += sp->rt_data.debug_injected_energy[g];
     }
+#endif
 
     for (int g = 0; g < RT_NGROUPS; g++) {
       sp->rt_data.debug_injected_energy[g] = 0.f;
@@ -96,9 +193,6 @@ static void rt_debugging_end_of_step_stars_mapper(void *restrict map_data,
   atomic_add(&e->rt_props->debug_radiation_emitted_this_step,
              emission_sum_this_step);
   atomic_add(&e->rt_props->debug_radiation_emitted_tot, emission_sum_tot);
-  for (int g = 0; g < RT_NGROUPS; g++)
-    atomic_add_f(&e->rt_props->debug_total_star_emitted_energy[g],
-                 emitted_energy[g]);
 }
 
 /**
@@ -121,6 +215,7 @@ static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data,
     struct part *restrict p = &parts[k];
     absorption_sum_this_step += p->rt_data.debug_iact_stars_inject;
     absorption_sum_tot += p->rt_data.debug_radiation_absorbed_tot;
+
     /* Reset all values here in case particles won't be active next step */
     p->rt_data.debug_iact_stars_inject = 0;
 
@@ -134,11 +229,6 @@ static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data,
   atomic_add(&e->rt_props->debug_radiation_absorbed_this_step,
              absorption_sum_this_step);
   atomic_add(&e->rt_props->debug_radiation_absorbed_tot, absorption_sum_tot);
-
-  for (int g = 0; g < RT_NGROUPS; g++) {
-    atomic_add_f(&(e->rt_props->debug_total_radiation_conserved_energy[g]),
-                 energy_sum[g]);
-  }
 }
 
 /**
@@ -154,11 +244,6 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) {
 
   struct space *s = e->s;
   if (!(e->policy & engine_policy_rt)) return;
-#ifdef WITH_MPI
-  /* Since we aren't sending data back, none of these checks will
-   * pass a run over MPI. */
-  return;
-#endif
 
   const ticks tic = getticks();
 
@@ -169,10 +254,6 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) {
   e->rt_props->debug_radiation_absorbed_this_step = 0ULL;
   e->rt_props->debug_radiation_emitted_tot = 0ULL;
   e->rt_props->debug_radiation_absorbed_tot = 0ULL;
-  for (int g = 0; g < RT_NGROUPS; g++) {
-    e->rt_props->debug_total_radiation_conserved_energy[g] = 0.f;
-    e->rt_props->debug_total_star_emitted_energy[g] = 0.f;
-  }
 
   /* hydro particle loop */
   if (s->nr_parts > 0)
@@ -186,7 +267,15 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) {
                    s->sparts, s->nr_sparts, sizeof(struct spart),
                    threadpool_auto_chunk_size, /*extra_data=*/e);
 
+#ifdef WITH_MPI
+  /* Since we aren't sending data back, none of these checks will
+   * pass a run over MPI. Make sure you run the threadpool functions
+   * first though so certain variables can get reset properly. */
+  return;
+#endif
+
   /* Have we accidentally invented or deleted some radiation somewhere? */
+
   if ((e->rt_props->debug_radiation_emitted_this_step !=
        e->rt_props->debug_radiation_absorbed_this_step) ||
       (e->rt_props->debug_radiation_emitted_tot !=
@@ -200,34 +289,84 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) {
         e->rt_props->debug_radiation_emitted_tot,
         e->rt_props->debug_radiation_absorbed_tot);
 
-  /* Write down energy budget for this timestep. */
-  if (e->step > 1) {
-    fprintf(e->rt_props->conserved_energy_filep, "\n");
-  } else {
-    fprintf(e->rt_props->conserved_energy_filep, "# nstars: %lld\n",
-            e->total_nr_sparts);
-  }
-  fprintf(e->rt_props->conserved_energy_filep, "%6d %12.6e ", e->step, e->time);
-  for (int g = 0; g < RT_NGROUPS; g++)
-    fprintf(e->rt_props->conserved_energy_filep, "%12.6e ",
-            e->rt_props->debug_total_radiation_conserved_energy[g]);
-
-  if (e->step > 1) {
-    fprintf(e->rt_props->star_emitted_energy_filep, "\n");
-  } else {
-    fprintf(e->rt_props->star_emitted_energy_filep, "# nstars: %lld\n",
-            e->total_nr_sparts);
-  }
-  fprintf(e->rt_props->star_emitted_energy_filep, "%6d %12.6e ", e->step,
-          e->time);
-  for (int g = 0; g < RT_NGROUPS; g++)
-    fprintf(e->rt_props->star_emitted_energy_filep, "%12.6e ",
-            e->rt_props->debug_total_star_emitted_energy[g]);
-
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
 }
 
+/**
+ * @brief Perform a series of consistency and sanity checks.
+ *
+ * @param p particle to check
+ * @param loc location where this is called from. This determines which checks
+ * will be done:
+ *
+ * 0: during kicks/after drifts.
+ * 1: during rt_ghost1/finalise_injection / after kicks.
+ * 2: during gradients / after injection.
+ * 3: during transport / after gradients.
+ * 4: during thermochem / after transport.
+ * 5: after thermochem.
+ *
+ * @param function_name: Function name (or message) you want printed on error.
+ */
+__attribute__((always_inline)) INLINE static void rt_debug_sequence_check(
+    struct part *restrict p, int loc, const char *function_name) {
+
+  /* Note: Checking whether a particle has been drifted at all is not
+   * compatible with subcycling. There is no reliable point where to
+   * reset the counters and have sensible results. */
+
+  if (loc > 0) {
+    /* Are kicks done? */
+    if (p->rt_data.debug_nsubcycles == 0) {
+      if (p->rt_data.debug_kicked != 1)
+        error(
+            "called %s on particle %lld with wrong kick count=%d (expected "
+            "1) cycle=%d",
+            function_name, p->id, p->rt_data.debug_kicked,
+            p->rt_data.debug_nsubcycles);
+    } else if (p->rt_data.debug_nsubcycles > 0) {
+      /* This covers case 1, 2, 3, 4, 5 */
+      if (p->rt_data.debug_kicked != 2)
+        error(
+            "called %s on particle %lld with wrong kick count=%d (expected 2) "
+            "cycle=%d",
+            function_name, p->id, p->rt_data.debug_kicked,
+            p->rt_data.debug_nsubcycles);
+    } else {
+      error("Got negative subcycle???");
+    }
+  }
+
+  if (loc > 1) {
+    /* is injection done? */
+    if (p->rt_data.debug_injection_done != 1)
+      error("called %s on part %lld when finalise injection count is %d ID",
+            function_name, p->id, p->rt_data.debug_injection_done);
+  }
+
+  if (loc > 2) {
+    /* are gradients done? */
+    if (p->rt_data.debug_gradients_done != 1)
+      error("called %s on part %lld when gradients_done count is %d",
+            function_name, p->id, p->rt_data.debug_gradients_done);
+  }
+
+  if (loc > 3) {
+    /* is transport done? */
+    if (p->rt_data.debug_transport_done != 1)
+      error("called %s on part %lld when transport_done != 1: %d",
+            function_name, p->id, p->rt_data.debug_transport_done);
+  }
+
+  if (loc > 4) {
+    /* is thermochemistry done? */
+    if (p->rt_data.debug_thermochem_done != 1)
+      error("called %s on part %lld with thermochem_done count=%d",
+            function_name, p->id, p->rt_data.debug_thermochem_done);
+  }
+}
+
 #endif /* SWIFT_RT_DEBUG_CHECKS */
 #endif /* SWIFT_RT_DEBUGGING_GEAR_H */
diff --git a/src/rt/GEAR/rt_getters.h b/src/rt/GEAR/rt_getters.h
index 7c57bce4c5b6774d56dceab99a1b7c0fa5d309f6..dc9e3e6b9cb4a5337e357aad05cb4da1976cb4bb 100644
--- a/src/rt/GEAR/rt_getters.h
+++ b/src/rt/GEAR/rt_getters.h
@@ -28,6 +28,21 @@
  * @brief Getter functions for GEAR RT scheme to keep code clean and lean
  */
 
+/**
+ * @brief Get the radiation energy densities of a particle.
+ *
+ * @param p Particle.
+ * @param E (return) Pointer to the array in which the result needs to be stored
+ */
+__attribute__((always_inline)) INLINE static void
+rt_part_get_radiation_energy_density(const struct part *restrict p,
+                                     float E[RT_NGROUPS]) {
+
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    E[g] = p->rt_data.radiation[g].energy_density;
+  }
+}
+
 /**
  * @brief Get a 4-element state vector U containing the radiation energy
  * density and fluxes for a specific photon group.
diff --git a/src/rt/GEAR/rt_grackle_utils.h b/src/rt/GEAR/rt_grackle_utils.h
new file mode 100644
index 0000000000000000000000000000000000000000..844958c273b407555702df63fd1380d0198d7b7e
--- /dev/null
+++ b/src/rt/GEAR/rt_grackle_utils.h
@@ -0,0 +1,440 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_RT_GRACKLE_UTILS_H
+#define SWIFT_RT_GRACKLE_UTILS_H
+
+/* need hydro gamma */
+#include "hydro.h"
+
+#include <grackle.h>
+
+/* need to rework (and check) code if changed */
+#define FIELD_SIZE 1
+
+/**
+ * @file src/rt/GEAR/rt_grackle_utils.h
+ * @brief Utility and helper functions related to using grackle.
+ */
+
+/**
+ * @brief initialize grackle during rt_props_init
+ *
+ * @param rtp #rt_props struct
+ * @param us #unit_system struct
+ * Declare this here to avoid cyclical inclusions.
+ **/
+__attribute__((always_inline)) INLINE static void rt_init_grackle(
+    code_units *grackle_units, chemistry_data *grackle_chemistry_data,
+    float hydrogen_mass_fraction,
+
+    const struct unit_system *us, struct swift_params *params) {
+
+  grackle_verbose =
+      parser_get_opt_param_int(params, "GEARRT:grackle_verbose", /*default=*/0);
+
+  /* Initialize units */
+  /* ---------------- */
+  /* we assume all quantities to be physical, not comoving */
+  grackle_units->a_units = 1.0;
+  grackle_units->a_value = 1.0;
+  grackle_units->comoving_coordinates = 0;
+  grackle_units->density_units =
+      units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
+  grackle_units->length_units =
+      units_cgs_conversion_factor(us, UNIT_CONV_LENGTH);
+  grackle_units->time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME);
+  /* Set velocity units */
+  set_velocity_units(grackle_units);
+
+  /* Chemistry Parameters */
+  /* -------------------- */
+  /* More details on https://grackle.readthedocs.io/en/latest/Parameters.html */
+
+  /* TODO: cleanup later with all other grackle stuff */
+  /* rtp->grackle_chemistry_data = _set_default_chemistry_parameters(); */
+  if (set_default_chemistry_parameters(grackle_chemistry_data) == 0) {
+    error("Error in set_default_chemistry_parameters.");
+  }
+  /* chemistry on */
+  grackle_chemistry_data->use_grackle = 1;
+  /* cooling on */
+  /* NOTE: without cooling on, it also won't heat... */
+  grackle_chemistry_data->with_radiative_cooling = 1;
+  /* 6 species atomic H and He-> */
+  grackle_chemistry_data->primordial_chemistry = 1;
+  /* dust processes */
+  grackle_chemistry_data->dust_chemistry = 0;
+  /* H2 formation on dust */
+  grackle_chemistry_data->h2_on_dust = 0;
+  /* metal cooling (uses Cloudy) off (for now) */
+  grackle_chemistry_data->metal_cooling = 0;
+  /* no cooling below CMB temperature */
+  grackle_chemistry_data->cmb_temperature_floor = 1;
+  /* UV background off */
+  grackle_chemistry_data->UVbackground = 0;
+  /* data file - currently not used-> */
+  grackle_chemistry_data->grackle_data_file = "";
+  /* adiabatic index */
+  grackle_chemistry_data->Gamma = hydro_gamma;
+  /* we'll provide grackle with ionization and heating rates from RT */
+  grackle_chemistry_data->use_radiative_transfer = 1;
+  /* fraction by mass of Hydrogen in the metal-free portion of the gas */
+  grackle_chemistry_data->HydrogenFractionByMass = hydrogen_mass_fraction;
+  /* Use case B recombination? (On-the-spot approximation) */
+  grackle_chemistry_data->CaseBRecombination = parser_get_opt_param_int(
+      params, "GEARRT:case_B_recombination", /*default=*/1);
+
+  /* TODO: cleanup later with all other grackle stuff */
+  /* Initialize the chemistry_data_storage object to be
+   * able to use local functions */
+  /* chemistry_data_storage* chem_data_storage = */
+  /*     malloc(sizeof(chemistry_data_storage)); */
+  /* rtp->grackle_chemistry_rates = chem_data_storage; */
+  /* if (!_initialize_chemistry_data(&rtp->grackle_chemistry_data, */
+  /*                                 rtp->grackle_chemistry_rates, */
+  /*                                 &rtp->grackle_units)) { */
+  if (initialize_chemistry_data(grackle_units) == 0) {
+    error("Error in initialize_chemistry_data");
+  }
+}
+
+/**
+ * @brief fill out a grackle field struct with the relevant (gas) data from a
+ *particle
+ *
+ * @param grackle_fields (return) grackle field to copy into
+ * @param density array of particle density
+ * @param internal_energy array of particle internal_energy
+ * @param species_densities array of species densities of particle (HI, HII,
+ *HeI, HeII, HeIII, e-)
+ * @param iact_rates array of interaction rates (heating, 3 ioniziation, H2
+ *dissociation)
+ *
+ **/
+__attribute__((always_inline)) INLINE static void
+rt_get_grackle_particle_fields(grackle_field_data *grackle_fields,
+                               gr_float density, gr_float internal_energy,
+                               gr_float species_densities[6],
+                               gr_float iact_rates[5]) {
+
+  int *dimension = malloc(3 * sizeof(int));
+  int *start = malloc(3 * sizeof(int));
+  int *end = malloc(3 * sizeof(int));
+
+  dimension[0] = FIELD_SIZE;
+  dimension[1] = 0;
+  dimension[2] = 0;
+  start[0] = 0;
+  start[1] = 0;
+  start[2] = 0;
+  end[0] = FIELD_SIZE - 1;
+  end[1] = 0;
+  end[2] = 0;
+
+  grackle_fields->grid_dx = 0.;
+  grackle_fields->grid_rank = 3;
+  grackle_fields->grid_dimension = dimension;
+  grackle_fields->grid_start = start;
+  grackle_fields->grid_end = end;
+
+  /* Set initial quantities */
+  grackle_fields->density = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->internal_energy = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->x_velocity = NULL;
+  grackle_fields->y_velocity = NULL;
+  grackle_fields->z_velocity = NULL;
+  /* for primordial_chemistry >= 1 */
+  grackle_fields->HI_density = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->HII_density = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->HeI_density = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->HeII_density = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->HeIII_density = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->e_density = malloc(FIELD_SIZE * sizeof(gr_float));
+  /* for primordial_chemistry >= 2 */
+  grackle_fields->HM_density = NULL;
+  grackle_fields->H2I_density = NULL;
+  grackle_fields->H2II_density = NULL;
+  /* for primordial_chemistry >= 3 */
+  grackle_fields->DI_density = NULL;
+  grackle_fields->DII_density = NULL;
+  grackle_fields->HDI_density = NULL;
+  /* for metal_cooling = 1 */
+  grackle_fields->metal_density = NULL;
+
+  /* volumetric heating rate (provide in units [erg s^-1 cm^-3]) */
+  grackle_fields->volumetric_heating_rate = NULL;
+  /* specific heating rate (provide in units [egs s^-1 g^-1] */
+  grackle_fields->specific_heating_rate = NULL;
+
+  /* radiative transfer ionization / dissociation rate fields (provide in units
+   * [1/s]) */
+  grackle_fields->RT_HI_ionization_rate = malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->RT_HeI_ionization_rate =
+      malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->RT_HeII_ionization_rate =
+      malloc(FIELD_SIZE * sizeof(gr_float));
+  grackle_fields->RT_H2_dissociation_rate =
+      malloc(FIELD_SIZE * sizeof(gr_float));
+  /* radiative transfer heating rate field
+   * (provide in units [erg s^-1 cm^-3] / nHI in cgs) */
+  grackle_fields->RT_heating_rate = malloc(FIELD_SIZE * sizeof(gr_float));
+
+  for (int i = 0; i < FIELD_SIZE; i++) {
+
+    grackle_fields->density[i] = density;
+    grackle_fields->internal_energy[i] = internal_energy;
+
+    grackle_fields->HI_density[i] = species_densities[0];
+    grackle_fields->HII_density[i] = species_densities[1];
+    grackle_fields->HeI_density[i] = species_densities[2];
+    grackle_fields->HeII_density[i] = species_densities[3];
+    grackle_fields->HeIII_density[i] = species_densities[4];
+    /* e_density = electron density*mh/me = n_e * m_h */
+    grackle_fields->e_density[i] = species_densities[5];
+
+    /* grackle_fields->HM_density[i] = species_densities[6]; */
+    /* grackle_fields->H2I_density[i] = species_densities[7]; */
+    /* grackle_fields->H2II_density[i] = species_densities[8]; */
+    /* grackle_fields->DI_density[i] = species_densities[9]; */
+    /* grackle_fields->DII_density[i] = species_densities[10]; */
+    /* grackle_fields->HDI_density[i] = species_densities[11]; */
+
+    /* grackle_fields->metal_density[i] = 0.0; */
+    /* solar metallicity */
+    /* grackle_chemistry_data.SolarMetalFractionByMass *
+     * grackle_fields->density[i]; */
+
+    /* grackle_fields->x_velocity[i] = 0.0; */
+    /* grackle_fields->y_velocity[i] = 0.0; */
+    /* grackle_fields->z_velocity[i] = 0.0; */
+
+    /* grackle_fields->volumetric_heating_rate[i] = 0.0; */
+    /* grackle_fields->specific_heating_rate[i] = 0.0; */
+
+    grackle_fields->RT_heating_rate[i] = iact_rates[0];
+    grackle_fields->RT_HI_ionization_rate[i] = iact_rates[1];
+    grackle_fields->RT_HeI_ionization_rate[i] = iact_rates[2];
+    grackle_fields->RT_HeII_ionization_rate[i] = iact_rates[3];
+    grackle_fields->RT_H2_dissociation_rate[i] = iact_rates[4];
+  }
+}
+
+/**
+ * @brief free arrays allocated in grackle_fields.
+ *
+ * @param grackle_fields grackle fields to clean up
+ *
+ **/
+__attribute__((always_inline)) INLINE static void rt_clean_grackle_fields(
+    grackle_field_data *grackle_fields) {
+
+  free(grackle_fields->grid_dimension);
+  free(grackle_fields->grid_start);
+  free(grackle_fields->grid_end);
+
+  free(grackle_fields->density);
+  free(grackle_fields->internal_energy);
+  /* free(grackle_fields->x_velocity); */
+  /* free(grackle_fields->y_velocity); */
+  /* free(grackle_fields->z_velocity); */
+  free(grackle_fields->HI_density);
+  free(grackle_fields->HII_density);
+  free(grackle_fields->HeI_density);
+  free(grackle_fields->HeII_density);
+  free(grackle_fields->HeIII_density);
+  free(grackle_fields->e_density);
+  /* free(grackle_fields->HM_density); */
+  /* free(grackle_fields->H2I_density); */
+  /* free(grackle_fields->H2II_density); */
+  /* free(grackle_fields->DI_density); */
+  /* free(grackle_fields->DII_density); */
+  /* free(grackle_fields->HDI_density); */
+  /* free(grackle_fields->metal_density); */
+  /* free(grackle_fields->volumetric_heating_rate); */
+  /* free(grackle_fields->specific_heating_rate); */
+
+  free(grackle_fields->RT_HI_ionization_rate);
+  free(grackle_fields->RT_HeI_ionization_rate);
+  free(grackle_fields->RT_HeII_ionization_rate);
+  free(grackle_fields->RT_H2_dissociation_rate);
+  free(grackle_fields->RT_heating_rate);
+}
+
+/**
+ * @brief Write out all available grackle field data for a given index
+ * and setup to a file.
+ * This function is intended for debugging.
+ *
+ * @param fp FILE pointer to write into.
+ * @param grackle_fields grackle field data
+ * @param grackle_chemistry_data grackle chemistry data.
+ * @param grackle_units units used by grackle
+ * @param field_index grackle field index to print out.
+ **/
+__attribute__((always_inline)) INLINE static void
+rt_write_grackle_setup_and_field(FILE *fp, grackle_field_data grackle_fields,
+                                 chemistry_data *grackle_chemistry_data,
+                                 code_units *grackle_units, int field_index) {
+
+  fprintf(fp, "Grackle chemistry parameters:\n");
+
+  fprintf(fp, "use_grackle                       = %d\n",
+          grackle_chemistry_data->use_grackle);
+  fprintf(fp, "with_radiative_cooling            = %d\n",
+          grackle_chemistry_data->with_radiative_cooling);
+  fprintf(fp, "primordial_chemistry              = %d\n",
+          grackle_chemistry_data->primordial_chemistry);
+  fprintf(fp, "dust_chemistry                    = %d\n",
+          grackle_chemistry_data->dust_chemistry);
+  fprintf(fp, "metal_cooling                     = %d\n",
+          grackle_chemistry_data->metal_cooling);
+  fprintf(fp, "UVbackground                      = %d\n",
+          grackle_chemistry_data->UVbackground);
+  fprintf(fp, "grackle_data_file                 = %s\n",
+          grackle_chemistry_data->grackle_data_file);
+  fprintf(fp, "cmb_temperature_floor             = %d\n",
+          grackle_chemistry_data->cmb_temperature_floor);
+  fprintf(fp, "Gamma                             = %g\n",
+          grackle_chemistry_data->Gamma);
+  fprintf(fp, "h2_on_dust                        = %d\n",
+          grackle_chemistry_data->h2_on_dust);
+  fprintf(fp, "use_dust_density_field            = %d\n",
+          grackle_chemistry_data->use_dust_density_field);
+  fprintf(fp, "dust_recombination_cooling        = %d\n",
+          grackle_chemistry_data->dust_recombination_cooling);
+  fprintf(fp, "photoelectric_heating             = %d\n",
+          grackle_chemistry_data->photoelectric_heating);
+  fprintf(fp, "photoelectric_heating_rate        = %g\n",
+          grackle_chemistry_data->photoelectric_heating_rate);
+  fprintf(fp, "use_isrf_field                    = %d\n",
+          grackle_chemistry_data->use_isrf_field);
+  fprintf(fp, "interstellar_radiation_field      = %g\n",
+          grackle_chemistry_data->interstellar_radiation_field);
+  fprintf(fp, "use_volumetric_heating_rate       = %d\n",
+          grackle_chemistry_data->use_volumetric_heating_rate);
+  fprintf(fp, "use_specific_heating_rate         = %d\n",
+          grackle_chemistry_data->use_specific_heating_rate);
+  fprintf(fp, "three_body_rate                   = %d\n",
+          grackle_chemistry_data->three_body_rate);
+  fprintf(fp, "cie_cooling                       = %d\n",
+          grackle_chemistry_data->cie_cooling);
+  fprintf(fp, "h2_optical_depth_approximation    = %d\n",
+          grackle_chemistry_data->h2_optical_depth_approximation);
+  fprintf(fp, "ih2co                             = %d\n",
+          grackle_chemistry_data->ih2co);
+  fprintf(fp, "ipiht                             = %d\n",
+          grackle_chemistry_data->ipiht);
+  fprintf(fp, "HydrogenFractionByMass            = %g\n",
+          grackle_chemistry_data->HydrogenFractionByMass);
+  fprintf(fp, "DeuteriumToHydrogenRatio          = %g\n",
+          grackle_chemistry_data->DeuteriumToHydrogenRatio);
+  fprintf(fp, "SolarMetalFractionByMass          = %g\n",
+          grackle_chemistry_data->SolarMetalFractionByMass);
+  fprintf(fp, "local_dust_to_gas_ratio           = %g\n",
+          grackle_chemistry_data->local_dust_to_gas_ratio);
+  fprintf(fp, "NumberOfTemperatureBins           = %d\n",
+          grackle_chemistry_data->NumberOfTemperatureBins);
+  fprintf(fp, "CaseBRecombination                = %d\n",
+          grackle_chemistry_data->CaseBRecombination);
+  fprintf(fp, "TemperatureStart                  = %g\n",
+          grackle_chemistry_data->TemperatureStart);
+  fprintf(fp, "TemperatureEnd                    = %g\n",
+          grackle_chemistry_data->TemperatureEnd);
+  fprintf(fp, "NumberOfDustTemperatureBins       = %d\n",
+          grackle_chemistry_data->NumberOfDustTemperatureBins);
+  fprintf(fp, "DustTemperatureStart              = %g\n",
+          grackle_chemistry_data->DustTemperatureStart);
+  fprintf(fp, "DustTemperatureEnd                = %g\n",
+          grackle_chemistry_data->DustTemperatureEnd);
+  fprintf(fp, "Compton_xray_heating              = %d\n",
+          grackle_chemistry_data->Compton_xray_heating);
+  fprintf(fp, "LWbackground_sawtooth_suppression = %d\n",
+          grackle_chemistry_data->LWbackground_sawtooth_suppression);
+  fprintf(fp, "LWbackground_intensity            = %g\n",
+          grackle_chemistry_data->LWbackground_intensity);
+  fprintf(fp, "UVbackground_redshift_on          = %g\n",
+          grackle_chemistry_data->UVbackground_redshift_on);
+  fprintf(fp, "UVbackground_redshift_off         = %g\n",
+          grackle_chemistry_data->UVbackground_redshift_off);
+  fprintf(fp, "UVbackground_redshift_fullon      = %g\n",
+          grackle_chemistry_data->UVbackground_redshift_fullon);
+  fprintf(fp, "UVbackground_redshift_drop        = %g\n",
+          grackle_chemistry_data->UVbackground_redshift_drop);
+  fprintf(fp, "cloudy_electron_fraction_factor   = %g\n",
+          grackle_chemistry_data->cloudy_electron_fraction_factor);
+  fprintf(fp, "use_radiative_transfer            = %d\n",
+          grackle_chemistry_data->use_radiative_transfer);
+  fprintf(fp, "radiative_transfer_coupled_rate_solver = %d\n",
+          grackle_chemistry_data->radiative_transfer_coupled_rate_solver);
+  fprintf(fp, "radiative_transfer_intermediate_step = %d\n",
+          grackle_chemistry_data->radiative_transfer_intermediate_step);
+  fprintf(fp, "radiative_transfer_hydrogen_only  = %d\n",
+          grackle_chemistry_data->radiative_transfer_hydrogen_only);
+  fprintf(fp, "self_shielding_method             = %d\n",
+          grackle_chemistry_data->self_shielding_method);
+  fprintf(fp, "H2_custom_shielding               = %d\n",
+          grackle_chemistry_data->H2_custom_shielding);
+  fprintf(fp, "H2_self_shielding                 = %d\n",
+          grackle_chemistry_data->H2_self_shielding);
+
+  fprintf(fp, "\nUnits:\n");
+  fprintf(fp, "a_units               = %g\n", grackle_units->a_units);
+  fprintf(fp, "a_value               = %g\n", grackle_units->a_value);
+  fprintf(fp, "comoving_coordinates  = %d\n",
+          grackle_units->comoving_coordinates);
+  fprintf(fp, "density_units         = %g\n", grackle_units->density_units);
+  fprintf(fp, "length_units          = %g\n", grackle_units->length_units);
+  fprintf(fp, "time_units            = %g\n", grackle_units->time_units);
+  fprintf(fp, "velocity_units        = %g\n", grackle_units->velocity_units);
+
+#define rt_print_grackle_field(v) \
+  if (grackle_fields.v != NULL)   \
+  fprintf(fp, "grackle_fields." #v " = %g\n", grackle_fields.v[field_index])
+
+  fprintf(fp, "\nGrackle field data:\n");
+  rt_print_grackle_field(density);
+  rt_print_grackle_field(internal_energy);
+  rt_print_grackle_field(HI_density);
+  rt_print_grackle_field(HII_density);
+  rt_print_grackle_field(HeI_density);
+  rt_print_grackle_field(HeII_density);
+  rt_print_grackle_field(HeIII_density);
+  rt_print_grackle_field(e_density);
+  rt_print_grackle_field(HM_density);
+  rt_print_grackle_field(H2I_density);
+  rt_print_grackle_field(H2II_density);
+  rt_print_grackle_field(DI_density);
+  rt_print_grackle_field(DII_density);
+  rt_print_grackle_field(HDI_density);
+  rt_print_grackle_field(metal_density);
+  rt_print_grackle_field(x_velocity);
+  rt_print_grackle_field(y_velocity);
+  rt_print_grackle_field(z_velocity);
+  rt_print_grackle_field(volumetric_heating_rate);
+  rt_print_grackle_field(specific_heating_rate);
+  rt_print_grackle_field(RT_HI_ionization_rate);
+  rt_print_grackle_field(RT_HeI_ionization_rate);
+  rt_print_grackle_field(RT_HeII_ionization_rate);
+  rt_print_grackle_field(RT_H2_dissociation_rate);
+  rt_print_grackle_field(RT_heating_rate);
+
+#undef rt_print_grackle_field
+}
+
+#endif /* SWIFT_RT_GRACKLE_UTILS_H */
diff --git a/src/rt/GEAR/rt_gradients.h b/src/rt/GEAR/rt_gradients.h
index 2db091a80502248da3c66c4f9a17c6f57de3c9ff..dc116c940240f68255dbb3523ddd20d1340498eb 100644
--- a/src/rt/GEAR/rt_gradients.h
+++ b/src/rt/GEAR/rt_gradients.h
@@ -147,29 +147,8 @@ __attribute__((always_inline)) INLINE static void rt_gradients_collect(
     struct part *restrict pj) {
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  if (pi->rt_data.debug_kicked != 1)
-    error(
-        "Trying to do symmetric iact gradient unkicked particle %lld "
-        "(count=%d)",
-        pi->id, pi->rt_data.debug_kicked);
-
-  if (pi->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do symmetric iact gradient when finalise injection count is "
-        "%d ID %lld",
-        pi->rt_data.debug_injection_done, pi->id);
-
-  if (pj->rt_data.debug_kicked != 1)
-    error(
-        "Trying to do symmetric iact gradient unkicked particle %lld "
-        "(count=%d)",
-        pj->id, pj->rt_data.debug_kicked);
-
-  if (pj->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do symmetric iact gradient when finalise injection count is "
-        "%d ID %lld",
-        pj->rt_data.debug_injection_done, pj->id);
+  rt_debug_sequence_check(pi, 2, __func__);
+  rt_debug_sequence_check(pj, 2, __func__);
 
   pi->rt_data.debug_calls_iact_gradient_interaction += 1;
   pj->rt_data.debug_calls_iact_gradient_interaction += 1;
@@ -307,18 +286,7 @@ __attribute__((always_inline)) INLINE static void rt_gradients_nonsym_collect(
     struct part *restrict pj) {
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  if (pi->rt_data.debug_kicked != 1)
-    error(
-        "Trying to do nonsym iact gradient on unkicked particle %lld "
-        "(count=%d)",
-        pi->id, pi->rt_data.debug_kicked);
-
-  if (pi->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do nonsym iact gradients when finalise injection count is "
-        "%d ID %lld",
-        pi->rt_data.debug_injection_done, pi->id);
-
+  rt_debug_sequence_check(pi, 2, __func__);
   pi->rt_data.debug_calls_iact_gradient_interaction += 1;
 #endif
 
@@ -415,6 +383,7 @@ __attribute__((always_inline)) INLINE static float rt_gradients_extrapolate(
  * particle i
  * @param Uj (return) Resulting predicted and limited radiation state of
  * particle j
+ * @param group which photon group to use
  * @param dx Comoving distance vector between the particles (dx = pi->x -
  * pj->x).
  * @param r Comoving distance between particle i and particle j.
@@ -471,4 +440,40 @@ __attribute__((always_inline)) INLINE static void rt_gradients_predict(
   rt_check_unphysical_state(Uj, &Uj[1], /*e_old=*/0.f, /*callloc=*/1);
 }
 
+/**
+ * @brief Gradients reconstruction. Predict the value at point x_ij given
+ * current values at particle positions and gradients at particle positions.
+ *
+ * @param p Particle to work on
+ * @param U (return) Resulting predicted and limited radiation state of
+ * particle
+ * @param group which photon group to use
+ * @param dx The drift distance
+ */
+__attribute__((always_inline)) INLINE static void rt_gradients_predict_drift(
+    const struct part *restrict p, float U[4], int group, const float dx[3]) {
+
+  rt_part_get_radiation_state_vector(p, group, U);
+  /* No need to check unphysical state here:
+   * they haven't been touched since the call
+   * to rt_injection_update_photon_density */
+
+  float dE[3], dFx[3], dFy[3], dFz[3];
+  rt_part_get_gradients(p, group, dE, dFx, dFy, dFz);
+
+  float dU[4];
+  dU[0] = rt_gradients_extrapolate(dE, dx);
+  dU[1] = rt_gradients_extrapolate(dFx, dx);
+  dU[2] = rt_gradients_extrapolate(dFy, dx);
+  dU[3] = rt_gradients_extrapolate(dFz, dx);
+
+  U[0] += dU[0];
+  U[1] += dU[1];
+  U[2] += dU[2];
+  U[3] += dU[3];
+
+  /* Check and correct unphysical extrapolated states */
+  rt_check_unphysical_state(U, &U[1], /*e_old=*/0.f, /*callloc=*/1);
+}
+
 #endif /* SWIFT_RT_GRADIENT_GEAR_H */
diff --git a/src/rt/GEAR/rt_iact.h b/src/rt/GEAR/rt_iact.h
index dce4ea4d4b2c33559ce07edeb244244aed83dcce..927d5881f9e7fb0a2d23fbe16e4532afe9af3c67 100644
--- a/src/rt/GEAR/rt_iact.h
+++ b/src/rt/GEAR/rt_iact.h
@@ -19,6 +19,7 @@
 #ifndef SWIFT_RT_IACT_GEAR_H
 #define SWIFT_RT_IACT_GEAR_H
 
+#include "rt_debugging.h"
 #include "rt_flux.h"
 #include "rt_gradients.h"
 
@@ -158,10 +159,6 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject(
   if (psi == 0.f || octw == 0.f) return;
 
   const float weight = psi / (nonempty_octants * octw);
-
-  const float minus_r_inv = -1.f / r;
-  const float n_unit[3] = {dx[0] * minus_r_inv, dx[1] * minus_r_inv,
-                           dx[2] * minus_r_inv};
   const float Vinv = 1.f / pj->geometry.volume;
 
   /* Nurse, the patient is ready now */
@@ -171,13 +168,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject(
         si->rt_data.emission_this_step[g] * weight * Vinv;
     pj->rt_data.radiation[g].energy_density += injected_energy_density;
 
-    /* Inject flux. */
-    /* We assume the path from the star to the gas is optically thin */
-    const float injected_flux =
-        injected_energy_density * rt_params.reduced_speed_of_light;
-    pj->rt_data.radiation[g].flux[0] += injected_flux * n_unit[0];
-    pj->rt_data.radiation[g].flux[1] += injected_flux * n_unit[1];
-    pj->rt_data.radiation[g].flux[2] += injected_flux * n_unit[2];
+    /* Don't inject flux. */
   }
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
@@ -219,42 +210,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common(
     struct part *restrict pj, float a, float H, int mode) {
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  if (pi->rt_data.debug_kicked != 1)
-    error("Trying to iact transport with unkicked particle %lld (count=%d)",
-          pi->id, pi->rt_data.debug_kicked);
-
-  if (pi->rt_data.debug_injection_done != 1)
-    error(
-        "Part %lld trying to do iact transport when "
-        "injection_done count is %d",
-        pi->id, pi->rt_data.debug_injection_done);
-
-  if (pi->rt_data.debug_gradients_done != 1)
-    error(
-        "Part %lld trying to do iact transport when "
-        "gradients_done count is %d",
-        pi->id, pi->rt_data.debug_gradients_done);
-
+  const char *func_name = (mode == 1) ? "sym flux iact" : "nonsym flux iact";
+  rt_debug_sequence_check(pi, 3, func_name);
   pi->rt_data.debug_calls_iact_transport_interaction += 1;
 
   if (mode == 1) {
-
-    if (pj->rt_data.debug_kicked != 1)
-      error("Trying to iact transport with unkicked particle %lld (count=%d)",
-            pj->id, pj->rt_data.debug_kicked);
-
-    if (pj->rt_data.debug_injection_done != 1)
-      error(
-          "Part %lld Trying to do iact transport when "
-          "injection_done count is %d",
-          pj->id, pj->rt_data.debug_injection_done);
-
-    if (pj->rt_data.debug_gradients_done != 1)
-      error(
-          "Part %lld Trying to do iact transport when "
-          "gradients_done count is %d",
-          pj->id, pj->rt_data.debug_gradients_done);
-
+    rt_debug_sequence_check(pj, 3, func_name);
     pj->rt_data.debug_calls_iact_transport_interaction += 1;
   }
 #endif
diff --git a/src/rt/GEAR/rt_interaction_rates.c b/src/rt/GEAR/rt_interaction_cross_sections.c
similarity index 51%
rename from src/rt/GEAR/rt_interaction_rates.c
rename to src/rt/GEAR/rt_interaction_cross_sections.c
index 898a0f4a63375a689df6977fb66692e29d4aa9b1..24983edf8623b2fae2f7e8b3389833562d4fa69d 100644
--- a/src/rt/GEAR/rt_interaction_rates.c
+++ b/src/rt/GEAR/rt_interaction_cross_sections.c
@@ -17,10 +17,12 @@
  *
  ******************************************************************************/
 
-#include "rt_interaction_rates.h"
+#include "rt_interaction_cross_sections.h"
 
+#include "error.h"
 #include "rt_blackbody.h"
 #include "rt_properties.h"
+#include "rt_species.h"
 
 #include <gsl/gsl_integration.h>
 
@@ -36,6 +38,9 @@
  */
 __attribute__((always_inline)) INLINE static double
 rt_interaction_rates_get_spectrum(const double nu, void *params) {
+  /* Keep this function in the .c file so you don't have to
+   * include the spectra .h files like rt_blackbody.h anywhere
+   * else */
 
   struct rt_spectrum_integration_params *pars =
       (struct rt_spectrum_integration_params *)params;
@@ -68,10 +73,30 @@ rt_interaction_rates_get_spectrum(const double nu, void *params) {
  * @param params spectrum integration params. Needs to be of type
  * void* for GSL integrators.
  */
-double spectrum_integrand(double nu, void *params) {
+static double spectrum_integrand(double nu, void *params) {
   return rt_interaction_rates_get_spectrum(nu, params);
 }
 
+/**
+ * Spectrum function divided by photon energy h*nu to be integrated.
+ * This function is called by the GSL integrator.
+ *
+ * @param nu frequency at which to evaluate spectrum
+ * @param params spectrum integration params. Needs to be of type
+ * void* for GSL integrators.
+ */
+static double spectrum_over_hnu_integrand(double nu, void *params) {
+  struct rt_spectrum_integration_params *p =
+      (struct rt_spectrum_integration_params *)params;
+  const double E = nu * p->h_planck;
+  const double J = rt_interaction_rates_get_spectrum(nu, params);
+  if (E > 0.) {
+    return J / E;
+  } else {
+    return 0.;
+  }
+}
+
 /**
  * Spectrum times cross section function to be integrated.
  * This function is called by the GSL integrator.
@@ -80,7 +105,7 @@ double spectrum_integrand(double nu, void *params) {
  * @param params spectrum integration params. Needs to be of type
  * void* for GSL integrators.
  */
-double spectrum_times_sigma_integrand(double nu, void *params) {
+static double spectrum_times_sigma_integrand(double nu, void *params) {
   struct rt_spectrum_integration_params *p =
       (struct rt_spectrum_integration_params *)params;
   const double E = nu * p->h_planck;
@@ -98,7 +123,7 @@ double spectrum_times_sigma_integrand(double nu, void *params) {
  * @param params spectrum integration params. Needs to be of type
  * void* for GSL integrators.
  */
-double spectrum_times_sigma_over_hnu_integrand(double nu, void *params) {
+static double spectrum_times_sigma_over_hnu_integrand(double nu, void *params) {
   struct rt_spectrum_integration_params *p =
       (struct rt_spectrum_integration_params *)params;
   const double E = nu * p->h_planck;
@@ -116,7 +141,7 @@ double spectrum_times_sigma_over_hnu_integrand(double nu, void *params) {
  * @param nu_stop upper boundary of the integral
  * @param params spectrum integration params.
  * */
-double rt_interaction_rates_integrate_gsl(
+static double rt_cross_sections_integrate_gsl(
     double (*function)(double, void *), double nu_start, double nu_stop,
     int npoints, struct rt_spectrum_integration_params *params) {
 
@@ -126,9 +151,15 @@ double rt_interaction_rates_integrate_gsl(
 
   F.function = function;
   F.params = (void *)params;
+  /* NOTE: there is an option to use the integrator with an upper limit
+   * of infinity, but this is accurate enough for now when setting a
+   * high enough maximal frequency. */
   gsl_integration_qags(&F, nu_start, nu_stop, /*espabs=*/0., /*epsrel=*/1e-7,
                        npoints, w, &result, &error);
 
+  /* Clean up after yourself. */
+  gsl_integration_workspace_free(w);
+
   return result;
 }
 
@@ -140,116 +171,196 @@ double rt_interaction_rates_integrate_gsl(
  * @param phys_const physical constants struct
  * @param us internal units struct
  **/
-void rt_interaction_rates_init(struct rt_props *restrict rt_props,
-                               const struct phys_const *restrict phys_const,
-                               const struct unit_system *restrict us) {
+void rt_cross_sections_init(struct rt_props *restrict rt_props,
+                            const struct phys_const *restrict phys_const,
+                            const struct unit_system *restrict us) {
 
   /* Allocate the space to store the (cross section) integrals */
   /* --------------------------------------------------------- */
+
   double **cse = malloc(RT_NGROUPS * sizeof(double *));
   double **csn = malloc(RT_NGROUPS * sizeof(double *));
+  double *av_energy = rt_props->average_photon_energy;
+  double *photon_number_integral = rt_props->photon_number_integral;
   for (int group = 0; group < RT_NGROUPS; group++) {
-    cse[group] = malloc(RT_NIONIZING_SPECIES * sizeof(double));
-    csn[group] = malloc(RT_NIONIZING_SPECIES * sizeof(double));
+    cse[group] = malloc(rt_ionizing_species_count * sizeof(double));
+    csn[group] = malloc(rt_ionizing_species_count * sizeof(double));
+    av_energy[group] = 0.;
+    photon_number_integral[group] = 0.;
   }
 
   double integral_E[RT_NGROUPS];
-  double integral_sigma_E[RT_NGROUPS][RT_NIONIZING_SPECIES];
-  double integral_sigma_E_over_hnu[RT_NGROUPS][RT_NIONIZING_SPECIES];
-
-  /* Prepare parameter struct for integration functions */
-  /* -------------------------------------------------- */
-  struct rt_spectrum_integration_params integration_params = {0,  0,  0., 0.,
-                                                              0., 0., 0., NULL};
-  integration_params.spectrum_type = rt_props->stellar_spectrum_type;
-  integration_params.const_stellar_spectrum_frequency_max =
-      rt_props->const_stellar_spectrum_max_frequency;
+  double integral_N[RT_NGROUPS];
+  double integral_sigma_E[RT_NGROUPS][rt_ionizing_species_count];
+  double integral_sigma_E_over_hnu[RT_NGROUPS][rt_ionizing_species_count];
 
   /* Grab constants and conversions in cgs */
-  const double T_cgs = rt_props->stellar_spectrum_blackbody_T *
-                       units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE);
-  integration_params.T = T_cgs;
+  /* ------------------------------------- */
+  const double T_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TEMPERATURE);
 
   const float dimension_kB[5] = {1, 2, -2, 0, -1}; /* [g cm^2 s^-2 K^-1] */
   const double kB_to_cgs =
       units_general_cgs_conversion_factor(us, dimension_kB);
   const double kB_cgs = phys_const->const_boltzmann_k * kB_to_cgs;
-  integration_params.kB = kB_cgs;
 
   const float dimension_h[5] = {1, 2, -1, 0, 0}; /* [g cm^2 s^-1] */
   const double h_to_cgs = units_general_cgs_conversion_factor(us, dimension_h);
   const double h_planck_cgs = phys_const->const_planck_h * h_to_cgs;
-  integration_params.h_planck = h_planck_cgs;
 
   const double c_cgs = phys_const->const_speed_light_c *
                        units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
-  integration_params.c = c_cgs;
 
+  /* Prepare parameter struct for integration functions */
+  /* -------------------------------------------------- */
+  const int spectype = rt_props->stellar_spectrum_type;
+  const double maxfreq_const_spectrum =
+      rt_props->const_stellar_spectrum_max_frequency;
+  const double T_bb = rt_props->stellar_spectrum_blackbody_T * T_cgs;
   struct rt_photoion_cs_parameters cs_params = rt_init_photoion_cs_params_cgs();
-  integration_params.cs_params = &cs_params;
 
-  /* Compute integrals */
-  /* ----------------- */
+  struct rt_spectrum_integration_params integration_params = {
+      /*species=*/0,
+      /*spectrum_type=*/spectype,
+      /*freq_max for const spectrum=*/maxfreq_const_spectrum,
+      /*T=*/T_bb,
+      /*kB=*/kB_cgs,
+      /*h_planck=*/h_planck_cgs,
+      /*c=*/c_cgs,
+      /*cross section params=*/&cs_params};
+
+  /* Set up Integration Limits */
+  /* ------------------------- */
 
   /* Get start and end points of the integrals */
+  double nu_start[RT_NGROUPS];
+  double nu_stop[RT_NGROUPS];
+  for (int group = 0; group < RT_NGROUPS; group++)
+    nu_start[group] = rt_props->photon_groups[group];
+  for (int group = 0; group < RT_NGROUPS - 1; group++)
+    nu_stop[group] = rt_props->photon_groups[group + 1];
+
+  if (RT_NGROUPS == 1) {
+    /* If we only have one group, start integrating from the Hydrogen
+     * ionization frequency, not from zero. The reasoning here is that
+     * typically you define the *ionizing* radiation as stellar emission
+     * rates, not the *total* radiation. */
+    nu_start[0] = cs_params.E_ion[rt_ionizing_species_HI] / h_planck_cgs;
+    message(
+        "Warning: with only 1 photon group, I'll start integrating"
+        " the cross sections at the first ionizing frequency %.3g",
+        nu_start[0]);
+  } else {
+    /* don't start at exactly 0 to avoid unlucky divisions */
+    if (nu_start[0] == 0.) nu_start[0] = min(1e-20, 1e-12 * nu_start[1]);
+  }
+
+  /* Get frequency at which we stop integrating */
   double nu_stop_final;
   if (rt_props->stellar_spectrum_type == 0) {
     nu_stop_final = rt_props->const_stellar_spectrum_max_frequency;
   } else if (rt_props->stellar_spectrum_type == 1) {
-    nu_stop_final = 10. * blackbody_peak_frequency(integration_params.T,
-                                                   integration_params.kB,
-                                                   integration_params.h_planck);
+    nu_stop_final = 10. * blackbody_peak_frequency(T_bb, kB_cgs, h_planck_cgs);
   } else {
     nu_stop_final = -1.;
     error("Unknown stellar spectrum type %d", rt_props->stellar_spectrum_type);
   }
-
-  double nu_start[RT_NGROUPS];
-  double nu_stop[RT_NGROUPS];
-  for (int group = 0; group < RT_NGROUPS; group++)
-    nu_start[group] = rt_props->photon_groups[group];
-  for (int group = 0; group < RT_NGROUPS - 1; group++)
-    nu_stop[group] = rt_props->photon_groups[group + 1];
-
-  nu_start[0] = 1e-20; /* don't start at exactly 0 to avoid unlucky divisions */
   nu_stop[RT_NGROUPS - 1] = nu_stop_final;
 
-  for (int group = 0; group < RT_NGROUPS; group++) {
+  /* Compute Integrals */
+  /* ----------------- */
+  for (int g = 0; g < RT_NGROUPS; g++) {
     /* This is independent of species. */
-    integral_E[group] = rt_interaction_rates_integrate_gsl(
-        spectrum_integrand, nu_start[group], nu_stop[group],
+    integral_E[g] = rt_cross_sections_integrate_gsl(
+        spectrum_integrand, nu_start[g], nu_stop[g], RT_INTEGRAL_NPOINTS,
+        &integration_params);
+    integral_N[g] = rt_cross_sections_integrate_gsl(
+        spectrum_over_hnu_integrand, nu_start[g], nu_stop[g],
         RT_INTEGRAL_NPOINTS, &integration_params);
 
-    for (int species = 0; species < RT_NIONIZING_SPECIES; species++) {
-      integration_params.species = species;
-      integral_sigma_E[group][species] = rt_interaction_rates_integrate_gsl(
-          spectrum_times_sigma_integrand, nu_start[group], nu_stop[group],
+    for (int s = 0; s < rt_ionizing_species_count; s++) {
+      integration_params.species = s;
+      integral_sigma_E[g][s] = rt_cross_sections_integrate_gsl(
+          spectrum_times_sigma_integrand, nu_start[g], nu_stop[g],
+          RT_INTEGRAL_NPOINTS, &integration_params);
+      integral_sigma_E_over_hnu[g][s] = rt_cross_sections_integrate_gsl(
+          spectrum_times_sigma_over_hnu_integrand, nu_start[g], nu_stop[g],
           RT_INTEGRAL_NPOINTS, &integration_params);
-      integral_sigma_E_over_hnu[group][species] =
-          rt_interaction_rates_integrate_gsl(
-              spectrum_times_sigma_over_hnu_integrand, nu_start[group],
-              nu_stop[group], RT_INTEGRAL_NPOINTS, &integration_params);
     }
   }
 
   /* Now compute the actual average cross sections */
   /* --------------------------------------------- */
-  for (int group = 0; group < RT_NGROUPS; group++) {
-    for (int spec = 0; spec < RT_NIONIZING_SPECIES; spec++) {
-      if (integral_E[group] > 0.) {
-        cse[group][spec] = integral_sigma_E[group][spec] / integral_E[group];
-        csn[group][spec] =
-            integral_sigma_E_over_hnu[group][spec] / integral_E[group];
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    photon_number_integral[g] = integral_N[g];
+    if (integral_N[g] > 0.) {
+      av_energy[g] = integral_E[g] / integral_N[g];
+    } else {
+      av_energy[g] = 0.;
+    }
+    for (int s = 0; s < rt_ionizing_species_count; s++) {
+      if (integral_E[g] > 0.) {
+        cse[g][s] = integral_sigma_E[g][s] / integral_E[g];
+        csn[g][s] = integral_sigma_E_over_hnu[g][s] / integral_N[g];
       } else {
         /* No radiation = no interaction */
-        cse[group][spec] = 0.;
-        csn[group][spec] = 0.;
+        cse[g][s] = 0.;
+        csn[g][s] = 0.;
       }
     }
   }
 
+  /* for (int g = 0; g < RT_NGROUPS; g++) { */
+  /*   printf("\nGroup %d\n", g); */
+  /*   printf("nu_start:                  %12.6g\n", nu_start[g]); */
+  /*   printf("nu_end:                    %12.6g\n", nu_stop[g]); */
+  /*   printf("spectrum energy integral:  %12.6g\n", integral_E[g]); */
+  /*   printf("spectrum number integral:  %12.6g\n", integral_N[g]); */
+  /*   printf("average photon energy:     %12.6g\n", av_energy[g]); */
+  /*  */
+  /*   printf("species:                   "); */
+  /*   for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12d ", s);
+   */
+  /*   printf("\n"); */
+  /*  */
+  /*   printf("integral sigma * E:        "); */
+  /*   for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ",  */
+  /*       integral_sigma_E[g][s]); */
+  /*   printf("\n"); */
+  /*  */
+  /*   printf("integral sigma * E / h nu: "); */
+  /*   for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ",  */
+  /*       integral_sigma_E_over_hnu[g][s]); */
+  /*   printf("\n"); */
+  /*  */
+  /*   printf("energy weighted c.section: "); */
+  /*   for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ",  */
+  /*       cse[g][s]); */
+  /*   printf("\n"); */
+  /*  */
+  /*   printf("number weighted c.section: "); */
+  /*   for (int s = 0; s < rt_ionizing_species_count; s++) printf("%12.6g ",  */
+  /*       csn[g][s]); */
+  /*   printf("\n"); */
+  /* } */
+
   /* Store the results */
   /* ----------------- */
+
+  if (rt_props->energy_weighted_cross_sections != NULL) {
+    for (int g = 0; g < RT_NGROUPS; g++) {
+      if (rt_props->energy_weighted_cross_sections[g] != NULL)
+        free(rt_props->energy_weighted_cross_sections[g]);
+    }
+    free(rt_props->energy_weighted_cross_sections);
+  }
   rt_props->energy_weighted_cross_sections = cse;
+
+  if (rt_props->number_weighted_cross_sections != NULL) {
+    for (int g = 0; g < RT_NGROUPS; g++) {
+      if (rt_props->number_weighted_cross_sections[g] != NULL)
+        free(rt_props->number_weighted_cross_sections[g]);
+    }
+    free(rt_props->number_weighted_cross_sections);
+  }
   rt_props->number_weighted_cross_sections = csn;
 }
diff --git a/src/rt/GEAR/rt_interaction_cross_sections.h b/src/rt/GEAR/rt_interaction_cross_sections.h
new file mode 100644
index 0000000000000000000000000000000000000000..02be07c5a4b8972d9ca19c09809abe19158b74ef
--- /dev/null
+++ b/src/rt/GEAR/rt_interaction_cross_sections.h
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_RT_GEAR_INTERACION_CROSS_SECTIONS_H
+#define SWIFT_RT_GEAR_INTERACION_CROSS_SECTIONS_H
+
+#include "inline.h"
+#include "rt_species.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+/**
+ * @file src/rt/GEAR/rt_interaction_cross_sections.h
+ * @brief header file concerning photoionization cross sections
+ **/
+
+#define RT_INTEGRAL_NPOINTS 10000
+
+/*! Struct containing the parametrized cross section parameters
+ * for each photoionizing species.
+ * Correct usage is to call rt_init_photoion_cs_params_cgs(),
+ * which returns a fully initialized struct. */
+struct rt_photoion_cs_parameters {
+  double E_ion[rt_ionizing_species_count];      /* erg */
+  double E_zero[rt_ionizing_species_count];     /* erg */
+  double sigma_zero[rt_ionizing_species_count]; /* cm^-2 */
+  double P[rt_ionizing_species_count];
+  double ya[rt_ionizing_species_count];
+  double yw[rt_ionizing_species_count];
+  double y0[rt_ionizing_species_count];
+  double y1[rt_ionizing_species_count];
+};
+
+/*! Parameters needed to compute the stellar spectra integrals -
+ * the data needs to be encapsulated in a single struct to be passed
+ * as an argument for GSL integrators. */
+struct rt_spectrum_integration_params {
+  /* Which species are we dealing with? */
+  int species;
+  /* Which spectrum type are we dealing with? */
+  int spectrum_type;
+  /* Max frequency for const spectrum */
+  double const_stellar_spectrum_frequency_max;
+  /* Temperature of blackbody in correct units */
+  double T;
+  /* Boltzmann constant in correct units */
+  double kB;
+  /* Planck constant in correct units */
+  double h_planck;
+  /* speed of light in correct units */
+  double c;
+  /* Values for the cross section parametrization */
+  struct rt_photoion_cs_parameters *cs_params;
+};
+
+/**
+ * Initialize the parameters for the cross section computation in cgs,
+ * and return a fully and correctly initialized struct.
+ * The data is taken from Verner et al. 1996
+ * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013
+ * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R) (Table E1)
+ */
+__attribute__((always_inline)) INLINE static struct rt_photoion_cs_parameters
+rt_init_photoion_cs_params_cgs(void) {
+
+  struct rt_photoion_cs_parameters photoion_cs_params_cgs;
+  double E_ion[rt_ionizing_species_count];
+  rt_species_get_ionizing_energy(E_ion);
+
+  photoion_cs_params_cgs.E_ion[rt_ionizing_species_HI] =
+      E_ion[rt_ionizing_species_HI];
+  photoion_cs_params_cgs.E_zero[rt_ionizing_species_HI] = 6.886e-13;
+  photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HI] = 5.475e-14;
+  photoion_cs_params_cgs.P[rt_ionizing_species_HI] = 2.963;
+  photoion_cs_params_cgs.ya[rt_ionizing_species_HI] = 32.88;
+  photoion_cs_params_cgs.yw[rt_ionizing_species_HI] = 0.;
+  photoion_cs_params_cgs.y0[rt_ionizing_species_HI] = 0.;
+  photoion_cs_params_cgs.y1[rt_ionizing_species_HI] = 0.;
+
+  photoion_cs_params_cgs.E_ion[rt_ionizing_species_HeI] =
+      E_ion[rt_ionizing_species_HeI];
+  /* Note: The value of 0.1361 eV for E_0 of HeI given in table 5.1
+   * in Rosdahl et al. is an error. The correct value is 13.61 eV */
+  photoion_cs_params_cgs.E_zero[rt_ionizing_species_HeI] = 2.181e-11;
+  photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HeI] = 9.492e-16;
+  photoion_cs_params_cgs.P[rt_ionizing_species_HeI] = 3.188;
+  photoion_cs_params_cgs.ya[rt_ionizing_species_HeI] = 1.469;
+  photoion_cs_params_cgs.yw[rt_ionizing_species_HeI] = 2.039;
+  photoion_cs_params_cgs.y0[rt_ionizing_species_HeI] = 0.4434;
+  photoion_cs_params_cgs.y1[rt_ionizing_species_HeI] = 2.136;
+
+  photoion_cs_params_cgs.E_ion[rt_ionizing_species_HeII] =
+      E_ion[rt_ionizing_species_HeII];
+  photoion_cs_params_cgs.E_zero[rt_ionizing_species_HeII] = 2.756e-12;
+  photoion_cs_params_cgs.sigma_zero[rt_ionizing_species_HeII] = 1.369e-14;
+  photoion_cs_params_cgs.P[rt_ionizing_species_HeII] = 2.963;
+  photoion_cs_params_cgs.ya[rt_ionizing_species_HeII] = 32.88;
+  photoion_cs_params_cgs.yw[rt_ionizing_species_HeII] = 0.;
+  photoion_cs_params_cgs.y0[rt_ionizing_species_HeII] = 0.;
+  photoion_cs_params_cgs.y1[rt_ionizing_species_HeII] = 0.;
+
+  return photoion_cs_params_cgs;
+}
+
+/**
+ * Compute the parametrized cross section for a given energy and species.
+ * The parametrization is taken from Verner et al. 1996
+ * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013
+ * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R)
+ *
+ * @param E energy for which to compute the cross section, in erg
+ * @param species index of species, 0 < species < rt_ionizing_species_count
+ * @param params cross section parameters struct
+ */
+__attribute__((always_inline)) INLINE static double
+photoionization_cross_section(const double E, const int species,
+                              const struct rt_photoion_cs_parameters *params) {
+
+  const double E0 = params->E_zero[species];
+  const double E_ion = params->E_ion[species];
+  const double y0 = params->y0[species];
+  const double y1 = params->y1[species];
+  const double yw = params->yw[species];
+  const double ya = params->ya[species];
+  const double P = params->P[species];
+  const double sigma_0 = params->sigma_zero[species];
+
+  if (E < E_ion) return 0.;
+
+  const double x = E / E0 - y0;
+  const double y = sqrt(x * x + y1 * y1);
+  const double temp1 = pow(y, 0.5 * P - 5.5);
+  const double temp2 = pow(1. + sqrt(y / ya), -P);
+  return sigma_0 * ((x - 1.) * (x - 1.) + yw * yw) * temp1 * temp2;
+}
+
+#endif /* SWIFT_RT_GEAR_INTERACION_CROSS_SECTIONS_H */
diff --git a/src/rt/GEAR/rt_interaction_rates.h b/src/rt/GEAR/rt_interaction_rates.h
index 67c2272c47408326ac6a2c2054909b1b76795b25..ebc14a307ec59a621811b91c31206f4b3f84550f 100644
--- a/src/rt/GEAR/rt_interaction_rates.h
+++ b/src/rt/GEAR/rt_interaction_rates.h
@@ -1,6 +1,6 @@
 /*******************************************************************************
  * This file is part of SWIFT.
- * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
+ * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -16,111 +16,162 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#ifndef SWIFT_RT_GEAR_INTERACION_RATES_H
-#define SWIFT_RT_GEAR_INTERACION_RATES_H
-
-#include "inline.h"
-
-#include <math.h>
-
-#define RT_INTEGRAL_NPOINTS 1000
-
-/* HI, HeI, HeII */
-#define RT_NIONIZING_SPECIES 3
-
-/*! Struct containing the parametrized cross section parameters
- * for each photoionizing species.
- * Correct usage is to call rt_init_photoion_cs_params_cgs(),
- * which returns a fully initialized struct. */
-struct rt_photoion_cs_parameters {
-  double E_ion[RT_NIONIZING_SPECIES];
-  double E_zero[RT_NIONIZING_SPECIES];
-  double sigma_zero[RT_NIONIZING_SPECIES];
-  double P[RT_NIONIZING_SPECIES];
-  double ya[RT_NIONIZING_SPECIES];
-  double yw[RT_NIONIZING_SPECIES];
-  double y0[RT_NIONIZING_SPECIES];
-  double y1[RT_NIONIZING_SPECIES];
-};
-
-/*! Parameters needed to compute the stellar spectra integrals -
- * the data needs to be encapsulated in a single struct to be passed
- * as an argument for GSL integrators. */
-struct rt_spectrum_integration_params {
-  /* Which species are we dealing with? */
-  int species;
-  /* Which spectrum type are we dealing with? */
-  int spectrum_type;
-  /* Max frequency for const spectrum */
-  double const_stellar_spectrum_frequency_max;
-  /* Temperature of blackbody in correct units */
-  double T;
-  /* Boltzmann constant in correct units */
-  double kB;
-  /* Planck constant in correct units */
-  double h_planck;
-  /* speed of light in correct units */
-  double c;
-  /* Values for the cross section parametrization */
-  struct rt_photoion_cs_parameters *cs_params;
-};
+#ifndef SWIFT_RT_GEAR_INTERACTION_RATES_H
+#define SWIFT_RT_GEAR_INTERACTION_RATES_H
+
+#include "rt_parameters.h"
+#include "rt_properties.h"
+#include "rt_species.h"
+#include "rt_thermochemistry_utils.h"
+
+/**
+ * @file src/rt/GEAR/rt_interaction_rates.h
+ * @brief header file concerning photoionization and photoheating rates
+ **/
 
 /**
- * Initialize the parameters for the cross section computation in cgs,
- * and return a fully and correctly initialized struct.
- * The data is taken from Verner et al. 1996
- * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013
- * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R)
- */
-__attribute__((always_inline)) INLINE static struct rt_photoion_cs_parameters
-rt_init_photoion_cs_params_cgs(void) {
-
-  struct rt_photoion_cs_parameters photoion_cs_params_cgs = {
-      /* E_ion =         {13.60,     24.59,     54.42}          eV */
-      /* E_zero =        {0.4298,    0.1361,    1.720},         eV */
-      /* E_ion =      */ {2.179e-11, 3.940e-11, 8.719e-11},  /* erg */
-      /* E_zero =     */ {6.886e-13, 2.181 - 13, 2.756e-12}, /* erg */
-      /* sigma_zero = */ {5.475e-14, 9.492e-16, 1.369e-14},  /* cm^-2 */
-      /* P =          */ {2.963, 3.188, 2.963},
-      /* ya =         */ {32.88, 1.469, 32.88},
-      /* yw =         */ {0., 2.039, 0.},
-      /* y0 =         */ {0., 0.4434, 0.},
-      /* y1 =         */ {0., 2.136, 0.}};
-
-  return photoion_cs_params_cgs;
+ * @brief compute the heating, ionization, and dissassociation rates
+ * for the particle radiation field as needed by grackle.
+ *
+ * @param rates (return) Interaction rates for grackle. [0]: heating rate.
+ * [1]: HI ionization. [2]: HeI ionization. [3]: HeII ionization.
+ * [4]: H2 dissociation.
+ * @param energy_density energy densities of each photon group [internal units]
+ * @param species_densities physical densities of all species [internal units]
+ * @param average_photon_energy mean photon energy in group, in erg
+ * @param cse energy weighted photon interaction cross sections, in cm^2
+ * @param csn number weighted photon interaction cross sections, in cm^2
+ * @param phys_const physical constants struct
+ * @param us internal units struct
+ **/
+__attribute__((always_inline)) INLINE static void
+rt_get_interaction_rates_for_grackle(
+    gr_float rates[5], float energy_density[RT_NGROUPS],
+    gr_float species_densities[6],
+    const double average_photon_energy[RT_NGROUPS], double **cse, double **csn,
+    const struct phys_const *restrict phys_const,
+    const struct unit_system *restrict us) {
+
+  rates[0] = 0.; /* Needs to be in [erg / s / cm^3 / nHI] for grackle. */
+  rates[1] = 0.; /* [1 / time_units] */
+  rates[2] = 0.; /* [1 / time_units] */
+  rates[3] = 0.; /* [1 / time_units] */
+  rates[4] = 0.; /* [1 / time_units] */
+
+  double E_ion_cgs[rt_ionizing_species_count];
+  rt_species_get_ionizing_energy(E_ion_cgs);
+
+  /* Get some conversions and constants first. */
+  const double c_cgs = rt_params.reduced_speed_of_light *
+                       units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
+  const double to_energy_density_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_DENSITY);
+  const double inv_time_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME);
+
+  /* get species number densities in cgs */
+  double ns_cgs[rt_ionizing_species_count]; /* in cm^-3 */
+  rt_tchem_get_ionizing_species_number_densities(ns_cgs, species_densities,
+                                                 phys_const, us);
+
+  /* store photoionization rate for each species here */
+  double ionization_rates[rt_ionizing_species_count];
+  for (int s = 0; s < rt_ionizing_species_count; s++) {
+    ionization_rates[s] = 0.;
+  }
+
+  for (int g = 0; g < RT_NGROUPS; g++) {
+
+    /* Sum results for this group over all species */
+    double heating_rate_group_cgs = 0.;
+    const double Eg = energy_density[g] * to_energy_density_cgs;
+    const double Emean_g = average_photon_energy[g];
+    const double Ng = (Emean_g > 0.) ? Eg / Emean_g : 0.;
+
+    for (int s = 0; s < rt_ionizing_species_count; s++) {
+      /* All quantities here are in cgs. */
+      heating_rate_group_cgs +=
+          (cse[g][s] * Emean_g - E_ion_cgs[s] * csn[g][s]) * ns_cgs[s];
+      ionization_rates[s] += csn[g][s] * Ng * c_cgs;
+    }
+    rates[0] += heating_rate_group_cgs * Ng * c_cgs;
+  }
+
+  /* Convert into correct units. */
+  const double nHI = ns_cgs[rt_ionizing_species_HI];
+  if (nHI > 0.)
+    rates[0] /= nHI;
+  else
+    rates[0] = 0.;
+
+  for (int s = 0; s < rt_ionizing_species_count; s++) {
+    ionization_rates[s] /= inv_time_cgs; /* internal units T^-1 */
+#ifdef SWIFT_RT_DEBUG_CHECKS
+    if (ionization_rates[s] < 0.)
+      error("unphysical ion rate spec %d - %.4g", s, ionization_rates[s]);
+#endif
+  }
+
+  /* We're done. Write the results in correct place */
+  rates[1] = ionization_rates[0];
+  rates[2] = ionization_rates[1];
+  rates[3] = ionization_rates[2];
+  /* rates[4] = skipped for now */
+
+#ifdef SWIFT_RT_DEBUG_CHECKS
+  for (int i = 0; i < 5; i++) {
+    if (rates[i] < 0.) error("unphysical rate %d %.4g", i, rates[i]);
+  }
+#endif
 }
 
 /**
- * Compute the parametrized cross section for a given energy and species.
- * The parametrization is taken from Verner et al. 1996
- * (ui.adsabs.harvard.edu/abs/1996ApJ...465..487V) via Rosdahl et al. 2013
- * (ui.adsabs.harvard.edu/abs/2013MNRAS.436.2188R)
+ * @brief compute the rates at which the photons get absorbed/destroyed
+ * during interactions with gas.
  *
- * @param E energy for which to compute the cross section.
- * @param species index of species, 0 < species < RT_NIONIZING_SPECIES
- * @param params cross section parameters struct
- */
-__attribute__((always_inline)) INLINE static double
-photoionization_cross_section(const double E, const int species,
-                              const struct rt_photoion_cs_parameters *params) {
-
-  const double E0 = params->E_zero[species];
-  const double E_ion = params->E_ion[species];
-  const double y0 = params->y0[species];
-  const double y1 = params->y1[species];
-  const double yw = params->yw[species];
-  const double ya = params->ya[species];
-  const double P = params->P[species];
-  const double sigma_0 = params->sigma_zero[species];
-
-  if (E < E_ion) return 0.;
-
-  const double x = E / E0 - y0;
-  const double y = sqrt(x * x + y1 * y1);
-  const double temp1 = pow(y, 0.5 * P - 5.5);
-  const double temp2 = pow(1. + sqrt(y / ya), -P);
-
-  return sigma_0 * ((x - 1.) * (x - 1.) + yw * yw) * temp1 * temp2;
+ * @param absorption_rates (return) the energy absorption rates in
+ * internal units for each photon group.
+ * @param species_densities the physical densities of all traced species
+ *[internal units]
+ * @param average_photon_energy mean photon energy in group, in erg
+ * @param csn number weighted photon interaction cross sections, in cm^2
+ * @param phys_const physical constants struct
+ * @param us internal units struct
+ **/
+__attribute__((always_inline)) INLINE static void rt_get_absorption_rates(
+    double absorption_rates[RT_NGROUPS], gr_float species_densities[6],
+    const double average_photon_energy[RT_NGROUPS], double **csn,
+    const struct phys_const *restrict phys_const,
+    const struct unit_system *restrict us) {
+
+  for (int g = 0; g < RT_NGROUPS; g++) absorption_rates[g] = 0.;
+
+  double E_ion_cgs[rt_ionizing_species_count];
+  rt_species_get_ionizing_energy(E_ion_cgs);
+
+  /* Get some conversions and constants first. */
+  const double c_cgs = rt_params.reduced_speed_of_light *
+                       units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
+  const double inv_time_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME);
+
+  double ns_cgs[rt_ionizing_species_count]; /* in cm^-3 */
+  rt_tchem_get_ionizing_species_number_densities(ns_cgs, species_densities,
+                                                 phys_const, us);
+
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    for (int s = 0; s < rt_ionizing_species_count; s++) {
+      absorption_rates[g] += c_cgs * csn[g][s] * ns_cgs[s];
+    }
+  }
+
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    absorption_rates[g] /= inv_time_cgs;
+#ifdef SWIFT_RT_DEBUG_CHECKS
+    if (absorption_rates[g] < 0.)
+      error("unphysical rate %d - %.4g", g, absorption_rates[g]);
+#endif
+  }
 }
 
 #endif /* SWIFT_RT_GEAR_INTERACION_RATES_H */
diff --git a/src/rt/GEAR/rt_io.h b/src/rt/GEAR/rt_io.h
index e67b73ed84c1375616edca071f5145e5822394ec..57b70efd02d66a16bf846d7e891057ee7d0fbc7e 100644
--- a/src/rt/GEAR/rt_io.h
+++ b/src/rt/GEAR/rt_io.h
@@ -175,7 +175,7 @@ INLINE static int rt_write_particles(const struct part* parts,
       "Mass fractions of all constituent species");
 
 #ifdef SWIFT_RT_DEBUG_CHECKS
-  num_elements += 7;
+  num_elements += 8;
   list[3] =
       io_make_output_field("RTDebugInjectionDone", INT, 1, UNIT_CONV_NO_UNITS,
                            0, parts, rt_data.debug_injection_done,
@@ -207,6 +207,9 @@ INLINE static int rt_write_particles(const struct part* parts,
       "RTDebugRadAbsorbedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, parts,
       rt_data.debug_radiation_absorbed_tot,
       "Radiation absorbed by this part during its lifetime");
+  list[10] = io_make_output_field(
+      "RTDebugSubcycles", INT, 1, UNIT_CONV_NO_UNITS, 0, parts,
+      rt_data.debug_nsubcycles, "How many times this part was subcycled");
 #endif
 
   return num_elements;
diff --git a/src/rt/GEAR/rt_ionization_equilibrium.h b/src/rt/GEAR/rt_ionization_equilibrium.h
index 376be5e5013f07d90f13028fa86f296615247272..c3c82b82c9529019135a3d01dc9afbca40d69a59 100644
--- a/src/rt/GEAR/rt_ionization_equilibrium.h
+++ b/src/rt/GEAR/rt_ionization_equilibrium.h
@@ -49,19 +49,22 @@ rt_ion_equil_mass_fractions_from_T(double T, float X, float Y, float* XHI,
                                    float* XHII, float* XHeI, float* XHeII,
                                    float* XHeIII) {
 
+  if (fabsf(X + Y - 1.f) > 1e-4)
+    error("mass fractions don't add up to one: X=%.3e, Y=%.3e", X, Y);
+
   /* Total number densities for all H and He species, respectively.
    * We don't really care about the actual number densities of the
    * species, but only want the mass fractions. So all quantities
    * will be per unit volume, specifically per rho_gas / m_proton. */
-  float nH, nHe;
+  double nH, nHe;
 
   /* TODO: this assumes no metals. */
-  if (X == 0.f) {
-    nH = 0.f;
-    nHe = 1.f;
+  if (X <= RT_GEAR_TINY_MASS_FRACTION) {
+    nH = RT_GEAR_TINY_MASS_FRACTION;
+    nHe = 1.;
   } else {
     nH = X;
-    nHe = 0.25f * Y;
+    nHe = 0.25 * Y;
   }
 
   /* Number densities of the actual species */
@@ -117,20 +120,25 @@ rt_ion_equil_mass_fractions_from_T(double T, float X, float Y, float* XHI,
 
   /* With the number densities per unit volume given,
    * let's compute the mass fractions now. */
-  const float mHI = nHI;
-  const float mHII = nHII;
-  const float mHeI = 4.0f * nHeI;
-  const float mHeII = 4.0f * nHeII;
-  const float mHeIII = 4.0f * nHeIII;
+  const double mHI = nHI;
+  const double mHII = nHII;
+  const double mHeI = 4.0f * nHeI;
+  const double mHeII = 4.0f * nHeII;
+  const double mHeIII = 4.0f * nHeIII;
   /* we ignore the electron mass fraction. */
 
-  const float mtot_inv = 1.f / (mHI + mHII + mHeI + mHeII + mHeIII);
-
-  *XHI = mHI * mtot_inv;
-  *XHII = mHII * mtot_inv;
-  *XHeI = mHeI * mtot_inv;
-  *XHeII = mHeII * mtot_inv;
-  *XHeIII = mHeIII * mtot_inv;
+  const double mtot_inv = 1. / (mHI + mHII + mHeI + mHeII + mHeIII);
+
+  *XHI = (float)(mHI * mtot_inv);
+  *XHI = max(*XHI, RT_GEAR_TINY_MASS_FRACTION);
+  *XHII = (float)(mHII * mtot_inv);
+  *XHII = max(*XHII, RT_GEAR_TINY_MASS_FRACTION);
+  *XHeI = (float)(mHeI * mtot_inv);
+  *XHeI = max(*XHeI, RT_GEAR_TINY_MASS_FRACTION);
+  *XHeII = (float)(mHeII * mtot_inv);
+  *XHeII = max(*XHeII, RT_GEAR_TINY_MASS_FRACTION);
+  *XHeIII = (float)(mHeIII * mtot_inv);
+  *XHeIII = max(*XHeIII, RT_GEAR_TINY_MASS_FRACTION);
 }
 
 /**
diff --git a/src/rt/GEAR/rt_properties.h b/src/rt/GEAR/rt_properties.h
index 89515d449bd9d468b3a0f76bb1c1f018f38e49fc..e580cc6f2c29ba67e35e507d556a316f30285764 100644
--- a/src/rt/GEAR/rt_properties.h
+++ b/src/rt/GEAR/rt_properties.h
@@ -19,10 +19,11 @@
 #ifndef SWIFT_RT_PROPERTIES_GEAR_H
 #define SWIFT_RT_PROPERTIES_GEAR_H
 
-#include "hydro.h" /* Need hydro_gamma */
+#include "rt_grackle_utils.h"
+#include "rt_interaction_rates.h"
 #include "rt_parameters.h"
+#include "rt_stellar_emission_model.h"
 
-#include <grackle.h>
 #include <string.h>
 
 /**
@@ -38,12 +39,10 @@
  */
 struct rt_props {
 
-  /* Are we using constant stellar emission rates? */
-  int use_const_emission_rates;
+  /* Which stellar emission model to use */
+  enum rt_stellar_emission_models stellar_emission_model;
 
-  /* Frequency bin edges for photon groups
-   * Includes 0 as leftmost edge, doesn't include infinity as
-   * rightmost bin edge*/
+  /* (Lower) frequency bin edges for photon groups */
   float photon_groups[RT_NGROUPS];
 
   /* Global constant stellar emission rates */
@@ -52,6 +51,9 @@ struct rt_props {
   /* CFL condition */
   float CFL_condition;
 
+  /* Factor to limit cooling time by */
+  float f_limit_cooling_time;
+
   /* do we set initial ionization mass fractions manually? */
   int set_initial_ionization_mass_fractions;
   int set_equilibrium_initial_ionization_mass_fractions;
@@ -83,8 +85,13 @@ struct rt_props {
   double stellar_spectrum_blackbody_T;
 
   /* Storage for integrated photoionization cross sections */
+  /* Note: they are always in cgs. */
   double** energy_weighted_cross_sections;
   double** number_weighted_cross_sections;
+  /* Mean photon energy in frequency bin for user provided spectrum. In erg.*/
+  double average_photon_energy[RT_NGROUPS];
+  /* Integral over photon numbers of user provided spectrum. */
+  double photon_number_integral[RT_NGROUPS];
 
   /* Grackle Stuff */
   /* ------------- */
@@ -118,140 +125,30 @@ struct rt_props {
    * but a placeholder to sum up a global variable */
   unsigned long long debug_radiation_absorbed_tot;
 
-  /* Total radiation energy in the gas. It's being reset every step. */
-  float debug_total_radiation_conserved_energy[RT_NGROUPS];
-  float debug_total_star_emitted_energy[RT_NGROUPS];
-
-  /* Files to write energy budget to after every step */
-  FILE* conserved_energy_filep;
-  FILE* star_emitted_energy_filep;
+  /* Max number of subcycles per hydro step */
+  int debug_max_nr_subcycles;
 #endif
 };
 
-/* Declare this here to avoid cyclical inclusions.
- * It needs to be after definition of rt_props, but
- * before the call to rt_props_init.*/
-void rt_interaction_rates_init(struct rt_props* restrict rt_props,
-                               const struct phys_const* restrict phys_const,
-                               const struct unit_system* restrict us);
+/* Some declarations to avoid cyclical inclusions. */
+/* ----------------------------------------------- */
+/* Keep the declarations for *after* the definition of rt_props struct */
 
 /**
- * @brief open up files to write some debugging check outputs.
- * This function is temporary for development, and shouldn't stay
- * long.
+ * @brief allocate and pre-compute the averaged cross sections
+ * for each photon group and ionizing species.
+ * Declare this here to avoid cyclical inclusions.
  *
- * @param rtp #rt_props struct
- * @param mode open files with this mode. "w" for new file, "a" for append.
+ * @param rt_props RT properties struct
+ * @param phys_const physical constants struct
+ * @param us internal units struct
  **/
-#ifdef SWIFT_RT_DEBUG_CHECKS
-static void rt_props_open_debugging_files(struct rt_props* rtp,
-                                          const char* mode) {
-#ifdef WITH_MPI
-  return;
-#endif
+void rt_cross_sections_init(struct rt_props* restrict rt_props,
+                            const struct phys_const* restrict phys_const,
+                            const struct unit_system* restrict us);
 
-  rtp->conserved_energy_filep = fopen("RT_conserved_energy_budget.txt", mode);
-  if (rtp->conserved_energy_filep == NULL)
-    error("Couldn't open RT conserved energy budget file to write in");
-  rtp->star_emitted_energy_filep = fopen("RT_star_injected_energy.txt", mode);
-  if (rtp->star_emitted_energy_filep == NULL)
-    error("Couldn't open RT star energy budget file to write in");
-
-  if (strcmp(mode, "w") == 0 && rtp->use_const_emission_rates) {
-    /* If we're starting a new file, dump the header first */
-    FILE* files[2] = {rtp->conserved_energy_filep,
-                      rtp->star_emitted_energy_filep};
-    for (int f = 0; f < 2; f++) {
-      fprintf(files[f], "# Emission rates: ");
-      const double solar_luminosity = 3.826e33; /* erg/s */
-      for (int g = 0; g < RT_NGROUPS; g++) {
-        fprintf(files[f], "%12.6e ",
-                rtp->stellar_const_emission_rates[g] * solar_luminosity);
-      }
-      fprintf(files[f], "\n");
-    }
-  }
-};
-#endif
-
-/**
- * @brief initialize grackle during rt_props_init
- *
- * @param rtp #rt_props struct
- * @param us #unit_system struct
- **/
-__attribute__((always_inline)) INLINE static void rt_props_init_grackle(
-    struct rt_props* rtp, const struct unit_system* us) {
-
-  /* TODO: cleanup later with all other grackle stuff */
-  /* #ifdef SWIFT_RT_DEBUG_CHECKS */
-  /*   grackle_verbose = 1; */
-  /* #endif */
-
-  /* Initialize units */
-  /* ---------------- */
-  /* we assume all quantities to be physical, not comoving */
-  rtp->grackle_units.a_units = 1.0;
-  rtp->grackle_units.a_value = 1.0;
-  rtp->grackle_units.comoving_coordinates = 0;
-  rtp->grackle_units.density_units =
-      units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
-  rtp->grackle_units.length_units =
-      units_cgs_conversion_factor(us, UNIT_CONV_LENGTH);
-  rtp->grackle_units.time_units =
-      units_cgs_conversion_factor(us, UNIT_CONV_TIME);
-  rtp->grackle_units.velocity_units =
-      units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
-
-  /* Chemistry Parameters */
-  /* -------------------- */
-  /* More details on https://grackle.readthedocs.io/en/latest/Parameters.html */
-
-  /* TODO: cleanup later with all other grackle stuff */
-  /* rtp->grackle_chemistry_data = _set_default_chemistry_parameters(); */
-  if (set_default_chemistry_parameters(&rtp->grackle_chemistry_data) == 0) {
-    error("Error in set_default_chemistry_parameters.");
-  }
-  /* chemistry on */
-  rtp->grackle_chemistry_data.use_grackle = 1;
-  /* cooling on */
-  /* NOTE: without cooling on, it also won't heat... */
-  rtp->grackle_chemistry_data.with_radiative_cooling = 1;
-  /* 6 species atomic H and He. */
-  rtp->grackle_chemistry_data.primordial_chemistry = 1;
-  /* dust processes */
-  rtp->grackle_chemistry_data.dust_chemistry = 0;
-  /* H2 formation on dust */
-  rtp->grackle_chemistry_data.h2_on_dust = 0;
-  /* metal cooling (uses Cloudy) off (for now) */
-  rtp->grackle_chemistry_data.metal_cooling = 0;
-  /* no cooling below CMB temperature */
-  rtp->grackle_chemistry_data.cmb_temperature_floor = 1;
-  /* UV background off */
-  rtp->grackle_chemistry_data.UVbackground = 0;
-  /* data file - currently not used. */
-  rtp->grackle_chemistry_data.grackle_data_file = "";
-  /* adiabatic index */
-  rtp->grackle_chemistry_data.Gamma = hydro_gamma;
-  /* we'll provide grackle with ionization and heating rates from RT */
-  rtp->grackle_chemistry_data.use_radiative_transfer = 1;
-  /* fraction by mass of Hydrogen in the metal-free portion of the gas */
-  rtp->grackle_chemistry_data.HydrogenFractionByMass =
-      rtp->hydrogen_mass_fraction;
-
-  /* TODO: cleanup later with all other grackle stuff */
-  /* Initialize the chemistry_data_storage object to be
-   * able to use local functions */
-  /* chemistry_data_storage* chem_data_storage = */
-  /*     malloc(sizeof(chemistry_data_storage)); */
-  /* rtp->grackle_chemistry_rates = chem_data_storage; */
-  /* if (!_initialize_chemistry_data(&rtp->grackle_chemistry_data, */
-  /*                                 rtp->grackle_chemistry_rates, */
-  /*                                 &rtp->grackle_units)) { */
-  if (initialize_chemistry_data(&rtp->grackle_units) == 0) {
-    error("Error in _initialize_chemistry_data");
-  }
-}
+/* Now for the good stuff                */
+/* ------------------------------------- */
 
 /**
  * @brief Print the RT model.
@@ -265,16 +162,16 @@ __attribute__((always_inline)) INLINE static void rt_props_print(
   if (engine_rank != 0) return;
 
   message("Radiative transfer scheme: '%s'", RT_IMPLEMENTATION);
-  char messagestring[200] = "Using photon frequency bins: [0.";
+  char messagestring[200] = "Using photon frequency bins: [ ";
   char freqstring[20];
-  for (int g = 1; g < RT_NGROUPS; g++) {
-    sprintf(freqstring, ", %.3g", rtp->photon_groups[g]);
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    sprintf(freqstring, "%.3g ", rtp->photon_groups[g]);
     strcat(messagestring, freqstring);
   }
   strcat(messagestring, "]");
   message("%s", messagestring);
 
-  if (rtp->use_const_emission_rates) {
+  if (rtp->stellar_emission_model == rt_stellar_emission_model_const) {
     strcpy(messagestring, "Using constant stellar emission rates: [ ");
     for (int g = 0; g < RT_NGROUPS; g++) {
       sprintf(freqstring, "%.3g ", rtp->stellar_const_emission_rates[g]);
@@ -282,6 +179,11 @@ __attribute__((always_inline)) INLINE static void rt_props_print(
     }
     strcat(messagestring, "]");
     message("%s", messagestring);
+  } else if (rtp->stellar_emission_model ==
+             rt_stellar_emission_model_IlievTest) {
+    message("Using Iliev+06 Test 4 stellar emission model.");
+  } else {
+    error("Unknown stellar emission model %d", rtp->stellar_emission_model);
   }
 
   if (rtp->set_equilibrium_initial_ionization_mass_fractions)
@@ -319,41 +221,66 @@ __attribute__((always_inline)) INLINE static void rt_props_init(
         "You need to run GEAR-RT with at least 1 photon group, "
         "you have %d",
         RT_NGROUPS);
-  } else if (RT_NGROUPS == 1) {
-    rtp->photon_groups[0] = 0.f;
   } else {
-    float frequencies[RT_NGROUPS - 1];
     /* !! Keep the frequencies in Hz for now. !! */
-    parser_get_param_float_array(params, "GEARRT:photon_groups_Hz",
-                                 RT_NGROUPS - 1, frequencies);
-    for (int g = 0; g < RT_NGROUPS - 1; g++) {
-      rtp->photon_groups[g + 1] = frequencies[g];
-    }
-    rtp->photon_groups[0] = 0.f;
+    parser_get_param_float_array(params, "GEARRT:photon_groups_Hz", RT_NGROUPS,
+                                 rtp->photon_groups);
+  }
+
+  /* Sanity check: photon group edges must be in increasing order. */
+  for (int g = 0; g < RT_NGROUPS - 1; g++) {
+    if (rtp->photon_groups[g + 1] <= rtp->photon_groups[g])
+      error(
+          "Photon frequency bin edges need to be in increasing order. "
+          "Found index %d %.3g <= %.3g",
+          g + 1, rtp->photon_groups[g + 1], rtp->photon_groups[g]);
   }
 
-  /* Are we using constant emission rates? */
-  /* ------------------------------------- */
-  rtp->use_const_emission_rates = parser_get_opt_param_int(
-      params, "GEARRT:use_const_emission_rates", /* default = */ 0);
+  /* Get stellar emission rate model related parameters */
+  /* -------------------------------------------------- */
 
-  if (rtp->use_const_emission_rates) {
+  /* First initialize everything */
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    rtp->stellar_const_emission_rates[g] = 0.;
+  }
+
+  rtp->stellar_emission_model = rt_stellar_emission_model_none;
+
+  char stellar_model_str[80];
+  parser_get_param_string(params, "GEARRT:stellar_luminosity_model",
+                          stellar_model_str);
+
+  if (strcmp(stellar_model_str, "const") == 0) {
+    rtp->stellar_emission_model = rt_stellar_emission_model_const;
+  } else if (strcmp(stellar_model_str, "IlievTest4") == 0) {
+    rtp->stellar_emission_model = rt_stellar_emission_model_IlievTest;
+  } else {
+    error("Unknown stellar luminosity model '%s'", stellar_model_str);
+  }
+
+  if (rtp->stellar_emission_model == rt_stellar_emission_model_const) {
+    /* Read the luminosities from the parameter file */
     double emission_rates[RT_NGROUPS];
-    parser_get_param_double_array(params, "GEARRT:star_emission_rates_LSol",
+    parser_get_param_double_array(params,
+                                  "GEARRT:const_stellar_luminosities_LSol",
                                   RT_NGROUPS, emission_rates);
     const double unit_power = units_cgs_conversion_factor(us, UNIT_CONV_POWER);
     const double unit_power_inv = 1. / unit_power;
     for (int g = 0; g < RT_NGROUPS; g++) {
       rtp->stellar_const_emission_rates[g] = emission_rates[g] * unit_power_inv;
     }
+  } else if (rtp->stellar_emission_model ==
+             rt_stellar_emission_model_IlievTest) {
+    /* Nothing to do here */
   } else {
-    /* kill the run for now */
-    error("GEAR-RT can't run without constant stellar emission rates for now.");
+    error("Unknown stellar emission model %d", rtp->stellar_emission_model);
   }
 
   /* get reduced speed of light factor */
   /* --------------------------------- */
   const float f_r = parser_get_param_float(params, "GEARRT:f_reduce_c");
+  if (f_r <= 0.f)
+    error("Invalid speed of light reduction factor: %.3e <= 0.", f_r);
   rt_params.reduced_speed_of_light = phys_const->const_speed_light_c * f_r;
   rt_params.reduced_speed_of_light_inverse =
       1.f / rt_params.reduced_speed_of_light;
@@ -361,13 +288,25 @@ __attribute__((always_inline)) INLINE static void rt_props_init(
   /* get CFL condition */
   /* ----------------- */
   const float CFL = parser_get_param_float(params, "GEARRT:CFL_condition");
+  if (CFL <= 0.f) error("Invalid CFL number: %.3e <= 0.", CFL);
   rtp->CFL_condition = CFL;
 
+  const float f_limit_cooling_time = parser_get_opt_param_float(
+      params, "GEARRT:f_limit_cooling_time", /*default=*/0.9);
+  if (f_limit_cooling_time < 0.f)
+    error("Invalid cooling time reduction factor: %.3e < 0.",
+          f_limit_cooling_time);
+  else if (f_limit_cooling_time == 0.f)
+    message("Warning: Computation of cooling time will be skipped");
+  rtp->f_limit_cooling_time = f_limit_cooling_time;
+
   /* Get thermochemistry set-up */
   /* -------------------------- */
   rtp->hydrogen_mass_fraction =
       parser_get_param_float(params, "GEARRT:hydrogen_mass_fraction");
   rtp->helium_mass_fraction = 1.f - rtp->hydrogen_mass_fraction;
+  if (rtp->hydrogen_mass_fraction <= 0.f || rtp->hydrogen_mass_fraction > 1.f)
+    error("Invalid hydrogen mass fraction: %g", rtp->hydrogen_mass_fraction);
 
   /* Are we manually overwriting initial mass fractions of H and He? */
   rtp->set_initial_ionization_mass_fractions = parser_get_opt_param_int(
@@ -477,31 +416,26 @@ __attribute__((always_inline)) INLINE static void rt_props_init(
   rtp->debug_radiation_absorbed_tot = 0ULL;
   rtp->debug_radiation_absorbed_this_step = 0ULL;
 
-  for (int g = 0; g < RT_NGROUPS; g++)
-    rtp->debug_total_star_emitted_energy[g] = 0.f;
-
-  rt_props_open_debugging_files(rtp, "w");
-
+  /* Don't make it an optional parameter here so we crash
+   * if I forgot to provide it */
+  rtp->debug_max_nr_subcycles =
+      parser_get_param_int(params, "TimeIntegration:max_nr_rt_subcycles");
 #endif
 
   /* Grackle setup */
   /* ------------- */
-  rt_props_init_grackle(rtp, us);
+  rt_init_grackle(&rtp->grackle_units, &rtp->grackle_chemistry_data,
+                  rtp->hydrogen_mass_fraction, us, params);
 
   /* Pre-compute interaction rates/cross sections */
   /* -------------------------------------------- */
-  rt_interaction_rates_init(rtp, phys_const, us);
+
+  rtp->energy_weighted_cross_sections = NULL;
+  rtp->number_weighted_cross_sections = NULL;
+  rt_cross_sections_init(rtp, phys_const, us);
 
   /* Finishers */
   /* --------- */
-
-  /* After initialisation, print params to screen */
-  rt_props_print(rtp);
-
-  /* Print a final message. */
-  if (engine_rank == 0) {
-    message("Radiative transfer initialized");
-  }
 }
 
 /**
@@ -538,10 +472,6 @@ __attribute__((always_inline)) INLINE static void rt_struct_restore(
    * not defined at compile time. So we need to write them down. */
   restart_read_blocks(&rt_params, sizeof(struct rt_parameters), 1, stream, NULL,
                       "RT global parameters struct");
-#ifdef SWIFT_RT_DEBUG_CHECKS
-  /* Reset the file pointers for temporary stats */
-  rt_props_open_debugging_files(props, "a");
-#endif
 }
 
 #endif /* SWIFT_RT_PROPERTIES_GEAR_H */
diff --git a/src/rt/GEAR/rt_slope_limiters_cell.h b/src/rt/GEAR/rt_slope_limiters_cell.h
index c6a3216524b991b6d8a4b880c74145a650e51c42..93ab187f7c57a4258c3f7fbf5e6814ed60e8f1ed 100644
--- a/src/rt/GEAR/rt_slope_limiters_cell.h
+++ b/src/rt/GEAR/rt_slope_limiters_cell.h
@@ -40,8 +40,8 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_cell_init(
     struct part* p) {
 
   for (int g = 0; g < RT_NGROUPS; g++) {
-    p->rt_data.limiter[g].energy[0] = FLT_MAX;
-    p->rt_data.limiter[g].energy[1] = -FLT_MAX;
+    p->rt_data.limiter[g].energy_density[0] = FLT_MAX;
+    p->rt_data.limiter[g].energy_density[1] = -FLT_MAX;
     p->rt_data.limiter[g].flux[0][0] = FLT_MAX;
     p->rt_data.limiter[g].flux[0][1] = -FLT_MAX;
     p->rt_data.limiter[g].flux[1][0] = FLT_MAX;
@@ -71,23 +71,23 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_cell_collect(
 
   /* basic slope limiter: collect the maximal and the minimal value for the
    * primitive variables among the ngbs */
-  rtdi->limiter[g].energy[0] =
-      min(rtdj->density[g].energy, rtdi->limiter[g].energy[0]);
-  rtdi->limiter[g].energy[1] =
-      max(rtdj->density[g].energy, rtdi->limiter[g].energy[1]);
+  rtdi->limiter[g].energy_density[0] = min(rtdj->radiation[g].energy_density,
+                                           rtdi->limiter[g].energy_density[0]);
+  rtdi->limiter[g].energy_density[1] = max(rtdj->radiation[g].energy_density,
+                                           rtdi->limiter[g].energy_density[1]);
 
   rtdi->limiter[g].flux[0][0] =
-      min(rtdj->density[g].flux[0], rtdi->limiter[g].flux[0][0]);
+      min(rtdj->radiation[g].flux[0], rtdi->limiter[g].flux[0][0]);
   rtdi->limiter[g].flux[0][1] =
-      max(rtdj->density[g].flux[0], rtdi->limiter[g].flux[0][1]);
+      max(rtdj->radiation[g].flux[0], rtdi->limiter[g].flux[0][1]);
   rtdi->limiter[g].flux[1][0] =
-      min(rtdj->density[g].flux[1], rtdi->limiter[g].flux[1][0]);
+      min(rtdj->radiation[g].flux[1], rtdi->limiter[g].flux[1][0]);
   rtdi->limiter[g].flux[1][1] =
-      max(rtdj->density[g].flux[1], rtdi->limiter[g].flux[1][1]);
+      max(rtdj->radiation[g].flux[1], rtdi->limiter[g].flux[1][1]);
   rtdi->limiter[g].flux[2][0] =
-      min(rtdj->density[g].flux[2], rtdi->limiter[g].flux[2][0]);
+      min(rtdj->radiation[g].flux[2], rtdi->limiter[g].flux[2][0]);
   rtdi->limiter[g].flux[2][1] =
-      max(rtdj->density[g].flux[2], rtdi->limiter[g].flux[2][1]);
+      max(rtdj->radiation[g].flux[2], rtdi->limiter[g].flux[2][1]);
   /* just use the hydro one */
   /* pi->limiter.maxr = max(r, pi->limiter.maxr); */
 }
@@ -113,7 +113,7 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_quantity(
     const float gradtrue_inv = 1.0f / gradtrue;
     const float gradmax = valmax - value;
     const float gradmin = valmin - value;
-    const float beta = 1.0f; /* TODO: test for best value here. For now, take
+    const float beta = 1.f; /* TODO: test for best value here. For now, take
                                 stability over diffusivity. */
     const float min_temp =
         min(gradmax * gradtrue_inv, gradmin * gradtrue_inv) * beta;
@@ -137,24 +137,24 @@ __attribute__((always_inline)) INLINE static void rt_slope_limit_cell(
   struct rt_part_data* rtd = &p->rt_data;
 
   for (int g = 0; g < RT_NGROUPS; g++) {
-    rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].energy,
+    rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].energy_density,
                             /*maxr=    */ maxr,
-                            /*value=   */ rtd->density[g].energy,
-                            /*valmin=  */ rtd->limiter[g].energy[0],
-                            /*valmax=  */ rtd->limiter[g].energy[1]);
+                            /*value=   */ rtd->radiation[g].energy_density,
+                            /*valmin=  */ rtd->limiter[g].energy_density[0],
+                            /*valmax=  */ rtd->limiter[g].energy_density[1]);
     rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[0],
                             /*maxr=    */ maxr,
-                            /*value=   */ rtd->density[g].flux[0],
+                            /*value=   */ rtd->radiation[g].flux[0],
                             /*valmin=  */ rtd->limiter[g].flux[0][0],
                             /*valmax=  */ rtd->limiter[g].flux[0][1]);
     rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[1],
                             /*maxr=    */ maxr,
-                            /*value=   */ rtd->density[g].flux[1],
+                            /*value=   */ rtd->radiation[g].flux[1],
                             /*valmin=  */ rtd->limiter[g].flux[1][0],
                             /*valmax=  */ rtd->limiter[g].flux[1][1]);
     rt_slope_limit_quantity(/*gradient=*/rtd->gradient[g].flux[2],
                             /*maxr=    */ maxr,
-                            /*value=   */ rtd->density[g].flux[2],
+                            /*value=   */ rtd->radiation[g].flux[2],
                             /*valmin=  */ rtd->limiter[g].flux[2][0],
                             /*valmax=  */ rtd->limiter[g].flux[2][1]);
   }
diff --git a/src/rt/GEAR/rt_species.h b/src/rt/GEAR/rt_species.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d03ce678528565ea5224fec87fb35f6027d8c83
--- /dev/null
+++ b/src/rt/GEAR/rt_species.h
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_RT_GEAR_SPECIES_H
+#define SWIFT_RT_GEAR_SPECIES_H
+
+/**
+ * @file src/rt/GEAR/rt_species.h
+ * @brief header file concerning (ionizing) species.
+ **/
+
+enum rt_ionizing_species {
+  rt_ionizing_species_HI = 0,
+  rt_ionizing_species_HeI,
+  rt_ionizing_species_HeII,
+  rt_ionizing_species_count
+};
+
+/**
+ * Get the ionizing energy in erg for all ionizing species.
+ *
+ * @param E_ion (return) the ionizing energies.
+ **/
+__attribute__((always_inline)) INLINE static void
+rt_species_get_ionizing_energy(double E_ion[rt_ionizing_species_count]) {
+
+  E_ion[rt_ionizing_species_HI] = 2.179e-11;   /* 13.60 eV in erg */
+  E_ion[rt_ionizing_species_HeI] = 3.940e-11;  /* 24.59 eV in erg */
+  E_ion[rt_ionizing_species_HeII] = 8.719e-11; /* 54.42 eV in erg */
+}
+
+#endif /* SWIFT_RT_GEAR_SPECIES_H */
diff --git a/src/rt/GEAR/rt_stellar_emission_model.h b/src/rt/GEAR/rt_stellar_emission_model.h
new file mode 100644
index 0000000000000000000000000000000000000000..295db0d83c45502f90cb80036861835068d90ec0
--- /dev/null
+++ b/src/rt/GEAR/rt_stellar_emission_model.h
@@ -0,0 +1,114 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2022 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_RT_STELLAR_EMISSION_MODEL_GEAR_H
+#define SWIFT_RT_STELLAR_EMISSION_MODEL_GEAR_H
+
+/**
+ * @file src/rt/GEAR/rt_stellar_emission_model.h
+ * @brief Main header file for the GEAR M1 closure radiative transfer scheme
+ * stellar radiation emission models.
+ */
+
+enum rt_stellar_emission_models {
+  rt_stellar_emission_model_none = 0,
+  rt_stellar_emission_model_const,
+  rt_stellar_emission_model_IlievTest,
+  rt_stellar_emission_model_count
+};
+
+/**
+ * @brief Compute the energy emitted from a star during the time step dt.
+ * This is for the constant emission rate model.
+ *
+ * @param emission_this_step (return) the emitted radiation energy of a star
+ * during the time interval dt
+ * @param const_stellar_emission_rates the constant emission rates used in this
+ * run
+ * @param dt time step size (in internal units)
+ */
+__attribute__((always_inline)) INLINE static void
+rt_get_emission_this_step_const(
+    double emission_this_step[RT_NGROUPS],
+    const double const_stellar_emission_rates[RT_NGROUPS], double dt) {
+
+  /* The read-in constant stellar emisison rates are in units of L_sol.
+   * But they have been read in assuming they are in cgs. Convert this
+   * only now to proper internal units to avoid float overflows. We only
+   * store the energy that is to be distributed from this spart to its
+   * neighbours in this step in internal units.*/
+  const double solar_luminosity = 3.828e33; /* erg/s */
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    const double emission_rate_internal_units =
+        const_stellar_emission_rates[g] * solar_luminosity;
+    emission_this_step[g] = emission_rate_internal_units * dt;
+  }
+}
+
+/**
+ * @brief Compute the energy emitted from a star during the time step dt.
+ * This is for the Iliev+2006 Test 4.
+ *
+ * @param emission_this_step (return) the emitted radiation energy of a star
+ * during the time interval dt
+ * @param M the star mass (in internal units)
+ * @param dt time step size (in internal units)
+ * @param photon_number_integral Integrated photon numbers over frequency
+ * interval
+ * @param average_photon_energy average photon energy in each frequency bin, in
+ * erg
+ * @param phys_const struct holding physical constants
+ * @param internal_units units struct containing internal units
+ */
+__attribute__((always_inline)) INLINE static void
+rt_get_emission_this_step_IlievTest(
+    double emission_this_step[RT_NGROUPS], float M, const double dt,
+    const double photon_number_integral[RT_NGROUPS],
+    const double average_photon_energy[RT_NGROUPS],
+    const struct phys_const* phys_const,
+    const struct unit_system* internal_units) {
+
+  /* Note that this model uses the halo mass to determine the luminosity
+   * of a source. I'm cheating the system here by storing the required halo
+   * mass as the star mass. This is only ok because the test is supposed to
+   * run with all dynamics and gravity turned off. */
+
+  const double Omega_b = 0.043;
+  const double Omega_0 = 0.27;
+  const double m_p = phys_const->const_proton_mass;
+  const double t_s = 3e6 * phys_const->const_year;
+  const double f_gamma = 250.;
+  const double Ndot_gamma = (f_gamma * M * Omega_b) / (Omega_0 * m_p * t_s);
+
+  double Nsum = 0.;
+  for (int g = 0; g < RT_NGROUPS; g++) Nsum += photon_number_integral[g];
+
+  if (Nsum <= 0.) error("No photons in spectrum...???");
+
+  const double energy_units =
+      units_cgs_conversion_factor(internal_units, UNIT_CONV_ENERGY);
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    const double fi = photon_number_integral[g] / Nsum;
+    const double Ndot_i = fi * Ndot_gamma;
+    /* average photon densities are in cgs! */
+    const double Edot_i = average_photon_energy[g] * Ndot_i / energy_units;
+    emission_this_step[g] = Edot_i * dt;
+  }
+}
+
+#endif /* SWIFT_RT_STELLAR_EMISSION_MODEL_GEAR_H */
diff --git a/src/rt/GEAR/rt_stellar_emission_rate.h b/src/rt/GEAR/rt_stellar_emission_rate.h
deleted file mode 100644
index f9980925fe7dc7f3f83c4a8fc9f767b7e8f353a8..0000000000000000000000000000000000000000
--- a/src/rt/GEAR/rt_stellar_emission_rate.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*******************************************************************************
- * This file is part of SWIFT.
- * Copyright (c) 2021 Mladen Ivkovic (mladen.ivkovic@hotmail.com)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- ******************************************************************************/
-#ifndef SWIFT_RT_STELLAR_EMISSION_RATE_GEAR_H
-#define SWIFT_RT_STELLAR_EMISSION_RATE_GEAR_H
-
-/**
- * @file src/rt/GEAR/rt_stellar_emission_rate.h
- * @brief Main header file for the GEAR M1 closure radiative transfer scheme
- * stellar radiation emission rates related functions.
- */
-
-/**
- * @brief Main function for getting the stellar emission rate and updating it
- * in the spart.
- *
- * @param sp Star particle to work on.
- * @param age_beg Age of the stars at the beginning of the step
- * @param age_end Age of the stars at the end of the step
- * @param rt_props RT properties struct
- * @param phys_const struct holding physical constants
- * @param internal_units units struct containing internal units
- */
-
-__attribute__((always_inline)) INLINE static void rt_set_stellar_emission_rate(
-    struct spart* restrict sp, double age_beg, double age_end,
-    const struct rt_props* rt_props, const struct phys_const* phys_const,
-    const struct unit_system* internal_units) {
-
-  if (rt_props->use_const_emission_rates) {
-    /* The read-in constant stellar emisison rates are in units of L_sol.
-     * But they have been read in assuming they are in cgs. Convert this
-     * only now to proper internal units to avoid float overflows. We only
-     * store the energy that is to be distributed from this spart to its
-     * neighbours in this step in internal units.*/
-    const double solar_luminosity = 3.826e33; /* erg/s */
-    const double dt = (age_end - age_beg);
-    for (int g = 0; g < RT_NGROUPS; g++) {
-      double emission_rate_internal_units =
-          rt_props->stellar_const_emission_rates[g] * solar_luminosity;
-      sp->rt_data.emission_this_step[g] = emission_rate_internal_units * dt;
-    }
-  } else {
-    error("Unknown stellar emission rate model");
-  }
-}
-
-#endif /* SWIFT_RT_STELLAR_EMISSION_RATE_GEAR_H */
diff --git a/src/rt/GEAR/rt_struct.h b/src/rt/GEAR/rt_struct.h
index b02d9f5f6054105fed150952acf5ff91e77901d2..632bc15b759588007682c6c71b67bdbe18221f65 100644
--- a/src/rt/GEAR/rt_struct.h
+++ b/src/rt/GEAR/rt_struct.h
@@ -55,7 +55,7 @@ struct rt_part_data {
   /* the Gizmo-style slope limiting doesn't help for RT as is,
    * so we're skipping it for now. */
   /* struct { */
-  /*   float energy[2]; */
+  /*   float energy_density[2]; */
   /*   float flux[3][2]; */
   /*   [> float maxr; [> just use the hydro one <] <] */
   /* } limiter[RT_NGROUPS]; */
@@ -115,6 +115,11 @@ struct rt_part_data {
   /*! thermochemistry done? */
   int debug_thermochem_done;
 
+  /* Subcycling flags */
+
+  /*! Current subcycle wrt (last) hydro step */
+  int debug_nsubcycles;
+
 #endif
 };
 
diff --git a/src/rt/GEAR/rt_thermochemistry.h b/src/rt/GEAR/rt_thermochemistry.h
index 1e8a33298b319fae6e1aae56af42800a6fae2df3..c4654010edad352cf1fec78feea3a923f619bdef 100644
--- a/src/rt/GEAR/rt_thermochemistry.h
+++ b/src/rt/GEAR/rt_thermochemistry.h
@@ -19,10 +19,8 @@
 #ifndef SWIFT_RT_GEAR_THERMOCHEMISTRY_H
 #define SWIFT_RT_GEAR_THERMOCHEMISTRY_H
 
-/* need to rework (and check) code if changed */
-#define GRACKLE_NPART 1
-#define GRACKLE_RANK 3
-
+#include "rt_grackle_utils.h"
+#include "rt_interaction_cross_sections.h"
 #include "rt_interaction_rates.h"
 #include "rt_ionization_equilibrium.h"
 
@@ -66,172 +64,22 @@ __attribute__((always_inline)) INLINE static void rt_tchem_first_init_part(
 
   /* Check that we didn't do something stupid */
   rt_check_unphysical_mass_fractions(p);
-}
-
-/**
- * @brief copies data to the grackle_field struct.
- */
-__attribute__((always_inline)) INLINE static void rt_tchem_copy_data_to_grackle(
-    grackle_field_data* grackle_field, int grid_dimension[GRACKLE_RANK],
-    int grid_start[GRACKLE_RANK], int grid_end[GRACKLE_RANK], gr_float* density,
-    gr_float* internal_energy, gr_float species_densities[6],
-    gr_float iact_rates[5]) {
-
-  grackle_field->grid_dx = 0.;
-  grackle_field->grid_rank = GRACKLE_RANK;
-  grackle_field->grid_dimension = grid_dimension;
-  grackle_field->grid_start = grid_start;
-  grackle_field->grid_end = grid_end;
-
-  /* initialize density */
-  grackle_field->density = density;
-  grackle_field->internal_energy = internal_energy;
-  /* grackle 3.0 doc: "Currently not used" */
-  grackle_field->x_velocity = NULL;
-  grackle_field->y_velocity = NULL;
-  grackle_field->z_velocity = NULL;
-
-  grackle_field->HI_density = &species_densities[0];
-  grackle_field->HII_density = &species_densities[1];
-  grackle_field->HeI_density = &species_densities[2];
-  grackle_field->HeII_density = &species_densities[3];
-  grackle_field->HeIII_density = &species_densities[4];
-  grackle_field->e_density = &species_densities[5];
-
-  /* general particle data */
-  grackle_field->volumetric_heating_rate = NULL;
-  grackle_field->specific_heating_rate = NULL;
-
-  grackle_field->RT_heating_rate = &iact_rates[0];
-  grackle_field->RT_HI_ionization_rate = &iact_rates[1];
-  grackle_field->RT_HeI_ionization_rate = &iact_rates[2];
-  grackle_field->RT_HeII_ionization_rate = &iact_rates[3];
-  grackle_field->RT_H2_dissociation_rate = &iact_rates[4];
-
-  grackle_field->metal_density = NULL;
-}
-
-/**
- * @brief compute the heating, ionization, and dissassociation rates
- * for the particle radiation field as needed by grackle, and the
- * net absorption/emission rates for each photon group
- *
- * @param rates (return) Interaction rates for grackle. [0]: heating rate.
- * [1]: HI ionization. [2]: HeI ionization. [3]: HeII ionization.
- * [4]: H2 dissociation.
- * @param heating_rates_by_group (return) net absorption/emission rates of each
- * photon frequency group in internal units.
- * @param p particle to work on
- * @param species_densities the physical densities of all traced species
- * @param rt_props rt_properties struct
- * @param phys_const physical constants struct
- * @param us internal units struct
- * @param cosmo cosmology struct
- **/
-__attribute__((always_inline)) INLINE static void
-rt_tchem_get_interaction_rates(gr_float rates[5],
-                               float heating_rates_by_group[RT_NGROUPS],
-                               const struct part* restrict p,
-                               gr_float species_densities[6],
-                               const struct rt_props* restrict rt_props,
-                               const struct phys_const* restrict phys_const,
-                               const struct unit_system* restrict us,
-                               const struct cosmology* restrict cosmo) {
-
-  rates[0] = 0.; /* Needs to be in [erg / s / cm^3 / nHI] for grackle. */
-  rates[1] = 0.; /* [1 / time_units] */
-  rates[2] = 0.; /* [1 / time_units] */
-  rates[3] = 0.; /* [1 / time_units] */
-  rates[4] = 0.; /* [1 / time_units] */
-  for (int group = 0; group < RT_NGROUPS; group++) {
-    heating_rates_by_group[group] = 0.;
-  }
-
-  /* "copy" ionization energies from cross section parameters */
-  struct rt_photoion_cs_parameters cs_params_cgs =
-      rt_init_photoion_cs_params_cgs();
-  const double* E_ion_cgs = cs_params_cgs.E_ion;
-
-  /* Integrate energy spectra and cross sections assuming blackbody spectra
-   * to obtain estimate for effective cross sections, then use the actual
-   * energies present to get the rates */
-  /* TODO: check whether we shouldn't be using actual speed of light here */
-  const double c_cgs = rt_params.reduced_speed_of_light *
-                       units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
-  const double to_erg = units_cgs_conversion_factor(us, UNIT_CONV_ENERGY);
-
-  /* First, get species number densities and number densities
-   * in units of neutral hydrogen number density. */
-  double m_p = phys_const->const_proton_mass;
-  double species_number_densities_cgs[RT_NIONIZING_SPECIES]; /* in cm^-3 */
-  double species_number_densities_nHI[RT_NIONIZING_SPECIES]; /* in nHI^-1 */
-  const double to_inv_volume =
-      units_cgs_conversion_factor(us, UNIT_CONV_INV_VOLUME);
-  const double mass_to_number_density_cgs = to_inv_volume / m_p;
-  /* neutral hydrogen */
-  species_number_densities_cgs[0] =
-      species_densities[0] * mass_to_number_density_cgs;
-  species_number_densities_nHI[0] = 1.;
-  /* neutral helium */
-  species_number_densities_cgs[1] =
-      0.25 * species_densities[2] * mass_to_number_density_cgs;
-  species_number_densities_nHI[1] =
-      0.25 * species_densities[2] / species_densities[0];
-  /* singly ionized helium */
-  species_number_densities_cgs[2] =
-      0.25 * species_densities[3] * mass_to_number_density_cgs;
-  species_number_densities_nHI[2] =
-      0.25 * species_densities[3] / species_densities[0];
-
-  const double inv_time_cgs =
-      units_cgs_conversion_factor(us, UNIT_CONV_INV_TIME);
-
-  /* For the grackle photoionization, we need to
-   * keep track of the rates for each species.
-   * For the heating rate, we need to sum up all species.
-   * To remove the correct amount of energy from the
-   * radiation fields, we additionally need to keep track
-   * of rates from each photon group. */
-
-  /* store photoionization rate for each species here */
-  double ionization_rates_by_species[RT_NIONIZING_SPECIES];
-  for (int spec = 0; spec < RT_NIONIZING_SPECIES; spec++)
-    ionization_rates_by_species[spec] = 0.;
-
-  for (int group = 0; group < RT_NGROUPS; group++) {
-
-    /* Sum results for this group over all species */
-    double heating_rate_group_nHI = 0.;
-    double heating_rate_group_cgs = 0.;
-    float energy_density_i_cgs =
-        p->rt_data.radiation[group].energy_density * to_erg * to_inv_volume;
-
-    for (int spec = 0; spec < RT_NIONIZING_SPECIES; spec++) {
-      /* Note: the cross sections are in cgs. */
-      const double cse = rt_props->energy_weighted_cross_sections[group][spec];
-      const double csn = rt_props->number_weighted_cross_sections[group][spec];
-
-      heating_rate_group_nHI +=
-          (cse - E_ion_cgs[spec] * csn) * species_number_densities_nHI[spec];
-      heating_rate_group_cgs +=
-          (cse - E_ion_cgs[spec] * csn) * species_number_densities_cgs[spec];
-      ionization_rates_by_species[spec] +=
-          energy_density_i_cgs * cse * species_number_densities_cgs[spec] *
-          c_cgs / inv_time_cgs; /* internal units T^-1 */
-    }
 
-    /* Store total heating rate for grackle */
-    rates[0] += heating_rate_group_nHI * c_cgs * energy_density_i_cgs;
-    /* Store rates for each group in internal units WITHOUT THE ENERGY DENSITY
-     * TERM */
-    heating_rates_by_group[group] +=
-        heating_rate_group_cgs * c_cgs / inv_time_cgs;
-  }
-
-  /* We're done. Write the results in correct place */
-  rates[1] = ionization_rates_by_species[0];
-  rates[2] = ionization_rates_by_species[1];
-  rates[3] = ionization_rates_by_species[2];
+  /* Check that the Hydrogen and Helium mass fractions correspond to those
+   * provided by the user in the parameter file. This mass fraction is also
+   * passed down to grackle internally, so it is error-prone if left
+   * unchecked. */
+  const float mH =
+      p->rt_data.tchem.mass_fraction_HI + p->rt_data.tchem.mass_fraction_HII;
+  if (fabsf(mH - rt_props->hydrogen_mass_fraction) > 1e-4)
+    error("Got wrong Hydrogen mass fraction: Got =%.6f provided in yml =%.6f",
+          mH, rt_props->hydrogen_mass_fraction);
+  const float mHe = p->rt_data.tchem.mass_fraction_HeI +
+                    p->rt_data.tchem.mass_fraction_HeII +
+                    p->rt_data.tchem.mass_fraction_HeIII;
+  if (fabsf(mHe - rt_props->helium_mass_fraction) > 1e-4)
+    error("Got wrong Helium mass fraction: Got =%.6f provided in yml =%.6f",
+          mHe, rt_props->helium_mass_fraction);
 }
 
 /**
@@ -246,14 +94,12 @@ rt_tchem_get_interaction_rates(gr_float rates[5],
  * @param us The internal system of units.
  * @param dt The time-step of this particle.
  */
-static void rt_do_thermochemistry(struct part* restrict p,
-                                  struct xpart* restrict xp,
-                                  struct rt_props* rt_props,
-                                  const struct cosmology* restrict cosmo,
-                                  const struct hydro_props* hydro_props,
-                                  const struct phys_const* restrict phys_const,
-                                  const struct unit_system* restrict us,
-                                  const double dt) {
+__attribute__((always_inline)) INLINE static void rt_do_thermochemistry(
+    struct part* restrict p, struct xpart* restrict xp,
+    struct rt_props* rt_props, const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us, const double dt) {
   /* Note: Can't pass rt_props as const struct because of grackle
    * accessinging its properties there */
 
@@ -267,31 +113,30 @@ static void rt_do_thermochemistry(struct part* restrict p,
   /* initialize data so it'll be in scope */
   grackle_field_data particle_grackle_data;
 
-  int grid_dimension[GRACKLE_RANK] = {GRACKLE_NPART, 1, 1};
-  int grid_start[GRACKLE_RANK] = {0, 0, 0};
-  int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0};
-
   gr_float density = hydro_get_physical_density(p, cosmo);
   const float u_minimal = hydro_props->minimal_internal_energy;
   gr_float internal_energy =
       max(hydro_get_physical_internal_energy(p, xp, cosmo), u_minimal);
 
-  const float u_old = internal_energy;
   gr_float species_densities[6];
   rt_tchem_get_species_densities(p, density, species_densities);
 
+  float radiation_energy_density[RT_NGROUPS];
+  rt_part_get_radiation_energy_density(p, radiation_energy_density);
+
   gr_float iact_rates[5];
-  float iact_rates_by_frequency_bin[RT_NGROUPS];
-  rt_tchem_get_interaction_rates(iact_rates, iact_rates_by_frequency_bin, p,
-                                 species_densities, rt_props, phys_const, us,
-                                 cosmo);
+  rt_get_interaction_rates_for_grackle(
+      iact_rates, radiation_energy_density, species_densities,
+      rt_props->average_photon_energy, rt_props->energy_weighted_cross_sections,
+      rt_props->number_weighted_cross_sections, phys_const, us);
 
-  rt_tchem_copy_data_to_grackle(
-      &particle_grackle_data, grid_dimension, grid_start, grid_end, &density,
-      &internal_energy, species_densities, iact_rates);
+  /* Put all the data into a grackle field struct */
+  rt_get_grackle_particle_fields(&particle_grackle_data, density,
+                                 internal_energy, species_densities,
+                                 iact_rates);
 
   /* solve chemistry */
-  /* Note: grackle_rates is a global variable defined by grackle itself.
+  /* Note: `grackle_rates` is a global variable defined by grackle itself.
    * Using a manually allocd and initialized variable here fails with MPI
    * for some reason. */
   if (local_solve_chemistry(&rt_props->grackle_chemistry_data, &grackle_rates,
@@ -299,37 +144,16 @@ static void rt_do_thermochemistry(struct part* restrict p,
                             dt) == 0)
     error("Error in solve_chemistry.");
 
+  /* copy updated grackle data to particle */
   /* update particle internal energy. Grackle had access by reference
    * to internal_energy */
+  internal_energy = particle_grackle_data.internal_energy[0];
   const float u_new = max(internal_energy, u_minimal);
 
-  /* Redo if we changed by more than 10% ? */
-  if (fabsf(u_new - u_old) > 0.1f * u_old) {
-    /* Redo because we changed by more than 10% ! */
-    rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, us,
-                          0.5 * dt);
-    rt_do_thermochemistry(p, xp, rt_props, cosmo, hydro_props, phys_const, us,
-                          0.5 * dt);
-    return;
-  }
-
   /* If we're good, update the particle data from grackle results */
   hydro_set_internal_energy(p, u_new);
 
-  /* update radiation fields */
-  for (int g = 0; g < RT_NGROUPS; g++) {
-    const float e_old = p->rt_data.radiation[g].energy_density;
-    const float factor_new = (1.f - dt * iact_rates_by_frequency_bin[g]);
-    p->rt_data.radiation[g].energy_density *= factor_new;
-    for (int i = 0; i < 3; i++) {
-      p->rt_data.radiation[g].flux[i] *= factor_new;
-    }
-    rt_check_unphysical_state(&p->rt_data.radiation[g].energy_density,
-                              p->rt_data.radiation[g].flux, e_old,
-                              /*callloc=*/2);
-  }
-
-  /* copy updated grackle data to particle */
+  /* Update mass fractions */
   const gr_float one_over_rho = 1. / density;
   p->rt_data.tchem.mass_fraction_HI =
       particle_grackle_data.HI_density[0] * one_over_rho;
@@ -343,6 +167,45 @@ static void rt_do_thermochemistry(struct part* restrict p,
       particle_grackle_data.HeIII_density[0] * one_over_rho;
 
   rt_check_unphysical_mass_fractions(p);
+
+  /* Update radiation fields */
+  /* First get absorption rates at the start and the end of the step */
+  double absorption_rates[RT_NGROUPS];
+  rt_get_absorption_rates(
+      absorption_rates, species_densities, rt_props->average_photon_energy,
+      rt_props->number_weighted_cross_sections, phys_const, us);
+
+  gr_float species_densities_new[6];
+  species_densities_new[0] = particle_grackle_data.HI_density[0];
+  species_densities_new[1] = particle_grackle_data.HII_density[0];
+  species_densities_new[2] = particle_grackle_data.HeI_density[0];
+  species_densities_new[3] = particle_grackle_data.HeII_density[0];
+  species_densities_new[4] = particle_grackle_data.HeIII_density[0];
+  species_densities_new[5] = particle_grackle_data.e_density[0];
+  double absorption_rates_new[RT_NGROUPS];
+  rt_get_absorption_rates(absorption_rates_new, species_densities_new,
+                          rt_props->average_photon_energy,
+                          rt_props->number_weighted_cross_sections, phys_const,
+                          us);
+
+  /* Now remove absorbed radiation */
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    const float E_old = p->rt_data.radiation[g].energy_density;
+    double f = dt * 0.5 * (absorption_rates[g] + absorption_rates_new[g]);
+    f = min(1., f);
+    f = max(0., f);
+    p->rt_data.radiation[g].energy_density *= (1. - f);
+    for (int i = 0; i < 3; i++) {
+      p->rt_data.radiation[g].flux[i] *= (1. - f);
+    }
+
+    rt_check_unphysical_state(&p->rt_data.radiation[g].energy_density,
+                              p->rt_data.radiation[g].flux, E_old,
+                              /*callloc=*/2);
+  }
+
+  /* Clean up after yourself. */
+  rt_clean_grackle_fields(&particle_grackle_data);
 }
 
 /**
@@ -368,10 +231,6 @@ __attribute__((always_inline)) INLINE static float rt_tchem_get_tchem_time(
   /* initialize data so it'll be in scope */
   grackle_field_data particle_grackle_data;
 
-  int grid_dimension[GRACKLE_RANK] = {GRACKLE_NPART, 1, 1};
-  int grid_start[GRACKLE_RANK] = {0, 0, 0};
-  int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0};
-
   gr_float density = hydro_get_physical_density(p, cosmo);
   const float u_minimal = hydro_props->minimal_internal_energy;
   gr_float internal_energy =
@@ -380,15 +239,18 @@ __attribute__((always_inline)) INLINE static float rt_tchem_get_tchem_time(
   gr_float species_densities[6];
   rt_tchem_get_species_densities(p, density, species_densities);
 
+  float radiation_energy_density[RT_NGROUPS];
+  rt_part_get_radiation_energy_density(p, radiation_energy_density);
+
   gr_float iact_rates[5];
-  float iact_rates_by_frequency_bin[RT_NGROUPS];
-  rt_tchem_get_interaction_rates(iact_rates, iact_rates_by_frequency_bin, p,
-                                 species_densities, rt_props, phys_const, us,
-                                 cosmo);
+  rt_get_interaction_rates_for_grackle(
+      iact_rates, radiation_energy_density, species_densities,
+      rt_props->average_photon_energy, rt_props->energy_weighted_cross_sections,
+      rt_props->number_weighted_cross_sections, phys_const, us);
 
-  rt_tchem_copy_data_to_grackle(
-      &particle_grackle_data, grid_dimension, grid_start, grid_end, &density,
-      &internal_energy, species_densities, iact_rates);
+  rt_get_grackle_particle_fields(&particle_grackle_data, density,
+                                 internal_energy, species_densities,
+                                 iact_rates);
 
   /* Compute 'cooling' time */
   /* Note: grackle_rates is a global variable defined by grackle itself.
@@ -400,6 +262,10 @@ __attribute__((always_inline)) INLINE static float rt_tchem_get_tchem_time(
                                    &particle_grackle_data, &tchem_time) == 0)
     error("Error in calculate_cooling_time.");
 
+  /* Clean up after yourself. */
+  rt_clean_grackle_fields(&particle_grackle_data);
+
   return (float)tchem_time;
 }
+
 #endif /* SWIFT_RT_GEAR_THERMOCHEMISTRY_H */
diff --git a/src/rt/GEAR/rt_thermochemistry_utils.h b/src/rt/GEAR/rt_thermochemistry_utils.h
index 3396bd1d4c38951b717b169a2f66c0070dc060af..580813471307f99884e2c9a03fbdf9d6bf177935 100644
--- a/src/rt/GEAR/rt_thermochemistry_utils.h
+++ b/src/rt/GEAR/rt_thermochemistry_utils.h
@@ -24,6 +24,8 @@
  * @brief thermochemistry utilities and misc functions.
  * */
 
+#include "rt_species.h"
+
 /**
  * @brief compute the mean molecular weight mu for given
  * hydrogen and helium mass fractions
@@ -111,7 +113,6 @@ __attribute__((always_inline)) INLINE static void
 rt_tchem_get_species_densities(const struct part* restrict p, gr_float rho,
                                gr_float species_densities[6]) {
 
-  /* TODO: check units here */
   species_densities[0] = p->rt_data.tchem.mass_fraction_HI * rho;
   species_densities[1] = p->rt_data.tchem.mass_fraction_HII * rho;
   species_densities[2] = p->rt_data.tchem.mass_fraction_HeI * rho;
@@ -128,6 +129,32 @@ rt_tchem_get_species_densities(const struct part* restrict p, gr_float rho,
   species_densities[5] = rho_e;
 }
 
+/**
+ * @brief get the number densities of the ionizing species in cgs.
+ *
+ * @param ns_cgs (return) number densities in cgs of ionizing species
+ * @param species_densities densities of all species as returned by
+ * rt_tchem_get_species_densities()
+ * @param phys_const physical constants struct
+ * @param us internal units struct
+ *
+ **/
+__attribute__((always_inline)) INLINE static void
+rt_tchem_get_ionizing_species_number_densities(
+    double ns_cgs[rt_ionizing_species_count], gr_float species_densities[6],
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us) {
+
+  const double m_p = phys_const->const_proton_mass;
+  const double to_inv_volume_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_INV_VOLUME);
+  const double rho_to_n_cgs = to_inv_volume_cgs / m_p;
+
+  ns_cgs[rt_ionizing_species_HI] = species_densities[0] * rho_to_n_cgs;
+  ns_cgs[rt_ionizing_species_HeI] = 0.25 * species_densities[2] * rho_to_n_cgs;
+  ns_cgs[rt_ionizing_species_HeII] = 0.25 * species_densities[3] * rho_to_n_cgs;
+}
+
 /**
  * @brief get the (physical) temperature of the gas
  *
@@ -157,4 +184,103 @@ rt_tchem_get_gas_temperature(const struct part* restrict p,
 
   return T;
 }
+
+/**
+ * @brief Set a particle's density and internal energy.
+ *
+ * This function is only intended for use in very special case idealized
+ * tests, like the Iliev+06 tests, where we require fixed densities and
+ * temperatures.
+ **/
+__attribute__((always_inline)) INLINE static void
+rt_tchem_set_particle_quantities_for_test(struct part* restrict p) {
+
+  /* Set the values that you actually want. Needs to be in internal units.*/
+  /* 1 hydrogen_atom_mass / cm^3 / (1.98848e18 g/IMU * 3.0857e15cm/ILU^3) */
+  /* float density = 2.471e+04; */
+
+  /* Set the values that you actually want. Needs to be in internal units.*/
+  /* 10^-3 hydrogen_atom_mass / cm^3 / (1.98848e18 g/IMU * 3.0857e15cm/ILU^3) */
+  float density = 2.471e+01;
+
+  /* 100 K  */
+  float internal_energy = 1.23816f;
+
+  /* 10000 K with xHII = 1e-3 for Iliev Test 1 */
+  /* float internal_energy = 124.8416491f; */
+
+  /* Be vocal, just in case somebody forgets you exist. */
+  if (p->id == 1) message("Setting density from %.3e to %.3e", p->rho, density);
+
+  float mass_corr = density / p->rho;
+  p->rho = density;
+  p->conserved.mass *= mass_corr;
+  /* This assumes zero velocity */
+  p->conserved.energy = p->conserved.mass * internal_energy;
+  hydro_set_internal_energy(p, internal_energy);
+}
+
+/**
+ * @brief Set a particle's radiation field given a photon flux.
+ *
+ * This function is only intended for use in very special case idealized
+ * tests, like the Iliev+06 tests, where we require fixed densities and
+ * temperatures.
+ *
+ * @param p particle to modify
+ * @param time current simulation time
+ * @param us unity system.
+ **/
+__attribute__((always_inline)) INLINE static void
+rt_tchem_set_particle_radiation_field_for_test(
+    struct part* restrict p, const double time,
+    const struct unit_system* restrict us) {
+
+  const double time_to_cgs = units_cgs_conversion_factor(us, UNIT_CONV_TIME);
+  const double t_Myr = time * time_to_cgs / (3600. * 24. * 365. * 1e6);
+
+  /* NOTE: this assumes that the test is set up with 3 photon groups. */
+  double fixed_fluxes[3];
+  for (int g = 0; g < 3; g++) fixed_fluxes[g] = 0.;
+
+  if (t_Myr < 0.5) {
+    /* Be vocal, just in case somebody forgets you exist. */
+    if (p->id == 1) message("Setting fixed radiation field.");
+    /* Set fixed radiation fields, in cgs*/
+    fixed_fluxes[0] = 1.350e01;
+    fixed_fluxes[1] = 2.779e01;
+    fixed_fluxes[2] = 6.152e00;
+  }
+
+  const double flux_to_cgs =
+      units_cgs_conversion_factor(us, UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE);
+  const double cf = rt_params.reduced_speed_of_light_inverse / flux_to_cgs;
+
+  /* Note that we inject energy / time / surface, not identical to what */
+  /* is in Iliev06 paper */
+  for (int g = 0; g < RT_NGROUPS; g++) {
+    p->rt_data.radiation[g].energy_density = fixed_fluxes[g] * cf;
+  }
+}
+
+/**
+ * @brief Modify a boundary particle.
+ *
+ * This function is only intended for use in very special case idealized
+ * tests, like the Iliev+06 tests, to deal with boundary conditions in
+ * a simple manner.
+ * */
+__attribute__((always_inline)) INLINE static void
+rt_tchem_set_boundary_particles_for_test(struct part* restrict p) {
+
+  if (p->id >= 1000000000) {
+    for (int g = 0; g < RT_NGROUPS; g++) {
+      p->rt_data.radiation[g].energy_density = 0.f;
+      p->rt_data.radiation[g].flux[0] = 0.f;
+      p->rt_data.radiation[g].flux[1] = 0.f;
+      p->rt_data.radiation[g].flux[2] = 0.f;
+    }
+  }
+}
+
 #endif /* SWIFT_RT_GEAR_THERMOCHEMISTRY_UTILS_H */
diff --git a/src/rt/GEAR/rt_unphysical.h b/src/rt/GEAR/rt_unphysical.h
index fbe51b78ef09bb17ef5b9e68c929b8095f2872f0..7fe83dd7b5f22527fb4426b7f4b725ca47645012 100644
--- a/src/rt/GEAR/rt_unphysical.h
+++ b/src/rt/GEAR/rt_unphysical.h
@@ -43,10 +43,19 @@ __attribute__((always_inline)) INLINE static void rt_check_unphysical_state(
    * by dividing the printed out fluxes by the speed of light in
    * code units */
 #ifdef SWIFT_DEBUG_CHECKS
-  float ratio = 2.;
-  if (e_old != 0.f) ratio = fabsf(*energy_density / e_old);
+  float ratio = -1.f;
+  char print = 0;
+  if (e_old == 0.) {
+    if (*energy_density < -1.e-4f) print = 1;
+  } else {
+    if (fabsf(*energy_density) > 1.e-30) {
+      if (*energy_density < -1.e-4f * fabsf(e_old)) print = 1;
+      ratio = fabsf(*energy_density / e_old);
+    }
+  }
   /* callloc = 1 is gradient extrapolation. Don't print out those. */
-  if (*energy_density < -1e-2f && fabsf(ratio - 1.f) > 1.e-3f && callloc != 1)
+  if (callloc == 1) print = 0;
+  if (print)
     message("Fixing unphysical energy case %d | %.6e | %.6e %.6e %.6e | %.6e",
             callloc, *energy_density, flux[0], flux[1], flux[2], ratio);
 #endif
@@ -99,11 +108,17 @@ __attribute__((always_inline)) INLINE static void rt_check_unphysical_state_ICs(
         "Found particle with negative energy density after reading in ICs: "
         "pid= %lld group=%d E=%.6g",
         p->id, group, *energy_density);
+  if (*energy_density > FLT_MAX || isnan(*energy_density))
+    error("Got inf/nan energy_density: %g", *energy_density);
 
   /* Check for too high fluxes */
   const float flux2 = flux[0] * flux[0] + flux[1] * flux[1] + flux[2] * flux[2];
   const float flux_norm = sqrtf(flux2);
   const float flux_max = c * *energy_density;
+  if (flux_max > FLT_MAX || isnan(flux_max))
+    error("Got inf/nan flux_max: %g", flux_max);
+  if (flux_norm > FLT_MAX || isnan(flux_norm))
+    error("Got inf/nan flux_norm: %g", flux_norm);
   if (flux_norm > flux_max * 1.0001) {
     error(
         "Found too high radiation flux for a particle: pid=%lld, group=%d, "
@@ -161,31 +176,27 @@ rt_check_unphysical_hyperbolic_flux(float flux[4][3]) {
 __attribute__((always_inline)) INLINE static void
 rt_check_unphysical_mass_fractions(struct part* restrict p) {
 
-/* GRACKLE doesn't really like exact zeroes, so use something
- * comparatively small instead. */
-#define RT_GEAR_TINY_MASS_FRACTION 1.e-6
-
-  if (p->rt_data.tchem.mass_fraction_HI < 0.f) {
+  if (p->rt_data.tchem.mass_fraction_HI <= 0.f) {
     if (p->rt_data.tchem.mass_fraction_HI < -1e4)
       message("WARNING: Got negative HI mass fraction?");
     p->rt_data.tchem.mass_fraction_HI = RT_GEAR_TINY_MASS_FRACTION;
   }
-  if (p->rt_data.tchem.mass_fraction_HII < 0.f) {
+  if (p->rt_data.tchem.mass_fraction_HII <= 0.f) {
     if (p->rt_data.tchem.mass_fraction_HII < -1e4)
       message("WARNING: Got negative HII mass fraction?");
     p->rt_data.tchem.mass_fraction_HII = RT_GEAR_TINY_MASS_FRACTION;
   }
-  if (p->rt_data.tchem.mass_fraction_HeI < 0.f) {
+  if (p->rt_data.tchem.mass_fraction_HeI <= 0.f) {
     if (p->rt_data.tchem.mass_fraction_HeI < -1e4)
       message("WARNING: Got negative HeI mass fraction?");
     p->rt_data.tchem.mass_fraction_HeI = RT_GEAR_TINY_MASS_FRACTION;
   }
-  if (p->rt_data.tchem.mass_fraction_HeII < 0.f) {
+  if (p->rt_data.tchem.mass_fraction_HeII <= 0.f) {
     if (p->rt_data.tchem.mass_fraction_HeII < -1e4)
       message("WARNING: Got negative HeII mass fraction?");
     p->rt_data.tchem.mass_fraction_HeII = RT_GEAR_TINY_MASS_FRACTION;
   }
-  if (p->rt_data.tchem.mass_fraction_HeIII < 0.f) {
+  if (p->rt_data.tchem.mass_fraction_HeIII <= 0.f) {
     if (p->rt_data.tchem.mass_fraction_HeIII < -1e4)
       message("WARNING: Got negative HeIII mass fraction?");
     p->rt_data.tchem.mass_fraction_HeIII = RT_GEAR_TINY_MASS_FRACTION;
diff --git a/src/rt/SPHM1RT/rt.h b/src/rt/SPHM1RT/rt.h
index 074a67d89ee7a90229a46f8e0022f18e1436b9df..0ae7c5805284fb83e684dbe75d5a3c373a7883f5 100644
--- a/src/rt/SPHM1RT/rt.h
+++ b/src/rt/SPHM1RT/rt.h
@@ -46,9 +46,11 @@ __attribute__((always_inline)) INLINE static void rt_init_part(
     struct part* restrict p) {}
 
 /**
- * @brief Reset of the RT hydro particle data not related to the density.
+ * @brief Reset the RT hydro particle data not related to the hydro density.
  * Note: during initalisation (space_init), rt_reset_part and rt_init_part
- * are both called individually.
+ * are both called individually. To reset RT data needed in each RT sub-cycle,
+ * use rt_reset_part_each_subcycle().
+ *
  * @param p particle to work on
  * @param cosmo Cosmology.
  */
@@ -93,6 +95,14 @@ __attribute__((always_inline)) INLINE static void rt_reset_part(
   }
 }
 
+/**
+ * @brief Reset RT particle data which needs to be reset each sub-cycle.
+ *
+ * @param p the particle to work on
+ */
+__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle(
+    struct part* restrict p){};
+
 /**
  * @brief First initialisation of the RT hydro particle data.
  *
@@ -124,20 +134,6 @@ __attribute__((always_inline)) INLINE static void rt_first_init_part(
   rt_reset_part(p, cosmo);
 }
 
-/**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the particle density and time step to be known.
- *
- * @param p particle to work on
- * @param rt_props RT properties struct
- * @param cosmo #cosmology data structure.
- */
-__attribute__((always_inline)) INLINE static void
-rt_init_part_after_zeroth_step(struct part* restrict p,
-                               const struct rt_props* rt_props,
-                               const struct cosmology* restrict cosmo) {}
-
 /**
  * @brief Initialisation of the RT density loop related star particle data.
  * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart
@@ -178,25 +174,6 @@ __attribute__((always_inline)) INLINE static void rt_first_init_spart(
   rt_reset_spart(sp);
 }
 
-/**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the star density and time step to be known.
- * @param sp star particle to work on
- * @param time current system time
- * @param star_age age of the star *at the end of the step*
- * @param dt star time step
- * @param rt_props RT properties struct
- * @param phys_const physical constants struct
- * @param internal_units struct holding internal units
- */
-__attribute__((always_inline)) INLINE static void
-rt_init_star_after_zeroth_step(struct spart* restrict sp, double time,
-                               double star_age, double dt,
-                               const struct rt_props* rt_props,
-                               const struct phys_const* phys_const,
-                               const struct unit_system* internal_units) {}
-
 /**
  * @brief Split the RT data of a particle into n pieces
  *
@@ -580,6 +557,16 @@ __attribute__((always_inline)) INLINE static void rt_prepare_force(
   rpd->force.f = omega_inv;
 }
 
+/**
+ * @brief Extra operations to be done during the drift
+ *
+ * @param p Particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param dt_drift The drift time-step for positions.
+ */
+__attribute__((always_inline)) INLINE static void rt_predict_extra(
+    struct part* p, struct xpart* xp, float dt_drift) {}
+
 /**
  * @brief Clean the allocated memory inside the RT properties struct.
  *
diff --git a/src/rt/SPHM1RT/rt_properties.h b/src/rt/SPHM1RT/rt_properties.h
index 0addde259c169563dc7cc0a1a2b16449943f5230..2b987f05133463d0b545202c27ff13cb640adff0 100644
--- a/src/rt/SPHM1RT/rt_properties.h
+++ b/src/rt/SPHM1RT/rt_properties.h
@@ -119,7 +119,7 @@ struct rt_props {
   double beta_cgs_H;
 
   /*! The cross section of ionizing photons for hydrogen (cgs) */
-  double sigma_cross_cgs_H[RT_NGROUPS];
+  double sigma_cross_cgs_H[3];
 
   /*** end of useparams = 1 ***/
 
diff --git a/src/rt/debug/rt.h b/src/rt/debug/rt.h
index b46b3baa703786cf6aa4d7dadd4e8826a9b6d264..b36e0b2972d50608ec90a7db1e553417942de8a8 100644
--- a/src/rt/debug/rt.h
+++ b/src/rt/debug/rt.h
@@ -51,8 +51,7 @@ rt_compute_stellar_emission_rate(struct spart* restrict sp, double time,
   sp->rt_data.debug_emission_rate_set += 1;
 
   /* rt_set_stellar_emission_rate(sp, star_age_begin_of_step, star_age,
-   * rt_props, */
-  /*                              phys_const, internal_units); */
+   *                              rt_props, phys_const, internal_units); */
 }
 
 /**
@@ -66,10 +65,10 @@ __attribute__((always_inline)) INLINE static void rt_init_part(
     struct part* restrict p) {}
 
 /**
- * @brief Reset of the RT hydro particle data not related to the density.
+ * @brief Reset the RT hydro particle data not related to the hydro density.
  * Note: during initalisation (space_init), rt_reset_part and rt_init_part
- * are both called individually. Also an extra call to rt_reset_part is made
- * in space_convert_rt_quantities_after_zeroth_step().
+ * are both called individually. To reset RT data needed in each RT sub-cycle,
+ * use rt_reset_part_each_subcycle().
  *
  * @param p the particle to work on
  * @param cosmo Cosmology.
@@ -80,57 +79,35 @@ __attribute__((always_inline)) INLINE static void rt_reset_part(
   /* reset this here as well as in the rt_debugging_checks_end_of_step()
    * routine to test task dependencies are done right */
   p->rt_data.debug_iact_stars_inject = 0;
-
-  p->rt_data.debug_calls_iact_gradient_interaction = 0;
-  p->rt_data.debug_calls_iact_transport_interaction = 0;
-
+  p->rt_data.debug_nsubcycles = 0;
   p->rt_data.debug_kicked = 0;
-  p->rt_data.debug_injection_done = 0;
-  p->rt_data.debug_gradients_done = 0;
-  p->rt_data.debug_transport_done = 0;
-  p->rt_data.debug_thermochem_done = 0;
 }
 
 /**
- * @brief First initialisation of the RT hydro particle data.
+ * @brief Reset RT particle data which needs to be reset each sub-cycle.
  *
- * @param p particle to work on
- * @param cosmo #cosmology data structure.
- * @param rt_props RT properties struct
+ * @param p the particle to work on
  */
-__attribute__((always_inline)) INLINE static void rt_first_init_part(
-    struct part* restrict p, const struct cosmology* cosmo,
-    const struct rt_props* restrict rt_props) {
+__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle(
+    struct part* restrict p) {
 
-  rt_init_part(p);
-  rt_reset_part(p, cosmo);
-  p->rt_data.debug_radiation_absorbed_tot = 0ULL;
+  rt_debugging_reset_each_subcycle(p);
 }
 
 /**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the particle density and time step to be known.
+ * @brief First initialisation of the RT hydro particle data.
  *
  * @param p particle to work on
- * @param rt_props RT properties struct
  * @param cosmo #cosmology data structure.
+ * @param rt_props RT properties struct
  */
-__attribute__((always_inline)) INLINE static void
-rt_init_part_after_zeroth_step(struct part* restrict p,
-                               const struct rt_props* rt_props,
-                               const struct cosmology* restrict cosmo) {
+__attribute__((always_inline)) INLINE static void rt_first_init_part(
+    struct part* restrict p, const struct cosmology* cosmo,
+    const struct rt_props* restrict rt_props) {
 
-  /* If we're running with debugging checks on, reset debugging
-   * counters and flags in particular after the zeroth step so
-   * that the checks work as intended. */
   rt_init_part(p);
   rt_reset_part(p, cosmo);
-  /* Since the inject_prep has been moved to the density loop, the
-   * initialization at startup is messing with the total counters for stars
-   * because the density is called, but not the force-and-kick tasks. So reset
-   * the total counters here as well so that they will match the star counters.
-   */
+  rt_reset_part_each_subcycle(p);
   p->rt_data.debug_radiation_absorbed_tot = 0ULL;
 }
 
@@ -154,8 +131,7 @@ __attribute__((always_inline)) INLINE static void rt_init_spart(
 /**
  * @brief Reset of the RT star particle data not related to the density.
  * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart
- * are both called individually. Also an extra call to rt_reset_spart is made
- * in space_convert_rt_quantities_after_zeroth_step().
+ * are both called individually.
  *
  * @param sp star particle to work on
  */
@@ -175,37 +151,6 @@ __attribute__((always_inline)) INLINE static void rt_first_init_spart(
   sp->rt_data.debug_radiation_emitted_tot = 0ULL;
 }
 
-/**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the star density and time step to be known.
- * @param sp star particle to work on
- * @param time current system time
- * @param star_age age of the star *at the end of the step*
- * @param dt star time step
- * @param rt_props RT properties struct
- * @param phys_const physical constants struct
- * @param internal_units struct holding internal units
- */
-__attribute__((always_inline)) INLINE static void
-rt_init_star_after_zeroth_step(struct spart* restrict sp, double time,
-                               double star_age, double dt,
-                               const struct rt_props* rt_props,
-                               const struct phys_const* phys_const,
-                               const struct unit_system* internal_units) {
-
-  /* If we're running with debugging checks on, reset debugging
-   * counters and flags in particular after the zeroth step so
-   * that the checks work as intended. */
-  rt_init_spart(sp);
-  rt_reset_spart(sp);
-  /* Since the inject_prep has been moved to the density loop, the
-   * initialization at startup is messing with the total counters because
-   * the density is called, but not the force-and-kick tasks. So reset
-   * the total counters here as well. */
-  sp->rt_data.debug_radiation_emitted_tot = 0ULL;
-}
-
 /**
  * @brief Split the RT data of a particle into n pieces
  *
@@ -213,7 +158,9 @@ rt_init_star_after_zeroth_step(struct spart* restrict sp, double time,
  * @param n The number of pieces to split into.
  */
 __attribute__((always_inline)) INLINE static void rt_split_part(struct part* p,
-                                                                double n) {}
+                                                                double n) {
+  error("RT can't run with split particles for now.");
+}
 
 /**
  * @brief Exception handle a hydro part not having any neighbours in ghost task
@@ -316,9 +263,8 @@ __attribute__((always_inline)) INLINE static double rt_part_dt(
 __attribute__((always_inline)) INLINE static void rt_finalise_injection(
     struct part* restrict p, struct rt_props* props) {
 
-  if (p->rt_data.debug_kicked != 1)
-    error("called rt_ghost1 when particle %lld is unkicked (count=%d)", p->id,
-          p->rt_data.debug_kicked);
+  rt_debug_sequence_check(p, 1, "rt_ghost1/rt_finalise_injection");
+
   p->rt_data.debug_injection_done += 1;
 }
 
@@ -331,15 +277,7 @@ __attribute__((always_inline)) INLINE static void rt_finalise_injection(
 __attribute__((always_inline)) INLINE static void rt_end_gradient(
     struct part* restrict p, const struct cosmology* cosmo) {
 
-  if (p->rt_data.debug_kicked != 1)
-    error("called finalise gradient when particle %lld is unkicked (count=%d)",
-          p->id, p->rt_data.debug_kicked);
-
-  if (p->rt_data.debug_injection_done != 1)
-    error(
-        "Called finalise gradient on particle %lld"
-        "where injection_done count = %d",
-        p->id, p->rt_data.debug_injection_done);
+  rt_debug_sequence_check(p, 2, __func__);
 
   if (p->rt_data.debug_calls_iact_gradient_interaction == 0)
     message(
@@ -361,21 +299,7 @@ __attribute__((always_inline)) INLINE static void rt_finalise_transport(
     struct part* restrict p, const double dt,
     const struct cosmology* restrict cosmo) {
 
-  if (p->rt_data.debug_kicked != 1)
-    error("called finalise transport when particle %lld is unkicked (count=%d)",
-          p->id, p->rt_data.debug_kicked);
-
-  if (p->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do finalise_transport on particle %lld when "
-        "injection_done count is %d",
-        p->id, p->rt_data.debug_injection_done);
-
-  if (p->rt_data.debug_gradients_done != 1)
-    error(
-        "Trying to do finalise_transport on particle %lld when "
-        "gradients_done count is %d",
-        p->id, p->rt_data.debug_gradients_done);
+  rt_debug_sequence_check(p, 3, __func__);
 
   if (p->rt_data.debug_calls_iact_transport_interaction == 0)
     message(
@@ -407,28 +331,15 @@ __attribute__((always_inline)) INLINE static void rt_tchem(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us, const double dt) {
 
-  if (p->rt_data.debug_kicked != 1)
-    error(
-        "Part %lld trying to do thermochemistry on unkicked particle "
-        "(count=%d)",
-        p->id, p->rt_data.debug_kicked);
-  if (p->rt_data.debug_injection_done != 1)
-    error("Part %lld trying to do thermochemistry when injection_done != 1: %d",
-          p->id, p->rt_data.debug_injection_done);
-  if (p->rt_data.debug_gradients_done != 1)
-    error("Part %lld trying to do thermochemistry when gradients_done != 1: %d",
-          p->id, p->rt_data.debug_gradients_done);
-  if (p->rt_data.debug_transport_done != 1)
-    error("Part %lld trying to do thermochemistry when transport_done != 1: %d",
-          p->id, p->rt_data.debug_transport_done);
-
+  rt_debug_sequence_check(p, 4, __func__);
   p->rt_data.debug_thermochem_done += 1;
 
   /* rt_do_thermochemistry(p); */
 }
 
 /**
- * @brief Extra operations done during the kick.
+ * @brief Extra operations done during the kick. This needs to be
+ * done before the particle mass is updated in the hydro_kick_extra.
  *
  * @param p Particle to act upon.
  * @param dt_therm Thermal energy time-step @f$\frac{dt}{a^2}@f$.
@@ -447,6 +358,8 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra(
   /* Don't account for timestep_sync backward kicks */
   if (dt_therm >= 0.f && dt_grav >= 0.f && dt_hydro >= 0.f &&
       dt_kick_corr >= 0.f) {
+
+    rt_debug_sequence_check(p, 0, __func__);
     p->rt_data.debug_kicked += 1;
   }
 }
@@ -463,6 +376,16 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra(
 __attribute__((always_inline)) INLINE static void rt_prepare_force(
     struct part* p) {}
 
+/**
+ * @brief Extra operations to be done during the drift
+ *
+ * @param p Particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param dt_drift The drift time-step for positions.
+ */
+__attribute__((always_inline)) INLINE static void rt_predict_extra(
+    struct part* p, struct xpart* xp, float dt_drift) {}
+
 /**
  * @brief Clean the allocated memory inside the RT properties struct.
  *
diff --git a/src/rt/debug/rt_debugging.h b/src/rt/debug/rt_debugging.h
index 83e670d3f22f8a018e76c654751f95a701c55a85..c1f42667c142dc5ad3078da441a7af6708ec777a 100644
--- a/src/rt/debug/rt_debugging.h
+++ b/src/rt/debug/rt_debugging.h
@@ -19,7 +19,9 @@
 #ifndef SWIFT_RT_DEBUGGING_DEBUG_H
 #define SWIFT_RT_DEBUGGING_DEBUG_H
 
+#include "active.h"
 #include "rt_properties.h"
+#include "timeline.h"
 
 /**
  * @file src/rt/debug/rt_debugging.h
@@ -27,6 +29,100 @@
  * extra debugging functions.
  */
 
+/**
+ * @brief This resets particle carried quantities after each subcycling
+ * step such that the internal checks are still consistent.
+ * @param p the particle to work on
+ */
+__attribute__((always_inline)) INLINE static void rt_debugging_count_subcycle(
+    struct part *restrict p) {
+  p->rt_data.debug_nsubcycles += 1;
+}
+
+/**
+ * @brief Check that the particle completed the correct number of subcycles.
+ * This is checked in every rt_reset_part, before the subcycling count is reset.
+ * @param p the particle to work on
+ * @param rt_props RT properties struct
+ */
+__attribute__((always_inline)) INLINE static void
+rt_debugging_check_nr_subcycles(struct part *restrict p,
+                                const struct rt_props *rt_props) {
+
+  /* TODO: this check may fail when running with limiter/sync. */
+
+  /* NOTE: we need to do this check somewhere in the hydro tasks.
+   * (1) it needs to be done when all tasks are active and before the
+   * particle hydro time step is changed.
+   * (2) If you do it during RT tasks, it won't properly check how
+   * many sub-cycles you did during a single hydro task.
+   * (3) You can't do it during the timestep task, since between
+   * the hydro and the timestep we already do an RT step. */
+
+  /* skip initialization */
+  if (p->time_bin == 0) return;
+  if (p->rt_time_data.time_bin == 0)
+    error("Got part %lld with RT time bin 0", p->id);
+
+  timebin_t bindiff = p->time_bin - p->rt_time_data.time_bin;
+
+  if (rt_props->debug_max_nr_subcycles <= 1) {
+    /* Running without subcycling. */
+    if (bindiff != 0)
+      error("Running without subcycling but got bindiff=%d for part=%lld",
+            bindiff, p->id);
+    if (p->rt_data.debug_nsubcycles != 1)
+      error("Running without subcycling but got part=%lld subcycle count=%d",
+            p->id, p->rt_data.debug_nsubcycles);
+    return;
+  }
+
+  /* TODO: this assumes that max_nr_subcycles is not an upper limit,
+   * but a fixed number of sub-cycles */
+  timebin_t bindiff_expect = 0;
+
+  while (!(rt_props->debug_max_nr_subcycles & (1 << bindiff_expect)) &&
+         bindiff_expect != num_time_bins)
+    ++bindiff_expect;
+
+  if (bindiff_expect == num_time_bins)
+    error(
+        "Couldn't determine expected time bin difference. Max nr subcycles %d "
+        "bindiff %d",
+        rt_props->debug_max_nr_subcycles, bindiff);
+
+  if (bindiff != bindiff_expect)
+    error("Particle %lld Got bindiff=%d expect=%d; timebins=%d rt=%d", p->id,
+          bindiff, bindiff_expect, p->time_bin, p->rt_time_data.time_bin);
+
+  int subcycles_expect = (1 << bindiff);
+  if (p->rt_data.debug_nsubcycles != subcycles_expect)
+
+    if (p->rt_data.debug_nsubcycles != rt_props->debug_max_nr_subcycles)
+      error(
+          "Particle %lld didn't do the expected amount of subcycles: Expected "
+          "%d, done %d; time bins %d RT: %d",
+          p->id, subcycles_expect, p->rt_data.debug_nsubcycles, p->time_bin,
+          p->rt_time_data.time_bin);
+}
+
+/**
+ * @brief This resets particle carried quantities after each subcycling
+ * step such that the internal checks are still consistent.
+ * @param p the particle to work on
+ */
+__attribute__((always_inline)) INLINE static void
+rt_debugging_reset_each_subcycle(struct part *restrict p) {
+
+  p->rt_data.debug_calls_iact_gradient_interaction = 0;
+  p->rt_data.debug_calls_iact_transport_interaction = 0;
+
+  p->rt_data.debug_injection_done = 0;
+  p->rt_data.debug_gradients_done = 0;
+  p->rt_data.debug_transport_done = 0;
+  p->rt_data.debug_thermochem_done = 0;
+}
+
 /**
  * @brief Debugging checks loop over all star particles after each time step
  */
@@ -73,6 +169,7 @@ static void rt_debugging_end_of_step_hydro_mapper(void *restrict map_data,
     struct part *restrict p = &parts[k];
     absorption_sum_this_step += p->rt_data.debug_iact_stars_inject;
     absorption_sum_tot += p->rt_data.debug_radiation_absorbed_tot;
+
     /* Reset all values here in case particles won't be active next step */
     p->rt_data.debug_iact_stars_inject = 0;
   }
@@ -95,11 +192,6 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) {
 
   struct space *s = e->s;
   if (!(e->policy & engine_policy_rt)) return;
-#ifdef WITH_MPI
-  /* Since we aren't sending data back, none of these checks will
-   * pass a run over MPI. */
-  return;
-#endif
 
   const ticks tic = getticks();
 
@@ -123,7 +215,15 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) {
                    s->sparts, s->nr_sparts, sizeof(struct spart),
                    threadpool_auto_chunk_size, /*extra_data=*/e);
 
+#ifdef WITH_MPI
+  /* Since we aren't sending data back, none of these checks will
+   * pass a run over MPI. Make sure you run the threadpool functions
+   * first though so certain variables can get reset properly. */
+  return;
+#endif
+
   /* Have we accidentally invented or deleted some radiation somewhere? */
+
   if ((e->rt_props->debug_radiation_emitted_this_step !=
        e->rt_props->debug_radiation_absorbed_this_step) ||
       (e->rt_props->debug_radiation_emitted_tot !=
@@ -142,4 +242,77 @@ rt_debugging_checks_end_of_step(struct engine *e, int verbose) {
             clocks_getunit());
 }
 
+/**
+ * @brief Perform a series of consistency and sanity checks.
+ *
+ * @param p particle to check
+ * @param loc location where this is called from. This determines which checks
+ * will be done:
+ *
+ * 0: during kicks/after drifts.
+ * 1: during rt_ghost1/finalise_injection / after kicks.
+ * 2: during gradients / after injection.
+ * 3: during transport / after gradients.
+ * 4: during thermochem / after transport.
+ * 5: after thermochem.
+ *
+ * @param function_name: Function name (or message) you want printed on error.
+ */
+__attribute__((always_inline)) INLINE static void rt_debug_sequence_check(
+    struct part *restrict p, int loc, const char *function_name) {
+
+  /* Note: Checking whether a particle has been drifted at all is not
+   * compatible with subcycling. There is no reliable point where to
+   * reset the counters and have sensible results. */
+
+  if (loc > 0) {
+    /* Are kicks done? */
+    if (p->rt_data.debug_nsubcycles == 0) {
+      if (p->rt_data.debug_kicked != 1)
+        error(
+            "called %s on particle %lld with wrong kick count=%d (expected "
+            "1) cycle=%d",
+            function_name, p->id, p->rt_data.debug_kicked,
+            p->rt_data.debug_nsubcycles);
+    } else if (p->rt_data.debug_nsubcycles > 0) {
+      if (p->rt_data.debug_kicked != 2)
+        error(
+            "called %s on particle %lld with wrong kick count=%d (expected 2) "
+            "cycle=%d",
+            function_name, p->id, p->rt_data.debug_kicked,
+            p->rt_data.debug_nsubcycles);
+    } else {
+      error("Got negative subcycle???");
+    }
+  }
+
+  if (loc > 1) {
+    /* is injection done? */
+    if (p->rt_data.debug_injection_done != 1)
+      error("called %s on part %lld when finalise injection count is %d ID",
+            function_name, p->id, p->rt_data.debug_injection_done);
+  }
+
+  if (loc > 2) {
+    /* are gradients done? */
+    if (p->rt_data.debug_gradients_done != 1)
+      error("called %s on part %lld when gradients_done count is %d",
+            function_name, p->id, p->rt_data.debug_gradients_done);
+  }
+
+  if (loc > 3) {
+    /* is transport done? */
+    if (p->rt_data.debug_transport_done != 1)
+      error("called %s on part %lld when transport_done != 1: %d",
+            function_name, p->id, p->rt_data.debug_transport_done);
+  }
+
+  if (loc > 4) {
+    /* is thermochemistry done? */
+    if (p->rt_data.debug_thermochem_done != 1)
+      error("called %s on part %lld with thermochem_done count=%d",
+            function_name, p->id, p->rt_data.debug_thermochem_done);
+  }
+}
+
 #endif /* SWIFT_RT_DEBUGGING_DEBUG_H */
diff --git a/src/rt/debug/rt_gradients.h b/src/rt/debug/rt_gradients.h
index f79affc17d586ea9ff7bee222038e7366e7d30f2..3d2dfc8b0cd238e100872df5d1827e928cd3034c 100644
--- a/src/rt/debug/rt_gradients.h
+++ b/src/rt/debug/rt_gradients.h
@@ -19,6 +19,8 @@
 #ifndef SWIFT_RT_GRADIENTS_DEBUG_H
 #define SWIFT_RT_GRADIENTS_DEBUG_H
 
+#include "rt_debugging.h"
+
 /**
  * @file src/rt/debug/rt_gradients.h
  * @brief Main header file for the debug radiative transfer scheme gradients
@@ -38,32 +40,10 @@ __attribute__((always_inline)) INLINE static void rt_gradients_collect(
     float r2, const float dx[3], float hi, float hj, struct part *restrict pi,
     struct part *restrict pj) {
 
-  if (pi->rt_data.debug_kicked != 1)
-    error(
-        "Trying to do symmetric iact gradient unkicked particle %lld "
-        "(count=%d)",
-        pi->id, pi->rt_data.debug_kicked);
-
-  if (pi->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do symmetric iact gradient when finalise injection count is "
-        "%d ID %lld",
-        pi->rt_data.debug_injection_done, pi->id);
-
-  if (pj->rt_data.debug_kicked != 1)
-    error(
-        "Trying to do symmetric iact gradient unkicked particle %lld "
-        "(count=%d)",
-        pj->id, pj->rt_data.debug_kicked);
-
-  if (pj->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do symmetric iact gradient when finalise injection count is "
-        "%d ID %lld",
-        pj->rt_data.debug_injection_done, pj->id);
+  rt_debug_sequence_check(pi, 2, __func__);
+  rt_debug_sequence_check(pj, 2, __func__);
 
   pi->rt_data.debug_calls_iact_gradient_interaction += 1;
-
   pj->rt_data.debug_calls_iact_gradient_interaction += 1;
 }
 
@@ -81,18 +61,7 @@ __attribute__((always_inline)) INLINE static void rt_gradients_nonsym_collect(
     float r2, const float dx[3], float hi, float hj, struct part *restrict pi,
     struct part *restrict pj) {
 
-  if (pi->rt_data.debug_kicked != 1)
-    error(
-        "Trying to do nonsym iact gradient on unkicked particle %lld "
-        "(count=%d)",
-        pi->id, pi->rt_data.debug_kicked);
-
-  if (pi->rt_data.debug_injection_done != 1)
-    error(
-        "Trying to do nonsym iact gradients when finalise injection count is "
-        "%d ID %lld",
-        pi->rt_data.debug_injection_done, pi->id);
-
+  rt_debug_sequence_check(pi, 2, __func__);
   pi->rt_data.debug_calls_iact_gradient_interaction += 1;
 }
 
diff --git a/src/rt/debug/rt_iact.h b/src/rt/debug/rt_iact.h
index 6c6276c85d27fee6b048b757935fb610fb6afe56..2eb590dd43281affb0c3aece562a7d5f71948376 100644
--- a/src/rt/debug/rt_iact.h
+++ b/src/rt/debug/rt_iact.h
@@ -19,6 +19,7 @@
 #ifndef SWIFT_RT_IACT_DEBUG_H
 #define SWIFT_RT_IACT_DEBUG_H
 
+#include "rt_debugging.h"
 #include "rt_gradients.h"
 
 /**
@@ -73,6 +74,8 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_inject(
    * have nothing to do here. */
   if (si->density.wcount == 0.f) return;
 
+  /* Do some checks and increase neighbour counts
+   * before other potential early exits */
   if (si->rt_data.debug_iact_hydro_inject_prep == 0)
     error(
         "Injecting energy from star that wasn't called during injection prep");
@@ -107,42 +110,12 @@ __attribute__((always_inline)) INLINE static void runner_iact_rt_flux_common(
     float r2, const float *dx, float hi, float hj, struct part *restrict pi,
     struct part *restrict pj, float a, float H, int mode) {
 
-  if (pi->rt_data.debug_kicked != 1)
-    error("Trying to iact transport with unkicked particle %lld (count=%d)",
-          pi->id, pi->rt_data.debug_kicked);
-
-  if (pi->rt_data.debug_injection_done != 1)
-    error(
-        "Part %lld trying to do iact transport when "
-        "injection_done count is %d",
-        pi->id, pi->rt_data.debug_injection_done);
-
-  if (pi->rt_data.debug_gradients_done != 1)
-    error(
-        "Part %lld trying to do iact transport when "
-        "gradients_done count is %d",
-        pi->id, pi->rt_data.debug_gradients_done);
-
+  const char *func_name = (mode == 1) ? "sym flux iact" : "nonsym flux iact";
+  rt_debug_sequence_check(pi, 3, func_name);
   pi->rt_data.debug_calls_iact_transport_interaction += 1;
 
   if (mode == 1) {
-
-    if (pj->rt_data.debug_kicked != 1)
-      error("Trying to iact transport with unkicked particle %lld (count=%d)",
-            pj->id, pj->rt_data.debug_kicked);
-
-    if (pj->rt_data.debug_injection_done != 1)
-      error(
-          "Part %lld Trying to do iact transport when "
-          "injection_done count is %d",
-          pj->id, pj->rt_data.debug_injection_done);
-
-    if (pj->rt_data.debug_gradients_done != 1)
-      error(
-          "Part %lld Trying to do iact transport when "
-          "gradients_done count is %d",
-          pj->id, pj->rt_data.debug_gradients_done);
-
+    rt_debug_sequence_check(pj, 3, func_name);
     pj->rt_data.debug_calls_iact_transport_interaction += 1;
   }
 }
diff --git a/src/rt/debug/rt_io.h b/src/rt/debug/rt_io.h
index 8ac58f13fb7747ae9edb04dddce66f701b80800b..779724d2576dd9a1d26ec25a3e78005894d584f4 100644
--- a/src/rt/debug/rt_io.h
+++ b/src/rt/debug/rt_io.h
@@ -95,8 +95,11 @@ INLINE static int rt_write_particles(const struct part* parts,
       "RTDebugRadAbsorbedTot", ULONGLONG, 1, UNIT_CONV_NO_UNITS, 0, parts,
       rt_data.debug_radiation_absorbed_tot,
       "Radiation absorbed by this part during its lifetime");
+  list[7] = io_make_output_field("RTDebugSubcycles", INT, 1, UNIT_CONV_NO_UNITS,
+                                 0, parts, rt_data.debug_nsubcycles,
+                                 "How many times this part was subcycled");
 
-  return 7;
+  return 8;
 }
 
 /**
diff --git a/src/rt/debug/rt_properties.h b/src/rt/debug/rt_properties.h
index eb213e117edcac7139096f287487e84bde5274c9..215639886413f8e39cff3d4b78b583bfaa4f8cb1 100644
--- a/src/rt/debug/rt_properties.h
+++ b/src/rt/debug/rt_properties.h
@@ -47,6 +47,9 @@ struct rt_props {
   /* total radiation absorbed by gas. This is not really a property,
    * but a placeholder to sum up a global variable */
   unsigned long long debug_radiation_absorbed_tot;
+
+  /* Max number of subcycles per hydro step */
+  int debug_max_nr_subcycles;
 };
 
 /**
@@ -83,13 +86,10 @@ __attribute__((always_inline)) INLINE static void rt_props_init(
   rtp->debug_radiation_absorbed_tot = 0ULL;
   rtp->debug_radiation_absorbed_this_step = 0ULL;
 
-  /* After initialisation, print params to screen */
-  rt_props_print(rtp);
-
-  /* Print a final message. */
-  if (engine_rank == 0) {
-    message("Radiative transfer initialized");
-  }
+  /* Don't make it an optional parameter here so we crash
+   * if I forgot to provide it */
+  rtp->debug_max_nr_subcycles =
+      parser_get_param_int(params, "TimeIntegration:max_nr_rt_subcycles");
 }
 
 /**
diff --git a/src/rt/debug/rt_struct.h b/src/rt/debug/rt_struct.h
index 2ab50cc7513979b34a901dcf0ccfb725a295c920..bb3742f98c0256b76fa8b33231abaa16fce1d72d 100644
--- a/src/rt/debug/rt_struct.h
+++ b/src/rt/debug/rt_struct.h
@@ -19,6 +19,8 @@
 #ifndef SWIFT_RT_STRUCT_DEBUG_H
 #define SWIFT_RT_STRUCT_DEBUG_H
 
+#include "timeline.h"
+
 /**
  * @file src/rt/debug/rt_struct.h
  * @brief Main header file for the debug radiative transfer struct.
@@ -61,6 +63,11 @@ struct rt_part_data {
 
   /*! thermochemistry done? */
   int debug_thermochem_done;
+
+  /* Subcycling flags */
+
+  /*! Current subcycle wrt (last) hydro step */
+  int debug_nsubcycles;
 };
 
 /* Additional RT data in star particle struct */
diff --git a/src/rt/none/rt.h b/src/rt/none/rt.h
index 57e2b77302b9a83abefed8a96b126dbe2c88c217..5f256cfc83deeaf7d2980de79f44316a763d42d2 100644
--- a/src/rt/none/rt.h
+++ b/src/rt/none/rt.h
@@ -61,15 +61,25 @@ __attribute__((always_inline)) INLINE static void rt_init_part(
     struct part* restrict p) {}
 
 /**
- * @brief Reset of the RT hydro particle data not related to the density.
+ * @brief Reset the RT hydro particle data not related to the hydro density.
  * Note: during initalisation (space_init), rt_reset_part and rt_init_part
- * are both called individually.
+ * are both called individually. To reset RT data needed in each RT sub-cycle,
+ * use rt_reset_part_each_subcycle().
+ *
  * @param p particle to work on
  * @param cosmo Cosmology.
  */
 __attribute__((always_inline)) INLINE static void rt_reset_part(
     struct part* restrict p, const struct cosmology* cosmo) {}
 
+/**
+ * @brief Reset RT particle data which needs to be reset each sub-cycle.
+ *
+ * @param p the particle to work on
+ */
+__attribute__((always_inline)) INLINE static void rt_reset_part_each_subcycle(
+    struct part* restrict p) {}
+
 /**
  * @brief First initialisation of the RT hydro particle data.
  *
@@ -81,20 +91,6 @@ __attribute__((always_inline)) INLINE static void rt_first_init_part(
     struct part* restrict p, const struct cosmology* cosmo,
     const struct rt_props* restrict rt_props) {}
 
-/**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the particle density and time step to be known.
- *
- * @param p particle to work on
- * @param rt_props RT properties struct
- * @param cosmo #cosmology data structure.
- */
-__attribute__((always_inline)) INLINE static void
-rt_init_part_after_zeroth_step(struct part* restrict p,
-                               const struct rt_props* rt_props,
-                               const struct cosmology* restrict cosmo) {}
-
 /**
  * @brief Initialisation of the RT density loop related star particle data.
  * Note: during initalisation (space_init), rt_reset_spart and rt_init_spart
@@ -123,25 +119,6 @@ __attribute__((always_inline)) INLINE static void rt_reset_spart(
 __attribute__((always_inline)) INLINE static void rt_first_init_spart(
     struct spart* restrict sp) {}
 
-/**
- * @brief Initialises particle quantities that can't be set
- * otherwise before the zeroth step is finished. E.g. because
- * they require the star density and time step to be known.
- * @param sp star particle to work on
- * @param time current system time
- * @param star_age age of the star *at the end of the step*
- * @param dt star time step
- * @param rt_props RT properties struct
- * @param phys_const physical constants struct
- * @param internal_units struct holding internal units
- */
-__attribute__((always_inline)) INLINE static void
-rt_init_star_after_zeroth_step(struct spart* restrict sp, double time,
-                               double star_age, double dt,
-                               const struct rt_props* rt_props,
-                               const struct phys_const* phys_const,
-                               const struct unit_system* internal_units) {}
-
 /**
  * @brief Split the RT data of a particle into n pieces
  *
@@ -318,6 +295,16 @@ __attribute__((always_inline)) INLINE static void rt_kick_extra(
 __attribute__((always_inline)) INLINE static void rt_prepare_force(
     struct part* p) {}
 
+/**
+ * @brief Extra operations to be done during the drift
+ *
+ * @param p Particle to act upon.
+ * @param xp The extended particle data to act upon.
+ * @param dt_drift The drift time-step for positions.
+ */
+__attribute__((always_inline)) INLINE static void rt_predict_extra(
+    struct part* p, struct xpart* xp, float dt_drift) {}
+
 /**
  * @brief Clean the allocated memory inside the RT properties struct.
  *
diff --git a/src/rt/none/rt_properties.h b/src/rt/none/rt_properties.h
index 251b142e3bf6217b152b5e22c34f5d4b082ac539..f769507f6871d6ddf8002dd2a909049ef570e211 100644
--- a/src/rt/none/rt_properties.h
+++ b/src/rt/none/rt_properties.h
@@ -57,11 +57,7 @@ __attribute__((always_inline)) INLINE static void rt_props_print(
 __attribute__((always_inline)) INLINE static void rt_props_init(
     struct rt_props* rtp, const struct phys_const* phys_const,
     const struct unit_system* us, struct swift_params* params,
-    struct cosmology* cosmo) {
-
-  /* After initialisation, print params to screen */
-  rt_props_print(rtp);
-}
+    struct cosmology* cosmo) {}
 
 /**
  * @brief Write an RT properties struct to the given FILE as a
diff --git a/src/rt_additions.h b/src/rt_additions.h
index 3525f3e9e840578d17d9dfef9162f7ad1d155844..cb7595c238171c57a5653ef3ace74857b5b39b4b 100644
--- a/src/rt_additions.h
+++ b/src/rt_additions.h
@@ -26,7 +26,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right RT definition */
 #if defined(RT_NONE)
diff --git a/src/rt_io.h b/src/rt_io.h
index 94ee5452cb984b2c1f10c0fdd47f4b7b31c2fcf7..655c13f3bdd1c7af3eebc6cd09b3d0c01456a9b6 100644
--- a/src/rt_io.h
+++ b/src/rt_io.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right RT definition */
 #if defined(RT_NONE)
diff --git a/src/rt_parameters.h b/src/rt_parameters.h
index e6cbd1b4feabeb975b5bcb96843af7ec219f9462..fa3821005f81c8e0157c0e9883f4f1bf27e38c26 100644
--- a/src/rt_parameters.h
+++ b/src/rt_parameters.h
@@ -27,7 +27,7 @@
  * */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right functions */
 #if defined(RT_DEBUG)
diff --git a/src/rt_properties.h b/src/rt_properties.h
index 9310d832851e104387493058ba1a81398b33c964..e0454afe79786f16c8f0c0b75fe3112709e2498d 100644
--- a/src/rt_properties.h
+++ b/src/rt_properties.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right RT definition */
 #if defined(RT_NONE)
diff --git a/src/rt_struct.h b/src/rt_struct.h
index 3c73e8867a41b269a79f64e2e1061ddee1916188..6474f2efa731366766d41594d867b845ef016b90 100644
--- a/src/rt_struct.h
+++ b/src/rt_struct.h
@@ -25,7 +25,9 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include "timeline.h"
+
+#include <config.h>
 
 /* Import the right RT struct definition */
 #if defined(RT_NONE)
@@ -40,4 +42,29 @@
 #error "Invalid choice of radiation scheme"
 #endif
 
+/* Define a struct to contain all RT sub-cycling related
+ * timestepping variables here. These variables need to be
+ * identical for every scheme and users should never touch
+ * them anyway, so hide them here. */
+
+#if defined(RT_NONE)
+
+/*! empty placeholder for RT timestepping data. */
+struct rt_timestepping_data {
+  union {
+    /*! Time-bin this particle uses for RT interactions */
+    timebin_t time_bin;
+  };
+};
+
+#else
+
+/*! data relevant to the sub-cycle timestepping of parts. */
+struct rt_timestepping_data {
+
+  /*! Time-bin this particle uses for RT interactions */
+  timebin_t time_bin;
+};
+#endif
+
 #endif /* SWIFT_RT_STRUCT_H */
diff --git a/src/runner.h b/src/runner.h
index a60710da3448373a744f12ab427bb4a63f91c189..4ccd3cec4ac7e6ef1edffb0e81ec16da54d15145 100644
--- a/src/runner.h
+++ b/src/runner.h
@@ -24,7 +24,7 @@
 #define SWIFT_RUNNER_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "cache.h"
@@ -100,7 +100,7 @@ void runner_do_black_holes_swallow_ghost(struct runner *r, struct cell *c,
                                          int timer);
 void runner_do_init_grav(struct runner *r, struct cell *c, int timer);
 void runner_do_hydro_sort(struct runner *r, struct cell *c, int flag,
-                          int cleanup, int clock);
+                          int cleanup, int rt_requests_sort, int clock);
 void runner_do_stars_sort(struct runner *r, struct cell *c, int flag,
                           int cleanup, int clock);
 void runner_do_all_hydro_sort(struct runner *r, struct cell *c);
@@ -153,6 +153,10 @@ void runner_do_pack_limiter(struct runner *r, struct cell *c, void **buffer,
 void runner_do_unpack_limiter(struct runner *r, struct cell *c, void *buffer,
                               const int timer);
 void runner_do_neutrino_weighting(struct runner *r, struct cell *c, int timer);
+void runner_do_rt_advance_cell_time(struct runner *r, struct cell *c,
+                                    int timer);
+void runner_do_collect_rt_times(struct runner *r, struct cell *c,
+                                const int timer);
 void *runner_main(void *data);
 
 ticks runner_get_active_time(const struct runner *restrict r);
diff --git a/src/runner_black_holes.c b/src/runner_black_holes.c
index cba52aff3fa924d0ef2782ba1e2a5eba13a481a7..aebef16591aa6feaff1d12c26f0733e4a7cad0d4 100644
--- a/src/runner_black_holes.c
+++ b/src/runner_black_holes.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner.h"
@@ -385,7 +385,8 @@ void runner_do_bh_swallow(struct runner *r, struct cell *c, int timer) {
             /* Swallow the BH particle (i.e. update the swallowing BH
              * properties with the properties of cell_bp) */
             black_holes_swallow_bpart(bp, cell_bp, e->cosmology, e->time,
-                                      with_cosmology, props);
+                                      with_cosmology, props,
+                                      e->physical_constants);
 
             /* Release the space as we are done updating the bpart */
             if (lock_unlock(&s->lock) != 0)
diff --git a/src/runner_doiact_black_holes.c b/src/runner_doiact_black_holes.c
index 8a23ccaef43df76bab5c6bdeda7e8f1d16c03be7..bed8b293d78dafb5608e15ae0e09ce07725fed1c 100644
--- a/src/runner_doiact_black_holes.c
+++ b/src/runner_doiact_black_holes.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "active.h"
diff --git a/src/runner_doiact_functions_hydro.h b/src/runner_doiact_functions_hydro.h
index e52cb9de50f563c18fd160b1c0cf4fede94466b3..d677b5f6b7f2223bc2449055876cb70a2f0d2daa 100644
--- a/src/runner_doiact_functions_hydro.h
+++ b/src/runner_doiact_functions_hydro.h
@@ -49,7 +49,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
+  if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return;
 
   const int count_i = ci->hydro.count;
   const int count_j = cj->hydro.count;
@@ -79,7 +79,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
     /* Skip inhibited particles. */
     if (part_is_inhibited(pi, e)) continue;
 
-    const int pi_active = part_is_active(pi, e);
+    const int pi_active = PART_IS_ACTIVE(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])),
@@ -97,7 +97,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
 
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
-      const int pj_active = part_is_active(pj, e);
+      const int pj_active = PART_IS_ACTIVE(pj, e);
 
       /* Compute the pairwise distance. */
       const float pjx[3] = {(float)(pj->x[0] - cj->loc[0]),
@@ -106,7 +106,7 @@ void DOPAIR1_NAIVE(struct runner *r, struct cell *restrict ci,
       float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]};
       const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
@@ -182,7 +182,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
+  if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return;
 
   const int count_i = ci->hydro.count;
   const int count_j = cj->hydro.count;
@@ -212,7 +212,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
     /* Skip inhibited particles. */
     if (part_is_inhibited(pi, e)) continue;
 
-    const int pi_active = part_is_active(pi, e);
+    const int pi_active = PART_IS_ACTIVE(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - (cj->loc[0] + shift[0])),
@@ -228,7 +228,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
       /* Skip inhibited particles. */
       if (part_is_inhibited(pj, e)) continue;
 
-      const int pj_active = part_is_active(pj, e);
+      const int pj_active = PART_IS_ACTIVE(pj, e);
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
 
@@ -239,7 +239,7 @@ void DOPAIR2_NAIVE(struct runner *r, struct cell *restrict ci,
       float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]};
       const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
@@ -329,7 +329,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e)) return;
+  if (!CELL_IS_ACTIVE(c, e)) return;
 
   /* Cosmological terms and physical constants */
   const float a = cosmo->a;
@@ -348,7 +348,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
     /* Skip inhibited particles. */
     if (part_is_inhibited(pi, e)) continue;
 
-    const int pi_active = part_is_active(pi, e);
+    const int pi_active = PART_IS_ACTIVE(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - c->loc[0]),
@@ -366,7 +366,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
 
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
-      const int pj_active = part_is_active(pj, e);
+      const int pj_active = PART_IS_ACTIVE(pj, e);
 
       /* Compute the pairwise distance. */
       const float pjx[3] = {(float)(pj->x[0] - c->loc[0]),
@@ -378,7 +378,7 @@ void DOSELF1_NAIVE(struct runner *r, struct cell *restrict c) {
       const int doi = pi_active && (r2 < hig2);
       const int doj = pj_active && (r2 < hjg2);
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
@@ -465,7 +465,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
   TIMER_TIC;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e)) return;
+  if (!CELL_IS_ACTIVE(c, e)) return;
 
   /* Cosmological terms and physical constants */
   const float a = cosmo->a;
@@ -484,7 +484,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
     /* Skip inhibited particles. */
     if (part_is_inhibited(pi, e)) continue;
 
-    const int pi_active = part_is_active(pi, e);
+    const int pi_active = PART_IS_ACTIVE(pi, e);
     const float hi = pi->h;
     const float hig2 = hi * hi * kernel_gamma2;
     const float pix[3] = {(float)(pi->x[0] - c->loc[0]),
@@ -502,7 +502,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
 
       const float hj = pj->h;
       const float hjg2 = hj * hj * kernel_gamma2;
-      const int pj_active = part_is_active(pj, e);
+      const int pj_active = PART_IS_ACTIVE(pj, e);
 
       /* Compute the pairwise distance. */
       const float pjx[3] = {(float)(pj->x[0] - c->loc[0]),
@@ -514,7 +514,7 @@ void DOSELF2_NAIVE(struct runner *r, struct cell *restrict c) {
       const int doi = pi_active && ((r2 < hig2) || (r2 < hjg2));
       const int doj = pj_active && ((r2 < hig2) || (r2 < hjg2));
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
@@ -649,7 +649,7 @@ void DOPAIR_SUBSET_NAIVE(struct runner *r, struct cell *restrict ci,
         r2 += dx[k] * dx[k];
       }
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
@@ -757,7 +757,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci,
                        (float)(piz - pjz)};
         const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
@@ -822,7 +822,7 @@ void DOPAIR_SUBSET(struct runner *r, struct cell *restrict ci,
                        (float)(piz - pjz)};
         const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
@@ -885,35 +885,41 @@ void DOPAIR_SUBSET_BRANCH(struct runner *r, struct cell *restrict ci,
       shift[k] = -e->s->dim[k];
   }
 
-#if !defined(SWIFT_USE_NAIVE_INTERACTIONS)
   /* Get the sorting index. */
   int sid = 0;
   for (int k = 0; k < 3; k++)
-    sid = 3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0)
-                         ? 0
-                         : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2 : 1);
+    sid = 3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0)   ? 0
+                     : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2
+                                                                : 1);
 
   /* Switch the cells around? */
   const int flipped = runner_flip[sid];
   sid = sortlistID[sid];
 
-  /* Has the cell cj been sorted? */
-  if (!(cj->hydro.sorted & (1 << sid)) ||
-      cj->hydro.dx_max_sort_old > space_maxreldx * cj->dmin)
-    error("Interacting unsorted cells.");
-#endif
+  /* Is it sorted, if not we use the naive interactions. */
+  const int is_sorted =
+      (cj->hydro.sorted & (1 << sid)) &&
+      (cj->hydro.dx_max_sort_old <= space_maxreldx * cj->dmin);
 
 #if defined(SWIFT_USE_NAIVE_INTERACTIONS)
-  DOPAIR_SUBSET_NAIVE(r, ci, parts_i, ind, count, cj, shift);
-#elif defined(WITH_VECTORIZATION) && defined(GADGET2_SPH)
-  if (sort_is_face(sid))
-    runner_dopair_subset_density_vec(r, ci, parts_i, ind, count, cj, sid,
-                                     flipped, shift);
-  else
-    DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift);
+  int force_naive = 1;
 #else
-  DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift);
+  int force_naive = 0;
 #endif
+
+  if (force_naive || !is_sorted) {
+    DOPAIR_SUBSET_NAIVE(r, ci, parts_i, ind, count, cj, shift);
+  } else {
+#if defined(WITH_VECTORIZATION) && defined(GADGET2_SPH)
+    if (sort_is_face(sid))
+      runner_dopair_subset_density_vec(r, ci, parts_i, ind, count, cj, sid,
+                                       flipped, shift);
+    else
+      DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift);
+#else
+    DOPAIR_SUBSET(r, ci, parts_i, ind, count, cj, sid, flipped, shift);
+#endif
+  }
 }
 
 /**
@@ -982,7 +988,7 @@ void DOSELF_SUBSET(struct runner *r, struct cell *restrict ci,
       float dx[3] = {pix[0] - pjx[0], pix[1] - pjx[1], pix[2] - pjx[2]};
       const float r2 = dx[0] * dx[0] + dx[1] * dx[1] + dx[2] * dx[2];
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
       /* Check that particles have been drifted to the current time */
       if (pi->ti_drift != e->ti_current)
         error("Particle pi not drifted to current time");
@@ -1067,15 +1073,16 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Some constants used to checks that the parts are in the right frame */
+  /* TODO MLADEN: coordinate 2. -> 2.02 with Matthieu */
   const float shift_threshold_x =
-      2. * ci->width[0] +
-      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
+      2.02 * ci->width[0] +
+      2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_y =
-      2. * ci->width[1] +
-      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
+      2.02 * ci->width[1] +
+      2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_z =
-      2. * ci->width[2] +
-      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
+      2.02 * ci->width[2] +
+      2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
 #endif /* SWIFT_DEBUG_CHECKS */
 
   /* Get some other useful values. */
@@ -1094,7 +1101,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
   const float H = cosmo->H;
   GET_MU0();
 
-  if (cell_is_active_hydro(ci, e)) {
+  if (CELL_IS_ACTIVE(ci, e)) {
 
     /* Loop over the parts in ci. */
     for (int pid = count_i - 1;
@@ -1105,7 +1112,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
       const float hi = pi->h;
 
       /* Skip inactive particles */
-      if (!part_is_active(pi, e)) continue;
+      if (!PART_IS_ACTIVE(pi, e)) continue;
 
       /* Is there anything we need to interact with ? */
       const double di = sort_i[pid].d + hi * kernel_gamma + dx_max - rshift;
@@ -1162,11 +1169,13 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)",
               pjz, ci->width[2]);
 
+#if defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
+#endif
 #endif
 
         /* Hit or miss? */
@@ -1191,7 +1200,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
     }   /* loop over the parts in ci. */
   }     /* Cell ci is active */
 
-  if (cell_is_active_hydro(cj, e)) {
+  if (CELL_IS_ACTIVE(cj, e)) {
 
     /* Loop over the parts in cj. */
     for (int pjd = 0; pjd < count_j && sort_j[pjd].d - hj_max - dx_max < di_max;
@@ -1202,7 +1211,7 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
       const float hj = pj->h;
 
       /* Skip inactive particles */
-      if (!part_is_active(pj, e)) continue;
+      if (!PART_IS_ACTIVE(pj, e)) continue;
 
       /* Is there anything we need to interact with ? */
       const double dj = sort_j[pjd].d - hj * kernel_gamma - dx_max + rshift;
@@ -1259,11 +1268,13 @@ void DOPAIR1(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)",
               pjz, ci->width[2]);
 
+#if defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
+#endif
 #endif
 
         /* Hit or miss? */
@@ -1308,10 +1319,10 @@ void DOPAIR1_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
   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;
+  if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return;
 
   /* Check that cells are drifted. */
-  if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e))
+  if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e))
     error("Interacting undrifted cells.");
 
   /* Get the sort ID. */
@@ -1416,15 +1427,16 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Some constants used to checks that the parts are in the right frame */
+  /* TODO MLADEN: coordinate 2. -> 2.02 with Matthieu */
   const float shift_threshold_x =
-      2. * ci->width[0] +
-      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
+      2.02 * ci->width[0] +
+      2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_y =
-      2. * ci->width[1] +
-      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
+      2.02 * ci->width[1] +
+      2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
   const float shift_threshold_z =
-      2. * ci->width[2] +
-      2. * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
+      2.02 * ci->width[2] +
+      2.02 * max(ci->hydro.dx_max_part, cj->hydro.dx_max_part);
 #endif /* SWIFT_DEBUG_CHECKS */
 
   /* Get some other useful values. */
@@ -1453,22 +1465,22 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
   const double shift_j[3] = {cj->loc[0], cj->loc[1], cj->loc[2]};
 
   int count_active_i = 0, count_active_j = 0;
-  struct sort_entry *restrict sort_active_i = NULL,
-                              *restrict sort_active_j = NULL;
+  struct sort_entry *restrict sort_active_i = NULL;
+  struct sort_entry *restrict sort_active_j = NULL;
 
   // MATTHIEU: temporary disable this optimization
   if (0 /*&& cell_is_all_active_hydro(ci, e)*/) {
     /* If everybody is active don't bother copying */
     sort_active_i = sort_i;
     count_active_i = count_i;
-  } else if (cell_is_active_hydro(ci, e)) {
+  } else if (CELL_IS_ACTIVE(ci, e)) {
     if (posix_memalign((void **)&sort_active_i, SWIFT_CACHE_ALIGNMENT,
                        sizeof(struct sort_entry) * count_i) != 0)
       error("Failed to allocate active sortlists.");
 
     /* Collect the active particles in ci */
     for (int k = 0; k < count_i; k++) {
-      if (part_is_active(&parts_i[sort_i[k].i], e)) {
+      if (PART_IS_ACTIVE(&parts_i[sort_i[k].i], e)) {
         sort_active_i[count_active_i] = sort_i[k];
         count_active_i++;
       }
@@ -1480,14 +1492,14 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
     /* If everybody is active don't bother copying */
     sort_active_j = sort_j;
     count_active_j = count_j;
-  } else if (cell_is_active_hydro(cj, e)) {
+  } else if (CELL_IS_ACTIVE(cj, e)) {
     if (posix_memalign((void **)&sort_active_j, SWIFT_CACHE_ALIGNMENT,
                        sizeof(struct sort_entry) * count_j) != 0)
       error("Failed to allocate active sortlists.");
 
     /* Collect the active particles in cj */
     for (int k = 0; k < count_j; k++) {
-      if (part_is_active(&parts_j[sort_j[k].i], e)) {
+      if (PART_IS_ACTIVE(&parts_j[sort_j[k].i], e)) {
         sort_active_j[count_active_j] = sort_j[k];
         count_active_j++;
       }
@@ -1521,7 +1533,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
     /* Do we need to only check active parts in cj
        (i.e. pi does not need updating) ? */
-    if (!part_is_active(pi, e)) {
+    if (!PART_IS_ACTIVE(pi, e)) {
 
       /* Loop over the *active* parts in cj within range of pi */
       for (int pjd = 0; pjd < count_active_j && sort_active_j[pjd].d < di;
@@ -1575,12 +1587,14 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)",
               pjz, ci->width[2]);
 
+#if defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
 
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
+#endif
 #endif
 
         /* Hit or miss?
@@ -1653,19 +1667,21 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)",
               pjz, ci->width[2]);
 
+#if defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
 
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
+#endif
 #endif
         /* Hit or miss?
            (note that we will do the other condition in the reverse loop) */
         if (r2 < hig2) {
 
           /* Does pj need to be updated too? */
-          if (part_is_active(pj, e)) {
+          if (PART_IS_ACTIVE(pj, e)) {
             IACT(r2, dx, hi, hj, pi, pj, a, H);
             IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -1727,7 +1743,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
 
     /* Do we need to only check active parts in ci
        (i.e. pj does not need updating) ? */
-    if (!part_is_active(pj, e)) {
+    if (!PART_IS_ACTIVE(pj, e)) {
 
       /* Loop over the *active* parts in ci. */
       for (int pid = count_active_i - 1;
@@ -1782,11 +1798,13 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)",
               pjz, ci->width[2]);
 
+#if defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
+#endif
 #endif
 
         /* Hit or miss?
@@ -1861,11 +1879,13 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
               "Invalid particle position in Z for pj (pjz=%e ci->width[2]=%e)",
               pjz, ci->width[2]);
 
+#if defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
         if (pj->ti_drift != e->ti_current)
           error("Particle pj not drifted to current time");
+#endif
 #endif
 
         /* Hit or miss?
@@ -1873,7 +1893,7 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
         if (r2 < hjg2 && r2 >= hig2) {
 
           /* Does pi need to be updated too? */
-          if (part_is_active(pi, e)) {
+          if (PART_IS_ACTIVE(pi, e)) {
             IACT(r2, dx, hj, hi, pj, pi, a, H);
             IACT_MHD(r2, dx, hj, hi, pj, pi, mu_0, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -1909,9 +1929,9 @@ void DOPAIR2(struct runner *r, struct cell *ci, struct cell *cj, const int sid,
   }     /* Loop over all cj */
 
   /* Clean-up if necessary */  // MATTHIEU: temporary disable this optimization
-  if (cell_is_active_hydro(ci, e))  // && !cell_is_all_active_hydro(ci, e))
+  if (CELL_IS_ACTIVE(ci, e))   // && !cell_is_all_active_hydro(ci, e))
     free(sort_active_i);
-  if (cell_is_active_hydro(cj, e))  // && !cell_is_all_active_hydro(cj, e))
+  if (CELL_IS_ACTIVE(cj, e))  // && !cell_is_all_active_hydro(cj, e))
     free(sort_active_j);
 
   TIMER_TOC(TIMER_DOPAIR);
@@ -1934,10 +1954,10 @@ void DOPAIR2_BRANCH(struct runner *r, struct cell *ci, struct cell *cj) {
   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;
+  if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return;
 
   /* Check that cells are drifted. */
-  if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e))
+  if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e))
     error("Interacting undrifted cells.");
 
   /* Get the sort ID. */
@@ -2038,7 +2058,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
                      count * sizeof(int)) != 0)
     error("Failed to allocate indt.");
   for (int k = 0; k < count; k++)
-    if (part_is_active(&parts[k], e)) {
+    if (PART_IS_ACTIVE(&parts[k], e)) {
       indt[countdt] = k;
       countdt += 1;
     }
@@ -2064,7 +2084,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
     const float hig2 = hi * hi * kernel_gamma2;
 
     /* Is the ith particle inactive? */
-    if (!part_is_active(pi, e)) {
+    if (!PART_IS_ACTIVE(pi, e)) {
 
       /* Loop over the other particles .*/
       for (int pjd = firstdt; pjd < countdt; pjd++) {
@@ -2073,7 +2093,7 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
         struct part *restrict pj = &parts[indt[pjd]];
         const float hj = pj->h;
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
@@ -2135,11 +2155,11 @@ void DOSELF1(struct runner *r, struct cell *restrict c) {
           r2 += dx[k] * dx[k];
         }
         const int doj =
-            (part_is_active(pj, e)) && (r2 < hj * hj * kernel_gamma2);
+            (PART_IS_ACTIVE(pj, e)) && (r2 < hj * hj * kernel_gamma2);
 
         const int doi = (r2 < hig2);
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
@@ -2229,14 +2249,14 @@ void DOSELF1_BRANCH(struct runner *r, struct cell *c) {
   if (c->hydro.count == 0) return;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e)) return;
+  if (!CELL_IS_ACTIVE(c, e)) return;
 
   /* Did we mess up the recursion? */
   if (c->hydro.h_max_old * kernel_gamma > c->dmin)
     error("Cell smaller than smoothing length");
 
   /* Check that cells are drifted. */
-  if (!cell_are_part_drifted(c, e)) error("Interacting undrifted cell.");
+  if (!CELL_ARE_PART_DRIFTED(c, e)) error("Interacting undrifted cell.");
 
 #if defined(SWIFT_USE_NAIVE_INTERACTIONS)
   DOSELF1_NAIVE(r, c);
@@ -2276,7 +2296,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
                      count * sizeof(int)) != 0)
     error("Failed to allocate indt.");
   for (int k = 0; k < count; k++)
-    if (part_is_active(&parts[k], e)) {
+    if (PART_IS_ACTIVE(&parts[k], e)) {
       indt[countdt] = k;
       countdt += 1;
     }
@@ -2302,7 +2322,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
     const float hig2 = hi * hi * kernel_gamma2;
 
     /* Is the ith particle not active? */
-    if (!part_is_active(pi, e)) {
+    if (!PART_IS_ACTIVE(pi, e)) {
 
       /* Loop over the other particles .*/
       for (int pjd = firstdt; pjd < countdt; pjd++) {
@@ -2319,7 +2339,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
           r2 += dx[k] * dx[k];
         }
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
@@ -2373,7 +2393,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
           r2 += dx[k] * dx[k];
         }
 
-#ifdef SWIFT_DEBUG_CHECKS
+#if defined(SWIFT_DEBUG_CHECKS) && defined(DO_DRIFT_DEBUG_CHECKS)
         /* Check that particles have been drifted to the current time */
         if (pi->ti_drift != e->ti_current)
           error("Particle pi not drifted to current time");
@@ -2385,7 +2405,7 @@ void DOSELF2(struct runner *r, struct cell *restrict c) {
         if (r2 < hig2 || r2 < hj * hj * kernel_gamma2) {
 
           /* Does pj need to be updated too? */
-          if (part_is_active(pj, e)) {
+          if (PART_IS_ACTIVE(pj, e)) {
             IACT(r2, dx, hi, hj, pi, pj, a, H);
             IACT_MHD(r2, dx, hi, hj, pi, pj, mu_0, a, H);
 #if (FUNCTION_TASK_LOOP == TASK_LOOP_DENSITY)
@@ -2441,14 +2461,14 @@ void DOSELF2_BRANCH(struct runner *r, struct cell *c) {
   if (c->hydro.count == 0) return;
 
   /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e)) return;
+  if (!CELL_IS_ACTIVE(c, e)) return;
 
   /* Did we mess up the recursion? */
   if (c->hydro.h_max_old * kernel_gamma > c->dmin)
     error("Cell smaller than smoothing length");
 
   /* Check that cells are drifted. */
-  if (!cell_are_part_drifted(c, e)) error("Interacting undrifted cell.");
+  if (!CELL_ARE_PART_DRIFTED(c, e)) error("Interacting undrifted cell.");
 
 #if defined(SWIFT_USE_NAIVE_INTERACTIONS)
   DOSELF2_NAIVE(r, c);
@@ -2480,7 +2500,7 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj,
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
+  if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return;
   if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
 
   /* Get the type of pair and flip ci/cj if needed. */
@@ -2500,10 +2520,10 @@ void DOSUB_PAIR1(struct runner *r, struct cell *ci, struct cell *cj,
   }
 
   /* Otherwise, compute the pair directly. */
-  else if (cell_is_active_hydro(ci, e) || cell_is_active_hydro(cj, e)) {
+  else if (CELL_IS_ACTIVE(ci, e) || CELL_IS_ACTIVE(cj, e)) {
 
     /* Make sure both cells are drifted to the current timestep. */
-    if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e))
+    if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e))
       error("Interacting undrifted cells.");
 
     /* Do any of the cells need to be sorted first? */
@@ -2539,7 +2559,7 @@ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) {
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->hydro.count == 0 || !cell_is_active_hydro(ci, r->e)) return;
+  if (ci->hydro.count == 0 || !CELL_IS_ACTIVE(ci, r->e)) return;
 
   /* Recurse? */
   if (cell_can_recurse_in_self_hydro_task(ci)) {
@@ -2558,7 +2578,7 @@ void DOSUB_SELF1(struct runner *r, struct cell *ci, int gettimer) {
   else {
 
     /* Drift the cell to the current timestep if needed. */
-    if (!cell_are_part_drifted(ci, r->e)) error("Interacting undrifted cell.");
+    if (!CELL_ARE_PART_DRIFTED(ci, r->e)) error("Interacting undrifted cell.");
 
     DOSELF1_BRANCH(r, ci);
   }
@@ -2586,7 +2606,7 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj,
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (!cell_is_active_hydro(ci, e) && !cell_is_active_hydro(cj, e)) return;
+  if (!CELL_IS_ACTIVE(ci, e) && !CELL_IS_ACTIVE(cj, e)) return;
   if (ci->hydro.count == 0 || cj->hydro.count == 0) return;
 
   /* Get the type of pair and flip ci/cj if needed. */
@@ -2606,10 +2626,10 @@ void DOSUB_PAIR2(struct runner *r, struct cell *ci, struct cell *cj,
   }
 
   /* Otherwise, compute the pair directly. */
-  else if (cell_is_active_hydro(ci, e) || cell_is_active_hydro(cj, e)) {
+  else if (CELL_IS_ACTIVE(ci, e) || CELL_IS_ACTIVE(cj, e)) {
 
     /* Make sure both cells are drifted to the current timestep. */
-    if (!cell_are_part_drifted(ci, e) || !cell_are_part_drifted(cj, e))
+    if (!CELL_ARE_PART_DRIFTED(ci, e) || !CELL_ARE_PART_DRIFTED(cj, e))
       error("Interacting undrifted cells.");
 
     /* Do any of the cells need to be sorted first? */
@@ -2645,7 +2665,7 @@ void DOSUB_SELF2(struct runner *r, struct cell *ci, int gettimer) {
   TIMER_TIC;
 
   /* Should we even bother? */
-  if (ci->hydro.count == 0 || !cell_is_active_hydro(ci, r->e)) return;
+  if (ci->hydro.count == 0 || !CELL_IS_ACTIVE(ci, r->e)) return;
 
   /* Recurse? */
   if (cell_can_recurse_in_self_hydro_task(ci)) {
diff --git a/src/runner_doiact_functions_stars.h b/src/runner_doiact_functions_stars.h
index 3ac99897d12d93d9c9779108d8f1c1b59a5574ad..b163c6fc8a290c93238960e68dbb142283c49003 100644
--- a/src/runner_doiact_functions_stars.h
+++ b/src/runner_doiact_functions_stars.h
@@ -1060,9 +1060,9 @@ void DOPAIR1_SUBSET_BRANCH_STARS(struct runner *r, struct cell *restrict ci,
   /* Get the sorting index. */
   int sid = 0;
   for (int k = 0; k < 3; k++)
-    sid = 3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0)
-                         ? 0
-                         : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2 : 1);
+    sid = 3 * sid + ((cj->loc[k] - ci->loc[k] + shift[k] < 0) ? 0
+                     : (cj->loc[k] - ci->loc[k] + shift[k] > 0) ? 2
+                                                                : 1);
 
   /* Switch the cells around? */
   const int flipped = runner_flip[sid];
diff --git a/src/runner_doiact_grav.c b/src/runner_doiact_grav.c
index 132a49b8a85ab4be279f9776f268358f2b19c1ff..93ec9216ca63641af093cfa95038abcca9fdcb34 100644
--- a/src/runner_doiact_grav.c
+++ b/src/runner_doiact_grav.c
@@ -17,7 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner_doiact_grav.h"
@@ -2698,9 +2698,9 @@ void runner_do_grav_long_range(struct runner *r, struct cell *ci,
 
     /* Maximal distance any interaction can take place
      * before the mesh kicks in, rounded up to the next integer */
-    const int d = ceil(max_distance * max3(s->iwidth[0],
+    const int d = max(ceil(max_distance * max3(s->iwidth[0],
                                            s->iwidth[1],
-                                           s->iwidth[2])) + 1;
+                                               s->iwidth[2])) + 1, 2);
 
     /* Loop over plausibly useful cells */
     for (int ii = top_i - d; ii <= top_i + d; ++ii) {
@@ -3031,7 +3031,7 @@ void runner_dopair_recursive_grav_bkgpool(struct runner *r, struct cell *ci,
 
   /* Convert the maximal search distance to a number of cells
    * Define a lower and upper delta in case things are not symmetric */
-  const int delta = (int)(sqrt(3) * distance / ci->width[0]) + 1;
+  const int delta = max((int)(sqrt(3) * distance / ci->width[0]) + 1, 2);
   int delta_m = delta;
   int delta_p = delta;
 
diff --git a/src/runner_doiact_grav.h b/src/runner_doiact_grav.h
index cefc8f7082479a299ad7aaf6ebc6c8496488274b..cc2e2e0cce9bf5bc4908b396b7978709ad925379 100644
--- a/src/runner_doiact_grav.h
+++ b/src/runner_doiact_grav.h
@@ -20,7 +20,7 @@
 #ifndef SWIFT_RUNNER_DOIACT_GRAV_H
 #define SWIFT_RUNNER_DOIACT_GRAV_H
 
-#include "../config.h"
+#include <config.h>
 
 struct runner;
 struct cell;
diff --git a/src/runner_doiact_hydro.c b/src/runner_doiact_hydro.c
index 144847d291304190d4d871c13a5daaaad5812d33..3ec53e5077df9da65348b385abccaca9830255a8 100644
--- a/src/runner_doiact_hydro.c
+++ b/src/runner_doiact_hydro.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "active.h"
diff --git a/src/runner_doiact_hydro.h b/src/runner_doiact_hydro.h
index 04ca44bfac3dd8c3122ba99cbbf266b7483f5b32..04824e87eecc32f91aa4352176212c853ceaf56b 100644
--- a/src/runner_doiact_hydro.h
+++ b/src/runner_doiact_hydro.h
@@ -124,6 +124,23 @@
   {}
 #endif
 
+#if (FUNCTION_TASK_LOOP == TASK_LOOP_RT_GRADIENT) || \
+    (FUNCTION_TASK_LOOP == TASK_LOOP_RT_TRANSPORT)
+/* RT specific function calls */
+#define PART_IS_ACTIVE part_is_rt_active
+#define CELL_IS_ACTIVE cell_is_rt_active
+#define CELL_ARE_PART_DRIFTED cell_are_part_drifted_rt_sub_cycle
+#else
+/* default hydro behaviour. */
+#define PART_IS_ACTIVE part_is_active
+#define CELL_IS_ACTIVE cell_is_active_hydro
+#define CELL_ARE_PART_DRIFTED cell_are_part_drifted
+/* when running with RT subcycling, we can have RT active
+ * particles in a normal swift step that aren't drifted to
+ * the current time, so we don't do those checks there. */
+#define DO_DRIFT_DEBUG_CHECKS 1
+#endif
+
 #define _IACT_NONSYM_VEC(f) PASTE(runner_iact_nonsym_vec, f)
 #define IACT_NONSYM_VEC _IACT_NONSYM_VEC(FUNCTION)
 
diff --git a/src/runner_doiact_hydro_vec.c b/src/runner_doiact_hydro_vec.c
index f5fde51939d51e8fcd6ecbca07d3012609acb88d..dde0c98e256ce6aa1580aad2c667ae1cbb01b85c 100644
--- a/src/runner_doiact_hydro_vec.c
+++ b/src/runner_doiact_hydro_vec.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner_doiact_hydro_vec.h"
diff --git a/src/runner_doiact_hydro_vec.h b/src/runner_doiact_hydro_vec.h
index 6b01987c15513343d7a8784322803d7f834dea2c..f283d9bc4c3ba5d3e9664e908aebcffecea23dac 100644
--- a/src/runner_doiact_hydro_vec.h
+++ b/src/runner_doiact_hydro_vec.h
@@ -21,7 +21,7 @@
 #define SWIFT_RUNNER_VEC_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "active.h"
diff --git a/src/runner_doiact_limiter.c b/src/runner_doiact_limiter.c
index b72d75d591878d013580ca9cb2cfe3f019ea81ff..6ceced81662ea8f1b6741053ef0bacaf81fa52d8 100644
--- a/src/runner_doiact_limiter.c
+++ b/src/runner_doiact_limiter.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "active.h"
diff --git a/src/runner_doiact_stars.c b/src/runner_doiact_stars.c
index 7802cdd489a6886f482e7475f1a13e631bd2a9be..00dd94cc24c79a4319810a7a619d26874cbc72f8 100644
--- a/src/runner_doiact_stars.c
+++ b/src/runner_doiact_stars.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "active.h"
diff --git a/src/runner_doiact_undef.h b/src/runner_doiact_undef.h
index 7ab48277d31fd67086fd8f11b67657fe986308a5..0a76c6d15df5a535a6cf7e93f6b54a9972b60bfa 100644
--- a/src/runner_doiact_undef.h
+++ b/src/runner_doiact_undef.h
@@ -33,3 +33,9 @@
 #undef GET_MU0
 #undef FUNCTION
 #undef FUNCTION_TASK_LOOP
+
+/* these are defined in runner_doiact_functions_hydro.h at every #include */
+#undef PART_IS_ACTIVE
+#undef CELL_IS_ACTIVE
+#undef CELL_ARE_PART_DRIFTED
+#undef DO_DRIFT_DEBUG_CHECKS
diff --git a/src/runner_drift.c b/src/runner_drift.c
index 598408fa625e2a25a09a2b0e17305bc226d98c1a..39bd0dcfa1f89e23e887a7f4d11d69e59ace4beb 100644
--- a/src/runner_drift.c
+++ b/src/runner_drift.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner.h"
diff --git a/src/runner_ghost.c b/src/runner_ghost.c
index 78ed980d3938bf2747bde9dd3b7f993d43f07143..e44ad9a05e7c3b461a4662d9d01fa4e8ddd89907 100644
--- a/src/runner_ghost.c
+++ b/src/runner_ghost.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner.h"
@@ -145,6 +145,9 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
     for (int num_reruns = 0; scount > 0 && num_reruns < max_smoothing_iter;
          num_reruns++) {
 
+      ghost_stats_account_for_stars(&c->ghost_statistics, num_reruns, scount,
+                                    sparts, sid);
+
       /* Reset the redo-count. */
       redo = 0;
 
@@ -173,6 +176,8 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
 
         if (sp->density.wcount < 1.e-5 * kernel_root) { /* No neighbours case */
 
+          ghost_stats_no_ngb_star_iteration(&c->ghost_statistics, num_reruns);
+
           /* Flag that there were no neighbours */
           has_no_neighbours = 1;
 
@@ -364,6 +369,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
 
             /* Do some damage control if no neighbours at all were found */
             if (has_no_neighbours) {
+              ghost_stats_no_ngb_star_converged(&c->ghost_statistics);
               stars_spart_has_no_neighbours(sp, cosmo);
               rt_spart_has_no_neighbours(sp);
             }
@@ -381,6 +387,8 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
         h_max = max(h_max, sp->h);
         h_max_active = max(h_max_active, sp->h);
 
+        ghost_stats_converged_star(&c->ghost_statistics, sp);
+
         stars_reset_feedback(sp);
 
         /* Only do feedback if stars have a reasonable birth time */
@@ -632,6 +640,9 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c,
     for (int num_reruns = 0; bcount > 0 && num_reruns < max_smoothing_iter;
          num_reruns++) {
 
+      ghost_stats_account_for_black_holes(&c->ghost_statistics, num_reruns,
+                                          bcount, bparts, sid);
+
       /* Reset the redo-count. */
       redo = 0;
 
@@ -658,6 +669,9 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c,
 
         if (bp->density.wcount < 1.e-5 * kernel_root) { /* No neighbours case */
 
+          ghost_stats_no_ngb_black_hole_iteration(&c->ghost_statistics,
+                                                  num_reruns);
+
           /* Flag that there were no neighbours */
           has_no_neighbours = 1;
 
@@ -774,6 +788,7 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c,
 
             /* Do some damage control if no neighbours at all were found */
             if (has_no_neighbours) {
+              ghost_stats_no_ngb_black_hole_converged(&c->ghost_statistics);
               black_holes_bpart_has_no_neighbours(bp, cosmo);
             }
 
@@ -786,6 +801,8 @@ void runner_do_black_holes_density_ghost(struct runner *r, struct cell *c,
 
         /* We now have a particle whose smoothing length has converged */
 
+        ghost_stats_converged_black_hole(&c->ghost_statistics, bp);
+
         black_holes_reset_feedback(bp);
 
         /* Check if h_max has increased */
@@ -1017,7 +1034,7 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
         /* Calculate the time-step for passing to hydro_prepare_force.
          * This is the physical time between the start and end of the time-step
          * without any scale-factor powers. */
-        double dt_alpha;
+        double dt_alpha, dt_therm;
 
         if (with_cosmology) {
           const integertime_t ti_step = get_integer_timestep(p->time_bin);
@@ -1026,12 +1043,15 @@ void runner_do_extra_ghost(struct runner *r, struct cell *c, int timer) {
 
           dt_alpha =
               cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step);
+          dt_therm = cosmology_get_therm_kick_factor(cosmo, ti_begin,
+                                                     ti_begin + ti_step);
         } else {
           dt_alpha = get_timestep(p->time_bin, time_base);
+          dt_therm = get_timestep(p->time_bin, time_base);
         }
 
         /* Compute variables required for the force loop */
-        hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
+        hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha, dt_therm);
         mhd_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
         timestep_limiter_prepare_force(p, xp);
         rt_prepare_force(p);
@@ -1074,6 +1094,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
   const struct hydro_props *hydro_props = e->hydro_properties;
 
   const int with_cosmology = (e->policy & engine_policy_cosmology);
+  const int with_rt = (e->policy & engine_policy_rt);
 
   const float hydro_h_max = e->hydro_properties->h_max;
   const float hydro_h_min = e->hydro_properties->h_min;
@@ -1135,6 +1156,9 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
     for (int num_reruns = 0; count > 0 && num_reruns < max_smoothing_iter;
          num_reruns++) {
 
+      ghost_stats_account_for_hydro(&c->ghost_statistics, num_reruns, count,
+                                    parts, pid);
+
       /* Reset the redo-count. */
       redo = 0;
 
@@ -1161,6 +1185,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
         if (p->density.wcount < 1.e-5 * kernel_root) { /* No neighbours case */
 
+          ghost_stats_no_ngb_hydro_iteration(&c->ghost_statistics, num_reruns);
+
           /* Flag that there were no neighbours */
           has_no_neighbours = 1;
 
@@ -1242,7 +1268,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
              * artificial viscosity and thermal conduction terms) */
             const double time_base = e->time_base;
             const integertime_t ti_current = e->ti_current;
-            double dt_alpha;
+            double dt_alpha, dt_therm;
 
             if (with_cosmology) {
               const integertime_t ti_step = get_integer_timestep(p->time_bin);
@@ -1251,14 +1277,17 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
               dt_alpha =
                   cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step);
+              dt_therm = cosmology_get_therm_kick_factor(cosmo, ti_begin,
+                                                         ti_begin + ti_step);
             } else {
               dt_alpha = get_timestep(p->time_bin, time_base);
+              dt_therm = get_timestep(p->time_bin, time_base);
             }
 
             /* As of here, particle force variables will be set. */
 
             /* Compute variables required for the force loop */
-            hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
+            hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha, dt_therm);
             mhd_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
             timestep_limiter_prepare_force(p, xp);
             rt_prepare_force(p);
@@ -1272,7 +1301,12 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
 #endif /* EXTRA_HYDRO_LOOP */
 
-            rt_reset_part(p, cosmo);
+            if (with_rt) {
+#ifdef SWIFT_RT_DEBUG_CHECKS
+              rt_debugging_check_nr_subcycles(p, e->rt_props);
+#endif
+              rt_reset_part(p, cosmo);
+            }
 
             /* Ok, we are done with this particle */
             continue;
@@ -1366,6 +1400,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
             /* Do some damage control if no neighbours at all were found */
             if (has_no_neighbours) {
+              ghost_stats_no_ngb_hydro_converged(&c->ghost_statistics);
               hydro_part_has_no_neighbours(p, xp, cosmo);
               mhd_part_has_no_neighbours(p, xp, cosmo);
               chemistry_part_has_no_neighbours(p, xp, chemistry, cosmo);
@@ -1388,6 +1423,8 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
         h_max = max(h_max, p->h);
         h_max_active = max(h_max_active, p->h);
 
+        ghost_stats_converged_hydro(&c->ghost_statistics, p);
+
 #ifdef EXTRA_HYDRO_LOOP
 
         /* As of here, particle gradient variables will be set. */
@@ -1411,7 +1448,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
          * artificial viscosity and thermal conduction terms) */
         const double time_base = e->time_base;
         const integertime_t ti_current = e->ti_current;
-        double dt_alpha;
+        double dt_alpha, dt_therm;
 
         if (with_cosmology) {
           const integertime_t ti_step = get_integer_timestep(p->time_bin);
@@ -1420,14 +1457,17 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
           dt_alpha =
               cosmology_get_delta_time(cosmo, ti_begin, ti_begin + ti_step);
+          dt_therm = cosmology_get_therm_kick_factor(cosmo, ti_begin,
+                                                     ti_begin + ti_step);
         } else {
           dt_alpha = get_timestep(p->time_bin, time_base);
+          dt_therm = get_timestep(p->time_bin, time_base);
         }
 
         /* As of here, particle force variables will be set. */
 
         /* Compute variables required for the force loop */
-        hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
+        hydro_prepare_force(p, xp, cosmo, hydro_props, dt_alpha, dt_therm);
         mhd_prepare_force(p, xp, cosmo, hydro_props, dt_alpha);
         timestep_limiter_prepare_force(p, xp);
         rt_prepare_force(p);
@@ -1441,7 +1481,12 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
 
 #endif /* EXTRA_HYDRO_LOOP */
 
-        rt_reset_part(p, cosmo);
+        if (with_rt) {
+#ifdef SWIFT_RT_DEBUG_CHECKS
+          rt_debugging_check_nr_subcycles(p, e->rt_props);
+#endif
+          rt_reset_part(p, cosmo);
+        }
       }
 
       /* We now need to treat the particles whose smoothing length had not
@@ -1562,7 +1607,7 @@ void runner_do_rt_ghost1(struct runner *r, struct cell *c, int timer) {
 
   /* Anything to do here? */
   if (count == 0) return;
-  if (!cell_is_active_hydro(c, e)) return;
+  if (!cell_is_rt_active(c, e)) return;
 
   TIMER_TIC;
 
@@ -1582,8 +1627,13 @@ void runner_do_rt_ghost1(struct runner *r, struct cell *c, int timer) {
       if (part_is_inhibited(p, e)) continue;
 
       /* Skip inactive parts */
-      if (!part_is_active(p, e)) continue;
+      if (!part_is_rt_active(p, e)) continue;
 
+      /* First reset everything that needs to be reset for the following
+       * subcycle */
+      rt_reset_part_each_subcycle(p);
+
+      /* Now finish up injection */
       rt_finalise_injection(p, e->rt_props);
     }
   }
@@ -1607,7 +1657,7 @@ void runner_do_rt_ghost2(struct runner *r, struct cell *c, int timer) {
 
   /* Anything to do here? */
   if (count == 0) return;
-  if (!cell_is_active_hydro(c, e)) return;
+  if (!cell_is_rt_active(c, e)) return;
 
   TIMER_TIC;
 
@@ -1627,7 +1677,7 @@ void runner_do_rt_ghost2(struct runner *r, struct cell *c, int timer) {
       if (part_is_inhibited(p, e)) continue;
 
       /* Skip inactive parts */
-      if (!part_is_active(p, e)) continue;
+      if (!part_is_rt_active(p, e)) continue;
 
       rt_end_gradient(p, cosmo);
     }
diff --git a/src/runner_main.c b/src/runner_main.c
index 12c671884826c13326f75e75c0c89a1ae549577b..5eb818787014db12c8495986183b07fb3f46b54c 100644
--- a/src/runner_main.c
+++ b/src/runner_main.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
@@ -135,8 +135,7 @@ void *runner_main(void *data) {
   struct runner *r = (struct runner *)data;
   struct engine *e = r->e;
   struct scheduler *sched = &e->sched;
-  unsigned int seed = r->id;
-  pthread_setspecific(sched->local_seed_pointer, &seed);
+
   /* Main loop. */
   while (1) {
 
@@ -412,7 +411,19 @@ void *runner_main(void *data) {
           /* Cleanup only if any of the indices went stale. */
           runner_do_hydro_sort(
               r, ci, t->flags,
-              ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, 1);
+              ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin,
+              cell_get_flag(ci, cell_flag_rt_requests_sort), 1);
+          /* Reset the sort flags as our work here is done. */
+          t->flags = 0;
+          break;
+        case task_type_rt_sort:
+          /* Cleanup only if any of the indices went stale.
+           * NOTE: we check whether we reset the sort flags when the
+           * recv tasks are running. Cells without an RT recv task
+           * don't have rt_sort tasks. */
+          runner_do_hydro_sort(
+              r, ci, t->flags,
+              ci->hydro.dx_max_sort_old > space_maxreldx * ci->dmin, 1, 1);
           /* Reset the sort flags as our work here is done. */
           t->flags = 0;
           break;
@@ -486,6 +497,9 @@ void *runner_main(void *data) {
         case task_type_collect:
           runner_do_timestep_collect(r, ci, 1);
           break;
+        case task_type_rt_collect_times:
+          runner_do_collect_rt_times(r, ci, 1);
+          break;
 #ifdef WITH_MPI
         case task_type_send:
           if (t->subtype == task_subtype_tend) {
@@ -515,7 +529,7 @@ void *runner_main(void *data) {
           } else if (t->subtype == task_subtype_gradient) {
             runner_do_recv_part(r, ci, 0, 1);
           } else if (t->subtype == task_subtype_rt_gradient) {
-            runner_do_recv_part(r, ci, 0, 1);
+            runner_do_recv_part(r, ci, 2, 1);
           } else if (t->subtype == task_subtype_rt_transport) {
             runner_do_recv_part(r, ci, 0, 1);
           } else if (t->subtype == task_subtype_part_swallow) {
@@ -601,6 +615,9 @@ void *runner_main(void *data) {
         case task_type_rt_tchem:
           runner_do_rt_tchem(r, t->ci, 1);
           break;
+        case task_type_rt_advance_cell_time:
+          runner_do_rt_advance_cell_time(r, t->ci, 1);
+          break;
         default:
           error("Unknown/invalid task type (%d).", t->type);
       }
diff --git a/src/runner_neutrino.c b/src/runner_neutrino.c
index a771cfc115047536e23dbd99838fb83a77fd9168..22807a870ab3b7e3879a9d2f14398de390944294 100644
--- a/src/runner_neutrino.c
+++ b/src/runner_neutrino.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner.h"
diff --git a/src/runner_others.c b/src/runner_others.c
index 271dd62642519633d4924c0cd582191574cedaf1..43748b762f58a55746818150614961a9316b59c6 100644
--- a/src/runner_others.c
+++ b/src/runner_others.c
@@ -22,7 +22,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
@@ -676,7 +676,7 @@ void runner_do_end_hydro_force(struct runner *r, struct cell *c, int timer) {
 
           /* Some values need to be reset in the Gizmo case. */
           hydro_prepare_force(p, &c->hydro.xparts[k], cosmo,
-                              e->hydro_properties, 0);
+                              e->hydro_properties, 0, 0);
           rt_prepare_force(p);
 #endif
         }
@@ -1028,7 +1028,7 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) {
 
   /* Anything to do here? */
   if (count == 0) return;
-  if (!cell_is_active_hydro(c, e)) return;
+  if (!cell_is_rt_active(c, e)) return;
 
   TIMER_TIC;
 
@@ -1038,7 +1038,6 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) {
       if (c->progeny[k] != NULL) runner_do_rt_tchem(r, c->progeny[k], 0);
   } else {
 
-    /* const struct cosmology *cosmo = e->cosmology; */
     struct part *restrict parts = c->hydro.parts;
     struct xpart *restrict xparts = c->hydro.xparts;
 
@@ -1053,26 +1052,29 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) {
       if (part_is_inhibited(p, e)) continue;
 
       /* Skip inactive parts */
-      if (!part_is_active(p, e)) continue;
+      if (!part_is_rt_active(p, e)) continue;
 
       /* Finish the force loop */
-      const integertime_t ti_current = e->ti_current;
-      const integertime_t ti_step = get_integer_timestep(p->time_bin);
-      const integertime_t ti_begin =
-          get_integer_time_begin(ti_current + 1, p->time_bin);
+      const integertime_t ti_current_subcycle = e->ti_current_subcycle;
+      const integertime_t ti_step =
+          get_integer_timestep(p->rt_time_data.time_bin);
+      const integertime_t ti_begin = get_integer_time_begin(
+          ti_current_subcycle + 1, p->rt_time_data.time_bin);
       const integertime_t ti_end = ti_begin + ti_step;
 
+      const double dt =
+          rt_part_dt(ti_begin, ti_end, e->time_base, with_cosmology, cosmo);
 #ifdef SWIFT_DEBUG_CHECKS
-      if (ti_begin != ti_current)
+      if (ti_begin != ti_current_subcycle)
         error(
             "Particle in wrong time-bin, ti_end=%lld, ti_begin=%lld, "
             "ti_step=%lld time_bin=%d wakeup=%d ti_current=%lld",
             ti_end, ti_begin, ti_step, p->time_bin, p->limiter_data.wakeup,
-            ti_current);
+            ti_current_subcycle);
+      if (dt < 0.)
+        error("Got part with negative time-step: %lld, %.6g", p->id, dt);
 #endif
 
-      const double dt = rt_part_dt(ti_begin, ti_end, e->time_base,
-                                   with_cosmology, e->cosmology);
       rt_finalise_transport(p, dt, cosmo);
 
       /* And finally do thermochemistry */
@@ -1080,5 +1082,5 @@ void runner_do_rt_tchem(struct runner *r, struct cell *c, int timer) {
     }
   }
 
-  if (timer) TIMER_TOC(timer_end_rt_tchem);
+  if (timer) TIMER_TOC(timer_do_rt_tchem);
 }
diff --git a/src/runner_pack.c b/src/runner_pack.c
index c337a5ad3cd942d7033113df70d238bcfb825fe8..d319290312e9aef2dbb36b73329bf3f4844e22e2 100644
--- a/src/runner_pack.c
+++ b/src/runner_pack.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
diff --git a/src/runner_recv.c b/src/runner_recv.c
index 3ecdc6203d481373ed5d76b276f8edc19fef3426..f225e1e6d6c7567b9cc06fbfccdae70e6b4e0017 100644
--- a/src/runner_recv.c
+++ b/src/runner_recv.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
@@ -63,6 +63,15 @@ void runner_do_recv_part(struct runner *r, struct cell *c, int clear_sorts,
   if (c->nodeID == engine_rank) error("Updating a local cell!");
 #endif
 
+  if (clear_sorts == 2) {
+    /* This is a call for the first RT recv task. Check whether
+     * we need to clear the sorts now. In the case of a foreign
+     * cell where no xv comms are done, but RT is active, we
+     * need to force a sort after the gradient recv. */
+    clear_sorts = !cell_get_flag(c, cell_flag_skip_rt_sort);
+    cell_clear_flag(c, cell_flag_skip_rt_sort);
+  }
+
   /* Clear this cell's sorted mask. */
   if (clear_sorts) c->hydro.sorted = 0;
 
diff --git a/src/runner_sinks.c b/src/runner_sinks.c
index a5868f12d9b1569ad1afa3eb34ecee3769f28250..208a7e99471056dc660e4212078f551c56643ea0 100644
--- a/src/runner_sinks.c
+++ b/src/runner_sinks.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner.h"
diff --git a/src/runner_sort.c b/src/runner_sort.c
index f81d5e2db218a72145e4372c16da9e0a4fbdba8a..550626517f861769bd38fa64db04a2b0270b81fe 100644
--- a/src/runner_sort.c
+++ b/src/runner_sort.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner.h"
@@ -193,11 +193,13 @@ RUNNER_CHECK_SORTS(stars)
  * @param flags Cell flag.
  * @param cleanup If true, re-build the sorts for the selected flags instead
  *        of just adding them.
+ * @param rt_requests_sort whether this sort was requested for RT. If true,
+ *        this cell is allowed to be undrifted.
  * @param clock Flag indicating whether to record the timing or not, needed
  *      for recursive calls.
  */
 void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags,
-                          int cleanup, int clock) {
+                          int cleanup, int rt_requests_sort, int clock) {
 
   struct sort_entry *fingers[8];
   const int count = c->hydro.count;
@@ -218,17 +220,22 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags,
   } else {
     flags &= ~c->hydro.sorted;
   }
-  if (flags == 0 && !cell_get_flag(c, cell_flag_do_hydro_sub_sort)) return;
+  if (flags == 0 && !cell_get_flag(c, cell_flag_do_hydro_sub_sort) &&
+      !cell_get_flag(c, cell_flag_do_rt_sub_sort))
+    return;
 
   /* Check that the particles have been moved to the current time */
-  if (flags && !cell_are_part_drifted(c, r->e))
-    error("Sorting un-drifted cell c->nodeID=%d", c->nodeID);
+  if (flags && !cell_are_part_drifted(c, r->e)) {
+    /* If the sort was requested by RT, cell may be intentionally
+     * undrifted. */
+    if (!rt_requests_sort) error("Sorting un-drifted cell");
+  }
 
 #ifdef SWIFT_DEBUG_CHECKS
   /* Make sure the sort flags are consistent (downward). */
   runner_check_sorts_hydro(c, c->hydro.sorted);
 
-  /* Make sure the sort flags are consistent (upard). */
+  /* Make sure the sort flags are consistent (upward). */
   for (struct cell *finger = c->parent; finger != NULL;
        finger = finger->parent) {
     if (finger->hydro.sorted & ~c->hydro.sorted)
@@ -259,7 +266,7 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags,
               r, c->progeny[k], flags,
               cleanup && (c->progeny[k]->hydro.dx_max_sort_old >
                           space_maxreldx * c->progeny[k]->dmin),
-              0);
+              rt_requests_sort, 0);
           dx_max_sort = max(dx_max_sort, c->progeny[k]->hydro.dx_max_sort);
           dx_max_sort_old =
               max(dx_max_sort_old, c->progeny[k]->hydro.dx_max_sort_old);
@@ -416,6 +423,8 @@ void runner_do_hydro_sort(struct runner *r, struct cell *c, int flags,
   /* Clear the cell's sort flags. */
   c->hydro.do_sort = 0;
   cell_clear_flag(c, cell_flag_do_hydro_sub_sort);
+  cell_clear_flag(c, cell_flag_do_rt_sub_sort);
+  cell_clear_flag(c, cell_flag_rt_requests_sort);
   c->hydro.requires_sorts = 0;
 
   if (clock) TIMER_TOC(timer_dosort);
@@ -673,7 +682,8 @@ void runner_do_all_hydro_sort(struct runner *r, struct cell *c) {
   if (c->hydro.super == c) {
 
     /* Sort everything */
-    runner_do_hydro_sort(r, c, 0x1FFF, /*cleanup=*/0, /*timer=*/0);
+    runner_do_hydro_sort(r, c, 0x1FFF, /*cleanup=*/0, /*rt_requests_sort=*/0,
+                         /*timer=*/0);
 
   } else {
 
diff --git a/src/runner_time_integration.c b/src/runner_time_integration.c
index cc86b478e00cc4120c8792832a9b0babccc96279..7b0eb6ef05442c4324bc6e9f584c3110da425e5a 100644
--- a/src/runner_time_integration.c
+++ b/src/runner_time_integration.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "runner.h"
@@ -643,12 +643,14 @@ void runner_do_kick2(struct runner *r, struct cell *c, const int timer) {
 void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
   const struct engine *e = r->e;
   const integertime_t ti_current = e->ti_current;
+  const integertime_t ti_current_subcycle = e->ti_current_subcycle;
   const struct cosmology *cosmo = e->cosmology;
   const struct feedback_props *feedback_props = e->feedback_props;
   const struct unit_system *us = e->internal_units;
   const struct phys_const *phys_const = e->physical_constants;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const int with_feedback = (e->policy & engine_policy_feedback);
+  const int with_rt = (e->policy & engine_policy_rt);
   const int count = c->hydro.count;
   const int gcount = c->grav.count;
   const int scount = c->stars.count;
@@ -667,11 +669,14 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
   if (!cell_is_active_hydro(c, e) && !cell_is_active_gravity(c, e) &&
       !cell_is_active_stars(c, e) && !cell_is_active_sinks(c, e) &&
       !cell_is_active_black_holes(c, e)) {
+    /* Note: cell_is_rt_active is deliberately skipped. We only change
+     * the RT subcycling time steps when particles are hydro active. */
     c->hydro.updated = 0;
     c->grav.updated = 0;
     c->stars.updated = 0;
     c->sinks.updated = 0;
     c->black_holes.updated = 0;
+    c->rt.updated = 0;
     return;
   }
 
@@ -679,6 +684,8 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
       b_updated = 0;
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0,
                 ti_hydro_beg_max = 0;
+  integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0;
+  integertime_t ti_rt_min_step_size = max_nr_timesteps;
   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, ti_stars_end_max = 0,
@@ -708,13 +715,25 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
 
         if (ti_end != ti_current)
           error("Computing time-step of rogue particle.");
+
+        if (with_rt) {
+          const integertime_t ti_rt_end = get_integer_time_end(
+              ti_current_subcycle, p->rt_time_data.time_bin);
+          if (ti_rt_end != ti_current_subcycle)
+            error("Computing RT time-step of rogue particle");
+        }
 #endif
 
         /* Get new time-step */
         const integertime_t ti_new_step = get_part_timestep(p, xp, e);
+        /* ToDo: For now, this needs to be done before we update the particle's
+         * time bins. When we aren't using a fixed number of sub-cycles, we
+         * can move this down with the rest of the RT block. */
+        integertime_t ti_rt_new_step = get_part_rt_timestep(p, xp, e);
 
         /* Update particle */
         p->time_bin = get_time_bin(ti_new_step);
+
         if (p->gpart != NULL) p->gpart->time_bin = p->time_bin;
 
         /* Update the tracers properties */
@@ -744,6 +763,20 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
           /* What is the next starting point for this cell ? */
           ti_gravity_beg_max = max(ti_current, ti_gravity_beg_max);
         }
+
+        /* Same for RT */
+        if (with_rt) {
+          /* Enforce RT time-step size <= hydro step size */
+          ti_rt_new_step = min(ti_new_step, ti_rt_new_step);
+
+          p->rt_time_data.time_bin = get_time_bin(ti_rt_new_step);
+
+          ti_rt_end_min =
+              min(ti_current_subcycle + ti_rt_new_step, ti_rt_end_min);
+          ti_rt_beg_max =
+              max(ti_current_subcycle + ti_rt_new_step, ti_rt_beg_max);
+          ti_rt_min_step_size = min(ti_rt_min_step_size, ti_rt_new_step);
+        }
       }
 
       else { /* part is inactive */
@@ -763,6 +796,28 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
           /* What is the next starting point for this cell ? */
           ti_hydro_beg_max = max(ti_beg, ti_hydro_beg_max);
 
+          /* Same for RT. */
+          if (with_rt) {
+            /* Here we assume that the particle is inactive, which is true for
+             * hydro, but not necessarily for a RT subcycle. RT time steps are
+             * only changed while the particle is hydro active. This allows to
+             * end up with results ti_rt_end == ti_current_subcyle, so we need
+             * to pretend we're past ti_current_subcycle already. */
+            integertime_t ti_rt_end = get_integer_time_end(
+                ti_current_subcycle + 1, p->rt_time_data.time_bin);
+
+            const integertime_t ti_rt_beg = get_integer_time_begin(
+                ti_current_subcycle + 1, p->rt_time_data.time_bin);
+
+            ti_rt_end_min = min(ti_rt_end, ti_rt_end_min);
+            ti_rt_beg_max = max(ti_rt_beg, ti_rt_beg_max);
+            /* We mustn't update ti_rt_min_step_size here, since the RT time
+             * step sizes don't change for particles when they are inactive.
+             * Leaving them here effectively prohibits them from ever increasing
+             * again. Instead, if we're working on a cell where each particle
+             * is inactive, do an appropriate check at the end. */
+          }
+
           if (p->gpart != NULL) {
 
             /* What is the next sync-point ? */
@@ -1048,6 +1103,11 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
         ti_hydro_end_min = min(cp->hydro.ti_end_min, ti_hydro_end_min);
         ti_hydro_beg_max = max(cp->hydro.ti_beg_max, ti_hydro_beg_max);
 
+        ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min);
+        ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max);
+        ti_rt_min_step_size =
+            min(cp->rt.ti_rt_min_step_size, ti_rt_min_step_size);
+
         ti_gravity_end_min = min(cp->grav.ti_end_min, ti_gravity_end_min);
         ti_gravity_beg_max = max(cp->grav.ti_beg_max, ti_gravity_beg_max);
 
@@ -1071,9 +1131,19 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
   c->stars.updated = s_updated;
   c->sinks.updated = sink_updated;
   c->black_holes.updated = b_updated;
+  /* We don't count the RT updates here because the
+   * timestep tasks aren't active during sub-cycles.
+   * We do that in rt_advanced_cell_time instead. */
 
   c->hydro.ti_end_min = ti_hydro_end_min;
   c->hydro.ti_beg_max = ti_hydro_beg_max;
+  c->rt.ti_rt_end_min = ti_rt_end_min;
+  c->rt.ti_rt_beg_max = ti_rt_beg_max;
+  if (cell_is_starting_hydro(c, e)) {
+    /* We only change the RT time steps when the cell is also hydro active.
+     * Without this check here, ti_rt_min_step_size = max_nr_steps... */
+    c->rt.ti_rt_min_step_size = ti_rt_min_step_size;
+  }
   c->grav.ti_end_min = ti_gravity_end_min;
   c->grav.ti_beg_max = ti_gravity_beg_max;
   c->stars.ti_end_min = ti_stars_end_min;
@@ -1099,6 +1169,12 @@ void runner_do_timestep(struct runner *r, struct cell *c, const int timer) {
   if (c->black_holes.ti_end_min == e->ti_current &&
       c->black_holes.ti_end_min < max_nr_timesteps)
     error("End of next black holes step is current time!");
+  /* Contrary to sinks, stars, bhs etc, we may have "rt particles"
+   * without running with RT. So additional if (with_rt) check is
+   * needed here. */
+  if (with_rt && (c->rt.ti_rt_end_min == e->ti_current &&
+                  c->rt.ti_rt_end_min < max_nr_timesteps))
+    error("Cell %lld End of next RT step is current time!", c->cellID);
 #endif
 
   if (timer) TIMER_TOC(timer_timestep);
@@ -1125,7 +1201,10 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c,
   size_t s_updated = 0;
   size_t b_updated = 0;
   size_t si_updated = 0;
+  size_t rt_updated = 0;
+
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_beg_max = 0;
+  integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0;
   integertime_t ti_grav_end_min = max_nr_timesteps, ti_grav_beg_max = 0;
   integertime_t ti_stars_end_min = max_nr_timesteps, ti_stars_beg_max = 0;
   integertime_t ti_black_holes_end_min = max_nr_timesteps,
@@ -1143,6 +1222,8 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c,
       /* And update */
       ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min);
       ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max);
+      ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min);
+      ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max);
       ti_grav_end_min = min(ti_grav_end_min, cp->grav.ti_end_min);
       ti_grav_beg_max = max(ti_grav_beg_max, cp->grav.ti_beg_max);
       ti_stars_end_min = min(ti_stars_end_min, cp->stars.ti_end_min);
@@ -1159,6 +1240,7 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c,
       s_updated += cp->stars.updated;
       b_updated += cp->black_holes.updated;
       si_updated += cp->sinks.updated;
+      rt_updated += cp->rt.updated;
 
       /* Collected, so clear for next time. */
       cp->hydro.updated = 0;
@@ -1166,12 +1248,15 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c,
       cp->stars.updated = 0;
       cp->black_holes.updated = 0;
       cp->sinks.updated = 0;
+      cp->rt.updated = 0;
     }
   }
 
   /* Store the collected values in the cell. */
   c->hydro.ti_end_min = ti_hydro_end_min;
   c->hydro.ti_beg_max = ti_hydro_beg_max;
+  c->rt.ti_rt_end_min = ti_rt_end_min;
+  c->rt.ti_rt_beg_max = ti_rt_beg_max;
   c->grav.ti_end_min = ti_grav_end_min;
   c->grav.ti_beg_max = ti_grav_beg_max;
   c->stars.ti_end_min = ti_stars_end_min;
@@ -1186,6 +1271,7 @@ void runner_do_timestep_collect(struct runner *r, struct cell *c,
   c->stars.updated = s_updated;
   c->black_holes.updated = b_updated;
   c->sinks.updated = si_updated;
+  c->rt.updated = rt_updated;
 }
 
 /**
@@ -1499,3 +1585,157 @@ void runner_do_sync(struct runner *r, struct cell *c, int force,
 
   if (timer) TIMER_TOC(timer_do_sync);
 }
+
+/**
+ * @brief Update the cell's t_rt_end_min so that the sub-cycling can proceed
+ * with correct cell times. (During sub-cycles, the regular timestep and
+ * timestep_collect tasks do not run. This replaces the collection of cell
+ * times of timestep tasks during sub-cycles. )
+ *
+ * @param r The #runner thread.
+ * @param c The #cell.
+ * @param timer Are we timing this ?
+ */
+void runner_do_rt_advance_cell_time(struct runner *r, struct cell *c,
+                                    int timer) {
+
+  struct engine *e = r->e;
+  const int count = c->hydro.count;
+  /* Reset update count regardless whether cell is active or not */
+  c->rt.updated = 0;
+
+#ifdef SWIFT_RT_DEBUG_CHECKS
+  if (c->super == c) c->rt.advanced_time = 1;
+#endif
+
+  /* Anything to do here? */
+  if (count == 0) return;
+  if (!cell_is_rt_active(c, e)) return;
+
+  TIMER_TIC;
+
+  int rt_updated = 0;
+
+  if (c->split) {
+    for (int k = 0; k < 8; k++) {
+      struct cell *cp = c->progeny[k];
+      if (cp != NULL) {
+        runner_do_rt_advance_cell_time(r, cp, 0);
+        rt_updated += cp->rt.updated;
+      }
+    }
+  } else {
+    /* Do some debugging stuff on active particles before setting the cell time.
+     * This test is not reliable on foreign cells. After a rebuild, we may end
+     * up with an active foreign cell which was not updated in this step because
+     * it had no active neighbouring cells, and its particle data may be random
+     * junk. */
+
+    if (c->nodeID == engine_rank) {
+      struct part *restrict parts = c->hydro.parts;
+
+      /* Loop over the gas particles in this cell. */
+      for (int i = 0; i < count; i++) {
+
+        /* Get a handle on the part. */
+        struct part *restrict p = &parts[i];
+
+        /* Skip inhibited parts */
+        if (part_is_inhibited(p, e)) continue;
+
+        /* Skip inactive parts */
+        if (!part_is_rt_active(p, e)) continue;
+
+#ifdef SWIFT_RT_DEBUG_CHECKS
+        /* Run checks. */
+        rt_debug_sequence_check(p, 5, __func__);
+        /* Mark that the subcycling has happened */
+        rt_debugging_count_subcycle(p);
+#endif
+        rt_updated++;
+      }
+    }
+  }
+
+  c->rt.updated = rt_updated;
+
+  /* Note: c->rt.ti_rt_min_step_size may be greater than
+   * c->super->rt.ti_rt_min_step_size. This is expected behaviour.
+   * We only update the cell's own time after it's been active. */
+  c->rt.ti_rt_end_min += c->rt.ti_rt_min_step_size;
+
+  if (timer) TIMER_TOC(timer_do_rt_advance_cell_time);
+}
+
+/**
+ * @brief Recursively collect the end-of-timestep information from the top-level
+ * to the super level for the RT sub-cycling. (During sub-cycles, the regular
+ * timestep and timestep_collect tasks do not run. This replaces the
+ * timestep_collect task.)
+ *
+ * @param r The runner thread.
+ * @param c The cell.
+ * @param timer Are we timing this ?
+ */
+void runner_do_collect_rt_times(struct runner *r, struct cell *c,
+                                const int timer) {
+
+  const struct engine *e = r->e;
+  size_t rt_updated = 0;
+
+  if (e->ti_current == e->ti_current_subcycle)
+    error("called collect_rt_times during a main step");
+
+  /* Early stop if we are at the super level.
+   * The time-step/rt_advance_cell_time tasks would have set things at
+   * this level already. */
+
+  if (c->super == c) {
+#ifdef SWIFT_RT_DEBUG_CHECKS
+    /* Do a check before the early exit.
+     * rt_advanced_cell_time should be called exactly once before
+     * collect times. Except on the first subcycle, because the
+     * collect_rt_times task shouldn't be called in the main steps.
+     * In that case, it should be exactly 2. */
+    if (e->ti_current_subcycle - c->rt.ti_rt_end_min == e->ti_current) {
+      /* This is the first subcycle */
+      if (c->rt.advanced_time != 2)
+        error("Called cell with wrong advanced_time counter. Expect=2, got=%d",
+              c->rt.advanced_time);
+    } else {
+      if (c->rt.advanced_time != 1)
+        error("Called cell with wrong advanced_time counter. Expect=1, got=%d",
+              c->rt.advanced_time);
+    }
+    c->rt.advanced_time = 0;
+#endif
+    return;
+  }
+
+  integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0;
+
+  /* Collect the values from the progeny. */
+  for (int k = 0; k < 8; k++) {
+    struct cell *cp = c->progeny[k];
+    if (cp != NULL) {
+
+      /* Recurse */
+      runner_do_collect_rt_times(r, cp, 0);
+
+      /* And update */
+      ti_rt_end_min = min(cp->rt.ti_rt_end_min, ti_rt_end_min);
+      ti_rt_beg_max = max(cp->rt.ti_rt_beg_max, ti_rt_beg_max);
+
+      /* Collected, so clear for next time. */
+      rt_updated += cp->rt.updated;
+      cp->rt.updated = 0;
+    }
+  }
+
+  /* Store the collected values in the cell. */
+  c->rt.ti_rt_end_min = ti_rt_end_min;
+  c->rt.ti_rt_beg_max = ti_rt_beg_max;
+  c->rt.updated = rt_updated;
+
+  if (timer) TIMER_TOC(timer_do_rt_collect_times);
+}
diff --git a/src/scheduler.c b/src/scheduler.c
index f5e8013cb5ccd36901f66f0fb5c5ea8930bb69f1..97c3e30fc0201db773f742f2bc0ba4167e1e70af 100644
--- a/src/scheduler.c
+++ b/src/scheduler.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <limits.h>
@@ -277,6 +277,7 @@ void task_dependency_sum(void *in_p, void *out_p, int *len,
   for (int i = 0; i < *len; i++) {
     /* loop over all the object set in invals */
     for (int j = 0; j < MAX_NUMBER_DEP; j++) {
+
       /* Have we reached the end of the links? */
       if (in[i].number_link[j] == -1) {
         break;
@@ -408,6 +409,10 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) {
     task_dep[i].task_in_is_top = 1;
     task_dep[i].task_in_is_grav_super = 1;
     task_dep[i].task_in_is_hydro_super = 1;
+    const int tt = i / task_subtype_count;
+    const int tst = i % task_subtype_count;
+    task_dep[i].type_in = tt;
+    task_dep[i].subtype_in = tst;
 
     for (int j = 0; j < MAX_NUMBER_DEP; j++) {
       /* Use number_link as indicator of the existance of a relation */
@@ -436,9 +441,15 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) {
     struct task_dependency *cur = &task_dep[ind];
     task_exists[ind]++;
 
-    /* Set ta */
-    cur->type_in = ta->type;
-    cur->subtype_in = ta->subtype;
+#ifdef SWIFT_DEBUG_CHECKS
+    if (cur->type_in != ta->type)
+      error("wrong indexing for task %d: Expect type %d got %d", i,
+            cur->type_in, ta->type);
+    if (cur->subtype_in != ta->subtype)
+      error("wrong indexing for task %d: Expect subtype %d got %d", i,
+            cur->subtype_in, ta->subtype);
+#endif
+    /* Is ta implicit? */
     cur->implicit_in = ta->implicit;
 
     /* Set the task level. */
@@ -510,6 +521,16 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) {
       if (step != 0 && tb->skip) continue;
 
       int indj = tb->type * task_subtype_count + tb->subtype;
+
+#ifdef SWIFT_DEBUG_CHECKS
+      const struct task_dependency *target = &task_dep[indj];
+      if (target->type_in != tb->type)
+        error("wrong indexing for task %d: Expect type %d got %d", i,
+              target->type_in, tb->type);
+      if (target->subtype_in != tb->subtype)
+        error("wrong indexing for task %d: Expect subtype %d got %d", i,
+              target->subtype_in, tb->subtype);
+#endif
       task_exists[indj]++;
 
       const struct cell *ci_b = tb->ci;
@@ -587,11 +608,11 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) {
 
 #ifdef WITH_MPI
   /* create MPI operator */
-  MPI_Datatype data_type;
-  task_dependency_define(&data_type);
+  MPI_Datatype dependency_data_type;
+  task_dependency_define(&dependency_data_type);
 
-  MPI_Op sum;
-  MPI_Op_create(task_dependency_sum, /* commute */ 1, &sum);
+  MPI_Op dependency_sum;
+  MPI_Op_create(task_dependency_sum, /* commute */ 1, &dependency_sum);
 
   /* create recv buffer */
   struct task_dependency *recv = NULL;
@@ -613,8 +634,8 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) {
   }
 
   /* Do the reduction */
-  int test =
-      MPI_Reduce(task_dep, recv, nber_tasks, data_type, sum, 0, MPI_COMM_WORLD);
+  int test = MPI_Reduce(task_dep, recv, nber_tasks, dependency_data_type,
+                        dependency_sum, 0, MPI_COMM_WORLD);
   if (test != MPI_SUCCESS) error("MPI reduce failed");
 
   test = MPI_Reduce(task_exists, recv_exists, nber_tasks, MPI_INT, MPI_SUM, 0,
@@ -802,8 +823,8 @@ void scheduler_write_dependencies(struct scheduler *s, int verbose, int step) {
   free(task_exists);
   free(task_has_deps);
 #ifdef WITH_MPI
-  MPI_Type_free(&data_type);
-  MPI_Op_free(&sum);
+  MPI_Type_free(&dependency_data_type);
+  MPI_Op_free(&dependency_sum);
 #endif
 
   if (verbose)
@@ -854,6 +875,10 @@ void scheduler_write_cell_dependencies(struct scheduler *s, int verbose,
     task_dep[i].task_in_is_top = 1;
     task_dep[i].task_in_is_grav_super = 1;
     task_dep[i].task_in_is_hydro_super = 1;
+    const int tt = i / task_subtype_count;
+    const int tst = i % task_subtype_count;
+    task_dep[i].type_in = tt;
+    task_dep[i].subtype_in = tst;
 
     for (int j = 0; j < MAX_NUMBER_DEP; j++) {
       /* Use number_link as indicator of the existance of a relation */
@@ -884,9 +909,15 @@ void scheduler_write_cell_dependencies(struct scheduler *s, int verbose,
     const int ind = ta->type * task_subtype_count + ta->subtype;
     struct task_dependency *cur = &task_dep[ind];
 
-    /* Set ta */
-    cur->type_in = ta->type;
-    cur->subtype_in = ta->subtype;
+#ifdef SWIFT_DEBUG_CHECKS
+    if (cur->type_in != ta->type)
+      error("wrong indexing for task %d: Expect type %d got %d", i,
+            cur->type_in, ta->type);
+    if (cur->subtype_in != ta->subtype)
+      error("wrong indexing for task %d: Expect subtype %d got %d", i,
+            cur->subtype_in, ta->subtype);
+#endif
+    /* Is ta implicit? */
     cur->implicit_in = ta->implicit;
 
     /* Set the task level. */
@@ -1956,6 +1987,7 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
 
     switch (t->type) {
       case task_type_sort:
+      case task_type_rt_sort:
         cost = wscale * intrinsics_popcount(t->flags) * count_i *
                (sizeof(int) * 8 - (count_i ? intrinsics_clz(count_i) : 0));
         break;
@@ -2273,6 +2305,10 @@ void scheduler_reweight(struct scheduler *s, int verbose) {
       case task_type_rt_tchem:
         cost = wscale * count_i;
         break;
+      case task_type_rt_advance_cell_time:
+      case task_type_rt_collect_times:
+        cost = wscale;
+        break;
       case task_type_csds:
         cost =
             wscale * (count_i + gcount_i + scount_i + sink_count_i + bcount_i);
@@ -2961,7 +2997,6 @@ void scheduler_init(struct scheduler *s, struct space *space, int nr_tasks,
   s->size = 0;
   s->tasks = NULL;
   s->tasks_ind = NULL;
-  pthread_key_create(&s->local_seed_pointer, NULL);
   scheduler_reset(s, nr_tasks);
 }
 
diff --git a/src/scheduler.h b/src/scheduler.h
index 38c4b8e3fd1913fa55ac4490fa6b4d3e0e4ba141..349cdc38a3c770d0418e43f00167a4e76edb8723 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -21,7 +21,7 @@
 #define SWIFT_SCHEDULER_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
@@ -106,9 +106,6 @@ struct scheduler {
    * MPI. */
   size_t mpi_message_limit;
 
-  /* 'Pointer' to the seed for the random number generator */
-  pthread_key_t local_seed_pointer;
-
   /* Total ticks spent running the tasks */
   ticks total_ticks;
 
diff --git a/src/serial_io.c b/src/serial_io.c
index 9d8a82f708e1f753295da64429f02ef881658cb9..660c8388fbdead00abc3a690858c8172e511dec5 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5)
 
@@ -1231,6 +1231,8 @@ void write_output_serial(struct engine* e,
                        swift_type_count);
     io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT,
                        numParticlesHighWord, swift_type_count);
+    io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total,
+                       swift_type_count);
     double MassTable[swift_type_count] = {0};
     io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count);
     io_write_attribute(h_grp, "InitialMassTable", DOUBLE,
@@ -1287,7 +1289,8 @@ void write_output_serial(struct engine* e,
       if (h_err < 0) error("Error while creating alias for particle group.\n");
 
       /* Write the number of particles as an attribute */
-      io_write_attribute_l(h_grp, "NumberOfParticles", N_total[ptype]);
+      io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]);
+      io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]);
 
       /* Close particle group */
       H5Gclose(h_grp);
diff --git a/src/serial_io.h b/src/serial_io.h
index 2d3577883beca90d890570cb37f8c58b74ee2b5f..350da844aa034575c04f7efa432e84abb933c5ef 100644
--- a/src/serial_io.h
+++ b/src/serial_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_SERIAL_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && defined(WITH_MPI) && !defined(HAVE_PARALLEL_HDF5)
 
diff --git a/src/signal_velocity.h b/src/signal_velocity.h
index 791073a5626601b4f1b9b3235069aec595df3ec8..7a0cd182a7e632a11ffb5c00eb3be3b7e1504ea3 100644
--- a/src/signal_velocity.h
+++ b/src/signal_velocity.h
@@ -20,7 +20,7 @@
 #define SWIFT_SIGNAL_VELOCITY_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifndef NONE_MHD
 
diff --git a/src/sincos.h b/src/sincos.h
index fbbee10fe54ce5ac48d1eaaca083cdc0eb51f52c..6b36814d1f5dbec3d03f06dbeab6502d036ef88b 100644
--- a/src/sincos.h
+++ b/src/sincos.h
@@ -20,7 +20,7 @@
 #define SWIFT_SINCOS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/single_io.c b/src/single_io.c
index eb1039460a0c7e09816ed1ffc398ea60f5a81d63..f6703524223c06f345e048cbc3a1d9b03acb3d58 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && !defined(WITH_MPI)
 
@@ -1060,6 +1060,8 @@ void write_output_single(struct engine* e,
                      swift_type_count);
   io_write_attribute(h_grp, "NumPart_Total_HighWord", UINT,
                      numParticlesHighWord, swift_type_count);
+  io_write_attribute(h_grp, "TotalNumberOfParticles", LONGLONG, N_total,
+                     swift_type_count);
   double MassTable[swift_type_count] = {0};
   io_write_attribute(h_grp, "MassTable", DOUBLE, MassTable, swift_type_count);
   io_write_attribute(h_grp, "InitialMassTable", DOUBLE,
@@ -1156,7 +1158,8 @@ void write_output_single(struct engine* e,
     if (h_err < 0) error("Error while creating alias for particle group.\n");
 
     /* Write the number of particles and fields as an attribute */
-    io_write_attribute_l(h_grp, "NumberOfParticles", numParticles[ptype]);
+    io_write_attribute_ll(h_grp, "NumberOfParticles", N_total[ptype]);
+    io_write_attribute_ll(h_grp, "TotalNumberOfParticles", N_total[ptype]);
 
     int num_fields = 0;
     struct io_props list[100];
diff --git a/src/single_io.h b/src/single_io.h
index 96407208dc52ee6433857a1893a2bee31910d65f..13a1e1be9c292aafc3fd8c0c5754fa32b7e53512 100644
--- a/src/single_io.h
+++ b/src/single_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_SINGLE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_HDF5) && !defined(WITH_MPI)
 
diff --git a/src/sink.h b/src/sink.h
index 4d66b307f4d7417029c0f942f479e8ccf54f4020..e5d3a78aa2a35e6604136194f68bc496cd088f05 100644
--- a/src/sink.h
+++ b/src/sink.h
@@ -20,7 +20,7 @@
 #define SWIFT_SINK_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct sink model */
 #if defined(SINK_NONE)
diff --git a/src/sink_debug.h b/src/sink_debug.h
index 75ab0a65bfecbdda901820f8d67ee6071cfd8375..2346711e7387ebe3e480c759e0322e3c1f407d8d 100644
--- a/src/sink_debug.h
+++ b/src/sink_debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_SINK_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right sink definition */
 #if defined(SINK_NONE)
diff --git a/src/sink_io.h b/src/sink_io.h
index c298a3c115a09a84bf5e6ba6a20e254086a3691d..f5b0be176ca4dadf75bc825a9f2f001e1eef2883 100644
--- a/src/sink_io.h
+++ b/src/sink_io.h
@@ -19,7 +19,7 @@
 #ifndef SWIFT_SINK_IO_H
 #define SWIFT_SINK_IO_H
 
-#include "../config.h"
+#include <config.h>
 
 /* Load the correct sink type */
 #if defined(SINK_NONE)
diff --git a/src/sink_properties.h b/src/sink_properties.h
index 54fa3707469594d165874445f186f01f0c2b7276..2ec9bd9d00fa09ef01792a42e3117096e65244ff 100644
--- a/src/sink_properties.h
+++ b/src/sink_properties.h
@@ -20,7 +20,7 @@
 #define SWIFT_SINK_PROPERTIES_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct sink model */
 #if defined(SINK_NONE)
diff --git a/src/sink_struct.h b/src/sink_struct.h
index 98b55ca44564db67392695a4a993f941e4fe5643..00e6380298182149d8666663c977c5be665ac11d 100644
--- a/src/sink_struct.h
+++ b/src/sink_struct.h
@@ -25,7 +25,9 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
+
+/*  Local includes. */
 #include "inline.h"
 
 /* Import the right black holes definition */
diff --git a/src/sort_part.h b/src/sort_part.h
index eb5f39a91691e5c6027e72ad1f703fe4e9a669f1..a22b7c1dd034cfa3ef4a479067a16b4cd93f6b5a 100644
--- a/src/sort_part.h
+++ b/src/sort_part.h
@@ -21,7 +21,7 @@
 #define SWIFT_SORT_PART_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "inline.h"
diff --git a/src/space.c b/src/space.c
index 4f05692de5fba515d0745557a1d662e81735b8b1..33a2809a59d8c032195ad534d441705e9eb55ce5 100644
--- a/src/space.c
+++ b/src/space.c
@@ -22,7 +22,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
@@ -795,101 +795,6 @@ void space_synchronize_particle_positions(struct space *s) {
             clocks_getunit());
 }
 
-void space_convert_rt_star_quantities_after_zeroth_step_mapper(
-    void *restrict map_data, int scount, void *restrict extra_data) {
-#ifdef SWIFT_RT_DEBUG_CHECKS
-  struct spart *restrict sparts = (struct spart *)map_data;
-  const struct engine *restrict e = (struct engine *)extra_data;
-  const int with_cosmology = (e->policy & engine_policy_cosmology);
-
-  for (int k = 0; k < scount; k++) {
-
-    struct spart *restrict sp = &sparts[k];
-    /* Skip extra buffer sparts for on-the-fly creation */
-    if (sparts[k].time_bin > num_time_bins) continue;
-
-    /* get star's age and time step for stellar emission rates */
-    const integertime_t ti_begin =
-        get_integer_time_begin(e->ti_current - 1, sp->time_bin);
-    const integertime_t ti_step = get_integer_timestep(sp->time_bin);
-
-    /* Get particle time-step */
-    double dt_star;
-    if (with_cosmology) {
-      dt_star =
-          cosmology_get_delta_time(e->cosmology, ti_begin, ti_begin + ti_step);
-    } else {
-      dt_star = get_timestep(sp->time_bin, e->time_base);
-    }
-
-    /* Calculate age of the star at current time */
-    const double star_age_end_of_step =
-        stars_compute_age(sp, e->cosmology, e->time, with_cosmology);
-
-    rt_init_star_after_zeroth_step(sp, e->time, star_age_end_of_step, dt_star,
-                                   e->rt_props, e->physical_constants,
-                                   e->internal_units);
-  }
-#endif
-}
-
-void space_convert_rt_hydro_quantities_after_zeroth_step_mapper(
-    void *restrict map_data, int count, void *restrict extra_data) {
-
-#ifdef SWIFT_RT_DEBUG_CHECKS
-  struct part *restrict parts = (struct part *)map_data;
-  const struct engine *restrict e = (struct engine *)extra_data;
-  const struct rt_props *restrict rt_props = e->rt_props;
-  const struct cosmology *restrict cosmo = e->cosmology;
-
-  /* Loop over all the particles ignoring the extra buffer ones for on-the-fly
-   * creation */
-  for (int k = 0; k < count; k++) {
-    struct part *restrict p = &parts[k];
-    if (parts[k].time_bin <= num_time_bins)
-      rt_init_part_after_zeroth_step(p, rt_props, cosmo);
-  }
-#endif
-}
-
-/**
- * @brief Initializes values of radiative transfer data for particles
- * that needs to be set before the first actual step is done, but will
- * be reset in forthcoming steps when the corresponding particle is
- * active. It used to be necessary for a "hydro controlled injection"
- * idea where the star time steps were necessary to be known, now it's
- * only used to set up debugging checks correctly.
- *
- * @param s The #space.
- * @param verbose Are we talkative?
- */
-void space_convert_rt_quantities_after_zeroth_step(struct space *s,
-                                                   int verbose) {
-#ifdef SWIFT_RT_DEBUG_CHECKS
-  const ticks tic = getticks();
-
-  if (s->nr_parts > 0)
-    /* Particle loop. Reset hydro particle values so we don't inject too much
-     * radiation into the gas, and other initialisations after zeroth step. */
-    threadpool_map(&s->e->threadpool,
-                   space_convert_rt_hydro_quantities_after_zeroth_step_mapper,
-                   s->parts, s->nr_parts, sizeof(struct part),
-                   threadpool_auto_chunk_size, /*extra_data=*/s->e);
-
-  if (s->nr_sparts > 0)
-    /* Star particle loop. Hydro controlled injection requires star particles
-     * to have their emission rates computed and ready for interactions. */
-    threadpool_map(&s->e->threadpool,
-                   space_convert_rt_star_quantities_after_zeroth_step_mapper,
-                   s->sparts, s->nr_sparts, sizeof(struct spart),
-                   threadpool_auto_chunk_size, /*extra_data=*/s->e);
-
-  if (verbose)
-    message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
-            clocks_getunit());
-#endif
-}
-
 void space_convert_quantities_mapper(void *restrict map_data, int count,
                                      void *restrict extra_data) {
   struct space *s = (struct space *)extra_data;
diff --git a/src/space.h b/src/space.h
index 9af4a2e4449cb89be205ec397da2be14a5fe0703..293551b01914d9358b972c2c552df2a70c0b4d23 100644
--- a/src/space.h
+++ b/src/space.h
@@ -24,7 +24,7 @@
 #define SWIFT_SPACE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stddef.h>
@@ -531,8 +531,6 @@ void space_init_gparts(struct space *s, int verbose);
 void space_init_sparts(struct space *s, int verbose);
 void space_init_bparts(struct space *s, int verbose);
 void space_init_sinks(struct space *s, int verbose);
-void space_convert_rt_quantities_after_zeroth_step(struct space *s,
-                                                   int verbose);
 void space_convert_quantities(struct space *s, int verbose);
 void space_convert_rt_quantities(struct space *s, int verbose);
 void space_link_cleanup(struct space *s);
diff --git a/src/space_cell_index.c b/src/space_cell_index.c
index 60f1eac00d5688256db601c7eb5bb6f78f3e2c60..1930176df281c0862303e2f6ebbb0450ca9c8ba1 100644
--- a/src/space_cell_index.c
+++ b/src/space_cell_index.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
diff --git a/src/space_extras.c b/src/space_extras.c
index 1d5c456e0555e30d7612c2055928c14afedf776f..99d7bd9611d431f48ea7b18297c0b10fef0ceceb 100644
--- a/src/space_extras.c
+++ b/src/space_extras.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
diff --git a/src/space_first_init.c b/src/space_first_init.c
index 6cee0cfb1fcc5293167b5876adfc1649cc9fcd00..5e76749e54d5b537c5127d21f942cf916130fe8b 100644
--- a/src/space_first_init.c
+++ b/src/space_first_init.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
diff --git a/src/space_getsid.h b/src/space_getsid.h
index 752f34834e12fe3c8ff0ac0cd9cec5c8a07421bc..2124e2e1f027c54248d084c0c35da8a19d9efe66 100644
--- a/src/space_getsid.h
+++ b/src/space_getsid.h
@@ -20,7 +20,7 @@
 #define SWIFT_SPACE_GETSID_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stddef.h>
diff --git a/src/space_init.c b/src/space_init.c
index f484d667b9670aeae1b3ac9d452473e778b7ebb3..c59ef1d2406deafa1284e8d090ad659e26a3ed80 100644
--- a/src/space_init.c
+++ b/src/space_init.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
diff --git a/src/space_rebuild.c b/src/space_rebuild.c
index 77f3be3f934cc996aef0328fc51d9728ed1fd2df..a422d81f1d2e32a052ddf4a12e897f7dc5cabceb 100644
--- a/src/space_rebuild.c
+++ b/src/space_rebuild.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
diff --git a/src/space_recycle.c b/src/space_recycle.c
index 749b24a22e12c8b6f9eea2f520844d20b05f6ee6..a9f450bd7262d4853722b580e394dbf7bb5ae011 100644
--- a/src/space_recycle.c
+++ b/src/space_recycle.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
@@ -200,15 +200,25 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
     c->sinks.ti_end_min = -1;
     c->stars.ti_end_min = -1;
     c->black_holes.ti_end_min = -1;
-    c->hydro.rt_in = NULL;
-    c->hydro.rt_ghost1 = NULL;
-    c->hydro.rt_gradient = NULL;
-    c->hydro.rt_ghost2 = NULL;
-    c->hydro.rt_transport = NULL;
-    c->hydro.rt_transport_out = NULL;
-    c->hydro.rt_tchem = NULL;
-    c->hydro.rt_out = NULL;
     c->void_parent = NULL;
+    c->rt.rt_in = NULL;
+    c->rt.rt_ghost1 = NULL;
+    c->rt.rt_gradient = NULL;
+    c->rt.rt_ghost2 = NULL;
+    c->rt.rt_transport = NULL;
+    c->rt.rt_transport_out = NULL;
+    c->rt.rt_tchem = NULL;
+    c->rt.rt_advance_cell_time = NULL;
+    c->rt.rt_sorts = NULL;
+    c->rt.rt_out = NULL;
+    c->rt.rt_collect_times = NULL;
+    c->rt.ti_rt_end_min = -1;
+    c->rt.ti_rt_min_step_size = -1;
+    c->rt.updated = 0;
+#ifdef SWIFT_RT_DEBUG_CHECKS
+    c->rt.advanced_time = 0;
+#endif
+
     star_formation_logger_init(&c->stars.sfh);
 #if defined(SWIFT_DEBUG_CHECKS) || defined(SWIFT_CELL_GRAPH)
     c->cellID = 0;
diff --git a/src/space_regrid.c b/src/space_regrid.c
index cc98989c54b3e87bb04f8c0d0b3ed8b5af1487fe..2b3cbc00299fe73f8d90c187ae976aa9e9494d6f 100644
--- a/src/space_regrid.c
+++ b/src/space_regrid.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
diff --git a/src/space_sort.c b/src/space_sort.c
index f7923611960830fec13ba37f934f6f59c4faf8fa..75a09ad78f06a81beaf507df470d6426b660368c 100644
--- a/src/space_sort.c
+++ b/src/space_sort.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "error.h"
diff --git a/src/space_split.c b/src/space_split.c
index d7c93b4c4602d786a54fa877cac0efb02b84c1a4..0e2137ad61908b6fbef0f68a0437d6e31bddce27 100644
--- a/src/space_split.c
+++ b/src/space_split.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "space.h"
@@ -76,6 +76,8 @@ void space_split_recursive(struct space *s, struct cell *c,
   float sinks_h_max_active = 0.f;
   integertime_t ti_hydro_end_min = max_nr_timesteps, ti_hydro_end_max = 0,
                 ti_hydro_beg_max = 0;
+  integertime_t ti_rt_end_min = max_nr_timesteps, ti_rt_beg_max = 0;
+  integertime_t ti_rt_min_step_size = max_nr_timesteps;
   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, ti_stars_end_max = 0,
@@ -320,6 +322,10 @@ void space_split_recursive(struct space *s, struct cell *c,
 
         ti_hydro_end_min = min(ti_hydro_end_min, cp->hydro.ti_end_min);
         ti_hydro_beg_max = max(ti_hydro_beg_max, cp->hydro.ti_beg_max);
+        ti_rt_end_min = min(ti_rt_end_min, cp->rt.ti_rt_end_min);
+        ti_rt_beg_max = max(ti_rt_beg_max, cp->rt.ti_rt_beg_max);
+        ti_rt_min_step_size =
+            min(ti_rt_min_step_size, cp->rt.ti_rt_min_step_size);
         ti_gravity_end_min = min(ti_gravity_end_min, cp->grav.ti_end_min);
         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);
@@ -481,13 +487,23 @@ void space_split_recursive(struct space *s, struct cell *c,
 
       /* When does this particle's time-step start and end? */
       const timebin_t time_bin = parts[k].time_bin;
+      const timebin_t time_bin_rt = parts[k].rt_time_data.time_bin;
       const integertime_t ti_end = get_integer_time_end(ti_current, time_bin);
       const integertime_t ti_beg = get_integer_time_begin(ti_current, time_bin);
+      const integertime_t ti_rt_end =
+          get_integer_time_end(ti_current, time_bin_rt);
+      const integertime_t ti_rt_beg =
+          get_integer_time_begin(ti_current, time_bin_rt);
+      const integertime_t ti_rt_step = get_integer_timestep(time_bin_rt);
 
       ti_hydro_end_min = min(ti_hydro_end_min, ti_end);
       ti_hydro_end_max = max(ti_hydro_end_max, ti_end);
       ti_hydro_beg_max = max(ti_hydro_beg_max, ti_beg);
 
+      ti_rt_end_min = min(ti_rt_end_min, ti_rt_end);
+      ti_rt_beg_max = max(ti_rt_beg_max, ti_rt_beg);
+      ti_rt_min_step_size = min(ti_rt_min_step_size, ti_rt_step);
+
       h_max = max(h_max, parts[k].h);
 
       if (part_is_active(&parts[k], e))
@@ -648,6 +664,9 @@ void space_split_recursive(struct space *s, struct cell *c,
   c->hydro.h_max_active = h_max_active;
   c->hydro.ti_end_min = ti_hydro_end_min;
   c->hydro.ti_beg_max = ti_hydro_beg_max;
+  c->rt.ti_rt_end_min = ti_rt_end_min;
+  c->rt.ti_rt_beg_max = ti_rt_beg_max;
+  c->rt.ti_rt_min_step_size = ti_rt_min_step_size;
   c->grav.ti_end_min = ti_gravity_end_min;
   c->grav.ti_beg_max = ti_gravity_beg_max;
   c->stars.ti_end_min = ti_stars_end_min;
@@ -703,11 +722,16 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data,
     struct cell *c = &cells_top[local_cells_with_particles[ind]];
     space_split_recursive(s, c, NULL, NULL, NULL, NULL, NULL, tid);
 
-    min_a_grav = min(min_a_grav, c->grav.multipole->m_pole.min_old_a_grav_norm);
-    max_softening = max(max_softening, c->grav.multipole->m_pole.max_softening);
-    for (int n = 0; n < SELF_GRAVITY_MULTIPOLE_ORDER + 1; ++n)
-      max_mpole_power[n] =
-          max(max_mpole_power[n], c->grav.multipole->m_pole.power[n]);
+    if (s->with_self_gravity) {
+      min_a_grav =
+          min(min_a_grav, c->grav.multipole->m_pole.min_old_a_grav_norm);
+      max_softening =
+          max(max_softening, c->grav.multipole->m_pole.max_softening);
+
+      for (int n = 0; n < SELF_GRAVITY_MULTIPOLE_ORDER + 1; ++n)
+        max_mpole_power[n] =
+            max(max_mpole_power[n], c->grav.multipole->m_pole.power[n]);
+    }
   }
 
 #ifdef SWIFT_DEBUG_CHECKS
diff --git a/src/space_unique_id.c b/src/space_unique_id.c
index 11773cdf0876c284e4ad08a5030370731be0bf57..137eb7189bc50bc5054265441691e42503abe0b2 100644
--- a/src/space_unique_id.c
+++ b/src/space_unique_id.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <limits.h>
diff --git a/src/space_unique_id.h b/src/space_unique_id.h
index 49ec49d2bdc09b46451a4decdadd6dce046d5a20..124c825d3d9d40206624e4aff600f6870e7af208 100644
--- a/src/space_unique_id.h
+++ b/src/space_unique_id.h
@@ -21,7 +21,7 @@
 #define SWIFT_SPACE_UNIQUE_ID_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "lock.h"
diff --git a/src/star_formation.c b/src/star_formation.c
index c0e8dfc2049c62ff827c7caa945bb08ee46e92e8..22426215e698249c0c4b2cdc2783c158a00b4068 100644
--- a/src/star_formation.c
+++ b/src/star_formation.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* This object's header. */
 #include "part.h"
diff --git a/src/star_formation.h b/src/star_formation.h
index cd21c8aa43c5c6a0a6747c3bd1e7c3544e3cc4e9..2fb08d3e82ad5940d05d67057ff74eb421f4d2fb 100644
--- a/src/star_formation.h
+++ b/src/star_formation.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "hydro.h"
diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h
index 5674bc76a3de063f55de089dab49ac0f4e68c83c..88fa46dfbef6a3f956bad2d85296a81126c0886c 100644
--- a/src/star_formation/EAGLE/star_formation.h
+++ b/src/star_formation/EAGLE/star_formation.h
@@ -208,8 +208,8 @@ INLINE static int star_formation_is_star_forming_Z_dep(
   /* Physical density of the particle */
   const double physical_density = hydro_get_physical_density(p, cosmo);
 
-  /* Get the Hydrogen number density (assuming primordial H abundance) */
-  const double n_H = physical_density * hydro_props->hydrogen_mass_fraction;
+  /* Get the Hydrogen mass density (assuming primordial H abundance) */
+  const double rho_H = physical_density * hydro_props->hydrogen_mass_fraction;
 
   /* Get the density threshold for star formation */
   const double Z =
@@ -232,7 +232,7 @@ INLINE static int star_formation_is_star_forming_Z_dep(
   density_threshold *= phys_const->const_proton_mass;
 
   /* Check if it exceeded the minimum density */
-  if (n_H < density_threshold) return 0;
+  if (rho_H < density_threshold) return 0;
 
   /* Calculate the entropy of the particle */
   const double entropy = hydro_get_physical_entropy(p, xp, cosmo);
diff --git a/src/star_formation/EAGLE/star_formation_io.h b/src/star_formation/EAGLE/star_formation_io.h
index a40ec2c2f804b0a82e99049d309dbf465594af61..d7c1cd2e69bb28215fc8508bbcd15deb080ef00c 100644
--- a/src/star_formation/EAGLE/star_formation_io.h
+++ b/src/star_formation/EAGLE/star_formation_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_STAR_FORMATION_EAGLE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "io_properties.h"
diff --git a/src/star_formation/GEAR/star_formation_io.h b/src/star_formation/GEAR/star_formation_io.h
index c294ca6ad76225a81f7c98be5bf4b50a84192154..9412097c79b5662009364da8144eb3be4053bd1f 100644
--- a/src/star_formation/GEAR/star_formation_io.h
+++ b/src/star_formation/GEAR/star_formation_io.h
@@ -21,7 +21,7 @@
 #define SWIFT_STAR_FORMATION_GEAR_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "io_properties.h"
diff --git a/src/star_formation/QLA/star_formation_io.h b/src/star_formation/QLA/star_formation_io.h
index d65a40bf70782dcaf104ea1afe2565df2b2b307e..ee8de2b778aaea0fa797599da35b0d2075b9f626 100644
--- a/src/star_formation/QLA/star_formation_io.h
+++ b/src/star_formation/QLA/star_formation_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_STAR_FORMATION_QLA_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "io_properties.h"
diff --git a/src/star_formation/none/star_formation_io.h b/src/star_formation/none/star_formation_io.h
index 5c2329b3978ebf0c5ed64c3f978c479456a3efc0..d89060282c9ebe0d633d8940931b1fe8027df803 100644
--- a/src/star_formation/none/star_formation_io.h
+++ b/src/star_formation/none/star_formation_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_STAR_FORMATION_NONE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "io_properties.h"
diff --git a/src/star_formation_csds.h b/src/star_formation_csds.h
index 7952fe59584417fd71fcce9178c76c91799dac8b..d34471860158c2dba29232a6713bd61cce98214e 100644
--- a/src/star_formation_csds.h
+++ b/src/star_formation_csds.h
@@ -20,7 +20,7 @@
 #define SWIFT_STAR_FORMATION_PARTICLE_CSDS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "align.h"
diff --git a/src/star_formation_debug.h b/src/star_formation_debug.h
index 4b0deda0d8c1b13c26076bf8cb97fe174bc43d4d..81b15daa01b841e5260ac80faccf80124c0966b5 100644
--- a/src/star_formation_debug.h
+++ b/src/star_formation_debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_STAR_FORMATION_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right star formation definition */
 #if defined(STAR_FORMATION_NONE)
diff --git a/src/star_formation_iact.h b/src/star_formation_iact.h
index 5baf914a2dd2e74d681edbf907a032109e39177d..9959c52d207f4cd81bdce0f39b9f0720b502257e 100644
--- a/src/star_formation_iact.h
+++ b/src/star_formation_iact.h
@@ -26,7 +26,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right star formation law definition */
 #if defined(STAR_FORMATION_NONE)
diff --git a/src/star_formation_io.h b/src/star_formation_io.h
index 3837b5d3d85255b43eeb19371ca868517fa0d4a1..4edd084e66f6a4018701e3c19c4b19d5e5d4f43c 100644
--- a/src/star_formation_io.h
+++ b/src/star_formation_io.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right cooling definition */
 #if defined(STAR_FORMATION_NONE)
diff --git a/src/star_formation_logger.h b/src/star_formation_logger.h
index bddd1e68af2ec42ee4a9f00e32c2062909f47642..fe1bc7d3891045befbac3a7d7d5ff3da9b611150 100644
--- a/src/star_formation_logger.h
+++ b/src/star_formation_logger.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right SFH logger definition */
 #if defined(STAR_FORMATION_NONE)
diff --git a/src/star_formation_logger_struct.h b/src/star_formation_logger_struct.h
index 0c6b9533106f895cd102671ee2d712fd65a1c0f9..5ba6654fd850cb05d5f0c8903339360f7404e192 100644
--- a/src/star_formation_logger_struct.h
+++ b/src/star_formation_logger_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right SFH logger struct definition */
 #if defined(STAR_FORMATION_NONE)
diff --git a/src/star_formation_struct.h b/src/star_formation_struct.h
index ae4cdace9c7cd0daf5e61ff2b87a3333e271dab7..2c8e72418e93f18e8daa5167d98d97ef8c9752cc 100644
--- a/src/star_formation_struct.h
+++ b/src/star_formation_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right star formation definition */
 #if defined(STAR_FORMATION_NONE)
diff --git a/src/stars.c b/src/stars.c
index 871668176d6a428bd4ceba396b5eed0b41ca85ce..e86f0a06922fc0df6ef779dfdbd619b1524e097f 100644
--- a/src/stars.c
+++ b/src/stars.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/stars.h b/src/stars.h
index 40ca805a1b835ed1cfa8e7a2afb408527e7898ea..68b8d7fa52e27238db642ca8b7d1e1ea1923401a 100644
--- a/src/stars.h
+++ b/src/stars.h
@@ -20,7 +20,7 @@
 #define SWIFT_STARS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Select the correct star model */
 #if defined(STARS_NONE)
diff --git a/src/stars_csds.h b/src/stars_csds.h
index f22c4ddaef21d2d5b56b62034064f81cad9bc09b..fe88620f8a06b066dba8f1ab207535be228a40a7 100644
--- a/src/stars_csds.h
+++ b/src/stars_csds.h
@@ -20,7 +20,7 @@
 #define SWIFT_STARS_CSDS_H
 
 /* Include config */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "./const.h"
diff --git a/src/stars_io.h b/src/stars_io.h
index 7882df53ba475d7f0d1b91cf5f6a092c4ec96e8e..a62923534662ff4c6962b1270b7cb18a108dcf1e 100644
--- a/src/stars_io.h
+++ b/src/stars_io.h
@@ -19,7 +19,7 @@
 #ifndef SWIFT_STARS_IO_H
 #define SWIFT_STARS_IO_H
 
-#include "../config.h"
+#include <config.h>
 
 /* Load the correct star type */
 #if defined(STARS_NONE)
diff --git a/src/statistics.c b/src/statistics.c
index f99aa6b3a43f0c9ed42255c367577565b0ab6dca..e4dea116dbaa1fef8db212367dbcb411b437f029 100644
--- a/src/statistics.c
+++ b/src/statistics.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
diff --git a/src/statistics.h b/src/statistics.h
index a5ee0a5c9feeaec06c06fed7b7aca8a52558e613..9083a496811b4044ba5bb4b1c709dd61d4961fc3 100644
--- a/src/statistics.h
+++ b/src/statistics.h
@@ -20,7 +20,7 @@
 #define SWIFT_STATISTICS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "lock.h"
diff --git a/src/swift.h b/src/swift.h
index 62a66b0c4d88e10d0fbe1481941f1590c963f75d..9cee769a6e9d4e92a5a13982795b3a98df5ec18c 100644
--- a/src/swift.h
+++ b/src/swift.h
@@ -20,7 +20,7 @@
 #define SWIFT_SWIFT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "active.h"
diff --git a/src/task.c b/src/task.c
index 52810a22b88e2a604bf2f2c9459adc443c3022ef..93254247083c3360c0d53151bdd208ddcdb92930 100644
--- a/src/task.c
+++ b/src/task.c
@@ -22,7 +22,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
@@ -123,6 +123,9 @@ const char *taskID_names[task_type_count] = {
     "rt_ghost2",
     "rt_transport_out",
     "rt_tchem",
+    "rt_advance_cell_time",
+    "rt_sorts",
+    "rt_collect_times",
 };
 
 /* Sub-task type names. */
@@ -189,22 +192,22 @@ MPI_Comm subtaskMPI_comms[task_subtype_count];
  * @param ARRAY is the array of this specific type.
  * @param COUNT is the number of elements in the array.
  */
-#define TASK_CELL_OVERLAP(TYPE, ARRAY, COUNT)                               \
-  __attribute__((always_inline))                                            \
-      INLINE static size_t task_cell_overlap_##TYPE(                        \
-          const struct cell *restrict ci, const struct cell *restrict cj) { \
-                                                                            \
-    if (ci == NULL || cj == NULL) return 0;                                 \
-                                                                            \
-    if (ci->ARRAY <= cj->ARRAY &&                                           \
-        ci->ARRAY + ci->COUNT >= cj->ARRAY + cj->COUNT) {                   \
-      return cj->COUNT;                                                     \
-    } else if (cj->ARRAY <= ci->ARRAY &&                                    \
-               cj->ARRAY + cj->COUNT >= ci->ARRAY + ci->COUNT) {            \
-      return ci->COUNT;                                                     \
-    }                                                                       \
-                                                                            \
-    return 0;                                                               \
+#define TASK_CELL_OVERLAP(TYPE, ARRAY, COUNT)                           \
+  __attribute__((always_inline))                                        \
+  INLINE static size_t task_cell_overlap_##TYPE(                        \
+      const struct cell *restrict ci, const struct cell *restrict cj) { \
+                                                                        \
+    if (ci == NULL || cj == NULL) return 0;                             \
+                                                                        \
+    if (ci->ARRAY <= cj->ARRAY &&                                       \
+        ci->ARRAY + ci->COUNT >= cj->ARRAY + cj->COUNT) {               \
+      return cj->COUNT;                                                 \
+    } else if (cj->ARRAY <= ci->ARRAY &&                                \
+               cj->ARRAY + cj->COUNT >= ci->ARRAY + ci->COUNT) {        \
+      return ci->COUNT;                                                 \
+    }                                                                   \
+                                                                        \
+    return 0;                                                           \
   }
 
 TASK_CELL_OVERLAP(part, hydro.parts, hydro.count);
@@ -261,6 +264,7 @@ __attribute__((always_inline)) INLINE static enum task_actions task_acts_on(
     case task_type_rt_ghost1:
     case task_type_rt_ghost2:
     case task_type_rt_tchem:
+    case task_type_rt_sort:
       return task_action_part;
       break;
 
@@ -552,6 +556,8 @@ void task_unlock(struct task *t) {
     case task_type_rt_ghost1:
     case task_type_rt_ghost2:
     case task_type_rt_tchem:
+    case task_type_rt_sort:
+    case task_type_rt_advance_cell_time:
       cell_unlocktree(ci);
       break;
 
@@ -783,6 +789,8 @@ int task_lock(struct task *t) {
     case task_type_rt_ghost1:
     case task_type_rt_ghost2:
     case task_type_rt_tchem:
+    case task_type_rt_sort:
+    case task_type_rt_advance_cell_time:
       if (ci->hydro.hold) return 0;
       if (cell_locktree(ci) != 0) return 0;
       break;
@@ -1821,6 +1829,8 @@ enum task_categories task_get_category(const struct task *t) {
     case task_type_rt_transport_out:
     case task_type_rt_tchem:
     case task_type_rt_out:
+    case task_type_rt_sort:
+    case task_type_rt_advance_cell_time:
       return task_category_rt;
 
     case task_type_neutrino_weight:
diff --git a/src/task.h b/src/task.h
index 9075a44b57a10606db71e5029f4799662de70109..a968aa2430f38daa7c3e83bdc4dbbfbd854859d7 100644
--- a/src/task.h
+++ b/src/task.h
@@ -23,7 +23,7 @@
 #ifndef SWIFT_TASK_H
 #define SWIFT_TASK_H
 
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "align.h"
@@ -116,6 +116,9 @@ enum task_types {
   task_type_rt_ghost2,
   task_type_rt_transport_out, /* Implicit */
   task_type_rt_tchem,
+  task_type_rt_advance_cell_time,
+  task_type_rt_sort,
+  task_type_rt_collect_times,
   task_type_count
 } __attribute__((packed));
 
diff --git a/src/threadpool.c b/src/threadpool.c
index 2013692f267c77aad2795dddf81b7e9629c55840..c50b088e9c6077f579cea06307112616888c30c9 100644
--- a/src/threadpool.c
+++ b/src/threadpool.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <float.h>
diff --git a/src/threadpool.h b/src/threadpool.h
index 6c7052cfd9b6a2f71c8993962cb7f1f1729744cb..84d24a02eeaaded95e2cca991e7970fc1c31a23d 100644
--- a/src/threadpool.h
+++ b/src/threadpool.h
@@ -20,7 +20,7 @@
 #define SWIFT_THREADPOOL_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard headers */
 #include <pthread.h>
diff --git a/src/timeline.h b/src/timeline.h
index d3fd9d2e44ac536b06fdc44155134a3758db81ab..63164d43203501b9e74824aeabd8df2868466c7f 100644
--- a/src/timeline.h
+++ b/src/timeline.h
@@ -20,7 +20,7 @@
 #define SWIFT_TIMELINE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/timers.c b/src/timers.c
index 4aa493a059270f71882ccc3ef61db42a4e1e6785..2f1e04bf83e5363c7786b379f7d446c9c745942e 100644
--- a/src/timers.c
+++ b/src/timers.c
@@ -140,6 +140,8 @@ const char* timers_names[timer_count] = {
     "dosub_self_rt_transport",
     "dosub_pair_rt_transport",
     "rt_tchem",
+    "rt_advance_cell_time",
+    "rt_collect_times",
 };
 
 /* File to store the timers */
diff --git a/src/timers.h b/src/timers.h
index 8b6d3c3adad738e0b8ffeffc1d510336a12d9f7e..ad20f6a414e0b36f8a2118ee28cc8768f6e4fc0a 100644
--- a/src/timers.h
+++ b/src/timers.h
@@ -23,7 +23,7 @@
 #define SWIFT_TIMERS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "atomic.h"
@@ -140,6 +140,8 @@ enum {
   timer_dosub_self_rt_transport,
   timer_dosub_pair_rt_transport,
   timer_do_rt_tchem,
+  timer_do_rt_advance_cell_time,
+  timer_do_rt_collect_times,
   timer_count,
 };
 
diff --git a/src/timestep.h b/src/timestep.h
index 10761fe886db32f38072f4e4e8c889739197a1e2..57c3d096eccb14dda1c5e436a4d289c6b2e843e6 100644
--- a/src/timestep.h
+++ b/src/timestep.h
@@ -20,7 +20,7 @@
 #define SWIFT_TIMESTEP_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "cooling.h"
@@ -179,10 +179,19 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep(
 
   /* Get the RT timestep */
   float new_dt_radiation = FLT_MAX;
-  if (e->policy & engine_policy_rt)
+  if (e->policy & engine_policy_rt) {
     new_dt_radiation = rt_compute_timestep(
         p, xp, e->rt_props, e->cosmology, e->hydro_properties,
         e->physical_constants, e->internal_units);
+    if (e->max_nr_rt_subcycles > 0 && new_dt_radiation != FLT_MAX) {
+      /* if max_nr_rt_subcycles == 0, we don't subcycle. */
+      /* ToDo: this is a temporary solution to enforce the exact
+       * number of RT subcycles. We multiply the new_dt_rad here by
+       * the number of subcycles, and don't when getting the
+       * actual RT time step. */
+      new_dt_radiation *= e->max_nr_rt_subcycles;
+    }
+  }
 
   /* Take the minimum of all */
   float new_dt = min3(new_dt_hydro, new_dt_cooling, new_dt_grav);
@@ -217,6 +226,28 @@ __attribute__((always_inline)) INLINE static integertime_t get_part_timestep(
   return new_dti;
 }
 
+/**
+ * @brief Compute the new (integer) time-step of a given #part
+ *
+ * @param p The #part.
+ * @param xp The #xpart partner of p.
+ * @param e The #engine (used to get some constants).
+ */
+__attribute__((always_inline)) INLINE static integertime_t get_part_rt_timestep(
+    const struct part *restrict p, const struct xpart *restrict xp,
+    const struct engine *restrict e) {
+
+  integertime_t new_dti;
+  /* TODO: for now, we abuse max_nr_rt_subcycles to fix the
+   * number of sub-cycles for each step. */
+  if (e->max_nr_rt_subcycles > 0) {
+    new_dti = get_part_timestep(p, xp, e) / e->max_nr_rt_subcycles;
+  } else {
+    new_dti = get_part_timestep(p, xp, e);
+  }
+  return new_dti;
+}
+
 /**
  * @brief Compute the new (integer) time-step of a given #spart
  *
diff --git a/src/timestep_limiter.h b/src/timestep_limiter.h
index 23dbcf2ee9cac9f0159ddd10c6f6462378117498..fc6362334e86dd34fae1da83b7fe6a6dbd08d0c1 100644
--- a/src/timestep_limiter.h
+++ b/src/timestep_limiter.h
@@ -20,7 +20,7 @@
 #define SWIFT_TIMESTEP_LIMITER_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "kick.h"
diff --git a/src/timestep_limiter_struct.h b/src/timestep_limiter_struct.h
index efab925a15b60d2830751cfce9770d7b99d7db2f..90989080928461b4d4843ba1da0f147e6a01e11e 100644
--- a/src/timestep_limiter_struct.h
+++ b/src/timestep_limiter_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "timeline.h"
diff --git a/src/timestep_sync.h b/src/timestep_sync.h
index 65d3b0f2482f8c33a219dfff3b1195c8d951e8a3..179fc361b28f703b31660d923baed867dad773ce 100644
--- a/src/timestep_sync.h
+++ b/src/timestep_sync.h
@@ -20,7 +20,7 @@
 #define SWIFT_TIMESTEP_SYNC_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "engine.h"
diff --git a/src/timestep_sync_part.h b/src/timestep_sync_part.h
index ee92fc7c93c69934011c6a6938f1d45171f7a770..b62fc99326a4be0f81cc870125e373f5bf9949dd 100644
--- a/src/timestep_sync_part.h
+++ b/src/timestep_sync_part.h
@@ -20,7 +20,7 @@
 #define SWIFT_TIMESTEP_SYNC_PART_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /**
  * @brief Flag a particle for synchronization on the time-line.
diff --git a/src/tools.c b/src/tools.c
index 419c57b7034ecc8d400d3415cafaef187d82dd9d..d771016591371d64b6adc1bc85169021ef262fef 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <ctype.h>
diff --git a/src/tracers.h b/src/tracers.h
index b844377035682c88b701e706bd313e79876ddb90..9e939207c9ee98818fc9e788f05f5ae7662a961f 100644
--- a/src/tracers.h
+++ b/src/tracers.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right cooling definition */
 #if defined(TRACERS_NONE)
diff --git a/src/tracers/EAGLE/tracers.h b/src/tracers/EAGLE/tracers.h
index cf58dfbe4bcdbeb08a1a8014b9e637655d4963d2..6ca4f1adfb62c2bf0d530e16ef22f83e18dfb711 100644
--- a/src/tracers/EAGLE/tracers.h
+++ b/src/tracers/EAGLE/tracers.h
@@ -21,7 +21,7 @@
 #define SWIFT_TRACERS_EAGLE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
@@ -210,6 +210,18 @@ static INLINE void tracers_after_black_holes_feedback(
   xp->tracers_data.AGN_feedback_energy += delta_energy;
 }
 
+static INLINE void tracers_after_jet_feedback(
+    const struct part *p, struct xpart *xp, const int with_cosmology,
+    const float scale_factor, const double time, const double delta_energy) {
+
+  if (with_cosmology)
+    xp->tracers_data.last_AGN_jet_feedback_scale_factor = scale_factor;
+  else
+    xp->tracers_data.last_AGN_jet_feedback_time = time;
+  xp->tracers_data.hit_by_jet_feedback++;
+  xp->tracers_data.jet_feedback_energy += delta_energy;
+}
+
 /**
  * @brief Split the tracer content of a particle into n pieces
  *
diff --git a/src/tracers/EAGLE/tracers_io.h b/src/tracers/EAGLE/tracers_io.h
index 625f8205319ddebbb4bfb9f4365acd8cc8ce6592..a8b35458409e53bd9a041e003049bc86388a1f1a 100644
--- a/src/tracers/EAGLE/tracers_io.h
+++ b/src/tracers/EAGLE/tracers_io.h
@@ -21,7 +21,7 @@
 #define SWIFT_TRACERS_EAGLE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "io_properties.h"
diff --git a/src/tracers/EAGLE/tracers_struct.h b/src/tracers/EAGLE/tracers_struct.h
index 05c39e356a3ca7672c88c5944a631249db94817d..71b1966a73c8515c725853a55dc2d87f12750a48 100644
--- a/src/tracers/EAGLE/tracers_struct.h
+++ b/src/tracers/EAGLE/tracers_struct.h
@@ -47,6 +47,15 @@ struct tracers_xpart_data {
     float last_AGN_injection_time;
   };
 
+  union {
+
+    /* Last scale factor this particle was kicked as part of jet feedback */
+    float last_AGN_jet_feedback_scale_factor;
+
+    /* Last time this particle was kicked as part of jet feedback */
+    float last_AGN_jet_feedback_time;
+  };
+
   /*! Density of the gas before the last AGN feedback event
    * (physical internal units) */
   float density_before_last_AGN_feedback_event;
@@ -67,6 +76,13 @@ struct tracers_xpart_data {
    * (physical units) */
   float AGN_feedback_energy;
 
+  /*! Total jet feedback energy received by this particle */
+  float jet_feedback_energy;
+
+  /*! Counts how many times this particle has been kicked as part of
+     jet feedback */
+  char hit_by_jet_feedback;
+
   /*! Has this particle been hit by SNII feedback? */
   char hit_by_SNII_feedback;
 
diff --git a/src/tracers/none/tracers.h b/src/tracers/none/tracers.h
index b0e1728cc85391f3fb295e881d9a0f7742e3f2db..11c1028a15b39eb68006ced9fe1ec007d9fdf8a7 100644
--- a/src/tracers/none/tracers.h
+++ b/src/tracers/none/tracers.h
@@ -20,7 +20,7 @@
 #define SWIFT_TRACERS_NONE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "cooling.h"
diff --git a/src/tracers/none/tracers_io.h b/src/tracers/none/tracers_io.h
index a7d74d9b62b795fc955af3aa9809c24df22678a9..2421ff725b48c914a34cd79adf9f27fcf96b4301 100644
--- a/src/tracers/none/tracers_io.h
+++ b/src/tracers/none/tracers_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_TRACERS_NONE_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes */
 #include "io_properties.h"
diff --git a/src/tracers_debug.h b/src/tracers_debug.h
index ec37e59ce7ffde090c485a37751d6033605d1610..3f99eaff3c9a1431282b10f1cca86fa5180e1bb0 100644
--- a/src/tracers_debug.h
+++ b/src/tracers_debug.h
@@ -20,7 +20,7 @@
 #define SWIFT_TRACERS_DEBUG_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the debug routines of the right tracers definition */
 #if defined(TRACERS_NONE)
diff --git a/src/tracers_io.h b/src/tracers_io.h
index cc7b799c9cb8d6e2a59e9576b3367a3275ea38e0..6b92e75cf0c3d4b68f90ba2339352133c6bf033d 100644
--- a/src/tracers_io.h
+++ b/src/tracers_io.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right cooling definition */
 #if defined(TRACERS_NONE)
diff --git a/src/tracers_struct.h b/src/tracers_struct.h
index f74b4d0c2a6c216081b846530be082f3af8452f3..12584e9efd36d0624adb752a542a3fbbdc5fd2b3 100644
--- a/src/tracers_struct.h
+++ b/src/tracers_struct.h
@@ -25,7 +25,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Import the right cooling definition */
 #if defined(TRACERS_NONE)
diff --git a/src/units.c b/src/units.c
index a5a3b375cdcb614a827b4cf67e1271279137d37c..76914bbe532949692f9742dda115348c5fb0865d 100644
--- a/src/units.c
+++ b/src/units.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <math.h>
@@ -460,6 +460,12 @@ void units_get_base_unit_exponents_array(float baseUnitsExp[5],
       baseUnitsExp[UNIT_TIME] = -3.f;
       break;
 
+    case UNIT_CONV_ENERGY_DENSITY:
+      baseUnitsExp[UNIT_MASS] = 1.f;
+      baseUnitsExp[UNIT_LENGTH] = -1.f;
+      baseUnitsExp[UNIT_TIME] = -2.f;
+      break;
+
     case UNIT_CONV_INV_TIME:
       baseUnitsExp[UNIT_TIME] = -1.f;
       break;
diff --git a/src/units.h b/src/units.h
index a0705b98b6d7bd387980eecee3bd352202cca9a0..84c28f578f59eda448d98b437a5e1129070ed72d 100644
--- a/src/units.h
+++ b/src/units.h
@@ -20,7 +20,7 @@
 #define SWIFT_UNITS_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "parser.h"
@@ -115,6 +115,7 @@ enum unit_conversion_factor {
   UNIT_CONV_RADIATION_FLUX,
   UNIT_CONV_ENERGY_FLUX_PER_UNIT_SURFACE,
   UNIT_CONV_ENERGY_FLUX_DENSITY,
+  UNIT_CONV_ENERGY_DENSITY,
   UNIT_CONV_POWER_DENSITY,
   UNIT_CONV_GASOLINE_DIFF_RATE,
   UNIT_CONV_THERMAL_DIFFUSIVITY,
diff --git a/src/vector.h b/src/vector.h
index 163ddbefe29ed5a2de5c580c8199354fd5bd1bb1..0c8c2c7c798270342b55fb23b209d3f6bd079e75 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -21,7 +21,7 @@
 #define SWIFT_VECTOR_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers */
 #include "inline.h"
diff --git a/src/vector_power.h b/src/vector_power.h
index f7a65be4efcf71dbcc5f39a4ed16c3959e1ea847..171d300a19eb7ed24723876f3dec4084f7f3ce43 100644
--- a/src/vector_power.h
+++ b/src/vector_power.h
@@ -32,7 +32,7 @@
  */
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "inline.h"
diff --git a/src/velociraptor_dummy.c b/src/velociraptor_dummy.c
index 36cb65bfbe6931464f33d7e4b641f8882fdf65d0..03f3469accc816684485134aedc5870e8c6439ab 100644
--- a/src/velociraptor_dummy.c
+++ b/src/velociraptor_dummy.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local includes. */
 #include "error.h"
diff --git a/src/velociraptor_interface.c b/src/velociraptor_interface.c
index afbd1fb2421575a9964b0b0c2796ad5723e6fa08..fab15248c16f0c775fb751fc6fbba99f8c98a1cb 100644
--- a/src/velociraptor_interface.c
+++ b/src/velociraptor_interface.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <hdf5.h>
diff --git a/src/velociraptor_interface.h b/src/velociraptor_interface.h
index 2547fa56c1677e93b1c59a1435e9a6ab92c1f308..0adf8ca45dc65ead58da0499fb11030879dea6e3 100644
--- a/src/velociraptor_interface.h
+++ b/src/velociraptor_interface.h
@@ -20,7 +20,7 @@
 #define SWIFT_VELOCIRAPTOR_INTERFACE_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Forward declaration */
 struct engine;
diff --git a/src/velociraptor_io.h b/src/velociraptor_io.h
index 446fc3131bfc638a0b37307110a2e802c3dd01ea..dc912e81c4848d6340afcb3a8332857ef9c36e52 100644
--- a/src/velociraptor_io.h
+++ b/src/velociraptor_io.h
@@ -20,7 +20,7 @@
 #define SWIFT_VELOCIRAPTOR_IO_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 INLINE static void velociraptor_convert_part_groupID(const struct engine* e,
                                                      const struct part* p,
diff --git a/src/velociraptor_struct.h b/src/velociraptor_struct.h
index b998263a6ba2fe0aaa6552f274cb8f4ee85d3b1c..cad5f711510174724cb307e42785522654d3eaf5 100644
--- a/src/velociraptor_struct.h
+++ b/src/velociraptor_struct.h
@@ -20,7 +20,7 @@
 #define SWIFT_VELOCIRAPTOR_STRUCT_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /**
  * @brief Data returned by VELOCIraptor for each #gpart.
diff --git a/src/version.c b/src/version.c
index 1ee724b6e2ac55788274b7cc220b103033771b78..9d8642573d3225a98af0bf6db20e774125af0bb9 100644
--- a/src/version.c
+++ b/src/version.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* MPI headers. */
 #ifdef WITH_MPI
diff --git a/src/xmf.c b/src/xmf.c
index 45860ba1ce41cc55aae19b96cecd216b80b577e3..b212d92d2d2cc5bbeeab9bc2f3b423ebf7a7ea60 100644
--- a/src/xmf.c
+++ b/src/xmf.c
@@ -19,7 +19,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <libgen.h>
diff --git a/src/xmf.h b/src/xmf.h
index b4b06c4d3a4733605c64cc58d4f21b5dc7cb3a23..7cc57ec67030dc8f2caf58dbc7d638b2d3518416 100644
--- a/src/xmf.h
+++ b/src/xmf.h
@@ -20,7 +20,7 @@
 #define SWIFT_XMF_H
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "common_io.h"
diff --git a/src/zoom_region.c b/src/zoom_region.c
index 67c31cb91001d3e3b761a6de5fefd40433dc2a87..0271cb516e3aed3978173f82a2a9f8d16d9aee19 100644
--- a/src/zoom_region.c
+++ b/src/zoom_region.c
@@ -1822,8 +1822,8 @@ void engine_make_self_gravity_tasks_mapper_natural_cells(void *map_data,
 
   /* Convert the maximal search distance to a number of cells
    * Define a lower and upper delta in case things are not symmetric */
-  const int delta = (int)(sqrt(3) * distance /
-                          cells[bkg_cell_offset].width[0]) + 1;
+  const int delta = max((int)(sqrt(3) * distance /
+                          cells[bkg_cell_offset].width[0]) + 1, 2);
   int delta_m = delta;
   int delta_p = delta;
 
@@ -2035,7 +2035,8 @@ void engine_make_self_gravity_tasks_mapper_zoom_cells(void *map_data,
 
   /* Convert the maximal search distance to a number of cells
    * Define a lower and upper delta in case things are not symmetric */
-  const int delta = (int)(sqrt(3) * distance / cells[0].width[0]) + 1;
+  const int delta = max((int)(sqrt(3) * distance /
+                          cells[0].width[0]) + 1, 2);
   int delta_m = delta;
   int delta_p = delta;
 
diff --git a/swift.c b/swift.c
index b97f4a288c42195f1a9d7fe1ce7c79c38bec33a0..f6e1798f4b5ceff19f69f5b053a33b91f9682db6 100644
--- a/swift.c
+++ b/swift.c
@@ -23,7 +23,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <errno.h>
@@ -879,25 +879,6 @@ int main(int argc, char *argv[]) {
   parser_get_opt_param_string(params, "Restarts:basename", restart_name,
                               "swift");
 
-  /* How often to check for the stop file and dump restarts and exit the
-   * application. */
-  const int restart_stop_steps =
-      parser_get_opt_param_int(params, "Restarts:stop_steps", 100);
-
-  /* Get the maximal wall-clock time of this run */
-  const float restart_max_hours_runtime =
-      parser_get_opt_param_float(params, "Restarts:max_run_time", FLT_MAX);
-
-  /* Do we want to resubmit when we hit the limit? */
-  const int resubmit_after_max_hours =
-      parser_get_opt_param_int(params, "Restarts:resubmit_on_exit", 0);
-
-  /* What command should we run to resubmit at the end? */
-  char resubmit_command[PARSER_MAX_LINE_SIZE];
-  if (resubmit_after_max_hours)
-    parser_get_param_string(params, "Restarts:resubmit_command",
-                            resubmit_command);
-
   /* If restarting, look for the restart files. */
   if (restart) {
 
@@ -987,13 +968,19 @@ int main(int argc, char *argv[]) {
 
     /* And initialize the engine with the space and policies. */
     engine_config(/*restart=*/1, /*fof=*/0, &e, params, nr_nodes, myrank,
-                  nr_threads, nr_pool_threads, with_aff, talking, restart_file,
-                  &reparttype);
+                  nr_threads, nr_pool_threads, with_aff, talking, restart_dir,
+                  restart_file, &reparttype);
 
     /* Check if we are already done when given steps on the command-line. */
     if (e.step >= nsteps && nsteps > 0)
       error("Not restarting, already completed %d steps", e.step);
 
+    /* If we are restarting at the very end of a run, just build the tree and
+     * prepare to dump.
+     * The main simulation loop below (where rebuild normally happens) won't be
+     * executed. */
+    if (engine_is_done(&e)) space_rebuild(e.s, /*repartitioned=*/0, e.verbose);
+
   } else {
 
     /* Prepare and verify the selection of outputs */
@@ -1521,8 +1508,8 @@ int main(int argc, char *argv[]) {
                 &chemistry, &extra_io_props, &fof_properties, &los_properties,
                 &lightcone_array_properties, &ics_metadata);
     engine_config(/*restart=*/0, /*fof=*/0, &e, params, nr_nodes, myrank,
-                  nr_threads, nr_pool_threads, with_aff, talking, restart_file,
-                  &reparttype);
+                  nr_threads, nr_pool_threads, with_aff, talking, restart_dir,
+                  restart_file, &reparttype);
 
     /* Compute some stats for the star formation */
     if (with_star_formation) {
@@ -1615,7 +1602,7 @@ int main(int argc, char *argv[]) {
     if (!e.output_list_stats) engine_print_stats(&e);
 
     /* Is there a dump before the end of the first time-step? */
-    engine_check_for_dumps(&e);
+    engine_io(&e);
   }
 
   /* Legend */
@@ -1687,7 +1674,7 @@ int main(int argc, char *argv[]) {
 
   /* Main simulation loop */
   /* ==================== */
-  int force_stop = 0, resubmit = 0;
+  int force_stop = 0;
   for (int j = 0; !engine_is_done(&e) && e.step - 1 != nsteps && !force_stop;
        j++) {
 
@@ -1695,30 +1682,15 @@ int main(int argc, char *argv[]) {
     timers_reset_all();
 
     /* Take a step. */
-    engine_step(&e);
+    force_stop = engine_step(&e);
 
     /* Print the timers. */
     if (with_verbose_timers) timers_print(e.step);
 
-    /* Every so often allow the user to stop the application and dump the
-     * restart files. */
-    if (j % restart_stop_steps == 0) {
-      force_stop = restart_stop_now(restart_dir, 0);
-      if (myrank == 0 && force_stop)
-        message("Forcing application exit, dumping restart files...");
-    }
-
-    /* Did we exceed the maximal runtime? */
-    if (e.runtime > restart_max_hours_runtime) {
-      force_stop = 1;
-      message("Runtime limit reached, dumping restart files...");
-      if (resubmit_after_max_hours) resubmit = 1;
-    }
-
-    /* Also if using nsteps to exit, will not have saved any restarts on exit,
-     * make sure we do that (useful in testing only). */
-    if (force_stop || (e.restart_onexit && e.step - 1 == nsteps))
-      engine_dump_restarts(&e, 0, 1);
+    /* Shall we write some check-point files?
+     * Note that this was already done by engine_step() if force_stop is set */
+    if (e.restart_onexit && e.step - 1 == nsteps && !force_stop)
+      engine_dump_restarts(&e, /*drifted=*/0, /*force=*/1);
 
     /* Dump the task data using the given frequency. */
     if (dump_tasks && (dump_tasks == 1 || j % dump_tasks == 1)) {
@@ -1740,6 +1712,8 @@ int main(int argc, char *argv[]) {
     }
 #endif
 
+    space_write_ghost_stats(e.s, j + 1);
+
     /* Dump memory use report if collected. */
 #ifdef SWIFT_MEMUSE_REPORTS
     {
@@ -1762,7 +1736,7 @@ int main(int argc, char *argv[]) {
                j + 1);
       mpiuse_log_dump(dumpfile, e.tic_step);
     }
-#endif  // WITH_MPI
+#endif
 
 #ifdef SWIFT_DEBUG_THREADPOOL
     /* Dump the task data using the given frequency. */
@@ -1778,7 +1752,7 @@ int main(int argc, char *argv[]) {
     } else {
       threadpool_reset_log(&e.threadpool);
     }
-#endif  // SWIFT_DEBUG_THREADPOOL
+#endif
   }
 
   /* Write final time information */
@@ -1896,9 +1870,9 @@ int main(int argc, char *argv[]) {
   if (myrank == 0) force_stop = restart_stop_now(restart_dir, 1);
 
   /* Did we want to run a re-submission command just before dying? */
-  if (myrank == 0 && resubmit) {
+  if (myrank == 0 && e.resubmit) {
     message("Running the resubmission command:");
-    restart_resubmit(resubmit_command);
+    restart_resubmit(e.resubmit_command);
     fflush(stdout);
     fflush(stderr);
     message("resubmission command completed.");
diff --git a/swift_fof.c b/swift_fof.c
index 3791b78d881175dc3c86ecdb063ea46ccee0fb6b..dfab9527b44f074f9eb14bf0647781264e1d1232 100644
--- a/swift_fof.c
+++ b/swift_fof.c
@@ -23,7 +23,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <errno.h>
@@ -664,7 +664,8 @@ int main(int argc, char *argv[]) {
       /*extra_io_props=*/NULL, &fof_properties, /*los_properties=*/NULL,
       /*lightcone_properties=*/NULL, &ics_metadata);
   engine_config(/*restart=*/0, /*fof=*/1, &e, params, nr_nodes, myrank,
-                nr_threads, nr_threads, with_aff, talking, NULL, &reparttype);
+                nr_threads, nr_threads, with_aff, talking, NULL, NULL,
+                &reparttype);
 
   /* Get some info to the user. */
   if (myrank == 0) {
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e385bd81940ac913f236b63024a5614c8088558f..53fce13c93487429a517e960e958b08ff6921c77 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -32,7 +32,7 @@ TESTS = testGreetings testMaths testReading.sh testKernel testKernelLongGrav \
         testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \
 	testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \
 	testPotentialPair testEOS testUtilities testSelectOutput.sh \
-	testCbrt testCosmology testOutputList testFormat.sh \
+	testCbrt testCosmology testRandomCone testOutputList testFormat.sh \
 	test27cellsStars.sh test27cellsStarsPerturbed.sh testHydroMPIrules \
         testAtomic testGravitySpeed testNeutrinoCosmology.sh testNeutrinoFermiDirac \
 	testLog testDistance testTimeline
@@ -42,7 +42,7 @@ check_PROGRAMS = testGreetings testReading testTimeIntegration testKernelLongGra
 		 testActivePair test27cells test27cells_subset test125cells testParser \
                  testKernel testFFT testInteractions testMaths testRandom testExp \
                  testSymmetry testDistance testThreadpool testRandomSpacing testErfc \
-                 testAdiabaticIndex testRiemannExact testRiemannTRRS testRandomPoisson \
+                 testAdiabaticIndex testRiemannExact testRiemannTRRS testRandomPoisson testRandomCone \
                  testRiemannHLLC testMatrixInversion testDump testCSDS \
 		 testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \
 		 testGravityDerivatives testPotentialSelf testPotentialPair testEOS testUtilities \
@@ -67,6 +67,8 @@ testRandomPoisson_SOURCES = testRandomPoisson.c
 
 testRandomSpacing_SOURCES = testRandomSpacing.c
 
+testRandomCone_SOURCES = testRandomCone.c
+
 testReading_SOURCES = testReading.c
 
 testSelectOutput_SOURCES = testSelectOutput.c
diff --git a/tests/test125cells.c b/tests/test125cells.c
index 07d524462fa448287d27f8a73ff5797339e09471..5716625aab829b403b90755038267785697dcec7 100644
--- a/tests/test125cells.c
+++ b/tests/test125cells.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <fenv.h>
@@ -669,7 +669,7 @@ int main(int argc, char *argv[]) {
 
     /* First, sort stuff */
     for (int j = 0; j < 125; ++j)
-      runner_do_hydro_sort(&runner, cells[j], 0x1FFF, 0, 0);
+      runner_do_hydro_sort(&runner, cells[j], 0x1FFF, 0, 0, 0);
 
       /* Do the density calculation */
 
diff --git a/tests/test27cells.c b/tests/test27cells.c
index a95f5137a438070f6c70395047aff44719149ee8..24f9143ae4055500bec5c18bac1bc054a49ae8a6 100644
--- a/tests/test27cells.c
+++ b/tests/test27cells.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <fenv.h>
@@ -516,7 +516,8 @@ int main(int argc, char *argv[]) {
 
         runner_do_drift_part(&runner, cells[i * 9 + j * 3 + k], 0);
 
-        runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0);
+        runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0,
+                             0);
       }
     }
   }
diff --git a/tests/test27cellsStars.c b/tests/test27cellsStars.c
index 61975a133465dd2dab27897886ddfda0dcafc23e..7785cdaed65e1415626a8f997c15bed1500ad80e 100644
--- a/tests/test27cellsStars.c
+++ b/tests/test27cellsStars.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <fenv.h>
@@ -430,7 +430,8 @@ int main(int argc, char *argv[]) {
         runner_do_drift_part(&runner, cells[i * 9 + j * 3 + k], 0);
         runner_do_drift_spart(&runner, cells[i * 9 + j * 3 + k], 0);
 
-        runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0);
+        runner_do_hydro_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0,
+                             0);
         runner_do_stars_sort(&runner, cells[i * 9 + j * 3 + k], 0x1FFF, 0, 0);
       }
     }
diff --git a/tests/testActivePair.c b/tests/testActivePair.c
index 2548fef2ab33b0dec36407b8c3dd543c2dcb4b17..4e27bbdacba0b1112ca9018109b718985cc5d051 100644
--- a/tests/testActivePair.c
+++ b/tests/testActivePair.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>
@@ -262,7 +262,7 @@ void zero_particle_fields_force(struct cell *c, const struct cosmology *cosmo,
 #endif
 
     /* And prepare for a round of force tasks. */
-    hydro_prepare_force(p, xp, cosmo, hydro_props, 0.);
+    hydro_prepare_force(p, xp, cosmo, hydro_props, 0., 0.);
     hydro_reset_acceleration(p);
   }
 }
@@ -345,8 +345,8 @@ void test_pair_interactions(struct runner *runner, struct cell **ci,
                             interaction_func vec_interaction, init_func init,
                             finalise_func finalise) {
 
-  runner_do_hydro_sort(runner, *ci, 0x1FFF, 0, 0);
-  runner_do_hydro_sort(runner, *cj, 0x1FFF, 0, 0);
+  runner_do_hydro_sort(runner, *ci, 0x1FFF, 0, 0, 0);
+  runner_do_hydro_sort(runner, *cj, 0x1FFF, 0, 0, 0);
 
   /* Zero the fields */
   init(*ci, runner->e->cosmology, runner->e->hydro_properties);
diff --git a/tests/testAdiabaticIndex.c b/tests/testAdiabaticIndex.c
index 6d754a06feb4cc640d2819014dcb4b75673c8261..2255d11fea0ecc74a7a4d7c2c17ee0ed3cadb659 100644
--- a/tests/testAdiabaticIndex.c
+++ b/tests/testAdiabaticIndex.c
@@ -16,9 +16,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "swift.h"
 
+/* System includes. */
 #include <fenv.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/tests/testAtomic.c b/tests/testAtomic.c
index 462d71d04f92fb674619206daeea7c870bbd2fc6..5441aebdecbd120451b70daec08fbfde58596ba0 100644
--- a/tests/testAtomic.c
+++ b/tests/testAtomic.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard includes. */
 #include <fenv.h>
diff --git a/tests/testCSDS.c b/tests/testCSDS.c
index 452237ffd3c7d5c23f767688d9e5976571cebc59..a055a96f83aa23f089423e51ab684c2b612f13ce 100644
--- a/tests/testCSDS.c
+++ b/tests/testCSDS.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #if defined(HAVE_POSIX_FALLOCATE) && \
     defined(WITH_CSDS) /* Are we on a sensible platform? */
diff --git a/tests/testCbrt.c b/tests/testCbrt.c
index fc89ea1bda7f68cb2b606eee1c80d6a8c1eee812..431753f2b6384d9b45aa7b501374ba24d8a245a2 100644
--- a/tests/testCbrt.c
+++ b/tests/testCbrt.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Standard includes. */
 #include <fenv.h>
diff --git a/tests/testComovingCooling.c b/tests/testComovingCooling.c
index c9ac39b3f3bd9c61e636a9aa88af0d9c2043d383..9189e718844b89f731614f1e16ce59363c89e16d 100644
--- a/tests/testComovingCooling.c
+++ b/tests/testComovingCooling.c
@@ -16,7 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "swift.h"
diff --git a/tests/testCooling.c b/tests/testCooling.c
index 55cd665b188e6a798f1807c53c3072817800a888..62a63e4ee07c0616f009994203ea94865c380425 100644
--- a/tests/testCooling.c
+++ b/tests/testCooling.c
@@ -16,7 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "swift.h"
diff --git a/tests/testCosmology.c b/tests/testCosmology.c
index 4b1ae40f0436c8fd29cd49e415c611c573ba149c..4c251bf07aca0b359f51361570885083c7b8877d 100644
--- a/tests/testCosmology.c
+++ b/tests/testCosmology.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Some standard headers. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "swift.h"
diff --git a/tests/testDistance.c b/tests/testDistance.c
index d141079b79c5035ee26ab4c63e3db3bf66c66011..91b2985a0a8121538a6b92015366766ac5301f93 100644
--- a/tests/testDistance.c
+++ b/tests/testDistance.c
@@ -16,9 +16,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "swift.h"
 
+/* System includes. */
 #include <fenv.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/tests/testDump.c b/tests/testDump.c
index bb5ec1a1d0ef329c3199568a16d3c5cab379bb37..67a31725c83e826bd9f4257a885ff24b9c839f96 100644
--- a/tests/testDump.c
+++ b/tests/testDump.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 #ifdef HAVE_POSIX_FALLOCATE /* Are we on a sensible platform? */
 #ifdef WITH_CSDS
diff --git a/tests/testEOS.c b/tests/testEOS.c
index 8ee3d6aab7d3192cef2dd86853ef4f98f38337de..db6fa4fbef8252245c0c38677dfe47a2ea130cca 100644
--- a/tests/testEOS.c
+++ b/tests/testEOS.c
@@ -20,7 +20,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <fenv.h>
diff --git a/tests/testErfc.c b/tests/testErfc.c
index 8506e3afd561c39eceeb2fac7fb5858dd65dbdb7..548558eaa4fe480e6190d7dd772feaf9f590c95d 100644
--- a/tests/testErfc.c
+++ b/tests/testErfc.c
@@ -17,7 +17,9 @@
  *
  ******************************************************************************/
 
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "swift.h"
 
 /* Standard includes */
diff --git a/tests/testExp.c b/tests/testExp.c
index 3f48736dc7d4ff171458828ebfd42207ea6b865d..7362eff2c9fbb05bdf00fbc92ec553ecf80a20eb 100644
--- a/tests/testExp.c
+++ b/tests/testExp.c
@@ -17,7 +17,9 @@
  *
  ******************************************************************************/
 
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "swift.h"
 
 /* Standard includes */
diff --git a/tests/testFFT.c b/tests/testFFT.c
index 679f819dedf45d5ff40734656a47b389fc175198..506405ff8c688fddf02c79960f2a692611912cb7 100644
--- a/tests/testFFT.c
+++ b/tests/testFFT.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Some standard headers. */
-#include "../config.h"
+#include <config.h>
 
 // MATTHIEU fix this test
 #if 1 || !defined(HAVE_FFTW)
diff --git a/tests/testGravityDerivatives.c b/tests/testGravityDerivatives.c
index 4ab338ea00c89016ba50f74deb23131c5d6991c3..e7776baf8ca02dd6d4a7df908195dff4367f3a70 100644
--- a/tests/testGravityDerivatives.c
+++ b/tests/testGravityDerivatives.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/tests/testGravitySpeed.c b/tests/testGravitySpeed.c
index b2a487e49cd2abb1dabf647f8a1a5c5c1aaf2478..5212a12f1f81fdedcc80534cf891be314c9c769b 100644
--- a/tests/testGravitySpeed.c
+++ b/tests/testGravitySpeed.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/tests/testHashmap.c b/tests/testHashmap.c
index af299732318744f2924a364ce4947482a23d3034..4feff1e63121567d7faea70a4265f36e066f9a30 100644
--- a/tests/testHashmap.c
+++ b/tests/testHashmap.c
@@ -18,8 +18,9 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <fenv.h>
 
 /* Local headers. */
diff --git a/tests/testHydroMPIrules.c b/tests/testHydroMPIrules.c
index f5946108bffc587b101c9fd9459d8867e91cc814..799d86adeec763af781c1316a1771224a31629ba 100644
--- a/tests/testHydroMPIrules.c
+++ b/tests/testHydroMPIrules.c
@@ -16,9 +16,12 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "swift.h"
 
+/* System includes. */
 #include <fenv.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/tests/testInteractions.c b/tests/testInteractions.c
index fa9c22fef955c13c2919a92ff4f799e4f6587b25..a4d7ead66ad0ef94accb722d61a48795617182ea 100644
--- a/tests/testInteractions.c
+++ b/tests/testInteractions.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/tests/testKernel.c b/tests/testKernel.c
index 391625214a6056393f3054eefbcc502f165a2b24..3de191598d898cc6c0df53c38b25daa6bb4bfa76 100644
--- a/tests/testKernel.c
+++ b/tests/testKernel.c
@@ -17,11 +17,14 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "align.h"
 #include "kernel_hydro.h"
 #include "vector.h"
 
+/* System includes. */
 #include <fenv.h>
 #include <stdlib.h>
 #include <strings.h>
diff --git a/tests/testKernelLongGrav.c b/tests/testKernelLongGrav.c
index b2e0207f7b3e1252cf9a930cbe82905db0ff4362..3b8c3d2711e209f7f0553fead74330b67e433afd 100644
--- a/tests/testKernelLongGrav.c
+++ b/tests/testKernelLongGrav.c
@@ -17,7 +17,9 @@
  *
  ******************************************************************************/
 
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "exp10.h"
 #include "swift.h"
 
diff --git a/tests/testLog.c b/tests/testLog.c
index 29940db6f592c645909b4359ed22c8a95873bf8b..6dc1c12eb9cc09fd954d9b6819d11ad54272f472 100644
--- a/tests/testLog.c
+++ b/tests/testLog.c
@@ -17,7 +17,9 @@
  *
  ******************************************************************************/
 
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "exp10.h"
 #include "log.h"
 #include "swift.h"
diff --git a/tests/testMaths.c b/tests/testMaths.c
index 0abfb2eb14bac92764c3316eeaa77ed04d69c6fe..cc3432ec26761c8d766a0dee3b85664330a68e95 100644
--- a/tests/testMaths.c
+++ b/tests/testMaths.c
@@ -17,10 +17,13 @@
  *
  ******************************************************************************/
 
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "approx_math.h"
 #include "vector.h"
 
+/* System includes. */
 #include <math.h>
 #include <stdio.h>
 
diff --git a/tests/testMatrixInversion.c b/tests/testMatrixInversion.c
index 504ce15d25f138b5933d984e4aebc42a6c386796..179b2fa688d5e3c2884bf0b53e8eabfe2fc2ea90 100644
--- a/tests/testMatrixInversion.c
+++ b/tests/testMatrixInversion.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 <stdlib.h>
diff --git a/tests/testNeutrinoCosmology.c b/tests/testNeutrinoCosmology.c
index cfec9acff88ba33e3378aa632212e03f781fcc1b..88c8b219c3a5727d36c36734355dd621581281f9 100644
--- a/tests/testNeutrinoCosmology.c
+++ b/tests/testNeutrinoCosmology.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <fenv.h>
diff --git a/tests/testNeutrinoFermiDirac.c b/tests/testNeutrinoFermiDirac.c
index c7c3dc8c722a8ed4907680ce1bbf6f80855aed96..5cf1ad6070e87e7625e621e57d698f6b56eaf14e 100644
--- a/tests/testNeutrinoFermiDirac.c
+++ b/tests/testNeutrinoFermiDirac.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers */
 #include <fenv.h>
diff --git a/tests/testOutputList.c b/tests/testOutputList.c
index 461a46e6def6e4cfabec0653bd263e665ad000fb..3a7300e7bc35c64730e7a406df8fe1938d01e40f 100644
--- a/tests/testOutputList.c
+++ b/tests/testOutputList.c
@@ -17,7 +17,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "swift.h"
diff --git a/tests/testPeriodicBC.c b/tests/testPeriodicBC.c
index 60821fc54b8a134b1d8b49a3c8e2e1ebf09d0f52..ed50bf380a567d79d8b13b248cd36ccd124d788e 100644
--- a/tests/testPeriodicBC.c
+++ b/tests/testPeriodicBC.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <fenv.h>
@@ -522,7 +522,7 @@ int main(int argc, char *argv[]) {
         runner_do_drift_part(runner, cells[i * (dim * dim) + j * dim + k], 0);
 
         runner_do_hydro_sort(runner, cells[i * (dim * dim) + j * dim + k],
-                             0x1FFF, 0, 0);
+                             0x1FFF, 0, 0, 0);
       }
     }
   }
diff --git a/tests/testPotentialPair.c b/tests/testPotentialPair.c
index 7f8c33f91ea0d163c832575a0ae12d264074d6d4..252c46ec86129503cd16b4302cae74d3f0dbca61 100644
--- a/tests/testPotentialPair.c
+++ b/tests/testPotentialPair.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/tests/testPotentialSelf.c b/tests/testPotentialSelf.c
index a2edb49c5b35edc4f4cd1a45b59b5ab51316d070..e325d0dfa4df5158984dc308a4bc4adfb9b157f9 100644
--- a/tests/testPotentialSelf.c
+++ b/tests/testPotentialSelf.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/tests/testRandom.c b/tests/testRandom.c
index d3ab4301e8391eb0c36a2115eda9c3afec51fada..1cc7fb151ac8f35bd2dd49c349f2a086e760fcb2 100644
--- a/tests/testRandom.c
+++ b/tests/testRandom.c
@@ -19,8 +19,9 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <fenv.h>
 
 /* Local headers. */
diff --git a/tests/testRandomCone.c b/tests/testRandomCone.c
new file mode 100644
index 0000000000000000000000000000000000000000..2393362bad991a2fa93aecda15a9d326b9ed82de
--- /dev/null
+++ b/tests/testRandomCone.c
@@ -0,0 +1,217 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (C) 2022 Filip Husko (filip.husko@durham.ac.uk).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#include <config.h>
+
+/* Local headers. */
+#include "swift.h"
+
+/* Number of different maximal opening angles to test between 0 and pi/2. */
+const int N_cos = 20;
+
+/* Number of random cones to draw for each opening angle. */
+const int N_cos_cone = 30;
+
+/* Cubical grid size when checking the cone function along axes of the Cartesian
+ * grid. Should be at least 2. */
+const int N_cube = 5;
+
+/**
+ * @brief Test to check whether the function that generates random directions
+ * within a cone actually generates vectors only within that cone. It also tests
+ * whether they are uniformly distributed in solid angle.
+ *
+ * @param id_bh The ID of a black hole particle around whose spin vector a given
+ * cone is drawn.
+ * @param ti_current Current time of the simulation.
+ * @param type Random number type used.
+ * @param opening_angle The opening angle of the cone (in radians).
+ * @param unit_vector The vector that defines where the cone is pointing.
+ * @param N_test How many random directions to draw within the cone.
+ * @param N_bins How many bins to distribute these directions into when testing
+ * the uniformity of the distribution.
+ * @param tolerance The tolerance of each bin relative to the expected value.
+ */
+void test_cone(int64_t id_bh, const integertime_t ti_current,
+               const enum random_number_type type, double opening_angle,
+               float unit_vector[3], const int N_test, const int N_bins,
+               const double tolerance) {
+
+  /* Compute cosine that corresponds to the maximum opening angle */
+  const double cos_theta_max = cos(opening_angle);
+
+  /* Initialize an array that will hold a random vector every step */
+  float rand_vector[3];
+
+  /* Initialize an array that will hold the binned number of drawn cosines,
+     i.e. this is the probability density function that we wish to test. */
+  double binned_cosines[N_bins];
+  for (int j = 0; j < N_bins; ++j) {
+    binned_cosines[j] = 0.;
+  }
+
+  for (int k = 0; k < N_test; ++k) {
+
+    /* Generate random ids. */
+    const long long id_p = rand() * (1LL << 31) + rand();
+
+    /* Generate a random unit vector within a cone around unit_vector  */
+    random_direction_in_cone(id_p, id_bh, ti_current, type, opening_angle,
+                             unit_vector, rand_vector);
+
+    /* Check that this vector is actually within the cone we want  */
+    const double cos_rand_unit = rand_vector[0] * unit_vector[0] +
+                                 rand_vector[1] * unit_vector[1] +
+                                 rand_vector[2] * unit_vector[2];
+    if (cos_rand_unit < 0.99999 * cos_theta_max) {
+      printf("Cos_opening_angle is: %f, Random cos is: %f\n", cos_theta_max,
+             cos_rand_unit);
+      error("Generated random unit vector is outside cone.");
+    }
+
+    /* Add the unit vector to the probability density function array. The solid
+     * angle subtended by some angle theta grows as (1-cos(theta)). Furthermore,
+     * we are limited to the spherical cap defined by the angles [0, theta_max].
+     * Therefore the variable which we expect to be uniformly distributed is (1
+     * - cos(theta)) / (1 - cos(theta_max)). */
+    double uniform_variable = (1. - cos_rand_unit) / (1 - cos_theta_max);
+    for (int j = 0; j < N_bins; ++j) {
+      if ((uniform_variable > (double)j / (double)N_bins) &&
+          (uniform_variable < (double)(j + 1) / (double)N_bins)) {
+        binned_cosines[j] = binned_cosines[j] + 1. / (double)N_test;
+      }
+    }
+  }
+
+  /* Check whether the binned quantity is really uniformly distributed. If it
+   * is, the density (value) of each bin should be 1/N_bin. */
+  for (int j = 0; j < N_bins; ++j) {
+    if ((binned_cosines[j] < (1. - tolerance) / (double)N_bins) ||
+        (binned_cosines[j] > (1. + tolerance) / (double)N_bins)) {
+      error(
+          "Generated distribution of random unit vectors within a cone exceeds "
+          "the limit imposed by the tolerance.");
+    }
+  }
+}
+
+int main(int argc, char *argv[]) {
+
+  /* Test the random-vector-in-cone function, for different values of opening
+   * angle from 0 to pi/2 (in radians). For each of these opening angles we draw
+   * some number of cones, and test whether each of those has a uniform
+   * distribution of randomly drawn vectors within it.*/
+  for (int i = 1; i < N_cos; ++i) {
+
+    /* Opening angle to use */
+    const double opening_angle = 0.5 * M_PI * (double)i / (double)N_cos;
+
+    /* Do the test for N_cos_cone cones with this opening angle */
+    for (int l = 0; l < N_cos_cone; ++l) {
+
+      /* Generate an id for the bh and a time. We do this for every opening
+       * angle and every cone. */
+      const long long id_bh = rand() * (1LL << 31) + rand();
+      const integertime_t ti_current = rand() * (1LL << 31) + rand();
+
+      /* Generate a random unit vector that defines a cone, along with the
+       * opening angle. */
+      float unit_vector[3];
+      const double cos_unit =
+          random_unit_interval(id_bh, ti_current, random_number_BH_kick);
+      const double sin_unit = sqrtf(max(0., (1. - cos_unit) * (1. + cos_unit)));
+      const double phi_unit = random_unit_interval(id_bh * id_bh, ti_current,
+                                                   random_number_BH_kick);
+      unit_vector[0] = sin_unit * cos(phi_unit);
+      unit_vector[1] = sin_unit * sin(phi_unit);
+      unit_vector[2] = cos_unit;
+
+      /* Do the test. */
+      test_cone(id_bh, ti_current, random_number_BH_kick, opening_angle,
+                unit_vector, 100000, 10, 0.1);
+    }
+  }
+
+  /* Repeat the same test but with a larger number of random directions and
+   * bins, but for just one opening angle and one randomly generated cone */
+  const double opening_angle_0 = 0.2;
+
+  /* Generate an id for the bh and a time. We do this for every opening
+   * angle and every cone. */
+  const long long id_bh_0 = rand() * (1LL << 31) + rand();
+  const integertime_t ti_current_0 = rand() * (1LL << 31) + rand();
+
+  /* Generate a random unit vector that defines a cone, along with the
+   * opening angle. */
+  float unit_vector_0[3];
+  const double cos_unit =
+      random_unit_interval(id_bh_0, ti_current_0, random_number_BH_kick);
+  const double sin_unit = sqrtf(max(0., (1. - cos_unit) * (1. + cos_unit)));
+  const double phi_unit = random_unit_interval(id_bh_0 * id_bh_0, ti_current_0,
+                                               random_number_BH_kick);
+  unit_vector_0[0] = sin_unit * cos(phi_unit);
+  unit_vector_0[1] = sin_unit * sin(phi_unit);
+  unit_vector_0[2] = cos_unit;
+
+  /* Do the test. */
+  test_cone(id_bh_0, ti_current_0, random_number_BH_kick, opening_angle_0,
+            unit_vector_0, 100000000, 100, 0.01);
+
+  /* We now repeat the same process, but we do not generate random unit vectors
+   * to define the cones. Instead, we sample unit vectors along the grid
+   * [-N_cube, -N_cube + 1, ..., N_cube -1, N_cube] ^ 3. This can be, e.g. [-2,
+   * -1, 0, 1, 2] ^ 3 (N_cube should be at least 2). This makes sure that the
+   * function that generates random unit vectors is well-defined if the unit
+   * vectors that define the cones point along any of the Cartesian axes, or if
+   * any of their components are equal. Here we use a fixed opening angle of
+   * 0.1, since we assume that the earlier test passing means that the function
+   * correctly does what it should for all opening angles. */
+  const double opening_angle = 0.1;
+  for (int x = -N_cube; x < N_cube + 1; ++x) {
+    for (int y = -N_cube; y < N_cube + 1; ++y) {
+      for (int z = -N_cube; z < N_cube + 1; ++z) {
+
+        /* Create our unit vector on this point of the grid */
+        float unit_vector[3] = {(float)x, (float)y, (float)z};
+        float unit_vector_norm =
+            sqrtf((float)(x * x) + (float)(y * y) + (float)(z * z));
+
+        /* Only do the test if the norm is >0, i.e. if we are not at the origin
+         * of the coordinate frame. */
+        if (unit_vector_norm > 0) {
+
+          /* Generate an id for the bh and a time. We do this for every opening
+           * angle and every cone. */
+          const long long id_bh = rand() * (1LL << 31) + rand();
+          const integertime_t ti_current = rand() * (1LL << 31) + rand();
+
+          /* Normalize the unit vector. */
+          unit_vector[0] = unit_vector[0] / unit_vector_norm;
+          unit_vector[1] = unit_vector[1] / unit_vector_norm;
+          unit_vector[2] = unit_vector[2] / unit_vector_norm;
+
+          /* Do the test. */
+          test_cone(id_bh, ti_current, random_number_BH_kick, opening_angle,
+                    unit_vector, 100000, 10, 0.1);
+        }
+      }
+    }
+  }
+
+  return 0;
+}
diff --git a/tests/testRandomPoisson.c b/tests/testRandomPoisson.c
index 5075e5f25273599ecbda9d0879c087c11276ad0a..afd84304ef543e75991eb7d33b9b6183ea388931 100644
--- a/tests/testRandomPoisson.c
+++ b/tests/testRandomPoisson.c
@@ -18,8 +18,9 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <fenv.h>
 
 /* Local headers. */
diff --git a/tests/testRandomSpacing.c b/tests/testRandomSpacing.c
index 59a4f2eb7f2c9bc5a59bd79c1d5dc3ade0811620..cb4dac9a60ef8dec7d5226242555017e96b8ea84 100644
--- a/tests/testRandomSpacing.c
+++ b/tests/testRandomSpacing.c
@@ -18,8 +18,9 @@
  ******************************************************************************/
 
 /* Config parameters. */
-#include "../config.h"
+#include <config.h>
 
+/* System includes. */
 #include <fenv.h>
 
 /* Local headers. */
diff --git a/tests/testReading.c b/tests/testReading.c
index ab0da0a3ce9d3c96cb60b0652538c6d8f90162d3..0fa06b790713e14c15605d2cd2597e8213390194 100644
--- a/tests/testReading.c
+++ b/tests/testReading.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Some standard headers. */
-#include "../config.h"
+#include <config.h>
 
 /* Some standard headers. */
 #include <stdlib.h>
diff --git a/tests/testRiemannExact.c b/tests/testRiemannExact.c
index aa630a76f5f82bd87dc15f00d7d90dff2405e749..8892ace011482983b0fc2c5a525f97359b8a55d7 100644
--- a/tests/testRiemannExact.c
+++ b/tests/testRiemannExact.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/tests/testRiemannHLLC.c b/tests/testRiemannHLLC.c
index e3be6fb8301ba1ce602a55e4fa80a62fa70c3d9c..2683ce77330818790f0e0d1d4d98e5a540a0e7ec 100644
--- a/tests/testRiemannHLLC.c
+++ b/tests/testRiemannHLLC.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/tests/testRiemannTRRS.c b/tests/testRiemannTRRS.c
index e975230c61cd58ad1a077e9b66949044cb7708da..16956bec0fac0b645a577c8e4869450a21eff6d8 100644
--- a/tests/testRiemannTRRS.c
+++ b/tests/testRiemannTRRS.c
@@ -16,7 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include <string.h>
diff --git a/tests/testSelectOutput.c b/tests/testSelectOutput.c
index e503c54149c79fee90175ffaa00fd412ade260d3..b6a73c54828be921a2907ab9ebd21488f4f245da 100644
--- a/tests/testSelectOutput.c
+++ b/tests/testSelectOutput.c
@@ -18,7 +18,7 @@
  ******************************************************************************/
 
 /* Some standard headers. */
-#include "../config.h"
+#include <config.h>
 
 /* Includes. */
 #include "swift.h"
diff --git a/tests/testSymmetry.c b/tests/testSymmetry.c
index 0a3cf762d707ad39719e7e00a491d05150e6d57d..a1fa8ed7bca97bb91d027fb52fd90920fcd78536 100644
--- a/tests/testSymmetry.c
+++ b/tests/testSymmetry.c
@@ -16,10 +16,13 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "swift.h"
 #include "timestep_limiter_iact.h"
 
+/* System includes. */
 #include <fenv.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/tests/testThreadpool.c b/tests/testThreadpool.c
index d06d163ce928e6a16a2f37cf39325e4fc7e0da30..a0e5d8e5129bd3e9ad110c2eafc1635409943a5f 100644
--- a/tests/testThreadpool.c
+++ b/tests/testThreadpool.c
@@ -18,7 +18,7 @@
  *
  ******************************************************************************/
 
-#include "../config.h"
+#include <config.h>
 
 // Standard includes.
 #include <stdio.h>
diff --git a/tests/testTimeline.c b/tests/testTimeline.c
index e7f5a8d150f035c62668876176da72d99216524c..51b8d2b51ae49a99e204e89b254d738f48a33f59 100644
--- a/tests/testTimeline.c
+++ b/tests/testTimeline.c
@@ -16,7 +16,9 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
+
+/* Local includes. */
 #include "timeline.h"
 #include "tools.h"
 
diff --git a/tests/testVoronoi2D.c b/tests/testVoronoi2D.c
index 57d90b64abeef9beae6abe29bb9fb22f39e96aaa..4708f7bdd44aea6b4ce9d022a5f5a0fc2e65f8c2 100644
--- a/tests/testVoronoi2D.c
+++ b/tests/testVoronoi2D.c
@@ -16,7 +16,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  ******************************************************************************/
-#include "../config.h"
+#include <config.h>
 
 /* Local headers. */
 #include "hydro/Shadowswift/voronoi2d_algorithm.h"
diff --git a/tests/testVoronoi3D.c b/tests/testVoronoi3D.c
index 5e0288fa9b3e13e0c6a6fb13db202e0f73f29a5b..de3eaf2d50782a20f526229484e2b255ec24dd52 100644
--- a/tests/testVoronoi3D.c
+++ b/tests/testVoronoi3D.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 <stdlib.h>
diff --git a/tools/plot_ghost_stats.py b/tools/plot_ghost_stats.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e88043076aa5b9011ea5e0b7d7c9cf7f0ddfe25
--- /dev/null
+++ b/tools/plot_ghost_stats.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python
+#
+# Usage:
+#  python plot_ghost_stats.py ghost_stats_file_1 [ghost_stats_file_2...]
+#    output_image.png
+#
+# Description:
+#  Plot the ghost statistics contained in the given input files. For each
+#  particle type (hydro, stars, black holes), three plots are generated:
+#   1. A histogram of the number of active particles as a function of iteration
+#      number.
+#   2. The distribution of h values for active particles as a function of the
+#      iteration number, compared with the final (converged) values.
+#   3. The number of particles that are treated as having no neighbours as a
+#      function of the iteration number.
+
+import numpy as np
+import matplotlib
+
+matplotlib.use("Agg")
+import matplotlib.pyplot as pl
+import argparse
+
+argparser = argparse.ArgumentParser()
+argparser.add_argument("input", nargs="+", action="store")
+argparser.add_argument("output", action="store")
+argparser.add_argument("--label", "-l", action="store", default=None)
+args = argparser.parse_args()
+
+data = None
+nbin = None
+nval = None
+for input in args.input:
+    # read the header block to figure out how many blocks per particle type
+    # we have
+    with open(input, "r") as ifile:
+        header = ifile.readlines()[:15]
+        this_nbin = int(header[3].split()[-1])
+        this_nval = int(header[4].split()[-1])
+    if nbin is None:
+        nbin = this_nbin
+    else:
+        if nbin != this_nbin:
+            print("Number of bins in files does not match!")
+            exit(1)
+    if nval is None:
+        nval = this_nval
+    else:
+        if nval != this_nval:
+            print("Number of values per bin in files does not match!")
+            exit(1)
+
+    tdata = np.loadtxt(input, ndmin=2)
+    if len(tdata) > 0:
+        if data is None:
+            data = np.array(tdata)
+        else:
+            data = np.append(data, tdata, axis=0)
+
+# omit meaningless first column
+data = data[:, 1:]
+
+hdata = data[:, : nval * nbin]
+sdata = data[:, nval * nbin : 2 * nval * nbin]
+bdata = data[:, 2 * nval * nbin :]
+
+
+def get_total_stats(pdata):
+    counts = pdata[:, ::nval]
+    ncell = (counts[:, 0] > 0).sum()
+    if ncell == 0:
+        return None, None, None, None, None, None, None, 0
+    nongb_counts = np.sum(pdata[:, 1::nval], axis=0)
+    hmin = np.min(pdata[:, 2::nval], axis=0, initial=np.inf, where=counts > 0)
+    hmax = np.max(pdata[:, 3::nval], axis=0, initial=0.0, where=counts > 0)
+    hsum = np.sum(pdata[:, 4::nval], axis=0, where=counts > 0)
+    hsum2 = np.sum(pdata[:, 5::nval], axis=0, where=counts > 0)
+    counts = counts.sum(axis=0)
+    filt = counts > 0
+    hmean = hsum
+    hmean[filt] /= counts[filt]
+    hmean2 = hsum2
+    hmean2[filt] /= counts[filt]
+    hstd = np.sqrt(hmean2 - hmean ** 2)
+    bins = np.arange(hmean.shape[0])
+    return (
+        bins[filt],
+        counts[filt],
+        nongb_counts[filt],
+        hmin[filt],
+        hmax[filt],
+        hmean[filt],
+        hstd[filt],
+        ncell,
+    )
+
+
+def no_values(ax):
+    ax.text(0.5, 0.5, "No values", horizontalalignment="center", transform=ax.transAxes)
+    return
+
+
+hbins, hcounts, hno_ngb, hhmin, hhmax, hhmean, hhstd, hncell = get_total_stats(
+    hdata[:, : nval * (nbin - 1)]
+)
+htbins, htcounts, htno_ngb, hthmin, hthmax, hthmean, hthstd, htncell = get_total_stats(
+    hdata[:, nval * (nbin - 1) :]
+)
+sbins, scounts, sno_ngb, shmin, shmax, shmean, shstd, sncell = get_total_stats(
+    sdata[:, : nval * (nbin - 1)]
+)
+stbins, stcounts, stno_ngb, sthmin, sthmax, sthmean, sthstd, stncell = get_total_stats(
+    sdata[:, nval * (nbin - 1) :]
+)
+bbins, bcounts, bno_ngb, bhmin, bhmax, bhmean, bhstd, bncell = get_total_stats(
+    bdata[:, : nval * (nbin - 1)]
+)
+btbins, btcounts, btno_ngb, bthmin, bthmax, bthmean, bthstd, btncell = get_total_stats(
+    bdata[:, nval * (nbin - 1) :]
+)
+
+fig, ax = pl.subplots(3, 3, sharex=True, figsize=(10, 8))
+
+if hncell > 0:
+    ax[0][0].axhline(y=htcounts[0], linestyle="--", color="k")
+    ax[1][0].axhline(y=hthmin, linestyle=":", color="k")
+    ax[1][0].axhline(y=hthmean - hthstd, linestyle="--", color="k")
+    ax[1][0].axhline(y=hthmean, linestyle="-", color="k")
+    ax[1][0].axhline(y=hthmean + hthstd, linestyle="--", color="k")
+    ax[1][0].axhline(y=hthmax, linestyle=":", color="k")
+    ax[2][0].axhline(y=htno_ngb[0], linestyle="--", color="k")
+
+    ax[0][0].bar(hbins, hcounts, log=True)
+
+    ax[1][0].plot(hbins, hhmin, label="min")
+    ax[1][0].plot(hbins, hhmax, label="max")
+    ax[1][0].fill_between(hbins, hhmean - hhstd, hhmean + hhstd, color="C2", alpha=0.5)
+    ax[1][0].plot(hbins, hhmean, color="C2", label="mean")
+    ax[1][0].set_yscale("log")
+
+    if hno_ngb.sum() > 0:
+        ax[2][0].bar(hbins, hno_ngb, log=True)
+    else:
+        no_values(ax[2][0])
+else:
+    no_values(ax[0][0])
+    no_values(ax[1][0])
+    no_values(ax[2][0])
+
+if sncell > 0:
+    ax[0][1].axhline(y=stcounts[0], linestyle="--", color="k")
+    ax[1][1].axhline(y=sthmin, linestyle=":", color="k")
+    ax[1][1].axhline(y=sthmean - sthstd, linestyle="--", color="k")
+    ax[1][1].axhline(y=sthmean, linestyle="-", color="k")
+    ax[1][1].axhline(y=sthmean + sthstd, linestyle="--", color="k")
+    ax[1][1].axhline(y=sthmax, linestyle=":", color="k")
+    ax[2][1].axhline(y=stno_ngb[0], linestyle="--", color="k")
+
+    ax[0][1].bar(sbins, scounts, log=True)
+
+    ax[1][1].plot(sbins, shmin)
+    ax[1][1].plot(sbins, shmax)
+    ax[1][1].fill_between(sbins, shmean - shstd, shmean + shstd, color="C2", alpha=0.5)
+    ax[1][1].plot(sbins, shmean, color="C2")
+    ax[1][1].set_yscale("log")
+
+    if sno_ngb.sum() > 0:
+        ax[2][1].bar(sbins, sno_ngb, log=True)
+    else:
+        no_values(ax[2][1])
+else:
+    no_values(ax[0][1])
+    no_values(ax[1][1])
+    no_values(ax[2][1])
+
+if bncell > 0:
+    ax[0][2].axhline(y=btcounts[0], linestyle="--", color="k")
+    ax[1][2].axhline(y=bthmin, linestyle=":", color="k")
+    ax[1][2].axhline(y=bthmean - bthstd, linestyle="--", color="k")
+    ax[1][2].axhline(y=bthmean, linestyle="-", color="k")
+    ax[1][2].axhline(y=bthmean + bthstd, linestyle="--", color="k")
+    ax[1][2].axhline(y=bthmax, linestyle=":", color="k")
+    ax[2][2].axhline(y=btno_ngb[0], linestyle="--", color="k")
+
+    ax[0][2].bar(bbins, bcounts, log=True)
+
+    ax[1][2].plot(bbins, bhmin)
+    ax[1][2].plot(bbins, bhmax)
+    ax[1][2].fill_between(bbins, bhmean - bhstd, bhmean + bhstd, color="C2", alpha=0.5)
+    ax[1][2].plot(bbins, bhmean, color="C2")
+    ax[1][2].set_yscale("log")
+
+    if bno_ngb.sum() > 0:
+        ax[2][2].bar(bbins, bno_ngb, log=True)
+    else:
+        no_values(ax[2][2])
+else:
+    no_values(ax[0][2])
+    no_values(ax[1][2])
+    no_values(ax[2][2])
+
+ax[1][0].set_xlabel("iteration")
+ax[1][1].set_xlabel("iteration")
+ax[1][2].set_xlabel("iteration")
+
+ax[0][0].set_ylabel("count")
+ax[1][0].set_ylabel("h")
+ax[2][0].set_ylabel("no ngb count")
+
+ax[1][0].legend(loc="best")
+ax[1][1].plot([], [], "k-", label="converged value")
+ax[1][1].legend(loc="best")
+
+ax[0][0].set_xlim(-0.5, nbin - 1.5)
+ax[0][0].set_xticks(np.arange(nbin))
+ax[0][0].set_title("hydro")
+ax[0][1].set_title("stars")
+ax[0][2].set_title("black holes")
+ax[1][0].set_title("{0} cells".format(hncell))
+ax[1][1].set_title("{0} cells".format(sncell))
+ax[1][2].set_title("{0} cells".format(bncell))
+
+if not args.label is None:
+    pl.suptitle(args.label)
+else:
+    pl.suptitle(",".join(args.input))
+
+pl.tight_layout()
+pl.savefig(args.output, dpi=300)
diff --git a/tools/plot_task_dependencies.py b/tools/plot_task_dependencies.py
index 8a6bb7ced8d7a40e3d1adb772765ffe5e174c677..0c6ab887fc0d0d2034584d834005b4152a01baf5 100755
--- a/tools/plot_task_dependencies.py
+++ b/tools/plot_task_dependencies.py
@@ -402,16 +402,18 @@ def write_task(
     txt = "\t " + name + "["
 
     if not cell_has_active_task:
-        # give this precedence over implicit tasks.
+        # give this precedence over implicit tasks and MPI
         # If you're this deep in debugging trouble,
         # you will most likely know which tasks are
         # implicit.
         txt += "style=filled,fillcolor=lightpink2,"
+        if mpi:
+            txt += "shape=diamond,"
     else:
         if implicit:
             txt += "style=filled,fillcolor=grey90,"
-    if mpi:
-        txt += "shape=diamond,style=filled,fillcolor=azure,"
+        if mpi:
+            txt += "shape=diamond,style=filled,fillcolor=azure,"
     if with_levels:
         levelstr = ""
         if task_is_in_top:
diff --git a/tools/timed_functions.py b/tools/timed_functions.py
index ad1cc33e892159aa8cf2d9f58f37912f0984d2f6..d4ad20fae9d95219df6bf573b7154f6b49a985e2 100644
--- a/tools/timed_functions.py
+++ b/tools/timed_functions.py
@@ -37,6 +37,7 @@ labels = [
     ["engine_collect_end_of_step:", 0],
     ["engine_launch: \(tasks\)", 0],
     ["engine_launch: \(timesteps\)", 0],
+    ["engine_launch: \(cycles\)", 0],
     ["writing particle properties", 0],
     ["engine_repartition:", 0],
     ["engine_exchange_cells:", 1],