diff --git a/configure.ac b/configure.ac
index e730619ea17ce87df7b5c62225094e1d7d0e34db..58f25b1eedbab44f9e3f0ecbf157f6c394674f78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -962,7 +962,7 @@ esac
 #  Cooling function
 AC_ARG_WITH([cooling],
    [AS_HELP_STRING([--with-cooling=<function>],
-      [cooling function @<:@none, const-du, const-lambda, grackle default: none@:>@]
+      [cooling function @<:@none, const-du, const-lambda, EAGLE, grackle, grackle1, grackle2, grackle3 default: none@:>@]
    )],
    [with_cooling="$withval"],
    [with_cooling="none"]
@@ -979,7 +979,20 @@ case "$with_cooling" in
    ;;
    grackle)
       AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
+      AC_DEFINE([COOLING_GRACKLE_MODE], [0], [Grackle chemistry network, mode 0])
+   ;; 
+   grackle1)
+      AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
+      AC_DEFINE([COOLING_GRACKLE_MODE], [1], [Grackle chemistry network, mode 1])
    ;;
+   grackle2)
+      AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
+      AC_DEFINE([COOLING_GRACKLE_MODE], [2], [Grackle chemistry network, mode 2])
+   ;; 
+   grackle3)
+      AC_DEFINE([COOLING_GRACKLE], [1], [Cooling via the grackle library])
+      AC_DEFINE([COOLING_GRACKLE_MODE], [3], [Grackle chemistry network, mode 3])
+   ;; 
    EAGLE)
       AC_DEFINE([COOLING_EAGLE], [1], [Cooling following the EAGLE model])
    ;;
diff --git a/examples/CoolingBox/coolingBox.yml b/examples/CoolingBox/coolingBox.yml
index c2ada783a316a08ee729907ba69e9a66aebe6910..2bd2f19f6d78388ae638521f590255d410bc8697 100644
--- a/examples/CoolingBox/coolingBox.yml
+++ b/examples/CoolingBox/coolingBox.yml
@@ -42,11 +42,17 @@ LambdaCooling:
 
 # Cooling with Grackle 2.0
 GrackleCooling:
-  GrackleCloudyTable: ../CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
-  UVbackground: 0 # Enable or not the UV background
-  GrackleRedshift: 0 # Redshift to use (-1 means time based redshift)
-  GrackleHSShieldingDensityThreshold: 1.1708e-26 # self shielding threshold in internal system of units
-
+  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  WithUVbackground: 0 # Enable or not the UV background
+  Redshift: 0 # Redshift to use (-1 means time based redshift)
+  WithMetalCooling: 1 # Enable or not the metal cooling
+  ProvideVolumetricHeatingRates: 0 # User provide volumetric heating rates
+  ProvideSpecificHeatingRates: 0 # User provide specific heating rates
+  SelfShieldingMethod: 0 # Grackle (<= 3) or Gear self shielding method
+  OutputMode: 1 # Write in output corresponding primordial chemistry mode
+  MaxSteps: 1000
+  ConvergenceLimit: 1e-2
+  
 EAGLEChemistry:
   InitMetallicity:         0.
   InitAbundance_Hydrogen:  0.752
@@ -60,3 +66,6 @@ EAGLEChemistry:
   InitAbundance_Iron:      0.000
   CalciumOverSilicon:      0.0941736
   SulphurOverSilicon:      0.6054160
+
+GearChemistry:
+  InitialMetallicity: 0.01295
diff --git a/examples/main.c b/examples/main.c
index ca91c9e718cca9f7f8d82fce2f5d237f9f5b98e4..39177560981c8d292ff4e69d871db7515645431c 100644
--- a/examples/main.c
+++ b/examples/main.c
@@ -124,7 +124,7 @@ int main(int argc, char *argv[]) {
 
   /* Structs used by the engine. Declare now to make sure these are always in
    * scope.  */
-  struct chemistry_data chemistry;
+  struct chemistry_global_data chemistry;
   struct cooling_function_data cooling_func;
   struct cosmology cosmo;
   struct external_potential potential;
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 0a81716a3e34bbb305916b1148a9d8199b84fa5a..edf1afe51a1169c672eedc6fc629e044dda0d966 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -179,12 +179,18 @@ LambdaCooling:
   hydrogen_mass_abundance:     0.75  # Hydrogen mass abundance (dimensionless)
   cooling_tstep_mult:          1.0   # Dimensionless pre-factor for the time-step condition
 
-# Cooling with Grackle 2.0
+# Cooling with Grackle 3.0
 GrackleCooling:
-  GrackleCloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
-  UVbackground: 1 # Enable or not the UV background
-  GrackleRedshift: 0 # Redshift to use (-1 means time based redshift)
-  GrackleHSShieldingDensityThreshold: 1.1708e-26 # self shielding threshold in internal system of units
+  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  WithUVbackground: 1                   # Enable or not the UV background
+  Redshift: 0                           # Redshift to use (-1 means time based redshift)
+  WithMetalCooling: 1                   # Enable or not the metal cooling
+  ProvideVolumetricHeatingRates: 0      # (optional) User provide volumetric heating rates
+  ProvideSpecificHeatingRates: 0        # (optional) User provide specific heating rates
+  SelfShieldingMethod: 0                # (optional) Grackle (<= 3) or Gear self shielding method
+  OutputMode: 0                         # (optional) Write in output corresponding primordial chemistry mode
+  MaxSteps: 10000                       # (optional) Max number of step when computing the initial composition
+  ConvergenceLimit: 1e-2                # (optional) Convergence threshold (relative) for initial composition
 
 # Parameters related to chemistry models  -----------------------------------------------
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 1bd80b669396d4b4597f6149acafe6b1a4165f43..e21cb6539d2b7ae360ea07c92c426e99342bc2ec 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -43,8 +43,8 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \
     engine.h swift.h serial_io.h timers.h debug.h scheduler.h proxy.h parallel_io.h \
     common_io.h single_io.h multipole.h map.h tools.h partition.h clocks.h parser.h \
     physical_constants.h physical_constants_cgs.h potential.h version.h \
-    hydro_properties.h riemann.h threadpool.h cooling.h cooling_struct.h sourceterms.h \
-    sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \
+    hydro_properties.h riemann.h threadpool.h cooling_io.h cooling.h cooling_struct.h \
+    sourceterms.h sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \
     dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h \
     gravity_softened_derivatives.h vector_power.h collectgroup.h hydro_space.h sort_part.h \
     chemistry.h chemistry_io.h chemistry_struct.h cosmology.h restart.h
@@ -118,10 +118,15 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h
                  potential/isothermal/potential.h potential/disc_patch/potential.h \
                  potential/sine_wave/potential.h \
 		 cooling/none/cooling.h cooling/none/cooling_struct.h \
+                 cooling/none/cooling_io.h \
 	         cooling/const_du/cooling.h cooling/const_du/cooling_struct.h \
+                 cooling/const_du/cooling_io.h \
                  cooling/const_lambda/cooling.h cooling/const_lambda/cooling_struct.h \
+                 cooling/const_lambda/cooling_io.h \
                  cooling/grackle/cooling.h cooling/grackle/cooling_struct.h \
+                 cooling/grackle/cooling_io.h \
 		 cooling/EAGLE/cooling.h cooling/EAGLE/cooling_struct.h \
+                 cooling/EAGLE/cooling_io.h \
                  chemistry/none/chemistry.h \
 		 chemistry/none/chemistry_io.h \
 		 chemistry/none/chemistry_struct.h \
diff --git a/src/chemistry.c b/src/chemistry.c
index 091a0b363d16507894f3dcbba2ca79860f3cbaa9..44cbea1361d96c4cf1d4d3d21c3c91e5225640a5 100644
--- a/src/chemistry.c
+++ b/src/chemistry.c
@@ -36,7 +36,7 @@
 void chemistry_init(const struct swift_params* parameter_file,
                     const struct unit_system* us,
                     const struct phys_const* phys_const,
-                    struct chemistry_data* data) {
+                    struct chemistry_global_data* data) {
 
   chemistry_init_backend(parameter_file, us, phys_const, data);
 }
@@ -46,9 +46,10 @@ void chemistry_init(const struct swift_params* parameter_file,
  *
  * Calls chemistry_print_backend for the chosen chemistry model.
  *
- * @brief The #chemistry_data containing information about the current model.
+ * @brief The #chemistry_global_data containing information about the current
+ * model.
  */
-void chemistry_print(const struct chemistry_data* data) {
+void chemistry_print(const struct chemistry_global_data* data) {
   chemistry_print_backend(data);
 }
 
@@ -58,10 +59,10 @@ void chemistry_print(const struct chemistry_data* data) {
  * @param chemistry the struct
  * @param stream the file stream
  */
-void chemistry_struct_dump(const struct chemistry_data* chemistry,
+void chemistry_struct_dump(const struct chemistry_global_data* chemistry,
                            FILE* stream) {
-  restart_write_blocks((void*)chemistry, sizeof(struct chemistry_data), 1,
-                       stream, "chemistry", "chemistry function");
+  restart_write_blocks((void*)chemistry, sizeof(struct chemistry_global_data),
+                       1, stream, "chemistry", "chemistry function");
 }
 
 /**
@@ -71,8 +72,8 @@ void chemistry_struct_dump(const struct chemistry_data* chemistry,
  * @param chemistry the struct
  * @param stream the file stream
  */
-void chemistry_struct_restore(const struct chemistry_data* chemistry,
+void chemistry_struct_restore(const struct chemistry_global_data* chemistry,
                               FILE* stream) {
-  restart_read_blocks((void*)chemistry, sizeof(struct chemistry_data), 1,
+  restart_read_blocks((void*)chemistry, sizeof(struct chemistry_global_data), 1,
                       stream, NULL, "chemistry function");
 }
diff --git a/src/chemistry.h b/src/chemistry.h
index a5cbd77efbaab99e98ca5f031512e7e5bef0a613..77b8d5b5fa5314d692fb8f46c8e6229b57119a6c 100644
--- a/src/chemistry.h
+++ b/src/chemistry.h
@@ -46,14 +46,14 @@
 void chemistry_init(const struct swift_params* parameter_file,
                     const struct unit_system* us,
                     const struct phys_const* phys_const,
-                    struct chemistry_data* data);
+                    struct chemistry_global_data* data);
 
-void chemistry_print(const struct chemistry_data* data);
+void chemistry_print(const struct chemistry_global_data* data);
 
 /* Dump/restore. */
-void chemistry_struct_dump(const struct chemistry_data* chemistry,
+void chemistry_struct_dump(const struct chemistry_global_data* chemistry,
                            FILE* stream);
-void chemistry_struct_restore(const struct chemistry_data* chemistry,
+void chemistry_struct_restore(const struct chemistry_global_data* chemistry,
                               FILE* stream);
 
 #endif /* SWIFT_CHEMISTRY_H */
diff --git a/src/chemistry/EAGLE/chemistry.h b/src/chemistry/EAGLE/chemistry.h
index ae333252426f5fb79cc1fd1cee818754483214f8..fa63c8cc877861d443d6cdb3cc1c8f0c94befefe 100644
--- a/src/chemistry/EAGLE/chemistry.h
+++ b/src/chemistry/EAGLE/chemistry.h
@@ -57,10 +57,10 @@ chemistry_get_element_name(enum chemistry_element elem) {
  * the various smooth metallicity tasks
  *
  * @param p The particle to act upon
- * @param cd #chemistry_data containing chemistry informations.
+ * @param cd #chemistry_global_data containing chemistry informations.
  */
 __attribute__((always_inline)) INLINE static void chemistry_init_part(
-    struct part* restrict p, const struct chemistry_data* cd) {}
+    struct part* restrict p, const struct chemistry_global_data* cd) {}
 
 /**
  * @brief Finishes the smooth metal calculation.
@@ -71,11 +71,11 @@ __attribute__((always_inline)) INLINE static void chemistry_init_part(
  * This function requires the #hydro_end_density to have been called.
  *
  * @param p The particle to act upon.
- * @param cd #chemistry_data containing chemistry informations.
+ * @param cd #chemistry_global_data containing chemistry informations.
  * @param cosmo The current cosmological model.
  */
 __attribute__((always_inline)) INLINE static void chemistry_end_density(
-    struct part* restrict p, const struct chemistry_data* cd,
+    struct part* restrict p, const struct chemistry_global_data* cd,
     const struct cosmology* cosmo) {}
 
 /**
@@ -87,8 +87,11 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density(
  * @param data The global chemistry information.
  */
 __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
-    struct part* restrict p, struct xpart* restrict xp,
-    const struct chemistry_data* data) {
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct chemistry_global_data* data, struct part* restrict p,
+    struct xpart* restrict xp) {
 
   p->chemistry_data.metal_mass_fraction_total =
       data->initial_metal_mass_fraction_total;
@@ -107,7 +110,7 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
  */
 static INLINE void chemistry_init_backend(
     const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const, struct chemistry_data* data) {
+    const struct phys_const* phys_const, struct chemistry_global_data* data) {
 
   /* Read the total metallicity */
   data->initial_metal_mass_fraction_total =
@@ -133,9 +136,11 @@ static INLINE void chemistry_init_backend(
 /**
  * @brief Prints the properties of the chemistry model to stdout.
  *
- * @brief The #chemistry_data containing information about the current model.
+ * @brief The #chemistry_global_data containing information about the current
+ * model.
  */
-static INLINE void chemistry_print_backend(const struct chemistry_data* data) {
+static INLINE void chemistry_print_backend(
+    const struct chemistry_global_data* data) {
 
   message("Chemistry model is 'EAGLE' tracking %d elements.",
           chemistry_element_count);
diff --git a/src/chemistry/EAGLE/chemistry_struct.h b/src/chemistry/EAGLE/chemistry_struct.h
index e76e082d9b92bcde800b084feea44515b9579dc8..9093709e62d0af638ae485bd1154a4537791e84a 100644
--- a/src/chemistry/EAGLE/chemistry_struct.h
+++ b/src/chemistry/EAGLE/chemistry_struct.h
@@ -38,7 +38,7 @@ enum chemistry_element {
 /**
  * @brief Global chemical abundance information in the EAGLE model.
  */
-struct chemistry_data {
+struct chemistry_global_data {
 
   /*! Fraction of the particle mass in given elements at the start of the run */
   float initial_metal_mass_fraction[chemistry_element_count];
diff --git a/src/chemistry/gear/chemistry.h b/src/chemistry/gear/chemistry.h
index e9d3d00febe2438c52728f94d0136fb72ff8dc3b..9653e7eb05005977c39bca20eab961af83f40b5f 100644
--- a/src/chemistry/gear/chemistry.h
+++ b/src/chemistry/gear/chemistry.h
@@ -29,6 +29,7 @@
 #include <math.h>
 
 /* Local includes. */
+#include "chemistry_io.h"
 #include "chemistry_struct.h"
 #include "error.h"
 #include "hydro.h"
@@ -38,16 +39,16 @@
 #include "units.h"
 
 /**
- * @brief Return a string containing the name of a given #chemistry_element.
+ * @brief Compute the metal mass fraction
+ *
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended particle data.
+ * @param data The global chemistry information.
  */
-__attribute__((always_inline)) INLINE static const char*
-chemistry_get_element_name(enum chemistry_element elem) {
-
-  static const char* chemistry_element_names[chemistry_element_count] = {
-      "Oxygen",    "Magnesium", "Sulfur", "Iron",    "Zinc",
-      "Strontium", "Yttrium",   "Barium", "Europium"};
-
-  return chemistry_element_names[elem];
+__attribute__((always_inline)) INLINE static float
+chemistry_metal_mass_fraction(const struct part* restrict p,
+                              const struct xpart* restrict xp) {
+  return p->chemistry_data.Z;
 }
 
 /**
@@ -62,16 +63,10 @@ chemistry_get_element_name(enum chemistry_element elem) {
  */
 static INLINE void chemistry_init_backend(
     const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const, struct chemistry_data* data) {}
+    const struct phys_const* phys_const, struct chemistry_global_data* data) {
 
-/**
- * @brief Prints the properties of the chemistry model to stdout.
- *
- * @brief The #chemistry_data containing information about the current model.
- */
-static INLINE void chemistry_print_backend(const struct chemistry_data* data) {
-
-  message("Chemistry function is 'Gear'.");
+  /* read parameters */
+  chemistry_read_parameters(parameter_file, us, phys_const, data);
 }
 
 /**
@@ -81,10 +76,10 @@ static INLINE void chemistry_print_backend(const struct chemistry_data* data) {
  * the various smooth metallicity tasks
  *
  * @param p The particle to act upon
- * @param cd #chemistry_data containing chemistry informations.
+ * @param cd #chemistry_global_data containing chemistry informations.
  */
 __attribute__((always_inline)) INLINE static void chemistry_init_part(
-    struct part* restrict p, const struct chemistry_data* cd) {
+    struct part* restrict p, const struct chemistry_global_data* cd) {
 
   struct chemistry_part_data* cpd = &p->chemistry_data;
 
@@ -102,11 +97,11 @@ __attribute__((always_inline)) INLINE static void chemistry_init_part(
  * This function requires the #hydro_end_density to have been called.
  *
  * @param p The particle to act upon.
- * @param cd #chemistry_data containing chemistry informations.
+ * @param cd #chemistry_global_data containing chemistry informations.
  * @param cosmo The current cosmological model.
  */
 __attribute__((always_inline)) INLINE static void chemistry_end_density(
-    struct part* restrict p, const struct chemistry_data* cd,
+    struct part* restrict p, const struct chemistry_global_data* cd,
     const struct cosmology* cosmo) {
 
   /* Some smoothing length multiples. */
@@ -138,8 +133,13 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density(
  * @param data The global chemistry information.
  */
 __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
-    struct part* restrict p, struct xpart* restrict xp,
-    const struct chemistry_data* data) {
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct chemistry_global_data* data, struct part* restrict p,
+    struct xpart* restrict xp) {
+
+  p->chemistry_data.Z = data->initial_metallicity;
   chemistry_init_part(p, data);
 }
 
diff --git a/src/chemistry/gear/chemistry_io.h b/src/chemistry/gear/chemistry_io.h
index 9b0852fa2d23c8dd3e4d36406b55f4353d23b807..2343b6ba18ada5d04825a12d53a6f9cc7e984ef2 100644
--- a/src/chemistry/gear/chemistry_io.h
+++ b/src/chemistry/gear/chemistry_io.h
@@ -19,9 +19,38 @@
 #ifndef SWIFT_CHEMISTRY_IO_GEAR_H
 #define SWIFT_CHEMISTRY_IO_GEAR_H
 
-#include "chemistry.h"
 #include "chemistry_struct.h"
+#include "error.h"
 #include "io_properties.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "units.h"
+
+/**
+ * @brief Return a string containing the name of a given #chemistry_element.
+ */
+__attribute__((always_inline)) INLINE static const char*
+chemistry_get_element_name(enum chemistry_element elem) {
+
+  static const char* chemistry_element_names[chemistry_element_count] = {
+      "Oxygen",    "Magnesium", "Sulfur", "Iron",    "Zinc",
+      "Strontium", "Yttrium",   "Barium", "Europium"};
+
+  return chemistry_element_names[elem];
+}
+
+/**
+ * @brief Prints the properties of the chemistry model to stdout.
+ *
+ * @brief The #chemistry_global_data containing information about the current
+ * model.
+ */
+static INLINE void chemistry_print_backend(
+    const struct chemistry_global_data* data) {
+
+  message("Chemistry function is 'Gear'.");
+}
 
 /**
  * @brief Specifies which particle fields to read from a dataset
@@ -31,13 +60,25 @@
  *
  * @return Returns the number of fields to read.
  */
-int chemistry_read_particles(struct part* parts, struct io_props* list) {
+__attribute__((always_inline)) INLINE static int chemistry_read_particles(
+    struct part* parts, struct io_props* list) {
 
   /* List what we want to read */
   list[0] = io_make_input_field(
       "ElementAbundance", FLOAT, chemistry_element_count, OPTIONAL,
       UNIT_CONV_NO_UNITS, parts, chemistry_data.metal_mass_fraction);
-  return 1;
+  list[1] = io_make_input_field("Z", FLOAT, 1, OPTIONAL, UNIT_CONV_NO_UNITS,
+                                parts, chemistry_data.Z);
+
+  return 2;
+}
+
+__attribute__((always_inline)) INLINE static void chemistry_read_parameters(
+    const struct swift_params* parameter_file, const struct unit_system* us,
+    const struct phys_const* phys_const, struct chemistry_global_data* data) {
+
+  data->initial_metallicity = parser_get_opt_param_float(
+      parameter_file, "GearChemistry:InitialMetallicity", -1);
 }
 
 /**
@@ -48,18 +89,21 @@ int chemistry_read_particles(struct part* parts, struct io_props* list) {
  *
  * @return Returns the number of fields to write.
  */
-int chemistry_write_particles(const struct part* parts, struct io_props* list) {
+__attribute__((always_inline)) INLINE static int chemistry_write_particles(
+    const struct part* parts, struct io_props* list) {
 
   /* List what we want to write */
   list[0] = io_make_output_field(
       "SmoothedElementAbundance", FLOAT, chemistry_element_count,
       UNIT_CONV_NO_UNITS, parts, chemistry_data.smoothed_metal_mass_fraction);
+  list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, parts,
+                                 chemistry_data.Z);
 
-  list[1] = io_make_output_field("ElementAbundance", FLOAT,
+  list[2] = io_make_output_field("ElementAbundance", FLOAT,
                                  chemistry_element_count, UNIT_CONV_NO_UNITS,
                                  parts, chemistry_data.metal_mass_fraction);
 
-  return 2;
+  return 3;
 }
 
 #ifdef HAVE_HDF5
@@ -68,7 +112,8 @@ int chemistry_write_particles(const struct part* parts, struct io_props* list) {
  * @brief Writes the current model of SPH to the file
  * @param h_grp The HDF5 group in which to write
  */
-void chemistry_write_flavour(hid_t h_grp) {
+__attribute__((always_inline)) INLINE static void chemistry_write_flavour(
+    hid_t h_grp) {
 
   io_write_attribute_s(h_grp, "Chemistry Model", "GEAR");
   for (enum chemistry_element i = chemistry_element_O;
diff --git a/src/chemistry/gear/chemistry_struct.h b/src/chemistry/gear/chemistry_struct.h
index b0ddee2b37c2d1126523ff7a4283f5c18ae3a7b4..c3443126cf0ac72c76774d9000fe218378cc6663 100644
--- a/src/chemistry/gear/chemistry_struct.h
+++ b/src/chemistry/gear/chemistry_struct.h
@@ -38,7 +38,11 @@ enum chemistry_element {
 /**
  * @brief Global chemical abundance information.
  */
-struct chemistry_data {};
+struct chemistry_global_data {
+
+  /* Initial metallicity Z */
+  float initial_metallicity;
+};
 
 /**
  * @brief Properties of the chemistry function.
@@ -50,6 +54,8 @@ struct chemistry_part_data {
 
   /*! Smoothed fraction of the particle mass in a given element */
   float smoothed_metal_mass_fraction[chemistry_element_count];
+
+  float Z;
 };
 
 #endif /* SWIFT_CHEMISTRY_STRUCT_GEAR_H */
diff --git a/src/chemistry/none/chemistry.h b/src/chemistry/none/chemistry.h
index 7fb83339bed6fc2db007e58a5b8372f003186db5..3ca51660ddfeead2b7ad0010979b719e59c4934e 100644
--- a/src/chemistry/none/chemistry.h
+++ b/src/chemistry/none/chemistry.h
@@ -61,14 +61,16 @@ chemistry_get_element_name(enum chemistry_element elem) {
  */
 static INLINE void chemistry_init_backend(
     const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const, struct chemistry_data* data) {}
+    const struct phys_const* phys_const, struct chemistry_global_data* data) {}
 
 /**
  * @brief Prints the properties of the chemistry model to stdout.
  *
- * @brief The #chemistry_data containing information about the current model.
+ * @brief The #chemistry_global_data containing information about the current
+ * model.
  */
-static INLINE void chemistry_print_backend(const struct chemistry_data* data) {
+static INLINE void chemistry_print_backend(
+    const struct chemistry_global_data* data) {
 
   message("Chemistry function is 'No chemistry'.");
 }
@@ -81,7 +83,7 @@ static INLINE void chemistry_print_backend(const struct chemistry_data* data) {
  * @param cosmo The current cosmological model.
  */
 __attribute__((always_inline)) INLINE static void chemistry_end_density(
-    struct part* restrict p, const struct chemistry_data* cd,
+    struct part* restrict p, const struct chemistry_global_data* cd,
     const struct cosmology* cosmo) {}
 
 /**
@@ -90,13 +92,19 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density(
  *
  * Nothing to do here.
  *
+ * @param phys_const The physical constant in internal units.
+ * @param us The unit system.
+ * @param cosmo The current cosmological model.
+ * @param data The global chemistry information used for this run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
- * @param data The global chemistry information used for this run.
  */
 __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
-    const struct part* restrict p, struct xpart* restrict xp,
-    const struct chemistry_data* data) {}
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct chemistry_global_data* data, const struct part* restrict p,
+    struct xpart* restrict xp) {}
 
 /**
  * @brief Sets the chemistry properties of the (x-)particles to a valid start
@@ -108,6 +116,6 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
  * @param data The global chemistry information.
  */
 __attribute__((always_inline)) INLINE static void chemistry_init_part(
-    struct part* restrict p, const struct chemistry_data* data) {}
+    struct part* restrict p, const struct chemistry_global_data* data) {}
 
 #endif /* SWIFT_CHEMISTRY_NONE_H */
diff --git a/src/chemistry/none/chemistry_struct.h b/src/chemistry/none/chemistry_struct.h
index 8ac5f6924c6893717033cd5f144fe873f6f17e24..a80699d66cccd2e290555006d45216ff53d9c99c 100644
--- a/src/chemistry/none/chemistry_struct.h
+++ b/src/chemistry/none/chemistry_struct.h
@@ -34,7 +34,7 @@ enum chemistry_element { chemistry_element_count = 0 };
  *
  * Nothing here.
  */
-struct chemistry_data {};
+struct chemistry_global_data {};
 
 /**
  * @brief Chemistry properties carried by the #part.
diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h
index 62d1cc9b72681fe96c901185a10efe5523b206e3..bdf3801887256cb97ae1d5b6a3095250764aa822 100644
--- a/src/cooling/EAGLE/cooling.h
+++ b/src/cooling/EAGLE/cooling.h
@@ -20,10 +20,13 @@
 #define SWIFT_COOLING_EAGLE_H
 
 /**
- * @file src/cooling/none/cooling.h
- * @brief Empty infrastructure for the cases without cooling function
+ * @file src/cooling/EAGLE/cooling.h
+ * @brief EAGLE cooling function
  */
 
+/* Config parameters. */
+#include "../config.h"
+
 /* Some standard headers. */
 #include <float.h>
 #include <math.h>
@@ -31,25 +34,11 @@
 /* Local includes. */
 #include "error.h"
 #include "hydro.h"
-#include "io_properties.h"
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
 #include "units.h"
 
-#ifdef HAVE_HDF5
-
-/**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
- */
-__attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
-
-  io_write_attribute_s(h_grpsph, "Cooling Model", "EAGLE");
-}
-#endif
-
 /**
  * @brief Apply the cooling function to a particle.
  *
@@ -95,8 +84,11 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  * @param cooling The properties of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
-    const struct part* restrict p, struct xpart* restrict xp,
-    const struct cooling_function_data* cooling) {}
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp) {}
 
 /**
  * @brief Returns the total radiated energy by this particle.
diff --git a/src/cooling/EAGLE/cooling_io.h b/src/cooling/EAGLE/cooling_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..d6ed4f122863be7b591b095b11803a3d2729f694
--- /dev/null
+++ b/src/cooling/EAGLE/cooling_io.h
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2017 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * 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_COOLING_EAGLE_IO_H
+#define SWIFT_COOLING_EAGLE_IO_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local includes */
+#include "io_properties.h"
+
+#ifdef HAVE_HDF5
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+__attribute__((always_inline)) INLINE static void cooling_write_flavour(
+    hid_t h_grpsph) {
+
+  io_write_attribute_s(h_grpsph, "Cooling Model", "EAGLE");
+}
+#endif
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param parts The particle array.
+ * @param list The list of i/o properties to write.
+ * @param cooling The #cooling_function_data
+ *
+ * @return Returns the number of fields to write.
+ */
+__attribute__((always_inline)) INLINE static int cooling_write_particles(
+    const struct xpart* xparts, struct io_props* list,
+    const struct cooling_function_data* cooling) {
+  return 0;
+}
+
+#endif /* SWIFT_COOLING_EAGLE_IO_H */
diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h
index 25aa974e47b277d0dfcc21077e5041a49e8168c8..ba8211174919419c37856dc1fcbdaa73b23e319e 100644
--- a/src/cooling/const_du/cooling.h
+++ b/src/cooling/const_du/cooling.h
@@ -30,33 +30,21 @@
  * realistic functions.
  */
 
+/* Config parameters. */
+#include "../config.h"
+
 /* Some standard headers. */
 #include <math.h>
 
 /* Local includes. */
 #include "const.h"
-#include "cooling_struct.h"
 #include "error.h"
 #include "hydro.h"
-#include "io_properties.h"
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
 #include "units.h"
 
-#ifdef HAVE_HDF5
-
-/**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
- */
-__attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
-
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Constant du/dt");
-}
-#endif
-
 /**
  * @brief Apply the cooling function to a particle.
  *
@@ -141,8 +129,11 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  * @param cooling The properties of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
-    const struct part* restrict p, struct xpart* restrict xp,
-    const struct cooling_function_data* cooling) {
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp) {
 
   xp->cooling_data.radiated_energy = 0.f;
 }
diff --git a/src/cooling/const_du/cooling_io.h b/src/cooling/const_du/cooling_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..52a943aca86e51665fd1841d7bcb8a100b046ed8
--- /dev/null
+++ b/src/cooling/const_du/cooling_io.h
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *                    Richard Bower (r.g.bower@durham.ac.uk)
+ *                    Stefan Arridge  (stefan.arridge@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_CONST_DU_IO_H
+#define SWIFT_COOLING_CONST_DU_IO_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local includes */
+#include "io_properties.h"
+
+#ifdef HAVE_HDF5
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+__attribute__((always_inline)) INLINE static void cooling_write_flavour(
+    hid_t h_grpsph) {
+
+  io_write_attribute_s(h_grpsph, "Cooling Model", "Constant du/dt");
+}
+#endif
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param parts The particle array.
+ * @param list The list of i/o properties to write.
+ * @param cooling The #cooling_function_data
+ *
+ * @return Returns the number of fields to write.
+ */
+__attribute__((always_inline)) INLINE static int cooling_write_particles(
+    const struct xpart* xparts, struct io_props* list,
+    const struct cooling_function_data* cooling) {
+  return 0;
+}
+
+#endif /* SWIFT_COOLING_CONST_DU_IO_H */
diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h
index a402b749300e64ef79253764c402d41f5a214d82..43ca7ab75b0bce370d7405e52cea9b54335ae73c 100644
--- a/src/cooling/const_lambda/cooling.h
+++ b/src/cooling/const_lambda/cooling.h
@@ -23,6 +23,9 @@
 #ifndef SWIFT_COOLING_CONST_LAMBDA_H
 #define SWIFT_COOLING_CONST_LAMBDA_H
 
+/* Config parameters. */
+#include "../config.h"
+
 /* Some standard headers. */
 #include <float.h>
 #include <math.h>
@@ -31,25 +34,11 @@
 #include "const.h"
 #include "error.h"
 #include "hydro.h"
-#include "io_properties.h"
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
 #include "units.h"
 
-#ifdef HAVE_HDF5
-
-/**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
- */
-__attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
-
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Constant Lambda");
-}
-#endif
-
 /**
  * @brief Calculates du/dt in code units for a particle.
  *
@@ -154,8 +143,11 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  * @param cooling The properties of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
-    const struct part* restrict p, struct xpart* restrict xp,
-    const struct cooling_function_data* cooling) {
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp) {
 
   xp->cooling_data.radiated_energy = 0.f;
 }
diff --git a/src/cooling/const_lambda/cooling_io.h b/src/cooling/const_lambda/cooling_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..89c9471a291a4a6a5740a8c6c816913cbc6316a0
--- /dev/null
+++ b/src/cooling/const_lambda/cooling_io.h
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Tom Theuns (tom.theuns@durham.ac.uk)
+ *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *                    Richard Bower (r.g.bower@durham.ac.uk)
+ *                    Stefan Arridge  (stefan.arridge@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_CONST_LAMBDA_IO_H
+#define SWIFT_COOLING_CONST_LAMBDA_IO_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local includes */
+#include "io_properties.h"
+
+#ifdef HAVE_HDF5
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+__attribute__((always_inline)) INLINE static void cooling_write_flavour(
+    hid_t h_grpsph) {
+
+  io_write_attribute_s(h_grpsph, "Cooling Model", "Constant Lambda");
+}
+#endif
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param parts The particle array.
+ * @param list The list of i/o properties to write.
+ * @param cooling The #cooling_function_data
+ *
+ * @return Returns the number of fields to write.
+ */
+__attribute__((always_inline)) INLINE static int cooling_write_particles(
+    const struct xpart* xparts, struct io_props* list,
+    const struct cooling_function_data* cooling) {
+  return 0;
+}
+
+#endif /* SWIFT_COOLING_CONST_LAMBDA_IO_H */
diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h
index d1da52300121ca56feea4114d74322120bd501ce..dd59e9af1431681a8c5bdc1e5cb0c22053063651 100644
--- a/src/cooling/grackle/cooling.h
+++ b/src/cooling/grackle/cooling.h
@@ -20,19 +20,24 @@
 #define SWIFT_COOLING_GRACKLE_H
 
 /**
- * @file src/cooling/none/cooling.h
- * @brief Empty infrastructure for the cases without cooling function
+ * @file src/cooling/grackle/cooling.h
+ * @brief Cooling using the GRACKLE 3.0 library.
  */
 
+/* Config parameters. */
+#include "../config.h"
+
 /* Some standard headers. */
 #include <float.h>
-#include <grackle.h>
 #include <math.h>
 
+/* The grackle library itself */
+#include <grackle.h>
+
 /* Local includes. */
+#include "chemistry.h"
 #include "error.h"
 #include "hydro.h"
-#include "io_properties.h"
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
@@ -42,18 +47,147 @@
 #define GRACKLE_NPART 1
 #define GRACKLE_RANK 3
 
-#ifdef HAVE_HDF5
+/* prototypes */
+static gr_float cooling_time(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp);
+
+static double cooling_rate(const struct phys_const* restrict phys_const,
+                           const struct unit_system* restrict us,
+                           const struct cosmology* restrict cosmo,
+                           const struct cooling_function_data* restrict cooling,
+                           const struct part* restrict p,
+                           struct xpart* restrict xp, double dt);
 
 /**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
+ * @brief Print the chemical network
+ *
+ * @param xp The #xpart to print
  */
-__attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
+__attribute__((always_inline)) INLINE static void cooling_print_fractions(
+    const struct xpart* restrict xp) {
+
+  const struct cooling_xpart_data tmp = xp->cooling_data;
+#if COOLING_GRACKLE_MODE > 0
+  message("HI %g, HII %g, HeI %g, HeII %g, HeIII %g, e %g", tmp.HI_frac,
+          tmp.HII_frac, tmp.HeI_frac, tmp.HeII_frac, tmp.HeIII_frac,
+          tmp.e_frac);
+#endif
 
-  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle");
+#if COOLING_GRACKLE_MODE > 1
+  message("HM %g, H2I %g, H2II %g", tmp.HM_frac, tmp.H2I_frac, tmp.H2II_frac);
+#endif
+
+#if COOLING_GRACKLE_MODE > 2
+  message("DI %g, DII %g, HDI %g", tmp.DI_frac, tmp.DII_frac, tmp.HDI_frac);
+#endif
+  message("Metal: %g", tmp.metal_frac);
 }
+
+/**
+ * @brief Check if the difference of a given field is lower than limit
+ *
+ * @param xp First #xpart
+ * @param old Second #xpart
+ * @param field The field to check
+ * @param limit Difference limit
+ *
+ * @return 0 if diff > limit
+ */
+#define cooling_check_field(xp, old, field, limit)                \
+  ({                                                              \
+    float tmp = xp->cooling_data.field - old->cooling_data.field; \
+    tmp = fabs(tmp) / xp->cooling_data.field;                     \
+    if (tmp > limit) return 0;                                    \
+  })
+
+/**
+ * @brief Check if difference between two particles is lower than a given value
+ *
+ * @param xp One of the #xpart
+ * @param old The other #xpart
+ * @param limit The difference limit
+ */
+__attribute__((always_inline)) INLINE static int cooling_converged(
+    const struct xpart* restrict xp, const struct xpart* restrict old,
+    const float limit) {
+
+#if COOLING_GRACKLE_MODE > 0
+  cooling_check_field(xp, old, HI_frac, limit);
+  cooling_check_field(xp, old, HII_frac, limit);
+  cooling_check_field(xp, old, HeI_frac, limit);
+  cooling_check_field(xp, old, HeII_frac, limit);
+  cooling_check_field(xp, old, HeIII_frac, limit);
+  cooling_check_field(xp, old, e_frac, limit);
 #endif
+#if COOLING_GRACKLE_MODE > 1
+  cooling_check_field(xp, old, HM_frac, limit);
+  cooling_check_field(xp, old, H2I_frac, limit);
+  cooling_check_field(xp, old, H2II_frac, limit);
+#endif
+
+#if COOLING_GRACKLE_MODE > 2
+  cooling_check_field(xp, old, DI_frac, limit);
+  cooling_check_field(xp, old, DII_frac, limit);
+  cooling_check_field(xp, old, HDI_frac, limit);
+#endif
+
+  return 1;
+}
+
+/**
+ * @brief Compute equilibrium fraction
+ *
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended particle data.
+ * @param cooling The properties of the cooling function.
+ */
+__attribute__((always_inline)) INLINE static void cooling_compute_equilibrium(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp) {
+
+  /* get temporary data */
+  struct part p_tmp = *p;
+  struct cooling_function_data cooling_tmp = *cooling;
+  cooling_tmp.chemistry.with_radiative_cooling = 0;
+  /* need density for computation, therefore quick estimate */
+  p_tmp.rho = 0.2387 * p_tmp.mass / pow(p_tmp.h, 3);
+
+  /* compute time step */
+  const double alpha = 0.01;
+  double dt =
+      fabs(cooling_time(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp));
+  cooling_rate(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
+  dt = alpha *
+       fabs(cooling_time(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp));
+
+  /* init simple variables */
+  int step = 0;
+  const int max_step = cooling_tmp.max_step;
+  const float conv_limit = cooling_tmp.convergence_limit;
+  struct xpart old;
+
+  do {
+    /* update variables */
+    step += 1;
+    old = *xp;
+
+    /* update chemistry */
+    cooling_rate(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
+  } while (step < max_step && !cooling_converged(xp, &old, conv_limit));
+
+  if (step == max_step)
+    error(
+        "A particle element fraction failed to converge."
+        "You can change 'GrackleCooling:MaxSteps' or "
+        "'GrackleCooling:ConvergenceLimit' to avoid this problem");
+}
 
 /**
  * @brief Sets the cooling properties of the (x-)particles to a valid start
@@ -64,10 +198,46 @@ __attribute__((always_inline)) INLINE static void cooling_write_flavour(
  * @param cooling The properties of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
-    const struct part* restrict p, struct xpart* restrict xp,
-    const struct cooling_function_data* cooling) {
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* cooling, const struct part* restrict p,
+    struct xpart* restrict xp) {
 
   xp->cooling_data.radiated_energy = 0.f;
+
+#if COOLING_GRACKLE_MODE >= 1
+  gr_float zero = 1.e-20;
+
+  /* primordial chemistry >= 1 */
+  xp->cooling_data.HI_frac = zero;
+  xp->cooling_data.HII_frac = grackle_data->HydrogenFractionByMass;
+  xp->cooling_data.HeI_frac = 1. - grackle_data->HydrogenFractionByMass;
+  xp->cooling_data.HeII_frac = zero;
+  xp->cooling_data.HeIII_frac = zero;
+  xp->cooling_data.e_frac = xp->cooling_data.HII_frac +
+                            0.25 * xp->cooling_data.HeII_frac +
+                            0.5 * xp->cooling_data.HeIII_frac;
+#endif  // MODE >= 1
+
+#if COOLING_GRACKLE_MODE >= 2
+  /* primordial chemistry >= 2 */
+  xp->cooling_data.HM_frac = zero;
+  xp->cooling_data.H2I_frac = zero;
+  xp->cooling_data.H2II_frac = zero;
+#endif  // MODE >= 2
+
+#if COOLING_GRACKLE_MODE >= 3
+  /* primordial chemistry >= 3 */
+  xp->cooling_data.DI_frac = grackle_data->DeuteriumToHydrogenRatio *
+                             grackle_data->HydrogenFractionByMass;
+  xp->cooling_data.DII_frac = zero;
+  xp->cooling_data.HDI_frac = zero;
+#endif  // MODE >= 3
+
+#if COOLING_GRACKLE_MODE > 0
+  cooling_compute_equilibrium(phys_const, us, cosmo, cooling, p, xp);
+#endif
 }
 
 /**
@@ -90,17 +260,17 @@ __attribute__((always_inline)) INLINE static void cooling_print_backend(
     const struct cooling_function_data* cooling) {
 
   message("Cooling function is 'Grackle'.");
-  message("Using Grackle           = %i", cooling->chemistry.use_grackle);
-  message("Chemical network        = %i",
-          cooling->chemistry.primordial_chemistry);
-  message("Radiative cooling       = %i",
-          cooling->chemistry.with_radiative_cooling);
-  message("Metal cooling           = %i", cooling->chemistry.metal_cooling);
-
-  message("CloudyTable             = %s", cooling->cloudy_table);
-  message("UVbackground            = %d", cooling->uv_background);
-  message("Redshift                = %g", cooling->redshift);
-  message("Density Self Shielding  = %g", cooling->density_self_shielding);
+  message("Using Grackle    = %i", cooling->chemistry.use_grackle);
+  message("Chemical network = %i", cooling->chemistry.primordial_chemistry);
+  message("CloudyTable      = %s", cooling->cloudy_table);
+  message("Redshift         = %g", cooling->redshift);
+  message("UV background    = %d", cooling->with_uv_background);
+  message("Metal cooling    = %i", cooling->chemistry.metal_cooling);
+  message("Self Shielding   = %i", cooling->self_shielding_method);
+  message("Specific Heating Rates   = %i",
+          cooling->provide_specific_heating_rates);
+  message("Volumetric Heating Rates = %i",
+          cooling->provide_volumetric_heating_rates);
   message("Units:");
   message("\tComoving     = %i", cooling->units.comoving_coordinates);
   message("\tLength       = %g", cooling->units.length_units);
@@ -110,29 +280,221 @@ __attribute__((always_inline)) INLINE static void cooling_print_backend(
 }
 
 /**
- * @brief Compute the cooling rate
+ * @brief copy a single field from the grackle data to a #xpart
+ *
+ * @param data The #grackle_field_data
+ * @param xp The #xpart
+ * @param rho Particle density
+ * @param field The field to copy
+ */
+#define cooling_copy_field_from_grackle(data, xp, rho, field) \
+  xp->cooling_data.field##_frac = *data.field##_density / rho;
+
+/**
+ * @brief copy a single field from a #xpart to the grackle data
+ *
+ * @param data The #grackle_field_data
+ * @param xp The #xpart
+ * @param rho Particle density
+ * @param field The field to copy
+ */
+#define cooling_copy_field_to_grackle(data, xp, rho, field)       \
+  gr_float grackle_##field = xp->cooling_data.field##_frac * rho; \
+  data.field##_density = &grackle_##field;
+
+/**
+ * @brief copy a #xpart to the grackle data
+ *
+ * Warning this function creates some variable, therefore the grackle call
+ * should be in a block that still has the variables.
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 0
+#define cooling_copy_to_grackle1(data, p, xp, rho)     \
+  cooling_copy_field_to_grackle(data, xp, rho, HI);    \
+  cooling_copy_field_to_grackle(data, xp, rho, HII);   \
+  cooling_copy_field_to_grackle(data, xp, rho, HeI);   \
+  cooling_copy_field_to_grackle(data, xp, rho, HeII);  \
+  cooling_copy_field_to_grackle(data, xp, rho, HeIII); \
+  cooling_copy_field_to_grackle(data, xp, rho, e);
+#else
+#define cooling_copy_to_grackle1(data, p, xp, rho) \
+  data.HI_density = NULL;                          \
+  data.HII_density = NULL;                         \
+  data.HeI_density = NULL;                         \
+  data.HeII_density = NULL;                        \
+  data.HeIII_density = NULL;                       \
+  data.e_density = NULL;
+#endif
+
+/**
+ * @brief copy a #xpart to the grackle data
+ *
+ * Warning this function creates some variable, therefore the grackle call
+ * should be in a block that still has the variables.
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 1
+#define cooling_copy_to_grackle2(data, p, xp, rho)   \
+  cooling_copy_field_to_grackle(data, xp, rho, HM);  \
+  cooling_copy_field_to_grackle(data, xp, rho, H2I); \
+  cooling_copy_field_to_grackle(data, xp, rho, H2II);
+#else
+#define cooling_copy_to_grackle2(data, p, xp, rho) \
+  data.HM_density = NULL;                          \
+  data.H2I_density = NULL;                         \
+  data.H2II_density = NULL;
+#endif
+
+/**
+ * @brief copy a #xpart to the grackle data
+ *
+ * Warning this function creates some variable, therefore the grackle call
+ * should be in a block that still has the variables.
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 2
+#define cooling_copy_to_grackle3(data, p, xp, rho)   \
+  cooling_copy_field_to_grackle(data, xp, rho, DI);  \
+  cooling_copy_field_to_grackle(data, xp, rho, DII); \
+  cooling_copy_field_to_grackle(data, xp, rho, HDI);
+#else
+#define cooling_copy_to_grackle3(data, p, xp, rho) \
+  data.DI_density = NULL;                          \
+  data.DII_density = NULL;                         \
+  data.HDI_density = NULL;
+#endif
+
+/**
+ * @brief copy the grackle data to a #xpart
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 0
+#define cooling_copy_from_grackle1(data, p, xp, rho)     \
+  cooling_copy_field_from_grackle(data, xp, rho, HI);    \
+  cooling_copy_field_from_grackle(data, xp, rho, HII);   \
+  cooling_copy_field_from_grackle(data, xp, rho, HeI);   \
+  cooling_copy_field_from_grackle(data, xp, rho, HeII);  \
+  cooling_copy_field_from_grackle(data, xp, rho, HeIII); \
+  cooling_copy_field_from_grackle(data, xp, rho, e);
+#else
+#define cooling_copy_from_grackle1(data, p, xp, rho)
+#endif
+
+/**
+ * @brief copy the grackle data to a #xpart
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 1
+#define cooling_copy_from_grackle2(data, p, xp, rho)   \
+  cooling_copy_field_from_grackle(data, xp, rho, HM);  \
+  cooling_copy_field_from_grackle(data, xp, rho, H2I); \
+  cooling_copy_field_from_grackle(data, xp, rho, H2II);
+#else
+#define cooling_copy_from_grackle2(data, p, xp, rho)
+#endif
+
+/**
+ * @brief copy the grackle data to a #xpart
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 2
+#define cooling_copy_from_grackle3(data, p, xp, rho)   \
+  cooling_copy_field_from_grackle(data, xp, rho, DI);  \
+  cooling_copy_field_from_grackle(data, xp, rho, DII); \
+  cooling_copy_field_from_grackle(data, xp, rho, HDI);
+#else
+#define cooling_copy_from_grackle3(data, p, xp, rho)
+#endif
+
+/**
+ * @brief copy a #xpart to the grackle data
+ *
+ * Warning this function creates some variable, therefore the grackle call
+ * should be in a block that still has the variables.
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#define cooling_copy_to_grackle(data, p, xp, rho)                      \
+  cooling_copy_to_grackle1(data, p, xp, rho);                          \
+  cooling_copy_to_grackle2(data, p, xp, rho);                          \
+  cooling_copy_to_grackle3(data, p, xp, rho);                          \
+  data.volumetric_heating_rate = NULL;                                 \
+  data.specific_heating_rate = NULL;                                   \
+  data.RT_heating_rate = NULL;                                         \
+  data.RT_HI_ionization_rate = NULL;                                   \
+  data.RT_HeI_ionization_rate = NULL;                                  \
+  data.RT_HeII_ionization_rate = NULL;                                 \
+  data.RT_H2_dissociation_rate = NULL;                                 \
+  gr_float metal_density = chemistry_metal_mass_fraction(p, xp) * rho; \
+  data.metal_density = &metal_density;
+
+/**
+ * @brief copy a #xpart to the grackle data
+ *
+ * Warning this function creates some variable, therefore the grackle call
+ * should be in a block that still has the variables.
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#define cooling_copy_from_grackle(data, p, xp, rho) \
+  cooling_copy_from_grackle1(data, p, xp, rho);     \
+  cooling_copy_from_grackle2(data, p, xp, rho);     \
+  cooling_copy_from_grackle3(data, p, xp, rho);
+
+/**
+ * @brief Compute the cooling rate and update the particle chemistry data
  *
  * @param phys_const The physical constants in internal units.
  * @param us The internal system of units.
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
+ * @param xp Pointer to the particle extra data
  * @param dt The time-step of this particle.
  *
  * @return du / dt
  */
-__attribute__((always_inline)) INLINE static double cooling_rate(
+__attribute__((always_inline)) INLINE static gr_float cooling_rate(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, float dt) {
-
-  if (cooling->chemistry.primordial_chemistry > 1) error("Not implemented");
+    const struct part* restrict p, struct xpart* restrict xp, double dt) {
 
   /* set current time */
   code_units units = cooling->units;
   if (cooling->redshift == -1)
-    error("TODO time dependant redshift");
+    units.a_value = cosmo->a;
   else
     units.a_value = 1. / (1. + cooling->redshift);
 
@@ -145,6 +507,7 @@ __attribute__((always_inline)) INLINE static double cooling_rate(
   int grid_start[GRACKLE_RANK] = {0, 0, 0};
   int grid_end[GRACKLE_RANK] = {GRACKLE_NPART - 1, 0, 0};
 
+  data.grid_dx = 0.;
   data.grid_rank = GRACKLE_RANK;
   data.grid_dimension = grid_dimension;
   data.grid_start = grid_start;
@@ -154,70 +517,114 @@ __attribute__((always_inline)) INLINE static double cooling_rate(
   gr_float density = hydro_get_physical_density(p, cosmo);
   const double energy_before = hydro_get_physical_internal_energy(p, cosmo);
   gr_float energy = energy_before;
-  gr_float vx = 0;
-  gr_float vy = 0;
-  gr_float vz = 0;
 
+  /* initialize density */
   data.density = &density;
+
+  /* initialize energy */
   data.internal_energy = &energy;
-  data.x_velocity = &vx;
-  data.y_velocity = &vy;
-  data.z_velocity = &vz;
-
-  /* /\* primordial chemistry >= 1 *\/ */
-  /* gr_float HI_density = density; */
-  /* gr_float HII_density = 0.; */
-  /* gr_float HeI_density = 0.; */
-  /* gr_float HeII_density = 0.; */
-  /* gr_float HeIII_density = 0.; */
-  /* gr_float e_density = 0.; */
-
-  /* data.HI_density = &HI_density; */
-  /* data.HII_density = &HII_density; */
-  /* data.HeI_density = &HeI_density; */
-  /* data.HeII_density = &HeII_density; */
-  /* data.HeIII_density = &HeIII_density; */
-  /* data.e_density = &e_density; */
-
-  /* /\* primordial chemistry >= 2 *\/ */
-  /* gr_float HM_density = 0.; */
-  /* gr_float H2I_density = 0.; */
-  /* gr_float H2II_density = 0.; */
-
-  /* data.HM_density = &HM_density; */
-  /* data.H2I_density = &H2I_density; */
-  /* data.H2II_density = &H2II_density; */
-
-  /* /\* primordial chemistry >= 3 *\/ */
-  /* gr_float DI_density = 0.; */
-  /* gr_float DII_density = 0.; */
-  /* gr_float HDI_density = 0.; */
-
-  /* data.DI_density = &DI_density; */
-  /* data.DII_density = &DII_density; */
-  /* data.HDI_density = &HDI_density; */
-
-  /* metal cooling = 1 */
-  gr_float metal_density = density * grackle_data->SolarMetalFractionByMass;
 
-  data.metal_density = &metal_density;
+  /* grackle 3.0 doc: "Currently not used" */
+  data.x_velocity = NULL;
+  data.y_velocity = NULL;
+  data.z_velocity = NULL;
+
+  /* copy to grackle structure */
+  cooling_copy_to_grackle(data, p, xp, density);
+
+  /* solve chemistry */
+  chemistry_data chemistry_grackle = cooling->chemistry;
+  chemistry_data_storage my_rates = grackle_rates;
+  int error_code = _solve_chemistry(
+      &chemistry_grackle, &my_rates, &units, dt, data.grid_dx, data.grid_rank,
+      data.grid_dimension, data.grid_start, data.grid_end, data.density,
+      data.internal_energy, data.x_velocity, data.y_velocity, data.z_velocity,
+      data.HI_density, data.HII_density, data.HM_density, data.HeI_density,
+      data.HeII_density, data.HeIII_density, data.H2I_density,
+      data.H2II_density, data.DI_density, data.DII_density, data.HDI_density,
+      data.e_density, data.metal_density, data.volumetric_heating_rate,
+      data.specific_heating_rate, data.RT_heating_rate,
+      data.RT_HI_ionization_rate, data.RT_HeI_ionization_rate,
+      data.RT_HeII_ionization_rate, data.RT_H2_dissociation_rate, NULL);
+  if (error_code == 0) error("Error in solve_chemistry.");
+  // if (solve_chemistry(&units, &data, dt) == 0) {
+  //  error("Error in solve_chemistry.");
+  //}
+
+  /* copy from grackle data to particle */
+  cooling_copy_from_grackle(data, p, xp, density);
+
+  /* compute rate */
+  return (energy - energy_before) / dt;
+}
 
-  /* /\* volumetric heating rate *\/ */
-  /* gr_float volumetric_heating_rate = 0.; */
+/**
+ * @brief Compute the cooling time
+ *
+ * @param cooling The #cooling_function_data used in the run.
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the particle extra data
+ *
+ * @return cooling time
+ */
+__attribute__((always_inline)) INLINE static gr_float cooling_time(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp) {
 
-  /* data.volumetric_heating_rate = &volumetric_heating_rate; */
+  /* set current time */
+  code_units units = cooling->units;
+  if (cooling->redshift == -1)
+    error("TODO time dependant redshift");
+  else
+    units.a_value = 1. / (1. + cooling->redshift);
 
-  /* /\* specific heating rate *\/ */
-  /* gr_float specific_heating_rate = 0.; */
+  /* initialize data */
+  grackle_field_data data;
+
+  /* set values */
+  /* grid */
+  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};
+
+  data.grid_rank = GRACKLE_RANK;
+  data.grid_dimension = grid_dimension;
+  data.grid_start = grid_start;
+  data.grid_end = grid_end;
 
-  /* data.specific_heating_rate = &specific_heating_rate; */
+  /* general particle data */
+  const gr_float energy_before = hydro_get_physical_internal_energy(p, cosmo);
+  gr_float density = hydro_get_physical_density(p, cosmo);
+  gr_float energy = energy_before;
+
+  /* initialize density */
+  data.density = &density;
+
+  /* initialize energy */
+  data.internal_energy = &energy;
+
+  /* grackle 3.0 doc: "Currently not used" */
+  data.x_velocity = NULL;
+  data.y_velocity = NULL;
+  data.z_velocity = NULL;
+
+  /* copy data from particle to grackle data */
+  cooling_copy_to_grackle(data, p, xp, density);
 
-  /* solve chemistry with table */
-  if (solve_chemistry(&units, &data, dt) == 0) {
-    error("Error in solve_chemistry.");
+  /* Compute cooling time */
+  gr_float cooling_time;
+  if (calculate_cooling_time(&units, &data, &cooling_time) == 0) {
+    error("Error in calculate_cooling_time.");
   }
 
-  return (energy - energy_before) / dt;
+  /* copy from grackle data to particle */
+  cooling_copy_from_grackle(data, p, xp, density);
+
+  /* compute rate */
+  return cooling_time;
 }
 
 /**
@@ -235,7 +642,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, float dt) {
+    struct part* restrict p, struct xpart* restrict xp, double dt) {
 
   if (dt == 0.) return;
 
@@ -243,7 +650,7 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
   const float hydro_du_dt = hydro_get_internal_energy_dt(p);
 
   /* compute cooling rate */
-  const float du_dt = cooling_rate(phys_const, us, cosmo, cooling, p, dt);
+  const float du_dt = cooling_rate(phys_const, us, cosmo, cooling, p, xp, dt);
 
   /* record energy lost */
   xp->cooling_data.radiated_energy += -du_dt * dt * hydro_get_mass(p);
@@ -273,37 +680,15 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
 }
 
 /**
- * @brief Initialises the cooling properties.
+ * @brief Initialises the cooling unit system.
  *
- * @param parameter_file The parsed parameter file.
  * @param us The current internal system of units.
- * @param phys_const The physical constants in internal units.
  * @param cooling The cooling properties to initialize
  */
-__attribute__((always_inline)) INLINE static void cooling_init_backend(
-    const struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const,
-    struct cooling_function_data* cooling) {
-
-  /* read parameters */
-  parser_get_param_string(parameter_file, "GrackleCooling:GrackleCloudyTable",
-                          cooling->cloudy_table);
-  cooling->uv_background =
-      parser_get_param_int(parameter_file, "GrackleCooling:UVbackground");
-
-  cooling->redshift =
-      parser_get_param_double(parameter_file, "GrackleCooling:GrackleRedshift");
-
-  cooling->density_self_shielding = parser_get_param_double(
-      parameter_file, "GrackleCooling:GrackleHSShieldingDensityThreshold");
-
-#ifdef SWIFT_DEBUG_CHECKS
-  /* enable verbose for grackle */
-  grackle_verbose = 1;
-#endif
+__attribute__((always_inline)) INLINE static void cooling_init_units(
+    const struct unit_system* us, struct cooling_function_data* cooling) {
 
-  /* Set up the units system.
-     These are conversions from code units to cgs. */
+  /* These are conversions from code units to cgs. */
 
   /* first cosmo */
   cooling->units.a_units = 1.0;  // units for the expansion factor (1/1+zi)
@@ -321,6 +706,20 @@ __attribute__((always_inline)) INLINE static void cooling_init_backend(
   cooling->units.velocity_units = cooling->units.a_units *
                                   cooling->units.length_units /
                                   cooling->units.time_units;
+}
+
+/**
+ * @brief Initialises Grackle.
+ *
+ * @param cooling The cooling properties to initialize
+ */
+__attribute__((always_inline)) INLINE static void cooling_init_grackle(
+    struct cooling_function_data* cooling) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  /* enable verbose for grackle */
+  grackle_verbose = 1;
+#endif
 
   chemistry_data* chemistry = &cooling->chemistry;
 
@@ -332,38 +731,60 @@ __attribute__((always_inline)) INLINE static void cooling_init_backend(
   // Set parameter values for chemistry.
   chemistry->use_grackle = 1;
   chemistry->with_radiative_cooling = 1;
+
   /* molecular network with H, He, D
    From Cloudy table */
-  chemistry->primordial_chemistry = 0;
-  chemistry->metal_cooling = 1;  // metal cooling on
-  chemistry->UVbackground = cooling->uv_background;
+  chemistry->primordial_chemistry = COOLING_GRACKLE_MODE;
+  chemistry->metal_cooling = cooling->with_metal_cooling;
+  chemistry->UVbackground = cooling->with_uv_background;
   chemistry->grackle_data_file = cooling->cloudy_table;
-  chemistry->use_radiative_transfer = 0;
-  chemistry->use_volumetric_heating_rate = 0;
-  chemistry->use_specific_heating_rate = 0;
+
+  /* radiative transfer */
+  chemistry->use_radiative_transfer = cooling->provide_specific_heating_rates ||
+                                      cooling->provide_volumetric_heating_rates;
+  chemistry->use_volumetric_heating_rate =
+      cooling->provide_volumetric_heating_rates;
+  chemistry->use_specific_heating_rate =
+      cooling->provide_specific_heating_rates;
+
+  if (cooling->provide_specific_heating_rates &&
+      cooling->provide_volumetric_heating_rates)
+    message(
+        "WARNING: You should specified either the specific or the volumetric "
+        "heating rates, not both");
+
+  /* self shielding */
+  chemistry->self_shielding_method = cooling->self_shielding_method;
 
   /* Initialize the chemistry object. */
   if (initialize_chemistry_data(&cooling->units) == 0) {
     error("Error in initialize_chemistry_data.");
   }
+}
+
+/**
+ * @brief Initialises the cooling properties.
+ *
+ * @param parameter_file The parsed parameter file.
+ * @param us The current internal system of units.
+ * @param phys_const The physical constants in internal units.
+ * @param cooling The cooling properties to initialize
+ */
+__attribute__((always_inline)) INLINE static void cooling_init_backend(
+    const struct swift_params* parameter_file, const struct unit_system* us,
+    const struct phys_const* phys_const,
+    struct cooling_function_data* cooling) {
 
-#ifdef SWIFT_DEBUG_CHECKS
   if (GRACKLE_NPART != 1)
     error("Grackle with multiple particles not implemented");
-  float threshold = cooling->density_self_shielding;
 
-  threshold /= phys_const->const_proton_mass;
-  threshold /= pow(us->UnitLength_in_cgs, 3);
+  /* read parameters */
+  cooling_read_parameters(parameter_file, cooling);
 
-  message("***************************************");
-  message("initializing grackle cooling function");
-  message("");
-  cooling_print_backend(cooling);
-  message("Density Self Shielding = %g atom/cm3", threshold);
+  /* Set up the units system. */
+  cooling_init_units(us, cooling);
 
-  message("");
-  message("***************************************");
-#endif
+  cooling_init_grackle(cooling);
 }
 
 #endif /* SWIFT_COOLING_GRACKLE_H */
diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..5a6edb8f1c559a7b495351e256559f251b97c1cf
--- /dev/null
+++ b/src/cooling/grackle/cooling_io.h
@@ -0,0 +1,169 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_GRACKLE_IO_H
+#define SWIFT_COOLING_GRACKLE_IO_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local includes */
+#include "cooling_struct.h"
+#include "io_properties.h"
+
+#ifdef HAVE_HDF5
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+__attribute__((always_inline)) INLINE static void cooling_write_flavour(
+    hid_t h_grpsph) {
+
+#if COOLING_GRACKLE_MODE == 0
+  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle");
+#elif COOLING_GRACKLE_MODE == 1
+  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle1");
+#elif COOLING_GRACKLE_MODE == 2
+  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle2");
+#elif COOLING_GRACKLE_MODE == 3
+  io_write_attribute_s(h_grpsph, "Cooling Model", "Grackle3");
+#else
+  error("This function should be called only with one of the Grackle cooling.");
+#endif
+}
+#endif
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param parts The particle array.
+ * @param list The list of i/o properties to write.
+ * @param cooling The #cooling_function_data
+ *
+ * @return Returns the number of fields to write.
+ */
+__attribute__((always_inline)) INLINE static int cooling_write_particles(
+    const struct xpart* xparts, struct io_props* list,
+    const struct cooling_function_data* cooling) {
+
+  int num = 0;
+
+  if (cooling->output_mode == 0) return num;
+
+#if COOLING_GRACKLE_MODE >= 1
+  /* List what we want to write */
+  list[0] = io_make_output_field("HI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.HI_frac);
+
+  list[1] = io_make_output_field("HII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.HII_frac);
+
+  list[2] = io_make_output_field("HeI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.HeI_frac);
+
+  list[3] = io_make_output_field("HeII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.HeII_frac);
+
+  list[4] = io_make_output_field("HeIII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.HeIII_frac);
+
+  list[5] = io_make_output_field("e", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.e_frac);
+
+  num += 6;
+#endif
+
+  if (cooling->output_mode == 1) return num;
+
+#if COOLING_GRACKLE_MODE >= 2
+  list += num;
+
+  list[0] = io_make_output_field("HM", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.HM_frac);
+
+  list[1] = io_make_output_field("H2I", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.H2I_frac);
+
+  list[2] = io_make_output_field("H2II", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.H2II_frac);
+
+  num += 3;
+#endif
+
+  if (cooling->output_mode == 2) return num;
+
+#if COOLING_GRACKLE_MODE >= 3
+  list += num;
+
+  list[0] = io_make_output_field("DI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.DI_frac);
+
+  list[1] = io_make_output_field("DII", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.DII_frac);
+
+  list[2] = io_make_output_field("HDI", FLOAT, 1, UNIT_CONV_NO_UNITS, xparts,
+                                 cooling_data.HDI_frac);
+
+  num += 3;
+#endif
+
+  return num;
+}
+
+/**
+ * @brief Parser the parameter file and initialize the #cooling_function_data
+ *
+ * @param parameter_file The parser parameter file
+ * @param cooling The cooling properties to initialize
+ */
+__attribute__((always_inline)) INLINE static void cooling_read_parameters(
+    const struct swift_params* parameter_file,
+    struct cooling_function_data* cooling) {
+
+  parser_get_param_string(parameter_file, "GrackleCooling:CloudyTable",
+                          cooling->cloudy_table);
+  cooling->with_uv_background =
+      parser_get_param_int(parameter_file, "GrackleCooling:WithUVbackground");
+
+  cooling->redshift =
+      parser_get_param_double(parameter_file, "GrackleCooling:Redshift");
+
+  cooling->with_metal_cooling =
+      parser_get_param_int(parameter_file, "GrackleCooling:WithMetalCooling");
+
+  cooling->provide_volumetric_heating_rates = parser_get_opt_param_int(
+      parameter_file, "GrackleCooling:ProvideVolumetricHeatingRates", 0);
+
+  cooling->provide_specific_heating_rates = parser_get_opt_param_int(
+      parameter_file, "GrackleCooling:ProvideSpecificHeatingRates", 0);
+
+  cooling->self_shielding_method = parser_get_opt_param_int(
+      parameter_file, "GrackleCooling:SelfShieldingMethod", 0);
+
+  cooling->output_mode =
+      parser_get_opt_param_int(parameter_file, "GrackleCooling:OutputMode", 0);
+
+  cooling->max_step = parser_get_opt_param_int(
+      parameter_file, "GrackleCooling:MaxSteps", 10000);
+
+  cooling->convergence_limit = parser_get_opt_param_double(
+      parameter_file, "GrackleCooling:ConvergenceLimit", 1e-2);
+}
+
+#endif /* SWIFT_COOLING_GRACKLE_IO_H */
diff --git a/src/cooling/grackle/cooling_struct.h b/src/cooling/grackle/cooling_struct.h
index 9ad1470922fdcbf892014f988389eccc1a4e9014..b714690ce4688268723748b29506e458cccc4be9 100644
--- a/src/cooling/grackle/cooling_struct.h
+++ b/src/cooling/grackle/cooling_struct.h
@@ -22,6 +22,8 @@
 /* include grackle */
 #include <grackle.h>
 
+#include "../config.h"
+
 /**
  * @file src/cooling/none/cooling_struct.h
  * @brief Empty infrastructure for the cases without cooling function
@@ -36,19 +38,40 @@ struct cooling_function_data {
   char cloudy_table[200];
 
   /* Enable/Disable UV backgroud */
-  int uv_background;
+  int with_uv_background;
 
   /* Redshift to use for the UV backgroud (-1 to use cosmological one) */
   double redshift;
 
-  /* Density Threshold for the shielding */
-  double density_self_shielding;
-
   /* unit system */
   code_units units;
 
   /* grackle chemistry data */
   chemistry_data chemistry;
+
+  /* Enable/Disable metal cooling */
+  int with_metal_cooling;
+
+  /* User provide volumetric heating rates */
+  int provide_volumetric_heating_rates;
+
+  /* User provide specific heating rates */
+  int provide_specific_heating_rates;
+
+  /* Self shielding method (<= 3) means grackle modes */
+  int self_shielding_method;
+
+  /* Output mode (correspond to primordial chemistry mode */
+  int output_mode;
+
+  /* convergence limit for first init */
+  float convergence_limit;
+
+  /* number of step max for first init */
+  int max_step;
+
+  /* over relaxation parameter */
+  float omega;
 };
 
 /**
@@ -58,6 +81,36 @@ struct cooling_xpart_data {
 
   /*! Energy radiated away by this particle since the start of the run */
   float radiated_energy;
+
+/* here all fractions are mass fraction */
+#if COOLING_GRACKLE_MODE >= 1
+  /* primordial chemistry >= 1 */
+  float HI_frac;
+  float HII_frac;
+  float HeI_frac;
+  float HeII_frac;
+  float HeIII_frac;
+  float e_frac;
+
+#if COOLING_GRACKLE_MODE >= 2
+  /* primordial chemistry >= 2 */
+  float HM_frac;
+  float H2I_frac;
+  float H2II_frac;
+
+#if COOLING_GRACKLE_MODE >= 3
+  /* primordial chemistry >= 3 */
+  float DI_frac;
+  float DII_frac;
+  float HDI_frac;
+#endif  // MODE >= 3
+
+#endif  // MODE >= 2
+
+#endif  // MODE >= 1
+
+  /* metal cooling = 1 */
+  float metal_frac;
 };
 
 #endif /* SWIFT_COOLING_STRUCT_NONE_H */
diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h
index a1cc6491115a38d6971a57603fef413c35b52027..5081c7cbe6c4b5168da082ead80687226f9d0c16 100644
--- a/src/cooling/none/cooling.h
+++ b/src/cooling/none/cooling.h
@@ -31,25 +31,11 @@
 /* Local includes. */
 #include "error.h"
 #include "hydro.h"
-#include "io_properties.h"
 #include "parser.h"
 #include "part.h"
 #include "physical_constants.h"
 #include "units.h"
 
-#ifdef HAVE_HDF5
-
-/**
- * @brief Writes the current model of SPH to the file
- * @param h_grpsph The HDF5 group in which to write
- */
-__attribute__((always_inline)) INLINE static void cooling_write_flavour(
-    hid_t h_grpsph) {
-
-  io_write_attribute_s(h_grpsph, "Cooling Model", "None");
-}
-#endif
-
 /**
  * @brief Apply the cooling function to a particle.
  *
@@ -96,13 +82,19 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  *
  * Nothing to do here.
  *
+ * @param phys_const The physical constant in internal units.
+ * @param us The unit system.
+ * @param cosmo The current cosmological model.
+ * @param data The properties of the cooling function.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
- * @param cooling The properties of the cooling function.
  */
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
-    const struct part* restrict p, struct xpart* restrict xp,
-    const struct cooling_function_data* cooling) {}
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* data, const struct part* restrict p,
+    struct xpart* restrict xp) {}
 
 /**
  * @brief Returns the total radiated energy by this particle.
diff --git a/src/cooling/none/cooling_io.h b/src/cooling/none/cooling_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..e4c84f506bcd31ff95ededb5be889fbf9a27261b
--- /dev/null
+++ b/src/cooling/none/cooling_io.h
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_NONE_IO_H
+#define SWIFT_COOLING_NONE_IO_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Local includes */
+#include "io_properties.h"
+
+#ifdef HAVE_HDF5
+
+/**
+ * @brief Writes the current model of SPH to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+__attribute__((always_inline)) INLINE static void cooling_write_flavour(
+    hid_t h_grpsph) {
+
+  io_write_attribute_s(h_grpsph, "Cooling Model", "None");
+}
+#endif
+
+/**
+ * @brief Specifies which particle fields to write to a dataset
+ *
+ * @param xparts The extended particle array.
+ * @param list The list of i/o properties to write.
+ * @param cooling The #cooling_function_data
+ *
+ * @return Returns the number of fields to write.
+ */
+__attribute__((always_inline)) INLINE static int cooling_write_particles(
+    const struct xpart* xparts, struct io_props* list,
+    const struct cooling_function_data* cooling) {
+  return 0;
+}
+
+#endif /* SWIFT_COOLING_NONE_IO_H */
diff --git a/src/cooling_io.h b/src/cooling_io.h
new file mode 100644
index 0000000000000000000000000000000000000000..88eeae2cabdaa8a0909977b84a7dbcf03145d988
--- /dev/null
+++ b/src/cooling_io.h
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_COOLING_IO_H
+#define SWIFT_COOLING_IO_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Import the i/o routines of the right cooling definition */
+#if defined(COOLING_NONE)
+#include "./cooling/none/cooling_io.h"
+#elif defined(COOLING_CONST_DU)
+#include "./cooling/const_du/cooling_io.h"
+#elif defined(COOLING_CONST_LAMBDA)
+#include "./cooling/const_lambda/cooling_io.h"
+#elif defined(COOLING_GRACKLE)
+#include "./cooling/grackle/cooling_io.h"
+#elif defined(COOLING_EAGLE)
+#include "./cooling/EAGLE/cooling_io.h"
+#else
+#error "Invalid choice of cooling function."
+#endif
+
+#endif /* SWIFT_COOLING_IO_H */
diff --git a/src/engine.c b/src/engine.c
index 6f8c0e9937ee40912964b88a3be69cacf060a0ae..2bdad767aeb198d3c4c9d0dc6113a7580445022e 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -5202,15 +5202,18 @@ void engine_unpin() {
  * @param chemistry The chemistry information.
  * @param sourceterms The properties of the source terms function.
  */
-void engine_init(
-    struct engine *e, struct space *s, const struct swift_params *params,
-    long long Ngas, long long Ndm, int policy, int verbose,
-    struct repartition *reparttype, const struct unit_system *internal_units,
-    const struct phys_const *physical_constants, struct cosmology *cosmo,
-    const struct hydro_props *hydro, struct gravity_props *gravity,
-    const struct external_potential *potential,
-    const struct cooling_function_data *cooling_func,
-    const struct chemistry_data *chemistry, struct sourceterms *sourceterms) {
+void engine_init(struct engine *e, struct space *s,
+                 const struct swift_params *params, long long Ngas,
+                 long long Ndm, int policy, int verbose,
+                 struct repartition *reparttype,
+                 const struct unit_system *internal_units,
+                 const struct phys_const *physical_constants,
+                 struct cosmology *cosmo, const struct hydro_props *hydro,
+                 struct gravity_props *gravity,
+                 const struct external_potential *potential,
+                 const struct cooling_function_data *cooling_func,
+                 const struct chemistry_global_data *chemistry,
+                 struct sourceterms *sourceterms) {
 
   /* Clean-up everything */
   bzero(e, sizeof(struct engine));
@@ -5973,8 +5976,9 @@ void engine_struct_restore(struct engine *e, FILE *stream) {
   cooling_struct_restore(cooling_func, stream);
   e->cooling_func = cooling_func;
 
-  struct chemistry_data *chemistry =
-      (struct chemistry_data *)malloc(sizeof(struct chemistry_data));
+  struct chemistry_global_data *chemistry =
+      (struct chemistry_global_data *)malloc(
+          sizeof(struct chemistry_global_data));
   chemistry_struct_restore(chemistry, stream);
   e->chemistry = chemistry;
 
diff --git a/src/engine.h b/src/engine.h
index 4c37aab593fdffc5f68ea16cb83b29ad782debf0..29a064876317d7e1a7ff1cba5e3cb740b6e171a9 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -287,7 +287,7 @@ struct engine {
   const struct cooling_function_data *cooling_func;
 
   /* Properties of the chemistry model */
-  const struct chemistry_data *chemistry;
+  const struct chemistry_global_data *chemistry;
 
   /* Properties of source terms */
   struct sourceterms *sourceterms;
@@ -330,15 +330,18 @@ void engine_drift_top_multipoles(struct engine *e);
 void engine_reconstruct_multipoles(struct engine *e);
 void engine_print_stats(struct engine *e);
 void engine_dump_snapshot(struct engine *e);
-void engine_init(
-    struct engine *e, struct space *s, const struct swift_params *params,
-    long long Ngas, long long Ndm, int policy, int verbose,
-    struct repartition *reparttype, const struct unit_system *internal_units,
-    const struct phys_const *physical_constants, struct cosmology *cosmo,
-    const struct hydro_props *hydro, struct gravity_props *gravity,
-    const struct external_potential *potential,
-    const struct cooling_function_data *cooling_func,
-    const struct chemistry_data *chemistry, struct sourceterms *sourceterms);
+void engine_init(struct engine *e, struct space *s,
+                 const struct swift_params *params, long long Ngas,
+                 long long Ndm, int policy, int verbose,
+                 struct repartition *reparttype,
+                 const struct unit_system *internal_units,
+                 const struct phys_const *physical_constants,
+                 struct cosmology *cosmo, const struct hydro_props *hydro,
+                 struct gravity_props *gravity,
+                 const struct external_potential *potential,
+                 const struct cooling_function_data *cooling_func,
+                 const struct chemistry_global_data *chemistry,
+                 struct sourceterms *sourceterms);
 void engine_config(int restart, struct engine *e,
                    const struct swift_params *params, int nr_nodes, int nodeID,
                    int nr_threads, int with_aff, int verbose,
diff --git a/src/io_properties.h b/src/io_properties.h
index 55e128e890b75db2d01911bd3c3b46388b0c6597..d0005531afa3a0cf4de0302e86dd362e0abc11ab 100644
--- a/src/io_properties.h
+++ b/src/io_properties.h
@@ -26,6 +26,9 @@
 #include "common_io.h"
 #include "inline.h"
 
+/* Standard includes. */
+#include <string.h>
+
 /**
  * @brief The two sorts of data present in the GADGET IC files: compulsory to
  * start a run or optional.
diff --git a/src/parallel_io.c b/src/parallel_io.c
index 51c88e95396eb6af1ab7f7eb2d1ed48eada59e0d..b5741fb747a0cd8a2dd9ff1645d4a22ffa780869 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -38,7 +38,7 @@
 /* Local includes. */
 #include "chemistry_io.h"
 #include "common_io.h"
-#include "cooling.h"
+#include "cooling_io.h"
 #include "dimension.h"
 #include "engine.h"
 #include "error.h"
@@ -860,6 +860,8 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
   int periodic = e->s->periodic;
   int numFiles = 1;
 
+  const struct cooling_function_data* cooling = e->cooling_func;
+
   /* First time, we need to create the XMF file */
   if (e->snapshotOutputCount == 0) xmf_create_file(baseName);
 
@@ -1243,6 +1245,8 @@ void write_output_parallel(struct engine* e, const char* baseName,
         Nparticles = Ngas;
         hydro_write_particles(parts, xparts, list, &num_fields);
         num_fields += chemistry_write_particles(parts, list + num_fields);
+        num_fields +=
+            cooling_write_particles(xparts, list + num_fields, cooling);
         break;
 
       case swift_type_dark_matter:
diff --git a/src/runner.c b/src/runner.c
index 35996a3ba9a760d5f019ede35abc207bbdcbe1b3..51801a779481a8ce0500d8c5f3f0b20d7ed79faf 100644
--- a/src/runner.c
+++ b/src/runner.c
@@ -656,7 +656,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
   const struct space *s = e->s;
   const struct hydro_space *hs = &s->hs;
   const struct cosmology *cosmo = e->cosmology;
-  const struct chemistry_data *chemistry = e->chemistry;
+  const struct chemistry_global_data *chemistry = e->chemistry;
   const float hydro_h_max = e->hydro_properties->h_max;
   const float eps = e->hydro_properties->h_tolerance;
   const float hydro_eta_dim =
diff --git a/src/serial_io.c b/src/serial_io.c
index dd623f946ce6ec32415586e5048979de3adb58fa..fa48d0ac96e58f9dfc4e9d8c513197b6e91800e4 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -38,7 +38,7 @@
 /* Local includes. */
 #include "chemistry_io.h"
 #include "common_io.h"
-#include "cooling.h"
+#include "cooling_io.h"
 #include "dimension.h"
 #include "engine.h"
 #include "error.h"
@@ -744,6 +744,7 @@ void write_output_serial(struct engine* e, const char* baseName,
   const struct gpart* gparts = e->s->gparts;
   struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
+  const struct cooling_function_data* cooling = e->cooling_func;
   FILE* xmfFile = 0;
 
   /* Number of unassociated gparts */
@@ -992,6 +993,8 @@ void write_output_serial(struct engine* e, const char* baseName,
             Nparticles = Ngas;
             hydro_write_particles(parts, xparts, list, &num_fields);
             num_fields += chemistry_write_particles(parts, list + num_fields);
+            num_fields +=
+                cooling_write_particles(xparts, list + num_fields, cooling);
             break;
 
           case swift_type_dark_matter:
diff --git a/src/single_io.c b/src/single_io.c
index 2170bcffc4ce3ab21f1edd168d1dc37b2b4af963..73c7f5d946544ed61ebfae01a6ccd4708a8a5f3c 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -37,7 +37,7 @@
 /* Local includes. */
 #include "chemistry_io.h"
 #include "common_io.h"
-#include "cooling.h"
+#include "cooling_io.h"
 #include "dimension.h"
 #include "engine.h"
 #include "error.h"
@@ -612,6 +612,7 @@ void write_output_single(struct engine* e, const char* baseName,
   const struct gpart* gparts = e->s->gparts;
   struct gpart* dmparts = NULL;
   const struct spart* sparts = e->s->sparts;
+  const struct cooling_function_data* cooling = e->cooling_func;
 
   /* Number of unassociated gparts */
   const size_t Ndm = Ntot > 0 ? Ntot - (Ngas + Nstars) : 0;
@@ -811,6 +812,8 @@ void write_output_single(struct engine* e, const char* baseName,
         N = Ngas;
         hydro_write_particles(parts, xparts, list, &num_fields);
         num_fields += chemistry_write_particles(parts, list + num_fields);
+        num_fields +=
+            cooling_write_particles(xparts, list + num_fields, cooling);
         break;
 
       case swift_type_dark_matter:
diff --git a/src/space.c b/src/space.c
index 4f2213793b270cde1c29281bbf411fcfd97c1b4c..b6aef6ea7db1b7bfe0ecbe6a39157f9be5994b50 100644
--- a/src/space.c
+++ b/src/space.c
@@ -2317,7 +2317,7 @@ void space_synchronize_particle_positions(struct space *s) {
  * Calls chemistry_first_init_part() on all the particles
  */
 void space_first_init_parts(struct space *s,
-                            const struct chemistry_data *chemistry,
+                            const struct chemistry_global_data *chemistry,
                             const struct cooling_function_data *cool_func) {
 
   const size_t nr_parts = s->nr_parts;
@@ -2325,6 +2325,8 @@ void space_first_init_parts(struct space *s,
   struct xpart *restrict xp = s->xparts;
 
   const struct cosmology *cosmo = s->e->cosmology;
+  const struct phys_const *phys_const = s->e->physical_constants;
+  const struct unit_system *us = s->e->internal_units;
   const float a_factor_vel = cosmo->a * cosmo->a;
 
   const struct hydro_props *hydro_props = s->e->hydro_properties;
@@ -2355,10 +2357,10 @@ void space_first_init_parts(struct space *s,
     if (u_min > 0.f) hydro_set_init_internal_energy(&p[i], u_min);
 
     /* Also initialise the chemistry */
-    chemistry_first_init_part(&p[i], &xp[i], chemistry);
+    chemistry_first_init_part(phys_const, us, cosmo, chemistry, &p[i], &xp[i]);
 
     /* And the cooling */
-    cooling_first_init_part(&p[i], &xp[i], cool_func);
+    cooling_first_init_part(phys_const, us, cosmo, cool_func, &p[i], &xp[i]);
 
     /* Check part->gpart->part linkeage. */
     if (p[i].gpart) p[i].gpart->id_or_neg_offset = -i;
diff --git a/src/space.h b/src/space.h
index 571b595eda8758c510a014bc784ae4bde85abaea..db9c9edad3d7435ea619e72814e2c97094344090 100644
--- a/src/space.h
+++ b/src/space.h
@@ -222,7 +222,7 @@ void space_do_parts_sort();
 void space_do_gparts_sort();
 void space_do_sparts_sort();
 void space_first_init_parts(struct space *s,
-                            const struct chemistry_data *chemistry,
+                            const struct chemistry_global_data *chemistry,
                             const struct cooling_function_data *cool_func);
 void space_first_init_gparts(struct space *s,
                              const struct gravity_props *grav_props);