From ff8b655cc43f23e1b82178283f63c253f9e5d4a3 Mon Sep 17 00:00:00 2001
From: Loic Hausammann <loic.hausammann@protonmail.ch>
Date: Fri, 24 Jan 2020 13:06:59 +0000
Subject: [PATCH] Gear

---
 configure.ac                                  |   23 +-
 examples/GEAR/AgoraDisk/agora_disk.yml        |   67 +-
 examples/GEAR/AgoraDisk/cleanupSwift.py       |   18 +-
 examples/GEAR/AgoraDisk/getIC.sh              |    2 +-
 examples/GEAR/AgoraDisk/getSolution.sh        |   12 +-
 examples/GEAR/AgoraDisk/plotSolution.py       |   45 +-
 examples/GEAR/AgoraDisk/run.sh                |   12 +-
 examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml    |   28 +
 examples/GEAR/DwarfGalaxy/run.sh              |    2 +-
 examples/GEAR/ZoomIn/run.sh                   |    2 +-
 examples/GEAR/ZoomIn/zoom_in.yml              |   27 +
 examples/GEAR/getChemistryTable.sh            |    2 +
 .../SupernovaeFeedback/SN_feedback.yml        |   44 -
 .../SupernovaeFeedback/getGlass.sh            |    2 -
 .../SubgridTests/SupernovaeFeedback/makeIC.py |  117 --
 .../SubgridTests/SupernovaeFeedback/run.sh    |   19 -
 examples/parameter_example.yml                |   34 +-
 src/Makefile.am                               |   25 +-
 src/chemistry/GEAR/chemistry.h                |  205 +++-
 src/chemistry/GEAR/chemistry_iact.h           |    4 +-
 src/chemistry/GEAR/chemistry_io.h             |   67 +-
 src/chemistry/GEAR/chemistry_struct.h         |   36 +-
 src/common_io.c                               |  142 ++-
 src/common_io.h                               |    7 +
 src/cooling/Compton/cooling.h                 |    7 +-
 src/cooling/EAGLE/cooling.c                   |    7 +-
 src/cooling/EAGLE/cooling.h                   |    3 +-
 src/cooling/const_du/cooling.h                |    7 +-
 src/cooling/const_lambda/cooling.h            |    7 +-
 src/cooling/grackle/cooling.c                 | 1038 +++++++++++++++++
 src/cooling/grackle/cooling.h                 |  959 ++-------------
 src/cooling/grackle/cooling_io.h              |   46 +-
 src/cooling/grackle/cooling_struct.h          |   37 +-
 src/cooling/none/cooling.h                    |    7 +-
 src/engine.c                                  |    4 +-
 src/feedback.h                                |    3 +
 src/feedback/EAGLE/feedback.h                 |   31 +-
 src/feedback/EAGLE/feedback_iact.h            |    2 +-
 src/feedback/EAGLE/feedback_struct.h          |    5 +
 src/feedback/GEAR/feedback.c                  |  279 +++++
 src/feedback/GEAR/feedback.h                  |   85 ++
 src/feedback/GEAR/feedback_iact.h             |  148 +++
 src/feedback/GEAR/feedback_properties.h       |  102 ++
 src/feedback/GEAR/feedback_struct.h           |   63 +
 src/feedback/GEAR/hdf5_functions.h            |  135 +++
 src/feedback/GEAR/initial_mass_function.c     |  543 +++++++++
 src/feedback/GEAR/initial_mass_function.h     |   60 +
 src/feedback/GEAR/interpolation.h             |  207 ++++
 src/feedback/GEAR/lifetime.h                  |  242 ++++
 src/feedback/GEAR/stellar_evolution.c         |  502 ++++++++
 src/feedback/GEAR/stellar_evolution.h         |   70 ++
 src/feedback/GEAR/stellar_evolution_struct.h  |  163 +++
 src/feedback/GEAR/supernovae_ia.c             |  418 +++++++
 src/feedback/GEAR/supernovae_ia.h             |   63 +
 src/feedback/GEAR/supernovae_ii.c             |  432 +++++++
 src/feedback/GEAR/supernovae_ii.h             |   67 ++
 src/feedback/none/feedback.h                  |   31 +-
 src/feedback/none/feedback_struct.h           |    5 +
 src/feedback_properties.h                     |    2 +
 src/feedback_struct.h                         |    2 +
 src/hydro/AnarchyPU/hydro_part.h              |    4 +
 src/hydro/Default/hydro_part.h                |    4 +
 src/hydro/Gadget2/hydro_part.h                |    4 +
 src/hydro/Gizmo/hydro_part.h                  |    4 +
 src/hydro/Minimal/hydro_part.h                |    4 +
 src/hydro/Planetary/hydro_part.h              |    4 +
 src/hydro/PressureEnergy/hydro.h              |    4 +
 src/hydro/PressureEnergy/hydro_part.h         |    4 +
 .../hydro_part.h                              |    4 +
 src/hydro/PressureEntropy/hydro_part.h        |    4 +
 src/hydro/SPHENIX/hydro_part.h                |    4 +
 src/hydro/Shadowswift/hydro_part.h            |    4 +
 src/parallel_io.c                             |    2 +
 src/pressure_floor/GEAR/pressure_floor.h      |    2 +-
 src/random.h                                  |    5 +-
 src/runner_ghost.c                            |   11 +-
 src/runner_others.c                           |   23 +-
 src/runner_recv.c                             |    2 +-
 src/serial_io.c                               |    2 +
 src/single_io.c                               |    2 +
 src/space.c                                   |    3 +-
 src/star_formation/EAGLE/star_formation.h     |    5 +-
 src/star_formation/GEAR/star_formation.h      |   23 +-
 .../GEAR/star_formation_struct.h              |    5 +-
 src/star_formation/none/star_formation.h      |    5 +-
 src/stars/EAGLE/stars.h                       |    6 +-
 src/stars/EAGLE/stars_part.h                  |    8 +-
 src/stars/GEAR/stars.h                        |   75 +-
 src/stars/GEAR/stars_iact.h                   |    9 +-
 src/stars/GEAR/stars_io.h                     |   11 +-
 src/stars/GEAR/stars_part.h                   |   38 +-
 src/task_order.h                              |    3 +
 src/task_order/EAGLE/task_order.h             |    8 +
 src/task_order/GEAR/task_order.h              |    8 +
 src/task_order/none/task_order.h              |   14 +
 95 files changed, 5704 insertions(+), 1338 deletions(-)
 create mode 100644 examples/GEAR/getChemistryTable.sh
 delete mode 100644 examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml
 delete mode 100755 examples/SubgridTests/SupernovaeFeedback/getGlass.sh
 delete mode 100644 examples/SubgridTests/SupernovaeFeedback/makeIC.py
 delete mode 100644 examples/SubgridTests/SupernovaeFeedback/run.sh
 create mode 100644 src/cooling/grackle/cooling.c
 create mode 100644 src/feedback/GEAR/feedback.c
 create mode 100644 src/feedback/GEAR/feedback.h
 create mode 100644 src/feedback/GEAR/feedback_iact.h
 create mode 100644 src/feedback/GEAR/feedback_properties.h
 create mode 100644 src/feedback/GEAR/feedback_struct.h
 create mode 100644 src/feedback/GEAR/hdf5_functions.h
 create mode 100644 src/feedback/GEAR/initial_mass_function.c
 create mode 100644 src/feedback/GEAR/initial_mass_function.h
 create mode 100644 src/feedback/GEAR/interpolation.h
 create mode 100644 src/feedback/GEAR/lifetime.h
 create mode 100644 src/feedback/GEAR/stellar_evolution.c
 create mode 100644 src/feedback/GEAR/stellar_evolution.h
 create mode 100644 src/feedback/GEAR/stellar_evolution_struct.h
 create mode 100644 src/feedback/GEAR/supernovae_ia.c
 create mode 100644 src/feedback/GEAR/supernovae_ia.h
 create mode 100644 src/feedback/GEAR/supernovae_ii.c
 create mode 100644 src/feedback/GEAR/supernovae_ii.h

diff --git a/configure.ac b/configure.ac
index f91c2bf710..9f50ba4563 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1439,13 +1439,14 @@ case "$with_subgrid" in
    ;;
    GEAR)
 	with_subgrid_cooling=grackle_0
-	with_subgrid_chemistry=GEAR
+	with_subgrid_chemistry=GEAR_10
 	with_subgrid_pressure_floor=GEAR
 	with_subgrid_stars=GEAR
 	with_subgrid_star_formation=GEAR
-	with_subgrid_feedback=none
+	with_subgrid_feedback=GEAR
 	with_subgrid_black_holes=none
-	with_subgrid_task_order=GEAR
+  # GEAR's order is not used anymore
+	with_subgrid_task_order=none
 	enable_fof=no
    ;;
    EAGLE)
@@ -1791,7 +1792,8 @@ esac
 #  chemistry function
 AC_ARG_WITH([chemistry],
    [AS_HELP_STRING([--with-chemistry=<function>],
-      [chemistry function @<:@none, GEAR, EAGLE default: none@:>@]
+      [chemistry function @<:@none, GEAR_*, EAGLE default: none@:>@
+      For GEAR, you need to provide the number of elements (e.g. GEAR_10)]
    )],
    [with_chemistry="$withval"],
    [with_chemistry="none"]
@@ -1809,8 +1811,10 @@ case "$with_chemistry" in
    none)
       AC_DEFINE([CHEMISTRY_NONE], [1], [No chemistry function])
    ;;
-   GEAR)
+   GEAR_*)
       AC_DEFINE([CHEMISTRY_GEAR], [1], [Chemistry taken from the GEAR model])
+      number_element=${with_chemistry:5}
+      AC_DEFINE_UNQUOTED([GEAR_CHEMISTRY_ELEMENT_COUNT], [$number_element], [Number of element to follow])
    ;;
    EAGLE)
       AC_DEFINE([CHEMISTRY_EAGLE], [1], [Chemistry taken from the EAGLE model])
@@ -1913,6 +1917,9 @@ case "$with_feedback" in
    EAGLE)
       AC_DEFINE([FEEDBACK_EAGLE], [1], [EAGLE stellar feedback and evolution model])
    ;;
+   GEAR)
+      AC_DEFINE([FEEDBACK_GEAR], [1], [GEAR stellar feedback and evolution model])
+   ;;
    none)
       AC_DEFINE([FEEDBACK_NONE], [1], [No feedback])
    ;;
@@ -2139,6 +2146,12 @@ AM_CONDITIONAL([HAVEEAGLECOOLING], [test $with_cooling = "EAGLE"])
 # Check if using EAGLE feedback
 AM_CONDITIONAL([HAVEEAGLEFEEDBACK], [test $with_feedback = "EAGLE"])
 
+# check if using grackle cooling
+AM_CONDITIONAL([HAVEGRACKLECOOLING], [test ${with_cooling:0:7} == "grackle"])
+
+# check if using gear feedback
+AM_CONDITIONAL([HAVEGEARFEEDBACK], [test $with_feedback == "GEAR"])
+
 # Handle .in files.
 AC_CONFIG_FILES([Makefile src/Makefile examples/Makefile examples/Cooling/CoolingRates/Makefile doc/Makefile doc/Doxyfile tests/Makefile])
 AC_CONFIG_FILES([argparse/Makefile tools/Makefile logger/Makefile logger/tests/Makefile])
diff --git a/examples/GEAR/AgoraDisk/agora_disk.yml b/examples/GEAR/AgoraDisk/agora_disk.yml
index 92f2532b31..839e969a74 100644
--- a/examples/GEAR/AgoraDisk/agora_disk.yml
+++ b/examples/GEAR/AgoraDisk/agora_disk.yml
@@ -7,15 +7,25 @@ InternalUnitSystem:
   UnitTemp_in_cgs:     1             # Kelvin
 
 Scheduler:
-  max_top_level_cells: 8
+  max_top_level_cells: 16
+  cell_extra_sparts:  5000       # (Optional) Number of spare sparts per top-level allocated at rebuild time for on-the-fly creation.
+  cell_max_size:             8000   # (Optional) Maximal number of interactions per task if we force the split (this is the default value).
+  cell_sub_size_pair_hydro:  2560 # (Optional) Maximal number of hydro-hydro interactions per sub-pair hydro/star task (this is the default value).
+  cell_sub_size_self_hydro:  3200     # (Optional) Maximal number of hydro-hydro interactions per sub-self hydro/star task (this is the default value).
+  cell_sub_size_pair_stars:  2560 # (Optional) Maximal number of hydro-star interactions per sub-pair hydro/star task (this is the default value).
+  cell_sub_size_self_stars:  3200     # (Optional) Maximal number of hydro-star interactions per sub-self hydro/star task (this is the default value).
+  cell_sub_size_pair_grav:   2560 # (Optional) Maximal number of interactions per sub-pair gravity task  (this is the default value).
+  cell_sub_size_self_grav:   3200     # (Optional) Maximal number of interactions per sub-self gravity task  (this is the default value).
+  cell_split_size:           100       # (Optional) Maximal number of particles per cell (this is the default value).
   
 # Parameters governing the time integration
 TimeIntegration:
   time_begin: 0.    # The starting time of the simulation (in internal units).
   time_end:   0.5  # The end time of the simulation (in internal units).
   dt_min:     1e-10 # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
-  
+  dt_max:     0.1  # The maximal time-step size of the simulation (in internal units).
+  max_dt_RMS_factor: 0.25  # (Optional) Dimensionless factor for the maximal displacement allowed based on the RMS velocities.
+
 # Parameters governing the snapshots
 Snapshots:
   basename:            agora_disk # Common part of the name of output files
@@ -29,10 +39,12 @@ Statistics:
 
 # Parameters for the self-gravity scheme
 Gravity:
-  eta:                    0.025    # Constant dimensionless multiplier for time integration.
+  eta:                    0.05    # Constant dimensionless multiplier for time integration.
   theta:                  0.7     # Opening angle (Multipole acceptance criterion)
-  comoving_softening:     0.08 # Comoving softening length (in internal units).
-  max_physical_softening: 0.08    # Physical softening length (in internal units).
+  comoving_DM_softening:     0.08 # Comoving softening length (in internal units).
+  max_physical_DM_softening: 0.08    # Physical softening length (in internal units).
+  comoving_baryon_softening:     0.08 # Comoving softening length (in internal units).
+  max_physical_baryon_softening: 0.08    # Physical softening length (in internal units).
   mesh_side_length:       32        # Number of cells along each axis for the periodic gravity mesh.
   
 # Parameters for the hydrodynamics scheme
@@ -40,6 +52,8 @@ SPH:
   resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
   CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
   minimal_temperature:   10.      # Kelvin
+  h_min_ratio:           0.1095       # (Optional) Minimal allowed smoothing length in units of the softening. Defaults to 0 if unspecified.
+  h_max:                     10.      # (Optional) Maximal allowed smoothing length in internal units. Defaults to FLT_MAX if unspecified.
 
 # Parameters related to the initial conditions
 InitialConditions:
@@ -55,13 +69,34 @@ LambdaCooling:
 
 # Cooling with Grackle 2.0
 GrackleCooling:
-  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
-  WithUVbackground: 1 # Enable or not the UV background
-  Redshift: 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
+  cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  with_UV_background: 1 # Enable or not the UV background
+  redshift: 0 # Redshift to use (-1 means time based redshift)
+  with_metal_cooling: 1 # Enable or not the metal cooling
+  provide_volumetric_heating_rates: 0 # User provide volumetric heating rates
+  provide_specific_heating_rates: 0 # User provide specific heating rates
+  self_shielding_method: -1 # Grackle (<= 3) or Gear self shielding method
+  self_shielding_threshold_atom_per_cm3: 0.007  # Required only with GEAR's self shielding. Density threshold of the self shielding
+  max_steps: 1000
+  convergence_limit: 1e-2
+  thermal_time_myr: 5
+
+
+GEARStarFormation:
+  star_formation_efficiency: 0.01   # star formation efficiency (c_*)
+  maximal_temperature:  1e10         # Upper limit to the temperature of a star forming particle
+
+GEARPressureFloor:
+  jeans_factor: 10
+
+GEARFeedback:
+  supernovae_energy_erg: 0.1e51
+  yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5
+  discrete_yields: 0
+
+GEARChemistry:
+  initial_metallicity: 1
+  scale_initial_metallicity: 1
+
+Restarts:
+  delta_hours:        72        # (Optional) decimal hours between dumps of restart files.
diff --git a/examples/GEAR/AgoraDisk/cleanupSwift.py b/examples/GEAR/AgoraDisk/cleanupSwift.py
index 31e6ed8ab0..865b0cd6d7 100644
--- a/examples/GEAR/AgoraDisk/cleanupSwift.py
+++ b/examples/GEAR/AgoraDisk/cleanupSwift.py
@@ -14,7 +14,7 @@ copyfile(filename, out)
 f = File(out)
 
 for i in range(6):
-    name = "PartType{}/ElementAbundance".format(i)
+    name = "PartType{}/ElementAbundances".format(i)
     if name in f:
         del f[name]
 
@@ -23,13 +23,27 @@ for i in range(NPartType):
     if name not in f:
         continue
 
-    grp = f[name + "/SmoothingLength"]
+    grp = f[name + "/SmoothingLengths"]
     grp[:] *= 1.823
 
+    # fix issue due to the name of densities
+    fields = [("Density", "Densities"),
+              ("Entropies", "Entropies"),
+              ("InternalEnergy", "InternalEnergies"),
+              ("SmoothingLength", "SmoothingLengths")]
+
+    for field in fields:
+        if field[1] in f[name] and field[0] not in f[name]:
+            f[name + "/" + field[0]] = f[name + "/" + field[1]]
+
+
+
+
 cosmo = f["Cosmology"].attrs
 head = f["Header"].attrs
 head["OmegaLambda"] = cosmo["Omega_lambda"]
 head["Omega0"] = cosmo["Omega_b"]
 head["HubbleParam"] = cosmo["H0 [internal units]"]
+head["Time"] = head["Time"][0]
 
 f.close()
diff --git a/examples/GEAR/AgoraDisk/getIC.sh b/examples/GEAR/AgoraDisk/getIC.sh
index c234b52b94..ca7fe3b251 100755
--- a/examples/GEAR/AgoraDisk/getIC.sh
+++ b/examples/GEAR/AgoraDisk/getIC.sh
@@ -6,4 +6,4 @@ if [ "$#" -ne 1 ]; then
     exit
 fi
 
-wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/$1.hdf5
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/AgoraDisk/$1.hdf5
diff --git a/examples/GEAR/AgoraDisk/getSolution.sh b/examples/GEAR/AgoraDisk/getSolution.sh
index 37b7f8031b..681786a76c 100755
--- a/examples/GEAR/AgoraDisk/getSolution.sh
+++ b/examples/GEAR/AgoraDisk/getSolution.sh
@@ -1,12 +1,4 @@
 #!/bin/bash
 
-# cleanup work space
-rm snapshot_0000.hdf5 snapshot_0500.hdf5
-
-wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/with_cooling/snapshot_0000.hdf5
-wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/with_cooling/snapshot_0500.hdf5
-
-# wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/without_cooling/snapshot_0000.hdf5
-# wget https://obswww.unige.ch/~lhausamm/swift/IC/AgoraDisk/Gear/without_cooling/snapshot_0500.hdf5
-
-
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/AgoraDisk/Gear/snapshot_0000.hdf5
+wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/AgoraDisk/Gear/snapshot_0050.hdf5
diff --git a/examples/GEAR/AgoraDisk/plotSolution.py b/examples/GEAR/AgoraDisk/plotSolution.py
index 8db2c98dbc..b3a831b2d9 100644
--- a/examples/GEAR/AgoraDisk/plotSolution.py
+++ b/examples/GEAR/AgoraDisk/plotSolution.py
@@ -34,7 +34,7 @@ from yt.analysis_modules.halo_analysis.api import *
 from yt.data_objects.particle_filters import add_particle_filter
 from scipy.stats import kde
 from subprocess import call
-#mylog.setLevel(1)
+# mylog.setLevel(1)
 
 from yt.utilities.math_utils import get_cyl_r, get_cyl_theta, get_cyl_z
 
@@ -53,7 +53,7 @@ draw_PDF                         = 2###2         # 0/1/2/3     = OFF/ON/ON with
 draw_pos_vel_PDF                 = 4##4          # 0/1/2/3/4   = OFF/ON/ON with 1D profile/ON also with 1D dispersion profile/ON also with separate 1D vertical dispersion profiles
 draw_star_pos_vel_PDF            = 4##4          # 0/1/2/3/4   = OFF/ON/ON with 1D profile/ON also with 1D dispersion profile/ON also with separate 1D vertical dispersion profiles
 draw_rad_height_PDF              = 2##2          # 0/1/2/3     = OFF/ON/ON with 1D profile/ON with analytic ftn subtracted
-draw_metal_PDF                   = 0##0          # 0/1         = OFF/ON
+draw_metal_PDF                   = 1##1          # 0/1         = OFF/ON
 draw_density_DF                  = 2#2           # 0/1/2       = OFF/ON/ON with difference plot between 1st and 2nd datasets (when 2, dataset_num should be set to 2)
 draw_radius_DF                   = 1#1           # 0/1         = OFF/ON
 draw_star_radius_DF              = 2#2           # 0/1/2       = OFF/ON/ON with SFR profile and K-S plot (when 2, this automatically turns on draw_radius_DF)
@@ -110,9 +110,9 @@ marker_names             = ['s', 'o', 'p', 'v', '^', '<', '>', 'h', '*']
 #             [file_location[1]+'GIZMO/snapshot_temp_000', file_location[1]+'GIZMO/snapshot_temp_100']]]
 codes = ['SWIFT', 'GEAR']
 filenames = [[["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"], # Sim-noSFF
-              ["./snapshot_0000.hdf5", "./snapshot_0500.hdf5"]],
+              ["./snapshot_0000.hdf5", "./snapshot_0050.hdf5"]],
              [["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"], # Sim-SFF (with star formation and feedback)
-              ["./snapshot_0000.hdf5", "./snapshot_0500.hdf5"]]] # I did not check the order, they can be switched
+              ["./snapshot_0000.hdf5", "./snapshot_0050.hdf5"]]] # I did not check the order, they can be switched
 
 # codes = ["SWIFT"]
 # filenames = [[["./agora_disk_0000.hdf5", "./agora_disk_0050.hdf5"]],
@@ -139,8 +139,8 @@ filenames = [[["./agora_disk_IC.hdf5", "./agora_disk_500Myr.hdf5"], # Sim-noSFF
 # filenames = [[[file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_000.hdf5', file_location[0]+'GADGET-3/AGORA_ISO_LOW_DRY/snap_iso_dry_010.hdf5']],
 #            [[file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_000.hdf5', file_location[1]+'GADGET-3/AGORA_ISO_LOW_SF_SNII_Thermal_Chevalier_SFT10/snap_iso_sf_010.hdf5']]]
 # codes = ['GEAR']
-# filenames = [[['snapshot_0000', 'snapshot_0500']],
-#            [['snapshot_0000', 'snapshot_0500']]]
+# filenames = [[['snapshot_0000.hdf5', 'snapshot_0500.hdf5']],
+#            [['snapshot_0000.hdf5', 'snapshot_0500']]]
 # codes = ['GIZMO']
 # filenames = [[[file_location[0]+'GIZMO/snapshot_temp_000', file_location[0]+'GIZMO/snapshot_temp_100']],
 #            [[file_location[1]+'GIZMO/snapshot_temp_000', file_location[1]+'GIZMO/snapshot_temp_100']]]
@@ -439,7 +439,7 @@ for time in range(len(times)):
                                         yield line
         if draw_PDF >= 1:
                 fig_PDF              += [plt.figure(figsize=(50, 80))]
-                grid_PDF             += [AxesGrid(fig_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True,
+                grid_PDF             += [AxesGrid(fig_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, int(math.ceil(len(codes)/2.0))), axes_pad = 0.05, add_all = True, share_all = True,
                                                   label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)]
         if draw_pos_vel_PDF >= 1:
                 fig_pos_vel_PDF      += [plt.figure(figsize=(50, 80))]
@@ -469,7 +469,7 @@ for time in range(len(times)):
                 rad_height_profiles.append([])
         if draw_metal_PDF == 1:
                 fig_metal_PDF        += [plt.figure(figsize=(50, 80))]
-                grid_metal_PDF       += [AxesGrid(fig_metal_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (3, int(math.ceil(len(codes)/3.0))), axes_pad = 0.05, add_all = True, share_all = True,
+                grid_metal_PDF       += [AxesGrid(fig_metal_PDF[time], (0.01,0.01,0.99,0.99), nrows_ncols = (2, int(math.ceil(len(codes)/2.0))), axes_pad = 0.05, add_all = True, share_all = True,
                                                   label_mode = "1", cbar_mode = "single", cbar_location = "right", cbar_size = "2%", cbar_pad = 0.05, aspect = False)]
         if draw_density_DF >= 1:
                 density_DF_xs.append([])
@@ -576,9 +576,9 @@ for time in range(len(times)):
                         PartType_StarBeforeFiltered_to_use = "PartType4"
                         if time != 0:
                             def _FormationTime(field, data):
-                                return pf.arr(data["PartType4", "BirthTime"].d, 'code_time')
+                                return pf.arr(data["PartType4", "BirthTimes"].d, 'code_time')
                             pf.add_field(("PartType4", FormationTimeType_to_use), function=_FormationTime, particle_type=True, take_log=False, units="code_time")
-                        
+
                         MassType_to_use = "Masses"
                 elif codes[code] == "GEAR":
                         PartType_Gas_to_use = "PartType0"
@@ -715,6 +715,20 @@ for time in range(len(times)):
                                 def _metallicity_3(field, data):
                                         return data["deposit", PartType_Gas_to_use+"_smoothed_"+MetallicityType_to_use]
                                 pf.add_field(("gas", "metallicity"), function=_metallicity_3, force_override=True, display_name="Metallicity", particle_type=False, take_log=True, units="")
+                        elif codes[code] == 'SWIFT': # "Metals" in SWIFT is 10-species field ([:,9] is the total metal fraction), so requires a change in _vector_fields in frontends/gadget/io.py: added ("Metals", 10)
+                                def _metallicity_2(field, data):
+                                        if len(data[PartType_Gas_to_use, "SmoothedElementAbundances"].shape) == 1:
+                                                return data[PartType_Gas_to_use, "SmoothedElementAbundances"]
+                                        else:
+                                                return data[PartType_Gas_to_use, "SmoothedElementAbundances"][:,9].in_units("") # in_units("") turned out to be crucial!; otherwise code_metallicity will be used and it will mess things up
+                                # We are creating ("Gas", "Metallicity") here, different from ("Gas", "metallicity") which is auto-generated by yt but doesn't work properly
+                                pf.add_field((PartType_Gas_to_use, MetallicityType_to_use), function=_metallicity_2, display_name="Metallicity", particle_type=True, take_log=True, units="")
+                                # Also creating smoothed field following an example in yt-project.org/docs/dev/cookbook/calculating_information.html; use hardcoded num_neighbors as in frontends/gadget/fields.py
+                                fn = add_volume_weighted_smoothed_field(PartType_Gas_to_use, "Coordinates", MassType_to_use, "SmoothingLength", "Density", MetallicityType_to_use, pf.field_info, nneighbors=64)
+                                # Alias doesn't work -- e.g. pf.field_info.alias(("gas", "metallicity"), fn[0]) -- probably because pf=GadgetDataset()?, not load()?; so I add and replace existing ("gas", "metallicity")
+                                def _metallicity_3(field, data):
+                                        return data["deposit", PartType_Gas_to_use+"_smoothed_"+MetallicityType_to_use]
+                                pf.add_field(("gas", "metallicity"), function=_metallicity_3, force_override=True, display_name="Metallicity", particle_type=False, take_log=True, units="")
                         if draw_metal_map >= 2:
                                 def _metal_mass(field, data):
                                         return data["gas", "cell_mass"] * data["gas", "metallicity"]
@@ -745,7 +759,7 @@ for time in range(len(times)):
                         if draw_metal_map >= 1 or draw_metal_PDF == 1:
                                 def _Metallicity_2(field, data):
                                         return data[(PartType_Gas_to_use, MetallicityType_to_use)]
-                                pf.add_field((PartType_Gas_to_use, "Metallicity_2"), function=_Metallicity_2, take_log=True, particle_type=False, display_name="Metallicity", units="")
+                                pf.add_field((PartType_Gas_to_use, "Metallicity_2"), function=_Metallicity_2, take_log=True, particle_type=True, display_name="Metallicity", units="")
                         def _Density_2_minus_analytic(field, data):
                                 return data[(PartType_Gas_to_use, "density")] - rho_agora_disk(data[(PartType_Gas_to_use, "radius")], data[(PartType_Gas_to_use, "particle_position_cylindrical_z_abs")])
                         pf.add_field((PartType_Gas_to_use, "Density_2_minus_analytic"), function=_Density_2_minus_analytic, take_log=False, particle_type=False, display_name="Density Residual", units="g/cm**3")
@@ -1565,13 +1579,18 @@ for time in range(len(times)):
                         else:
                                 # Because ParticlePhasePlot doesn't yet work for a log-log PDF for some reason, I will do the following trick.
                                 pf.field_info[(PartType_Gas_to_use, "Metallicity_2")].take_log = False
-                                p3 = PhasePlot(sp, (PartType_Gas_to_use, "density"), (PartType_Gas_to_use, "temperature"), (PartType_Gas_to_use, "Metallicity_2"), weight_field=(PartType_Gas_to_use, MassType_to_use), fontsize=12, x_bins=300, y_bins=300)
+                                p3 = ParticlePhasePlot(sp, (PartType_Gas_to_use, "density"), (PartType_Gas_to_use, "temperature"), (PartType_Gas_to_use, "Metallicity_2"),
+                                                       weight_field=(PartType_Gas_to_use, MassType_to_use), fontsize=12, x_bins=300, y_bins=300)
                                 p3.set_zlim((PartType_Gas_to_use, "Metallicity_2"), 0.01, 0.04)
                                 p3.set_cmap((PartType_Gas_to_use, "Metallicity_2"), my_cmap2)
                                 plot3 = p3.plots[(PartType_Gas_to_use, "Metallicity_2")]
 
+                        p3.set_unit("density", "g/cm**3")
                         p3.set_xlim(1e-29, 1e-21)
+                        p3.set_unit("temperature", "K")
                         p3.set_ylim(10, 1e7)
+                        p3.set_log("density", True)
+                        p3.set_log("temperature", True)
 
                         plot3.figure = fig_metal_PDF[time]
                         plot3.axes = grid_metal_PDF[time][code].axes
@@ -2271,7 +2290,7 @@ for time in range(len(times)):
         if draw_metal_PDF == 1:
                 fig_metal_PDF[time].savefig("metal_PDF_%dMyr" % times[time], bbox_inches='tight', pad_inches=0.03, dpi=300)
         if draw_density_DF >= 1:
-                plt.clf()
+#                 plt.clf()
 #		plt.subplot(111, aspect=1)
                 fig = plt.figure(figsize=(8, 8))
                 gridspec.GridSpec(4, 1)
diff --git a/examples/GEAR/AgoraDisk/run.sh b/examples/GEAR/AgoraDisk/run.sh
index 8284098a1a..b2956e086d 100755
--- a/examples/GEAR/AgoraDisk/run.sh
+++ b/examples/GEAR/AgoraDisk/run.sh
@@ -5,6 +5,8 @@
 # currently only the low resolution is available
 sim=low
 
+rm agora_disk_0*.hdf5
+
 # make run.sh fail if a subcommand fails
 set -e
 
@@ -19,7 +21,13 @@ fi
 if [ ! -e CloudyData_UVB=HM2012.h5 ]
 then
     echo "Fetching the Cloudy tables required by Grackle..."
-    ../../Cooling/getGrackleCoolingTable.sh 
+    ../../Cooling/getGrackleCoolingTable.sh
+fi
+
+if [ ! -e chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5 ]
+then
+    echo "Fetching the chemistry tables..."
+    ../getChemistryTable.sh
 fi
 
 # copy the initial conditions
@@ -28,7 +36,7 @@ cp $sim.hdf5 agora_disk.hdf5
 python3 changeType.py agora_disk.hdf5
 
 # Run SWIFT
-../../swift --cooling --hydro --self-gravity --threads=4 agora_disk.yml 2>&1 | tee output.log
+../../swift --sync --limiter --cooling --hydro --self-gravity --star-formation --feedback --stars --threads=8 agora_disk.yml 2>&1 | tee output.log
 
 
 echo "Changing smoothing length to be Gadget compatible"
diff --git a/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml
index 00fd889d4f..d377031585 100644
--- a/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml
+++ b/examples/GEAR/DwarfGalaxy/dwarf_galaxy.yml
@@ -68,3 +68,31 @@ InitialConditions:
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
+
+# Cooling with Grackle 3.0
+GrackleCooling:
+  cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  with_UV_background: 1                   # Enable or not the UV background
+  redshift: -1                           # Redshift to use (-1 means time based redshift)
+  with_metal_cooling: 1                   # Enable or not the metal cooling
+  provide_volumetric_heating_rates: 0      # (optional) User provide volumetric heating rates
+  provide_specific_heating_rates: 0        # (optional) User provide specific heating rates
+  self_shielding_method: 0                # (optional) Grackle (<= 3) or Gear self shielding method
+  max_steps: 10000                       # (optional) Max number of step when computing the initial composition
+  convergence_limit: 1e-2                # (optional) Convergence threshold (relative) for initial composition
+  thermal_time_Myr:  5
+
+GearChemistry:
+  initial_metallicity: 0.01295
+
+GEARFeedback:
+  supernovae_energy_erg: 1e50
+  yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5
+
+GearPressureFloor:
+  jeans_factor: 10.       # Number of particles required to suppose a resolved clump and avoid the pressure floor.
+
+
+GEARStarFormation:
+  star_formation_efficiency: 0.01   # star formation efficiency (c_*)
+  maximal_temperature:  3e4         # Upper limit to the temperature of a star forming particle
diff --git a/examples/GEAR/DwarfGalaxy/run.sh b/examples/GEAR/DwarfGalaxy/run.sh
index 1f63556e7f..11be3465d2 100755
--- a/examples/GEAR/DwarfGalaxy/run.sh
+++ b/examples/GEAR/DwarfGalaxy/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../../swift --feedback --self-gravity --hydro --stars --threads=8 $@ dwarf_galaxy.yml 2>&1 | tee output.log
+../../swift --feedback --limiter --sync --self-gravity --hydro --stars --cooling --star-formation --threads=8 $@ dwarf_galaxy.yml 2>&1 | tee output.log
 
diff --git a/examples/GEAR/ZoomIn/run.sh b/examples/GEAR/ZoomIn/run.sh
index 59c8ff0d63..58961e0253 100755
--- a/examples/GEAR/ZoomIn/run.sh
+++ b/examples/GEAR/ZoomIn/run.sh
@@ -7,5 +7,5 @@ then
     ./getIC.sh
 fi
 
-../../swift --feedback --cosmology --self-gravity --hydro --stars --threads=8 zoom_in.yml 2>&1 | tee output.log
+../../swift --feedback --cosmology  --limiter --sync --self-gravity --hydro --stars --star-formation --threads=8 zoom_in.yml 2>&1 | tee output.log
 
diff --git a/examples/GEAR/ZoomIn/zoom_in.yml b/examples/GEAR/ZoomIn/zoom_in.yml
index 8e5763c4af..1d01e6abd3 100644
--- a/examples/GEAR/ZoomIn/zoom_in.yml
+++ b/examples/GEAR/ZoomIn/zoom_in.yml
@@ -60,3 +60,30 @@ InitialConditions:
   cleanup_h_factors: 1               # Remove the h-factors inherited from Gadget
   cleanup_velocity_factors: 1        # Remove the sqrt(a) factor in the velocities inherited from Gadget
 
+
+# Cooling with Grackle 3.0
+GrackleCooling:
+  cloudy_table: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  with_UV_background: 1                   # Enable or not the UV background
+  redshift: -1                           # Redshift to use (-1 means time based redshift)
+  with_metal_cooling: 1                   # Enable or not the metal cooling
+  provide_volumetric_heating_rates: 0      # (optional) User provide volumetric heating rates
+  provide_specific_heating_rates: 0        # (optional) User provide specific heating rates
+  self_shielding_method: 0                # (optional) Grackle (<= 3) or Gear self shielding method
+  max_steps: 10000                       # (optional) Max number of step when computing the initial composition
+  convergence_limit: 1e-2                # (optional) Convergence threshold (relative) for initial composition
+  thermal_time_Myr:  5
+
+GearChemistry:
+  initial_metallicity: 0.01295
+
+GEARFeedback:
+  supernovae_energy_erg: 1e50
+  yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5
+
+GEARStarFormation:
+  star_formation_efficiency: 0.01   # star formation efficiency (c_*)
+  maximal_temperature:  3e4         # Upper limit to the temperature of a star forming particle
+
+GEARPressureFloor:
+  jeans_factor: 10.       # Number of particles required to suppose a resolved clump and avoid the pressure floor.
diff --git a/examples/GEAR/getChemistryTable.sh b/examples/GEAR/getChemistryTable.sh
new file mode 100644
index 0000000000..b8f8ac7886
--- /dev/null
+++ b/examples/GEAR/getChemistryTable.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+wget https://obswww.unige.ch/lastro/projects/Clastro/PySWIFTsim/chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5
diff --git a/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml b/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml
deleted file mode 100644
index a59ae302ff..0000000000
--- a/examples/SubgridTests/SupernovaeFeedback/SN_feedback.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-# Define the system of units to use internally. 
-InternalUnitSystem:
-  UnitMass_in_cgs:     1   # Grams
-  UnitLength_in_cgs:   1   # Centimeters
-  UnitVelocity_in_cgs: 1   # Centimeters per second
-  UnitCurrent_in_cgs:  1   # Amperes
-  UnitTemp_in_cgs:     1   # Kelvin
-
-# Values of some physical constants
-PhysicalConstants:
-  G:            0 # (Optional) Overwrite the value of Newton's constant used internally by the code.
-
-# Parameters governing the time integration
-TimeIntegration:
-  time_begin: 0.    # The starting time of the simulation (in internal units).
-  time_end:   5e-2  # The end time of the simulation (in internal units).
-  dt_min:     1e-7  # The minimal time-step size of the simulation (in internal units).
-  dt_max:     1e-4  # The maximal time-step size of the simulation (in internal units).
-
-# Parameters governing the snapshots
-Snapshots:
-  basename:            SN_feedback # Common part of the name of output files
-  time_first:          0.    # Time of the first output (in internal units)
-  delta_time:          1e-2  # Time difference between consecutive outputs (in internal units)
-  compression:         1
- 
-# Parameters governing the conserved quantities statistics
-Statistics:
-  delta_time:          1e-3 # Time between statistics output
-
-# Parameters for the hydrodynamics scheme
-SPH:
-  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
-  CFL_condition:         0.1      # Courant-Friedrich-Levy condition for time integration.
-
-# Parameters related to the initial conditions
-InitialConditions:
-  file_name:                    ./SN_feedback.hdf5          
-  smoothing_length_scaling:     1.
-  periodic:                    1    # Are we running with periodic ICs?
- 
-# Parameters for the stellar models
-Stars:
-  resolution_eta:        1.2348   # Target smoothing length in units of the mean inter-particle separation (1.2348 == 48Ngbs with the cubic spline kernel).
diff --git a/examples/SubgridTests/SupernovaeFeedback/getGlass.sh b/examples/SubgridTests/SupernovaeFeedback/getGlass.sh
deleted file mode 100755
index d5c5f590ac..0000000000
--- a/examples/SubgridTests/SupernovaeFeedback/getGlass.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/bash
-wget http://virgodb.cosma.dur.ac.uk/swift-webstorage/ICs/glassCube_64.hdf5
diff --git a/examples/SubgridTests/SupernovaeFeedback/makeIC.py b/examples/SubgridTests/SupernovaeFeedback/makeIC.py
deleted file mode 100644
index 8a0fca2bfd..0000000000
--- a/examples/SubgridTests/SupernovaeFeedback/makeIC.py
+++ /dev/null
@@ -1,117 +0,0 @@
-###############################################################################
- # 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/>.
- # 
- ##############################################################################
-
-import h5py
-from numpy import *
-
-# Generates a swift IC file for the Sedov blast test in a periodic cubic box
-
-# Parameters
-gamma = 5./3.      # Gas adiabatic index
-rho0 = 1.          # Background density
-P0 = 1.e-6         # Background pressure
-E0= 1.             # Energy of the explosion
-N_inject = 15      # Number of particles in which to inject energy
-fileName = "SN_feedback.hdf5" 
-
-#---------------------------------------------------
-glass = h5py.File("glassCube_64.hdf5", "r")
-
-# Read particle positions and h from the glass
-pos = glass["/PartType0/Coordinates"][:,:]
-eps = 1e-6
-pos = (pos - pos.min()) / (pos.max() - pos.min() + eps)
-h = glass["/PartType0/SmoothingLength"][:] * 0.3 * 3.3
-
-numPart = size(h)
-vol = 1.
-Boxsize = 1.
-
-# Generate extra arrays
-v = zeros((numPart, 3))
-ids = linspace(1, numPart, numPart)
-m = zeros(numPart)
-u = zeros(numPart)
-r = zeros(numPart)
-
-r = sqrt((pos[:,0] - 0.5)**2 + (pos[:,1] - 0.5)**2 + (pos[:,2] - 0.5)**2)
-m[:] = rho0 * vol / numPart    
-u[:] = P0 / (rho0 * (gamma - 1))
-
-#--------------------------------------------------
-
-star_pos = zeros((1, 3))
-star_pos[:,:] = 0.5 * Boxsize
-
-star_v = zeros((1, 3))
-star_v[:,:] = 0.
-
-# increase mass to keep it at center
-star_m = 1e3 * array([rho0 * vol / numPart])
-star_ids = array([numPart + 1])
-star_h = array([h.max()])
-
-#--------------------------------------------------
-
-#File
-file = h5py.File(fileName, 'w')
-
-# Header
-grp = file.create_group("/Header")
-grp.attrs["BoxSize"] = [Boxsize]*3
-grp.attrs["NumPart_Total"] =  [numPart, 0, 0, 0, 1, 0]
-grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
-grp.attrs["NumPart_ThisFile"] = [numPart, 0, 0, 0, 1, 0]
-grp.attrs["Time"] = 0.0
-grp.attrs["NumFilesPerSnapshot"] = 1
-grp.attrs["MassTable"] = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
-grp.attrs["Flag_Entropy_ICs"] = 0
-grp.attrs["Dimension"] = 3
-
-#Runtime parameters
-grp = file.create_group("/RuntimePars")
-grp.attrs["PeriodicBoundariesOn"] = 0
-
-#Units
-grp = file.create_group("/Units")
-grp.attrs["Unit length in cgs (U_L)"] = 1.
-grp.attrs["Unit mass in cgs (U_M)"] = 1.
-grp.attrs["Unit time in cgs (U_t)"] = 1.
-grp.attrs["Unit current in cgs (U_I)"] = 1.
-grp.attrs["Unit temperature in cgs (U_T)"] = 1.
-
-#Particle group
-grp = file.create_group("/PartType0")
-grp.create_dataset('Coordinates', data=pos, dtype='d')
-grp.create_dataset('Velocities', data=v, dtype='f')
-grp.create_dataset('Masses', data=m, dtype='f')
-grp.create_dataset('SmoothingLength', data=h, dtype='f')
-grp.create_dataset('InternalEnergy', data=u, dtype='f')
-grp.create_dataset('ParticleIDs', data=ids, dtype='L')
-
-# stellar group
-grp = file.create_group("/PartType4")
-grp.create_dataset("Coordinates", data=star_pos, dtype="d")
-grp.create_dataset('Velocities', data=star_v, dtype='f')
-grp.create_dataset('Masses', data=star_m, dtype='f')
-grp.create_dataset('SmoothingLength', data=star_h, dtype='f')
-grp.create_dataset('ParticleIDs', data=star_ids, dtype='L')
-
-
-file.close()
diff --git a/examples/SubgridTests/SupernovaeFeedback/run.sh b/examples/SubgridTests/SupernovaeFeedback/run.sh
deleted file mode 100644
index af8802164c..0000000000
--- a/examples/SubgridTests/SupernovaeFeedback/run.sh
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-
- # Generate the initial conditions if they are not present.
-if [ ! -e glassCube_64.hdf5 ]
-then
-    echo "Fetching initial glass file for the Supernovae feedback example..."
-    ./getGlass.sh
-fi
-if [ ! -e SN_feedback.hdf5 ]
-then
-    echo "Generating initial conditions for the Supernovae feedback example..."
-    python makeIC.py
-fi
-
-# Run SWIFT
-../../swift --external-gravity --feedback --hydro --stars --threads=4 SN_feedback.yml 2>&1 | tee output.log
-
-# Plot the solution
-# TODO
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 754b0d9986..4d43ae30f5 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -307,7 +307,7 @@ EAGLEEntropyFloor:
 # Parameters related to pressure floors    ----------------------------------------------
 
 GEARPressureFloor:
-  Jeans_factor: 10.       # Number of particles required to suppose a resolved clump and avoid the pressure floor.
+  jeans_factor: 10.       # Number of particles required to suppose a resolved clump and avoid the pressure floor.
 
 # Parameters related to cooling function  ----------------------------------------------
 
@@ -335,15 +335,17 @@ EAGLECooling:
   
 # Cooling with Grackle 3.0
 GrackleCooling:
-  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table (available on the grackle bitbucket repository)
-  WithUVbackground: 1                   # Enable or not the UV background
-  Redshift: 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
-  MaxSteps: 10000                       # (optional) Max number of step when computing the initial composition
-  ConvergenceLimit: 1e-2                # (optional) Convergence threshold (relative) for initial composition
+  cloudy_table: CloudyData_UVB=HM2012.h5       # Name of the Cloudy Table (available on the grackle bitbucket repository)
+  with_UV_background: 1                         # Enable or not the UV background
+  redshift: 0                                 # Redshift to use (-1 means time based redshift)
+  with_metal_cooling: 1                         # Enable or not the metal cooling
+  provide_volumetric_heating_rates: 0            # (optional) User provide volumetric heating rates
+  provide_specific_heating_rates: 0              # (optional) User provide specific heating rates
+  max_steps: 10000                             # (optional) Max number of step when computing the initial composition
+  convergence_limit: 1e-2                      # (optional) Convergence threshold (relative) for initial composition
+  thermal_time_myr: 5                          # (optional) Time (in Myr) for adiabatic cooling after a feedback event.
+  self_shielding_method: -1                     # (optional) Grackle (1->3 for Grackle's ones, 0 for none and -1 for GEAR)
+  self_shielding_threshold_atom_per_cm3: 0.007  # Required only with GEAR's self shielding. Density threshold of the self shielding
 
 # Parameters related to chemistry models  -----------------------------------------------
 
@@ -360,6 +362,12 @@ EAGLEChemistry:
   init_abundance_Silicon:   0.000        # Inital fraction of particle mass in Silicon
   init_abundance_Iron:      0.000        # Inital fraction of particle mass in Iron
 
+# GEAR chemistry model (Revaz and Jablonka 2018)
+GEARChemistry:
+  initial_metallicity: 1         # Initial metallicity of the gas (mass fraction)
+  scale_initial_metallicity: 1    # Should we scale the initial metallicity with the solar one?
+
+
 # Parameters related to star formation models  -----------------------------------------------
 
 # GEAR star formation model (Revaz and Jablonka 2018)
@@ -428,6 +436,12 @@ EAGLEFeedback:
   SNII_yield_factor_Silicon:        1.0             # (Optional) Correction factor to apply to the Silicon yield from the SNII channel.
   SNII_yield_factor_Iron:           0.5             # (Optional) Correction factor to apply to the Iron yield from the SNII channel.
 
+# GEAR feedback model
+GEARFeedback:
+  supernovae_energy_erg: 0.1e51                      # Energy released by a single supernovae.
+  yields_table: chemistry-AGB+OMgSFeZnSrYBaEu-16072013.h5  # Table containing the yields.
+  discrete_yields: 0                                 # Should we use discrete yields or the IMF integrated one?
+
 # Parameters related to AGN models  -----------------------------------------------
   
 # EAGLE AGN model
diff --git a/src/Makefile.am b/src/Makefile.am
index 54b766c46e..7144496b68 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -71,6 +71,19 @@ if HAVEEAGLEFEEDBACK
 EAGLE_FEEDBACK_SOURCES += feedback/EAGLE/feedback.c
 endif
 
+# source files for GRACKLE cooling
+GRACKLE_COOLING_SOURCES =
+if HAVEGRACKLECOOLING
+GRACKLE_COOLING_SOURCES += cooling/grackle/cooling.c
+endif
+
+# source files for GRACKLE cooling
+GEAR_FEEDBACK_SOURCES =
+if HAVEGEARFEEDBACK
+GEAR_FEEDBACK_SOURCES += feedback/GEAR/stellar_evolution.c feedback/GEAR/feedback.c \
+	feedback/GEAR/initial_mass_function.c feedback/GEAR/supernovae_ia.c feedback/GEAR/supernovae_ii.c
+endif
+
 # Common source files
 AM_SOURCES = space.c runner_main.c runner_doiact_hydro.c runner_doiact_limiter.c \
     runner_doiact_stars.c runner_doiact_black_holes.c runner_ghost.c runner_recv.c \
@@ -89,7 +102,8 @@ AM_SOURCES = space.c runner_main.c runner_doiact_hydro.c runner_doiact_limiter.c
     chemistry.c cosmology.c restart.c mesh_gravity.c velociraptor_interface.c \
     outputlist.c velociraptor_dummy.c logger_io.c memuse.c mpiuse.c memuse_rnodes.c fof.c \
     hashmap.c pressure_floor.c \
-    $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES)
+    $(EAGLE_COOLING_SOURCES) $(EAGLE_FEEDBACK_SOURCES) $(GRACKLE_COOLING_SOURCES) \
+		$(GEAR_FEEDBACK_SOURCES)
 
 # Include files for distribution, not installation.
 nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h inline.h kernel_hydro.h kernel_gravity.h \
@@ -182,6 +196,8 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h
 		 stars/Default/stars_debug.h stars/Default/stars_part.h  \
 		 stars/EAGLE/stars.h stars/EAGLE/stars_iact.h stars/EAGLE/stars_io.h \
 		 stars/EAGLE/stars_debug.h stars/EAGLE/stars_part.h \
+		 stars/GEAR/stars.h stars/GEAR/stars_iact.h stars/GEAR/stars_io.h \
+		 stars/GEAR/stars_debug.h stars/GEAR/stars_part.h \
 	         potential/none/potential.h potential/point_mass/potential.h \
                  potential/isothermal/potential.h potential/disc_patch/potential.h \
                  potential/sine_wave/potential.h \
@@ -229,6 +245,11 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h
 	         feedback/EAGLE/feedback.h feedback/EAGLE/feedback_struct.h feedback/EAGLE/feedback_iact.h \
                  feedback/EAGLE/feedback_properties.h feedback/EAGLE/imf.h feedback/EAGLE/interpolate.h \
                  feedback/EAGLE/yield_tables.h \
+		 feedback/GEAR/stellar_evolution_struct.h feedback/GEAR/stellar_evolution.h \
+		 feedback/GEAR/feedback.h feedback/GEAR/feedback_iact.h \
+		 feedback/GEAR/feedback_properties.h feedback/GEAR/feedback_struct.h \
+                 feedback/GEAR/initial_mass_function.h feedback/GEAR/supernovae_ia.h feedback/GEAR/supernovae_ii.h \
+                 feedback/GEAR/lifetime.h feedback/GEAR/hdf5_functions.h feedback/GEAR/interpolation.h \
                  black_holes/Default/black_holes.h black_holes/Default/black_holes_io.h \
 		 black_holes/Default/black_holes_part.h black_holes/Default/black_holes_iact.h \
                  black_holes/Default/black_holes_properties.h \
@@ -245,7 +266,7 @@ nobase_noinst_HEADERS = align.h approx_math.h atomic.h barrier.h cycle.h error.h
 
 # Sources and special flags for the gravity library
 libgrav_la_SOURCES = runner_doiact_grav.c
-libgrav_la_CFLAGS = $(AM_CFLAGS) $(GRAVITY_CFLAGS) 
+libgrav_la_CFLAGS = $(AM_CFLAGS) $(GRAVITY_CFLAGS)
 libgrav_la_LDFLAGS = $(AM_LDFLAGS) $(EXTRA_LIBS)
 
 # Sources and special flags for the gravity MPI library
diff --git a/src/chemistry/GEAR/chemistry.h b/src/chemistry/GEAR/chemistry.h
index d301661a96..ebeb41b3ba 100644
--- a/src/chemistry/GEAR/chemistry.h
+++ b/src/chemistry/GEAR/chemistry.h
@@ -27,6 +27,7 @@
 /* Some standard headers. */
 #include <float.h>
 #include <math.h>
+#include <string.h>
 
 /* Local includes. */
 #include "chemistry_struct.h"
@@ -38,16 +39,21 @@
 #include "units.h"
 
 /**
- * @brief Compute the metal mass fraction
+ * @brief Copies the chemistry properties of the gas particle over to the
+ * star particle.
  *
- * @param p Pointer to the particle data.
- * @param xp Pointer to the extended particle data.
- * @param data The global chemistry information.
+ * @param p the gas particles.
+ * @param xp the additional properties of the gas particles.
+ * @param sp the new created star particle with its properties.
  */
-__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;
+INLINE static void chemistry_copy_star_formation_properties(
+    const struct part* p, const struct xpart* xp, struct spart* sp) {
+
+  /* Store the chemistry struct in the star particle */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    sp->chemistry_data.metal_mass_fraction[i] =
+        p->chemistry_data.smoothed_metal_mass_fraction[i];
+  }
 }
 
 /**
@@ -62,6 +68,60 @@ static INLINE void chemistry_print_backend(
   message("Chemistry function is 'Gear'.");
 }
 
+/**
+ * @brief Read the solar abundances and scale with them the initial
+ * metallicities.
+ *
+ * @param parameter_file The parsed parameter file.
+ * @param data The properties to initialise.
+ */
+static INLINE void chemistry_scale_initial_metallicities(
+    struct swift_params* parameter_file, struct chemistry_global_data* data) {
+#ifdef HAVE_HDF5
+
+  /* Get the yields table */
+  char filename[DESCRIPTION_BUFFER_SIZE];
+  parser_get_param_string(parameter_file, "GEARFeedback:yields_table",
+                          filename);
+
+  /* Open file. */
+  hid_t file_id = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT);
+  if (file_id < 0) error("unable to open file %s.\n", filename);
+
+  /* Open group. */
+  hid_t group_id = H5Gopen(file_id, "Data", H5P_DEFAULT);
+  if (group_id < 0) error("unable to open group Data.\n");
+
+  /* Read the data */
+  float* sol_ab = (float*)malloc(sizeof(float) * GEAR_CHEMISTRY_ELEMENT_COUNT);
+  io_read_array_attribute(group_id, "SolarMassAbundances", FLOAT, sol_ab,
+                          GEAR_CHEMISTRY_ELEMENT_COUNT);
+
+  /* Close group */
+  hid_t status = H5Gclose(group_id);
+  if (status < 0) error("error closing group.");
+
+  /* Close file */
+  status = H5Fclose(file_id);
+  if (status < 0) error("error closing file.");
+
+  /* Scale the initial metallicities */
+  char txt[DESCRIPTION_BUFFER_SIZE] = "Scaling initial metallicities by:";
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    data->initial_metallicities[i] *= sol_ab[i];
+    char tmp[10];
+    sprintf(tmp, " %.2g", sol_ab[i]);
+    strcat(txt, tmp);
+  }
+
+  if (engine_rank == 0) {
+    message("%s", txt);
+  }
+#else
+  error("Cannot scale the solar abundances without HDF5");
+#endif
+}
+
 /**
  * @brief Initialises the chemistry properties.
  *
@@ -78,8 +138,22 @@ static INLINE void chemistry_init_backend(struct swift_params* parameter_file,
                                           struct chemistry_global_data* data) {
 
   /* read parameters */
-  data->initial_metallicity = parser_get_opt_param_float(
-      parameter_file, "GearChemistry:InitialMetallicity", -1);
+  const float initial_metallicity = parser_get_param_float(
+      parameter_file, "GEARChemistry:initial_metallicity");
+
+  /* Set the initial metallicities */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    data->initial_metallicities[i] = initial_metallicity;
+  }
+
+  /* Check if need to scale the initial metallicity */
+  const int scale_metallicity = parser_get_opt_param_int(
+      parameter_file, "GEARChemistry:scale_initial_metallicity", 0);
+
+  /* Scale the metallicities if required */
+  if (scale_metallicity) {
+    chemistry_scale_initial_metallicities(parameter_file, data);
+  }
 }
 
 /**
@@ -96,8 +170,12 @@ __attribute__((always_inline)) INLINE static void chemistry_init_part(
 
   struct chemistry_part_data* cpd = &p->chemistry_data;
 
-  for (int i = 0; i < chemistry_element_count; i++) {
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    /* Reset the smoothed metallicity */
     cpd->smoothed_metal_mass_fraction[i] = 0.f;
+
+    /* Convert the total mass into mass fraction */
+    cpd->metal_mass_fraction[i] = cpd->metal_mass[i] / p->mass;
   }
 }
 
@@ -125,13 +203,16 @@ __attribute__((always_inline)) INLINE static void chemistry_end_density(
 
   struct chemistry_part_data* cpd = &p->chemistry_data;
 
-  for (int i = 0; i < chemistry_element_count; i++) {
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
     /* Final operation on the density (add self-contribution). */
     cpd->smoothed_metal_mass_fraction[i] +=
         m * cpd->metal_mass_fraction[i] * kernel_root;
 
     /* Finish the calculation by inserting the missing h-factors */
     cpd->smoothed_metal_mass_fraction[i] *= factor;
+
+    /* Convert the mass fraction into a total mass */
+    cpd->metal_mass[i] = m * cpd->metal_mass_fraction[i];
   }
 }
 
@@ -157,7 +238,14 @@ chemistry_part_has_no_neighbours(struct part* restrict p,
                                  struct xpart* restrict xp,
                                  const struct chemistry_global_data* cd,
                                  const struct cosmology* cosmo) {
-  error("Needs implementing!");
+
+  /* Set the smoothed fractions with the non smoothed fractions */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    p->chemistry_data.smoothed_metal_mass_fraction[i] =
+        p->chemistry_data.metal_mass_fraction[i];
+    p->chemistry_data.metal_mass[i] =
+        p->chemistry_data.metal_mass_fraction[i] * p->mass;
+  }
 }
 
 /**
@@ -198,7 +286,10 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
     const struct chemistry_global_data* data, struct part* restrict p,
     struct xpart* restrict xp) {
 
-  p->chemistry_data.Z = data->initial_metallicity;
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    p->chemistry_data.metal_mass[i] = data->initial_metallicities[i] * p->mass;
+  }
+
   chemistry_init_part(p, data);
 }
 
@@ -212,9 +303,9 @@ __attribute__((always_inline)) INLINE static void chemistry_first_init_part(
 __attribute__((always_inline)) INLINE static void chemistry_first_init_spart(
     const struct chemistry_global_data* data, struct spart* restrict sp) {
 
-  error(
-      "MATTHIEU: Loic this is a new function. I don't know whether you"
-      " want something here.");
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    sp->chemistry_data.metal_mass_fraction[i] = data->initial_metallicities[i];
+  }
 }
 
 /**
@@ -273,4 +364,84 @@ __attribute__((always_inline)) INLINE static void chemistry_split_part(
   error("Loic: to be implemented");
 }
 
+/**
+ * @brief Returns the total metallicity (metal mass fraction) of the
+ * star particle to be used in feedback/enrichment related routines.
+ *
+ * @param sp Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+chemistry_get_total_metal_mass_fraction_for_feedback(
+    const struct spart* restrict sp) {
+
+  return sp->chemistry_data
+      .metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT - 1];
+}
+
+/**
+ * @brief Returns the abundances (metal mass fraction) of the
+ * star particle to be used in feedback/enrichment related routines.
+ *
+ * @param sp Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float const*
+chemistry_get_metal_mass_fraction_for_feedback(
+    const struct spart* restrict sp) {
+
+  return sp->chemistry_data.metal_mass_fraction;
+}
+
+/**
+ * @brief Returns the total metallicity (metal mass fraction) of the
+ * gas particle to be used in cooling related routines.
+ *
+ * @param p Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+chemistry_get_total_metal_mass_fraction_for_cooling(
+    const struct part* restrict p) {
+
+  return p->chemistry_data
+      .smoothed_metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT - 1];
+}
+
+/**
+ * @brief Returns the abundance array (metal mass fractions) of the
+ * gas particle to be used in cooling related routines.
+ *
+ * @param p Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float const*
+chemistry_get_metal_mass_fraction_for_cooling(const struct part* restrict p) {
+
+  return p->chemistry_data.smoothed_metal_mass_fraction;
+}
+
+/**
+ * @brief Returns the total metallicity (metal mass fraction) of the
+ * gas particle to be used in star formation related routines.
+ *
+ * @param p Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+chemistry_get_total_metal_mass_fraction_for_star_formation(
+    const struct part* restrict p) {
+
+  return p->chemistry_data
+      .smoothed_metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT - 1];
+}
+
+/**
+ * @brief Returns the abundance array (metal mass fractions) of the
+ * gas particle to be used in star formation related routines.
+ *
+ * @param p Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float const*
+chemistry_get_metal_mass_fraction_for_star_formation(
+    const struct part* restrict p) {
+
+  return p->chemistry_data.smoothed_metal_mass_fraction;
+}
+
 #endif /* SWIFT_CHEMISTRY_GEAR_H */
diff --git a/src/chemistry/GEAR/chemistry_iact.h b/src/chemistry/GEAR/chemistry_iact.h
index f1d724b680..ff49af4abb 100644
--- a/src/chemistry/GEAR/chemistry_iact.h
+++ b/src/chemistry/GEAR/chemistry_iact.h
@@ -68,7 +68,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_chemistry(
   kernel_deval(uj, &wj, &wj_dx);
 
   /* Compute contribution to the smooth metallicity */
-  for (int i = 0; i < chemistry_element_count; i++) {
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
     chi->smoothed_metal_mass_fraction[i] +=
         mj * chj->metal_mass_fraction[i] * wi;
     chj->smoothed_metal_mass_fraction[i] +=
@@ -109,7 +109,7 @@ __attribute__((always_inline)) INLINE static void runner_iact_nonsym_chemistry(
   kernel_deval(ui, &wi, &wi_dx);
 
   /* Compute contribution to the smooth metallicity */
-  for (int i = 0; i < chemistry_element_count; i++) {
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
     chi->smoothed_metal_mass_fraction[i] +=
         mj * chj->metal_mass_fraction[i] * wi;
   }
diff --git a/src/chemistry/GEAR/chemistry_io.h b/src/chemistry/GEAR/chemistry_io.h
index 008268657f..0943f369b1 100644
--- a/src/chemistry/GEAR/chemistry_io.h
+++ b/src/chemistry/GEAR/chemistry_io.h
@@ -27,19 +27,6 @@
 #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 Specifies which particle fields to read from a dataset
  *
@@ -53,12 +40,10 @@ INLINE static int chemistry_read_particles(struct part* parts,
 
   /* List what we want to read */
   list[0] = io_make_input_field(
-      "ElementAbundance", FLOAT, chemistry_element_count, OPTIONAL,
+      "ElementAbundance", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT, OPTIONAL,
       UNIT_CONV_NO_UNITS, parts, chemistry_data.metal_mass_fraction);
-  list[1] = io_make_input_field("Z", FLOAT, 1, OPTIONAL, UNIT_CONV_NO_UNITS,
-                                parts, chemistry_data.Z);
 
-  return 2;
+  return 1;
 }
 
 /**
@@ -73,21 +58,18 @@ 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("SmoothedElementAbundances", FLOAT,
-                           chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f,
-                           parts, chemistry_data.smoothed_metal_mass_fraction,
-                           "Element abundances smoothed over the neighbors");
-
-  list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, parts,
-                                 chemistry_data.Z, "Temporary field");
-
-  list[2] = io_make_output_field("ElementAbundances", FLOAT,
-                                 chemistry_element_count, UNIT_CONV_NO_UNITS,
-                                 0.f, parts, chemistry_data.metal_mass_fraction,
-                                 "Mass fraction of each element");
+  list[0] = io_make_output_field(
+      "SmoothedElementAbundances", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT,
+      UNIT_CONV_NO_UNITS, 0.f, parts,
+      chemistry_data.smoothed_metal_mass_fraction,
+      "Element abundances smoothed over the neighbors");
+
+  list[1] = io_make_output_field(
+      "ElementAbundances", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT,
+      UNIT_CONV_NO_UNITS, 0.f, parts, chemistry_data.metal_mass_fraction,
+      "Mass fraction of each element");
 
-  return 3;
+  return 2;
 }
 
 /**
@@ -102,21 +84,12 @@ INLINE static int chemistry_write_sparticles(const struct spart* sparts,
                                              struct io_props* list) {
 
   /* List what we want to write */
-  list[0] =
-      io_make_output_field("SmoothedElementAbundances", FLOAT,
-                           chemistry_element_count, UNIT_CONV_NO_UNITS, 0.f,
-                           sparts, chemistry_data.smoothed_metal_mass_fraction,
-                           "Element abundances smoothed over the neighbors");
-
-  list[1] = io_make_output_field("Z", FLOAT, 1, UNIT_CONV_NO_UNITS, 0.f, sparts,
-                                 chemistry_data.Z, "Temporary field");
-
-  list[2] = io_make_output_field(
-      "ElementAbundance", FLOAT, chemistry_element_count, UNIT_CONV_NO_UNITS,
-      0.f, sparts, chemistry_data.metal_mass_fraction,
+  list[0] = io_make_output_field(
+      "ElementAbundances", FLOAT, GEAR_CHEMISTRY_ELEMENT_COUNT,
+      UNIT_CONV_NO_UNITS, 0.f, sparts, chemistry_data.metal_mass_fraction,
       "Mass fraction of each element");
 
-  return 3;
+  return 1;
 }
 
 /**
@@ -143,12 +116,6 @@ INLINE static int chemistry_write_bparticles(const struct bpart* bparts,
 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;
-       i < chemistry_element_count; i++) {
-    char buffer[20];
-    sprintf(buffer, "Element %d", (int)i);
-    io_write_attribute_s(h_grp, buffer, chemistry_get_element_name(i));
-  }
 }
 #endif
 
diff --git a/src/chemistry/GEAR/chemistry_struct.h b/src/chemistry/GEAR/chemistry_struct.h
index 105b72d7fd..ae108bf271 100644
--- a/src/chemistry/GEAR/chemistry_struct.h
+++ b/src/chemistry/GEAR/chemistry_struct.h
@@ -19,43 +19,37 @@
 #ifndef SWIFT_CHEMISTRY_STRUCT_GEAR_H
 #define SWIFT_CHEMISTRY_STRUCT_GEAR_H
 
-/**
- * @brief The individual elements traced in the model.
- */
-enum chemistry_element {
-  chemistry_element_O = 0,
-  chemistry_element_Mg,
-  chemistry_element_S,
-  chemistry_element_Fe,
-  chemistry_element_Zn,
-  chemistry_element_Sr,
-  chemistry_element_Y,
-  chemistry_element_Ba,
-  chemistry_element_Eu,
-  chemistry_element_count
-};
-
 /**
  * @brief Global chemical abundance information.
  */
 struct chemistry_global_data {
 
   /* Initial metallicity Z */
-  float initial_metallicity;
+  float initial_metallicities[GEAR_CHEMISTRY_ELEMENT_COUNT];
 };
 
 /**
- * @brief Properties of the chemistry function.
+ * @brief Properties of the chemistry function for #part.
  */
 struct chemistry_part_data {
 
   /*! Fraction of the particle mass in a given element */
-  float metal_mass_fraction[chemistry_element_count];
+  float metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT];
+
+  /*! Total mass of element in a particle */
+  float metal_mass[GEAR_CHEMISTRY_ELEMENT_COUNT];
 
   /*! Smoothed fraction of the particle mass in a given element */
-  float smoothed_metal_mass_fraction[chemistry_element_count];
+  float smoothed_metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT];
+};
 
-  float Z;
+/**
+ * @brief Properties of the chemistry function for #spart.
+ */
+struct chemistry_spart_data {
+
+  /*! Fraction of the particle mass in a given element */
+  float metal_mass_fraction[GEAR_CHEMISTRY_ELEMENT_COUNT];
 };
 
 /**
diff --git a/src/common_io.c b/src/common_io.c
index 67b011098c..f1238482f4 100644
--- a/src/common_io.c
+++ b/src/common_io.c
@@ -120,7 +120,7 @@ int io_is_double_precision(enum IO_DATA_TYPE type) {
 }
 
 /**
- * @brief Reads an attribute from a given HDF5 group.
+ * @brief Reads an attribute (scalar) from a given HDF5 group.
  *
  * @param grp The group from which to read.
  * @param name The name of the attribute to read.
@@ -220,6 +220,146 @@ void io_assert_valid_header_cosmology(hid_t h_grp, double a) {
   }
 }
 
+/**
+ * @brief Reads the number of elements in a HDF5 attribute.
+ *
+ * @param attr The attribute from which to read.
+ *
+ * @return The number of elements.
+ *
+ * Calls #error() if an error occurs.
+ */
+hsize_t io_get_number_element_in_attribute(hid_t attr) {
+  /* Get the dataspace */
+  hid_t space = H5Aget_space(attr);
+  if (space < 0) error("Failed to get data space");
+
+  /* Read the number of dimensions */
+  const int ndims = H5Sget_simple_extent_ndims(space);
+
+  /* Read the dimensions */
+  hsize_t* dims = (hsize_t*)malloc(sizeof(hsize_t) * ndims);
+  H5Sget_simple_extent_dims(space, dims, NULL);
+
+  /* Compute number of elements */
+  hsize_t count = 1;
+  for (int i = 0; i < ndims; i++) {
+    count *= dims[i];
+  }
+
+  /* Cleanup */
+  free(dims);
+  H5Sclose(space);
+  return count;
+};
+
+/**
+ * @brief Reads an attribute (array) from a given HDF5 group.
+ * @param data (output) The attribute read from the HDF5 group (need to be
+ * already allocated).
+ * @param number_element Number of elements in the attribute.
+ *
+ * Calls #error() if an error occurs.
+ */
+void io_read_array_attribute(hid_t grp, const char* name,
+                             enum IO_DATA_TYPE type, void* data,
+                             hsize_t number_element) {
+
+  /* Open attribute */
+  const hid_t h_attr = H5Aopen(grp, name, H5P_DEFAULT);
+  if (h_attr < 0) error("Error while opening attribute '%s'", name);
+
+  /* Get the number of elements */
+  hsize_t count = io_get_number_element_in_attribute(h_attr);
+
+  /* Check if correct number of element */
+  if (count != number_element) {
+    error(
+        "Error found a different number of elements than expected (%lli != "
+        "%lli) in attribute %s",
+        count, number_element, name);
+  }
+
+  /* Read attribute */
+  const hid_t h_err = H5Aread(h_attr, io_hdf5_type(type), data);
+  if (h_err < 0) error("Error while reading attribute '%s'", name);
+
+  /* Cleanup */
+  H5Aclose(h_attr);
+}
+
+/**
+ * @brief Reads the number of elements in a HDF5 dataset.
+ *
+ * @param dataset The dataset from which to read.
+ *
+ * @return The number of elements.
+ *
+ * Calls #error() if an error occurs.
+ */
+hsize_t io_get_number_element_in_dataset(hid_t dataset) {
+  /* Get the dataspace */
+  hid_t space = H5Dget_space(dataset);
+  if (space < 0) error("Failed to get data space");
+
+  /* Read the number of dimensions */
+  const int ndims = H5Sget_simple_extent_ndims(space);
+
+  /* Read the dimensions */
+  hsize_t* dims = (hsize_t*)malloc(sizeof(hsize_t) * ndims);
+  H5Sget_simple_extent_dims(space, dims, NULL);
+
+  /* Compute number of elements */
+  hsize_t count = 1;
+  for (int i = 0; i < ndims; i++) {
+    count *= dims[i];
+  }
+
+  /* Cleanup */
+  free(dims);
+  H5Sclose(space);
+  return count;
+};
+
+/**
+ * @brief Reads a dataset (array) from a given HDF5 group.
+ *
+ * @param grp The group from which to read.
+ * @param name The name of the dataset to read.
+ * @param type The #IO_DATA_TYPE of the attribute.
+ * @param data (output) The attribute read from the HDF5 group (need to be
+ * already allocated).
+ * @param number_element Number of elements in the attribute.
+ *
+ * Calls #error() if an error occurs.
+ */
+void io_read_array_dataset(hid_t grp, const char* name, enum IO_DATA_TYPE type,
+                           void* data, hsize_t number_element) {
+
+  /* Open dataset */
+  const hid_t h_dataset = H5Dopen(grp, name, H5P_DEFAULT);
+  if (h_dataset < 0) error("Error while opening attribute '%s'", name);
+
+  /* Get the number of elements */
+  hsize_t count = io_get_number_element_in_dataset(h_dataset);
+
+  /* Check if correct number of element */
+  if (count != number_element) {
+    error(
+        "Error found a different number of elements than expected (%lli != "
+        "%lli) in dataset %s",
+        count, number_element, name);
+  }
+
+  /* Read dataset */
+  const hid_t h_err = H5Dread(h_dataset, io_hdf5_type(type), H5S_ALL, H5S_ALL,
+                              H5P_DEFAULT, data);
+  if (h_err < 0) error("Error while reading dataset '%s'", name);
+
+  /* Cleanup */
+  H5Dclose(h_dataset);
+}
+
 /**
  * @brief Write an attribute to a given HDF5 group.
  *
diff --git a/src/common_io.h b/src/common_io.h
index 024d6027c5..3d160d6778 100644
--- a/src/common_io.h
+++ b/src/common_io.h
@@ -68,12 +68,19 @@ enum IO_DATA_TYPE {
 
 hid_t io_hdf5_type(enum IO_DATA_TYPE type);
 
+hsize_t io_get_number_element_in_attribute(hid_t attr);
+hsize_t io_get_number_element_in_dataset(hid_t dataset);
 void io_read_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type,
                        void* data);
 void io_read_attribute_graceful(hid_t grp, const char* name,
                                 enum IO_DATA_TYPE type, void* data);
 void io_assert_valid_header_cosmology(hid_t h_grp, double a);
 
+void io_read_array_attribute(hid_t grp, const char* name,
+                             enum IO_DATA_TYPE type, void* data,
+                             hsize_t number_element);
+void io_read_array_dataset(hid_t grp, const char* name, enum IO_DATA_TYPE type,
+                           void* data, hsize_t number_element);
 void io_write_attribute(hid_t grp, const char* name, enum IO_DATA_TYPE type,
                         const void* data, int num);
 
diff --git a/src/cooling/Compton/cooling.h b/src/cooling/Compton/cooling.h
index 3e2c0ea8a3..a594cdca69 100644
--- a/src/cooling/Compton/cooling.h
+++ b/src/cooling/Compton/cooling.h
@@ -136,6 +136,7 @@ __attribute__((always_inline)) INLINE static double Compton_cooling_rate_cgs(
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the particle' extended data.
+ * @param time The current time.
  * @param dt The time-step of this particle.
  * @param dt_therm The time-step operator used for thermal quantities.
  */
@@ -146,8 +147,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct hydro_props* hydro_props,
     const struct entropy_floor_properties* floor_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, const float dt,
-    const float dt_therm) {
+    struct part* restrict p, struct xpart* restrict xp, const double time,
+    const float dt, const float dt_therm) {
 
   /* Nothing to do here? */
   if (dt == 0.) return;
@@ -240,6 +241,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  * @param phys_const The physical constants in internal units.
  * @param cooling The properties of the cooling function.
  * @param us The internal system of units.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cosmo The current cosmological model.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
@@ -247,6 +249,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
     const struct part* restrict p, struct xpart* restrict xp) {
diff --git a/src/cooling/EAGLE/cooling.c b/src/cooling/EAGLE/cooling.c
index 694d429db0..3a6fb523b8 100644
--- a/src/cooling/EAGLE/cooling.c
+++ b/src/cooling/EAGLE/cooling.c
@@ -372,6 +372,8 @@ INLINE static double bisection_iter(
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
+ * @param time The current time (since the Big Bang or start of the run) in
+ * internal units.
  * @param dt The cooling time-step of this particle.
  * @param dt_therm The hydro time-step of this particle.
  */
@@ -382,7 +384,8 @@ void cooling_cool_part(const struct phys_const *phys_const,
                        const struct entropy_floor_properties *floor_props,
                        const struct cooling_function_data *cooling,
                        struct part *restrict p, struct xpart *restrict xp,
-                       const float dt, const float dt_therm) {
+                       const double time, const float dt,
+                       const float dt_therm) {
 
   /* No cooling happens over zero time */
   if (dt == 0.) return;
@@ -548,6 +551,7 @@ __attribute__((always_inline)) INLINE float cooling_timestep(
  *
  * @param phys_const #phys_const data structure.
  * @param us The internal system of units.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cosmo #cosmology data structure.
  * @param cooling #cooling_function_data struct.
  * @param p #part data.
@@ -556,6 +560,7 @@ __attribute__((always_inline)) INLINE float cooling_timestep(
 __attribute__((always_inline)) INLINE void cooling_first_init_part(
     const struct phys_const *restrict phys_const,
     const struct unit_system *restrict us,
+    const struct hydro_props *hydro_props,
     const struct cosmology *restrict cosmo,
     const struct cooling_function_data *restrict cooling,
     const struct part *restrict p, struct xpart *restrict xp) {
diff --git a/src/cooling/EAGLE/cooling.h b/src/cooling/EAGLE/cooling.h
index c115a8ce3f..90c1c01e82 100644
--- a/src/cooling/EAGLE/cooling.h
+++ b/src/cooling/EAGLE/cooling.h
@@ -44,7 +44,7 @@ void cooling_cool_part(const struct phys_const *phys_const,
                        const struct entropy_floor_properties *floor_props,
                        const struct cooling_function_data *cooling,
                        struct part *restrict p, struct xpart *restrict xp,
-                       const float dt, const float dt_therm);
+                       const double time, const float dt, const float dt_therm);
 
 float cooling_timestep(const struct cooling_function_data *restrict cooling,
                        const struct phys_const *restrict phys_const,
@@ -57,6 +57,7 @@ float cooling_timestep(const struct cooling_function_data *restrict cooling,
 void cooling_first_init_part(
     const struct phys_const *restrict phys_const,
     const struct unit_system *restrict us,
+    const struct hydro_props *hydro_props,
     const struct cosmology *restrict cosmo,
     const struct cooling_function_data *restrict cooling,
     const struct part *restrict p, struct xpart *restrict xp);
diff --git a/src/cooling/const_du/cooling.h b/src/cooling/const_du/cooling.h
index e78badfa85..de5164e594 100644
--- a/src/cooling/const_du/cooling.h
+++ b/src/cooling/const_du/cooling.h
@@ -76,6 +76,7 @@ INLINE static void cooling_update(const struct cosmology* cosmo,
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
+ * @param time The current time.
  * @param dt The time-step of this particle.
  * @param dt_therm The time-step operator used for thermal quantities.
  */
@@ -86,8 +87,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct hydro_props* hydro_props,
     const struct entropy_floor_properties* floor_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, const float dt,
-    const float dt_therm) {
+    struct part* restrict p, struct xpart* restrict xp, const double time,
+    const float dt, const float dt_therm) {
 
   /* Internal energy floor */
   const float u_floor = cooling->min_energy;
@@ -155,6 +156,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
  * @param phys_const The physical constants in internal units.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cooling The properties of the cooling function.
  * @param us The internal system of units.
  * @param cosmo The current cosmological model.
@@ -162,6 +164,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
     const struct part* restrict p, struct xpart* restrict xp) {
diff --git a/src/cooling/const_lambda/cooling.h b/src/cooling/const_lambda/cooling.h
index 7820a3a5e4..ef56b6e357 100644
--- a/src/cooling/const_lambda/cooling.h
+++ b/src/cooling/const_lambda/cooling.h
@@ -106,6 +106,7 @@ __attribute__((always_inline)) INLINE static double cooling_rate_cgs(
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the particle' extended data.
+ * @param time The current time.
  * @param dt The time-step of this particle.
  * @param dt_therm The time-step operator used for thermal quantities.
  */
@@ -116,8 +117,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct hydro_props* hydro_props,
     const struct entropy_floor_properties* floor_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, const float dt,
-    const float dt_therm) {
+    struct part* restrict p, struct xpart* restrict xp, const double time,
+    const float dt, const float dt_therm) {
 
   /* Nothing to do here? */
   if (dt == 0.) return;
@@ -234,6 +235,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  * @param phys_const The physical constants in internal units.
  * @param cooling The properties of the cooling function.
  * @param us The internal system of units.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cosmo The current cosmological model.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
@@ -241,6 +243,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
     const struct part* restrict p, struct xpart* restrict xp) {
diff --git a/src/cooling/grackle/cooling.c b/src/cooling/grackle/cooling.c
new file mode 100644
index 0000000000..6b74b72aef
--- /dev/null
+++ b/src/cooling/grackle/cooling.c
@@ -0,0 +1,1038 @@
+/*******************************************************************************
+ * 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/>.
+ *
+ ******************************************************************************/
+/**
+ * @file src/cooling/grackle/cooling.c
+ * @brief Cooling using the GRACKLE 3.0 library.
+ */
+
+#include "../config.h"
+
+/* Include header */
+#include "cooling.h"
+
+/* Some standard headers. */
+#include <fenv.h>
+#include <float.h>
+#include <math.h>
+
+/* The grackle library itself */
+#include <grackle.h>
+
+/* Local includes. */
+#include "chemistry.h"
+#include "cooling_io.h"
+#include "entropy_floor.h"
+#include "error.h"
+#include "hydro.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "units.h"
+
+/* need to rework (and check) code if changed */
+#define GRACKLE_NPART 1
+#define GRACKLE_RANK 3
+
+/**
+ * @brief Common operations performed on the cooling function at a
+ * given time-step or redshift.
+ *
+ * @param cosmo The current cosmological model.
+ * @param cooling The #cooling_function_data used in the run.
+ * @param s The #space containing all the particles.
+ */
+void cooling_update(const struct cosmology* cosmo,
+                    struct cooling_function_data* cooling, struct space* s) {
+  /* set current time */
+  if (cooling->redshift == -1)
+    cooling->units.a_value = cosmo->a;
+  else
+    cooling->units.a_value = 1. / (1. + cooling->redshift);
+}
+
+/**
+ * @brief Print the chemical network
+ *
+ * @param xp The #xpart to print
+ */
+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
+
+#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
+ */
+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.
+ */
+void cooling_compute_equilibrium(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct hydro_props* hydro_properties,
+    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, hydro_properties, cosmo,
+                                &cooling_tmp, &p_tmp, xp));
+  cooling_new_energy(phys_const, us, cosmo, hydro_properties, &cooling_tmp,
+                     &p_tmp, xp, dt);
+  dt = alpha * fabs(cooling_time(phys_const, us, hydro_properties, 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_new_energy(phys_const, us, cosmo, hydro_properties, &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
+ * state.
+ *
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the extended particle data.
+ * @param cooling The properties of the cooling function.
+ */
+void cooling_first_init_part(const struct phys_const* restrict phys_const,
+                             const struct unit_system* restrict us,
+                             const struct hydro_props* hydro_props,
+                             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;
+  xp->cooling_data.time_last_event = -cooling->thermal_time;
+
+#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, hydro_props, cosmo, cooling, p,
+                              xp);
+#endif
+}
+
+/**
+ * @brief Returns the total radiated energy by this particle.
+ *
+ * @param xp The extended particle data
+ */
+float cooling_get_radiated_energy(const struct xpart* restrict xp) {
+
+  return xp->cooling_data.radiated_energy;
+}
+
+/**
+ * @brief Prints the properties of the cooling model to stdout.
+ *
+ * @param cooling The properties of the cooling function.
+ */
+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("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);
+  if (cooling->self_shielding_method == -1) {
+    message("Self Shelding density = %g", cooling->self_shielding_threshold);
+  }
+  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);
+  message("\tDensity = %g", cooling->units.density_units);
+  message("\tTime = %g", cooling->units.time_units);
+  message("\tScale Factor = %g (units: %g)", cooling->units.a_value,
+          cooling->units.a_units);
+}
+
+/**
+ * @brief copy a #xpart to the grackle data
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 0
+void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float rho) {
+  /* HI */
+  xp->cooling_data.HI_frac *= rho;
+  data->HI_density = &xp->cooling_data.HI_frac;
+  /* HII */
+  xp->cooling_data.HII_frac *= rho;
+  data->HII_density = &xp->cooling_data.HII_frac;
+
+  /* HeI */
+  xp->cooling_data.HeI_frac *= rho;
+  data->HeI_density = &xp->cooling_data.HeI_frac;
+
+  /* HeII */
+  xp->cooling_data.HeII_frac *= rho;
+  data->HeII_density = &xp->cooling_data.HeII_frac;
+
+  /* HeIII */
+  xp->cooling_data.HeIII_frac *= rho;
+  data->HeIII_density = &xp->cooling_data.HeIII_frac;
+
+  /* HeII */
+  xp->cooling_data.e_frac *= rho;
+  data->e_density = &xp->cooling_data.e_frac;
+}
+#else
+void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float 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
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 1
+void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float rho) {
+  /* HM */
+  xp->cooling_data.HM_frac *= rho;
+  data->HM_density = &xp->cooling_data.HM_frac;
+
+  /* H2I */
+  xp->cooling_data.H2I_frac *= rho;
+  data->H2I_density = &xp->cooling_data.H2I_frac;
+
+  /* H2II */
+  xp->cooling_data.H2II_frac *= rho;
+  data->H2II_density = &xp->cooling_data.H2II_frac;
+}
+#else
+void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float rho) {
+  data->HM_density = NULL;
+  data->H2I_density = NULL;
+  data->H2II_density = NULL;
+}
+#endif
+
+/**
+ * @brief copy a #xpart to the grackle data
+ *
+ * @param data The #grackle_field_data
+ * @param p The #part
+ * @param xp The #xpart
+ * @param rho Particle density
+ */
+#if COOLING_GRACKLE_MODE > 2
+void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float rho) {
+  /* DI */
+  xp->cooling_data.DI_frac *= rho;
+  data->DI_density = &xp->cooling_data.DI_frac;
+
+  /* DII */
+  xp->cooling_data.DII_frac *= rho;
+  data->DII_density = &xp->cooling_data.DII_frac;
+
+  /* HDI */
+  xp->cooling_data.HDI_frac *= rho;
+  data->HDI_density = &xp->cooling_data.HDI_frac;
+}
+#else
+void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float 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
+void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float rho) {
+
+  /* HI */
+  xp->cooling_data.HI_frac = *data->HI_density / rho;
+
+  /* HII */
+  xp->cooling_data.HII_frac = *data->HII_density / rho;
+
+  /* HeI */
+  xp->cooling_data.HeI_frac = *data->HeI_density / rho;
+
+  /* HeII */
+  xp->cooling_data.HeII_frac = *data->HeII_density / rho;
+
+  /* HeIII */
+  xp->cooling_data.HeIII_frac = *data->HeIII_density / rho;
+
+  /* e */
+  xp->cooling_data.e_frac = *data->e_density / rho;
+}
+#else
+void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float 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
+void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float rho) {
+  /* HM */
+  xp->cooling_data.HM_frac = *data->HM_density / rho;
+  /* H2I */
+  xp->cooling_data.H2I_frac = *data->H2I_density / rho;
+  /* H2II */
+  xp->cooling_data.H2II_frac = *data->H2II_density / rho;
+}
+#else
+void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float 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
+void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float rho) {
+
+  /* DI */
+  xp->cooling_data.DI_frac = *data->DI_density / rho;
+
+  /* DII */
+  xp->cooling_data.DII_frac = *data->DII_density / rho;
+
+  /* HDI */
+  xp->cooling_data.HDI_frac = *data->HDI_density / rho;
+}
+#else
+void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float 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
+ */
+void cooling_copy_to_grackle(grackle_field_data* data, const struct part* p,
+                             struct xpart* xp, gr_float 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 = (gr_float*)malloc(sizeof(gr_float));
+  *metal_density = chemistry_get_total_metal_mass_fraction_for_cooling(p) * 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
+ */
+void cooling_copy_from_grackle(grackle_field_data* data, const struct part* p,
+                               struct xpart* xp, gr_float 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);
+
+  free(data->metal_density);
+}
+
+/**
+ * @brief Apply the self shielding (if needed) by turning on/off the UV
+ * background.
+ *
+ * @param cooling The #cooling_function_data used in the run.
+ * @param chemistry The #chemistry_data from grackle.
+ * @param p Pointer to the particle data.
+ *
+ */
+void cooling_apply_self_shielding(
+    const struct cooling_function_data* restrict cooling,
+    chemistry_data* restrict chemistry, const struct part* restrict p,
+    const struct cosmology* cosmo) {
+
+  /* Are we using self shielding or UV background? */
+  if (!cooling->with_uv_background || cooling->self_shielding_method >= 0) {
+    return;
+  }
+
+  /* Are we in a self shielding regime? */
+  const float rho = hydro_get_physical_density(p, cosmo);
+  if (rho > cooling->self_shielding_threshold) {
+    chemistry->UVbackground = 0;
+  } else {
+    chemistry->UVbackground = 1;
+  }
+}
+
+/**
+ * @brief Compute the energy of a particle after dt and update the particle
+ * chemistry data
+ *
+ * @param phys_const The physical constants in internal units.
+ * @param us The internal system of units.
+ * @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
+ */
+gr_float cooling_new_energy(
+    const struct phys_const* restrict phys_const,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_props,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, struct xpart* restrict xp, double dt) {
+
+  /* set current time */
+  code_units units = cooling->units;
+  chemistry_data chemistry_grackle = cooling->chemistry;
+
+  /* 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_dx = 0.;
+  data.grid_rank = GRACKLE_RANK;
+  data.grid_dimension = grid_dimension;
+  data.grid_start = grid_start;
+  data.grid_end = grid_end;
+
+  /* general particle data */
+  gr_float density = hydro_get_physical_density(p, cosmo);
+  gr_float energy = hydro_get_physical_internal_energy(p, xp, cosmo) +
+                    dt * hydro_get_physical_internal_energy_dt(p, cosmo);
+
+  /* We now need to check that we are not going to go below any of the limits */
+  const double u_minimal = hydro_props->minimal_internal_energy;
+  energy = max(energy, u_minimal);
+
+  /* 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 to grackle structure */
+  cooling_copy_to_grackle(&data, p, xp, density);
+
+  /* Apply the self shielding if requested */
+  cooling_apply_self_shielding(cooling, &chemistry_grackle, p, cosmo);
+
+  /* solve chemistry */
+  if (local_solve_chemistry(&chemistry_grackle, &grackle_rates, &units, &data,
+                            dt) == 0) {
+    error("Error in solve_chemistry.");
+  }
+
+  /* copy from grackle data to particle */
+  cooling_copy_from_grackle(&data, p, xp, density);
+
+  return energy;
+}
+
+/**
+ * @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
+ */
+gr_float cooling_time(const struct phys_const* restrict phys_const,
+                      const struct unit_system* restrict us,
+                      const struct hydro_props* hydro_props,
+                      const struct cosmology* restrict cosmo,
+                      const struct cooling_function_data* restrict cooling,
+                      const struct part* restrict p,
+                      struct xpart* restrict xp) {
+
+  error("TODO: use energy after adiabatic cooling");
+
+  /* set current time */
+  code_units units = cooling->units;
+
+  /* initialize data */
+  grackle_field_data data;
+  chemistry_data chemistry_grackle = cooling->chemistry;
+
+  /* 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;
+
+  /* general particle data */
+  gr_float density = hydro_get_physical_density(p, cosmo);
+  gr_float energy = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* 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);
+
+  /* Apply the self shielding if requested */
+  cooling_apply_self_shielding(cooling, &chemistry_grackle, p, cosmo);
+
+  /* Compute cooling time */
+  gr_float cooling_time;
+  chemistry_data_storage chemistry_rates = grackle_rates;
+  if (local_calculate_cooling_time(&chemistry_grackle, &chemistry_rates, &units,
+                                   &data, &cooling_time) == 0) {
+    error("Error in calculate_cooling_time.");
+  }
+
+  /* copy from grackle data to particle */
+  cooling_copy_from_grackle(&data, p, xp, density);
+
+  /* compute rate */
+  return cooling_time;
+}
+
+/**
+ * @brief Apply the cooling to a particle.
+ *
+ * Depending on the task order, you may wish to either
+ * cool down the particle immediately or do it during the drift.
+ *
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the xparticle data.
+ * @param cosmo The current cosmological model.
+ * @param cooling_du_dt Time derivative of the cooling.
+ * @param u_new Internal energy after the cooling.
+ */
+void cooling_apply(struct part* restrict p, struct xpart* restrict xp,
+                   const struct cosmology* restrict cosmo, float cooling_du_dt,
+                   gr_float u_new) {
+
+#ifdef TASK_ORDER_GEAR
+  /* Cannot use du / dt as it will be erased before being used */
+  hydro_set_physical_internal_energy(p, xp, cosmo, u_new);
+  hydro_set_drifted_physical_internal_energy(p, cosmo, u_new);
+#else
+  hydro_set_physical_internal_energy_dt(p, cosmo, cooling_du_dt);
+#endif
+}
+
+/**
+ * @brief Apply the cooling function to a particle.
+ *
+ * @param phys_const The physical constants in internal units.
+ * @param us The internal system of units.
+ * @param cosmo The current cosmological model.
+ * @param floor_props Properties of the entropy floor.
+ * @param cooling The #cooling_function_data used in the run.
+ * @param p Pointer to the particle data.
+ * @param xp Pointer to the particle' extended data.
+ * @param time The current time.
+ * @param dt The time-step of this particle.
+ * @param dt_therm The time-step operator used for thermal quantities.
+ */
+void cooling_cool_part(const struct phys_const* restrict phys_const,
+                       const struct unit_system* restrict us,
+                       const struct cosmology* restrict cosmo,
+                       const struct hydro_props* hydro_props,
+                       const struct entropy_floor_properties* floor_props,
+                       const struct cooling_function_data* restrict cooling,
+                       struct part* restrict p, struct xpart* restrict xp,
+                       const double time, const double dt,
+                       const double dt_therm) {
+
+  /* Nothing to do here? */
+  if (dt == 0.) return;
+
+  /* Is the cooling turn off */
+  if (time - xp->cooling_data.time_last_event < cooling->thermal_time) {
+    return;
+  }
+
+  /* Get the change in internal energy due to hydro forces */
+  const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo);
+
+  /* Current energy */
+  const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo);
+
+  /* Calculate energy after dt */
+  gr_float u_new = cooling_new_energy(phys_const, us, cosmo, hydro_props,
+                                      cooling, p, xp, dt);
+
+  /* We now need to check that we are not going to go below any of the limits */
+  const double u_minimal = hydro_props->minimal_internal_energy;
+  u_new = max(u_new, u_minimal);
+
+  /* Expected change in energy over the next kick step
+     (assuming no change in dt) */
+  const double delta_u = u_new - u_old;
+
+  /* Turn this into a rate of change (including cosmology term) */
+  const float cooling_du_dt = delta_u / dt_therm;
+
+  /* Update the internal energy time derivative */
+  cooling_apply(p, xp, cosmo, cooling_du_dt, u_new);
+
+  /* Store the radiated energy */
+  xp->cooling_data.radiated_energy -=
+      hydro_get_mass(p) * (cooling_du_dt - hydro_du_dt) * dt;
+}
+
+/**
+ * @brief Compute the temperature of a #part based on the cooling function.
+ *
+ * @param phys_const #phys_const data structure.
+ * @param hydro_props The properties of the hydro scheme.
+ * @param us The internal system of units.
+ * @param cosmo #cosmology data structure.
+ * @param cooling #cooling_function_data struct.
+ * @param p #part data.
+ * @param xp Pointer to the #xpart data.
+ */
+float cooling_get_temperature(
+    const struct phys_const* restrict phys_const,
+    const struct hydro_props* restrict hydro_props,
+    const struct unit_system* restrict us,
+    const struct cosmology* restrict cosmo,
+    const struct cooling_function_data* restrict cooling,
+    const struct part* restrict p, const struct xpart* restrict xp) {
+  // TODO use the grackle library
+
+  /* Physical constants */
+  const double m_H = phys_const->const_proton_mass;
+  const double k_B = phys_const->const_boltzmann_k;
+
+  /* Gas properties */
+  const double T_transition = hydro_props->hydrogen_ionization_temperature;
+  const double mu_neutral = hydro_props->mu_neutral;
+  const double mu_ionised = hydro_props->mu_ionised;
+
+  /* Particle temperature */
+  const double u = hydro_get_drifted_physical_internal_energy(p, cosmo);
+
+  /* Temperature over mean molecular weight */
+  const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B;
+
+  /* Are we above or below the HII -> HI transition? */
+  if (T_over_mu > (T_transition + 1.) / mu_ionised)
+    return T_over_mu * mu_ionised;
+  else if (T_over_mu < (T_transition - 1.) / mu_neutral)
+    return T_over_mu * mu_neutral;
+  else
+    return T_transition;
+}
+
+/**
+ * @brief Computes the cooling time-step.
+ *
+ * We return FLT_MAX so as to impose no limit on the time-step.
+ *
+ * @param cooling The #cooling_function_data used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param cosmo The current cosmological model.
+ * @param us The internal system of units.
+ * @param p Pointer to the particle data.
+ */
+float cooling_timestep(const struct cooling_function_data* restrict cooling,
+                       const struct phys_const* restrict phys_const,
+                       const struct cosmology* restrict cosmo,
+                       const struct unit_system* restrict us,
+                       const struct hydro_props* hydro_props,
+                       const struct part* restrict p,
+                       const struct xpart* restrict xp) {
+
+  return FLT_MAX;
+}
+
+/**
+ * @brief Split the coolong content of a particle into n pieces
+ *
+ * @param p The #part.
+ * @param xp The #xpart.
+ * @param n The number of pieces to split into.
+ */
+void cooling_split_part(struct part* p, struct xpart* xp, double n) {
+
+  error("Loic: to be implemented");
+}
+
+/**
+ * @brief Initialises the cooling unit system.
+ *
+ * @param us The current internal system of units.
+ * @param cooling The cooling properties to initialize
+ */
+void cooling_init_units(const struct unit_system* us,
+                        const struct phys_const* phys_const,
+                        struct cooling_function_data* cooling) {
+
+  /* These are conversions from code units to cgs. */
+
+  /* first cosmo */
+  cooling->units.a_units = 1.0;  // units for the expansion factor
+  cooling->units.a_value = 1.0;
+
+  /* We assume here all physical quantities to
+     be in proper coordinate (not comobile)  */
+  cooling->units.comoving_coordinates = 0;
+
+  /* then units */
+  cooling->units.density_units =
+      units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
+  cooling->units.length_units =
+      units_cgs_conversion_factor(us, UNIT_CONV_LENGTH);
+  cooling->units.time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME);
+  cooling->units.velocity_units =
+      units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
+
+  /* Self shielding */
+  if (cooling->self_shielding_method == -1) {
+    cooling->self_shielding_threshold *=
+        phys_const->const_proton_mass *
+        pow(units_cgs_conversion_factor(us, UNIT_CONV_LENGTH), 3.);
+  }
+}
+
+/**
+ * @brief Initialises Grackle.
+ *
+ * @param cooling The cooling properties to initialize
+ */
+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;
+
+  /* Create a chemistry object for parameters and rate data. */
+  if (set_default_chemistry_parameters(chemistry) == 0) {
+    error("Error in set_default_chemistry_parameters.");
+  }
+
+  // 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 = cooling->primordial_chemistry;
+  chemistry->metal_cooling = cooling->with_metal_cooling;
+  chemistry->UVbackground = cooling->with_uv_background;
+  chemistry->grackle_data_file = cooling->cloudy_table;
+
+  /* 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 hydro_props The properties of the hydro scheme.
+ * @param cooling The cooling properties to initialize
+ */
+void cooling_init_backend(struct swift_params* parameter_file,
+                          const struct unit_system* us,
+                          const struct phys_const* phys_const,
+                          const struct hydro_props* hydro_props,
+                          struct cooling_function_data* cooling) {
+
+  if (GRACKLE_NPART != 1)
+    error("Grackle with multiple particles not implemented");
+
+  /* read parameters */
+  cooling_read_parameters(parameter_file, cooling, phys_const);
+
+  /* Set up the units system. */
+  cooling_init_units(us, phys_const, cooling);
+
+  /* Set up grackle */
+  cooling_init_grackle(cooling);
+}
+
+/**
+ * @brief Clean-up the memory allocated for the cooling routines
+ *
+ * @param cooling the cooling data structure.
+ */
+void cooling_clean(struct cooling_function_data* cooling) {
+  _free_chemistry_data(&cooling->chemistry, &grackle_rates);
+}
+
+/**
+ * @brief Write a cooling struct to the given FILE as a stream of bytes.
+ *
+ * Nothing to do beyond writing the structure from the stream.
+ *
+ * @param cooling the struct
+ * @param stream the file stream
+ */
+void cooling_struct_dump(const struct cooling_function_data* cooling,
+                         FILE* stream) {
+  restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1,
+                       stream, "cooling", "cooling function");
+}
+
+/**
+ * @brief Restore a hydro_props struct from the given FILE as a stream of
+ * bytes.
+ *
+ * Nothing to do beyond reading the structure from the stream.
+ *
+ * @param cooling the struct
+ * @param stream the file stream
+ * @param cosmo #cosmology structure
+ */
+void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream,
+                            const struct cosmology* cosmo) {
+  restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1,
+                      stream, NULL, "cooling function");
+
+  /* Set up grackle */
+  cooling_init_grackle(cooling);
+}
diff --git a/src/cooling/grackle/cooling.h b/src/cooling/grackle/cooling.h
index 2b646aedfe..209b986c6f 100644
--- a/src/cooling/grackle/cooling.h
+++ b/src/cooling/grackle/cooling.h
@@ -25,6 +25,7 @@
  */
 
 /* Some standard headers. */
+#include <fenv.h>
 #include <float.h>
 #include <math.h>
 
@@ -46,888 +47,108 @@
 #define GRACKLE_NPART 1
 #define GRACKLE_RANK 3
 
-/* prototype */
-static gr_float cooling_time(
+void cooling_update(const struct cosmology* cosmo,
+                    struct cooling_function_data* cooling, struct space* s);
+void cooling_print_fractions(const struct xpart* restrict xp);
+int cooling_converged(const struct xpart* restrict xp,
+                      const struct xpart* restrict old, const float limit);
+void cooling_compute_equilibrium(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
+    const struct hydro_props* hydro_properties,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
     const struct part* restrict p, struct xpart* restrict xp);
-static gr_float cooling_new_energy(
-    const struct phys_const* restrict phys_const,
-    const struct unit_system* restrict us,
-    const struct cosmology* restrict cosmo,
+void cooling_first_init_part(const struct phys_const* restrict phys_const,
+                             const struct unit_system* restrict us,
+                             const struct hydro_props* hydro_properties,
+                             const struct cosmology* restrict cosmo,
+                             const struct cooling_function_data* cooling,
+                             const struct part* restrict p,
+                             struct xpart* restrict xp);
+
+float cooling_get_radiated_energy(const struct xpart* restrict xp);
+void cooling_print_backend(const struct cooling_function_data* cooling);
+
+void cooling_copy_to_grackle1(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float rho);
+void cooling_copy_to_grackle2(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float rho);
+void cooling_copy_to_grackle3(grackle_field_data* data, const struct part* p,
+                              struct xpart* xp, gr_float rho);
+void cooling_copy_from_grackle1(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float rho);
+void cooling_copy_from_grackle2(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float rho);
+void cooling_copy_from_grackle3(grackle_field_data* data, const struct part* p,
+                                struct xpart* xp, gr_float rho);
+void cooling_copy_to_grackle(grackle_field_data* data, const struct part* p,
+                             struct xpart* xp, gr_float rho);
+void cooling_copy_from_grackle(grackle_field_data* data, const struct part* p,
+                               struct xpart* xp, gr_float rho);
+void cooling_apply_self_shielding(
     const struct cooling_function_data* restrict cooling,
-    const struct part* restrict p, struct xpart* restrict xp, double dt);
-
-/**
- * @brief Common operations performed on the cooling function at a
- * given time-step or redshift.
- *
- * @param cosmo The current cosmological model.
- * @param cooling The #cooling_function_data used in the run.
- * @param s The #space containing all the particles.
- */
-INLINE static void cooling_update(const struct cosmology* cosmo,
-                                  struct cooling_function_data* cooling,
-                                  struct space* s) {
-  /* set current time */
-  if (cooling->redshift == -1)
-    cooling->units.a_value = cosmo->a;
-  else
-    cooling->units.a_value = 1. / (1. + cooling->redshift);
-}
-
-/**
- * @brief Print the chemical network
- *
- * @param xp The #xpart to print
- */
-__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
-
-#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(
+    chemistry_data* restrict chemistry, const struct part* restrict p,
+    const struct cosmology* cosmo);
+gr_float cooling_new_energy(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
+    const struct hydro_props* hydro_properties,
     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_new_energy(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
-  dt = alpha *
-       fabs(cooling_time(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp));
-
-  /* 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_new_energy(phys_const, us, cosmo, &cooling_tmp, &p_tmp, xp, dt);
-  } while (step < max_step && !cooling_converged(xp, &old, conv_limit));
-
-  if (step == max_step)
-    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
- * state.
- *
- * @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 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
-}
-
-/**
- * @brief Returns the total radiated energy by this particle.
- *
- * @param xp The extended particle data
- */
-__attribute__((always_inline)) INLINE static float cooling_get_radiated_energy(
-    const struct xpart* restrict xp) {
-
-  return xp->cooling_data.radiated_energy;
-}
-
-/**
- * @brief Prints the properties of the cooling model to stdout.
- *
- * @param cooling The properties of the cooling function.
- */
-__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("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);
-  message("\tDensity      = %g", cooling->units.density_units);
-  message("\tTime         = %g", cooling->units.time_units);
-  message("\tScale Factor = %g (units: %g)", cooling->units.a_value,
-          cooling->units.a_units);
-}
-
-/**
- * @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 energy of a particle after dt and update the particle
- * chemistry data
- *
- * @param phys_const The physical constants in internal units.
- * @param us The internal system of units.
- * @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 gr_float cooling_new_energy(
-    const struct phys_const* restrict phys_const,
-    const struct unit_system* restrict us,
-    const struct cosmology* restrict cosmo,
-    const struct cooling_function_data* restrict cooling,
-    const struct part* restrict p, struct xpart* restrict xp, double dt) {
-
-  /* set current time */
-  code_units units = cooling->units;
-
-  /* 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_dx = 0.;
-  data.grid_rank = GRACKLE_RANK;
-  data.grid_dimension = grid_dimension;
-  data.grid_start = grid_start;
-  data.grid_end = grid_end;
-
-  /* general particle data */
-  gr_float density = hydro_get_physical_density(p, cosmo);
-  const float energy_before = hydro_get_physical_internal_energy(p, xp, 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 to grackle structure */
-  cooling_copy_to_grackle(data, p, xp, density);
-
-  /* solve chemistry */
-  chemistry_data chemistry_grackle = cooling->chemistry;
-  if (local_solve_chemistry(&chemistry_grackle, &grackle_rates, &units, &data,
-                            dt) == 0) {
-    error("Error in solve_chemistry.");
-  }
-
-  /* copy from grackle data to particle */
-  cooling_copy_from_grackle(data, p, xp, density);
-
-  return energy;
-}
-
-/**
- * @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) {
-
-  /* set current time */
-  code_units units = cooling->units;
-
-  /* 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;
-
-  /* general particle data */
-  const gr_float energy_before =
-      hydro_get_physical_internal_energy(p, xp, 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);
-
-  /* Compute cooling time */
-  gr_float cooling_time;
-  chemistry_data chemistry_grackle = cooling->chemistry;
-  chemistry_data_storage chemistry_rates = grackle_rates;
-  if (local_calculate_cooling_time(&chemistry_grackle, &chemistry_rates, &units,
-                                   &data, &cooling_time) == 0) {
-    error("Error in calculate_cooling_time.");
-  }
-
-  /* copy from grackle data to particle */
-  cooling_copy_from_grackle(data, p, xp, density);
-
-  /* compute rate */
-  return cooling_time;
-}
-
-/**
- * @brief Apply the cooling function to a particle.
- *
- * @param phys_const The physical constants in internal units.
- * @param us The internal system of units.
- * @param cosmo The current cosmological model.
- * @param cooling The #cooling_function_data used in the run.
- * @param p Pointer to the particle data.
- * @param dt The time-step of this particle.
- * @param hydro_properties the hydro_props struct, used for
- * getting the minimal internal energy allowed in by SWIFT.
- * Read from yml file into engine struct.
- */
-__attribute__((always_inline)) INLINE static void cooling_cool_part(
-    const struct phys_const* restrict phys_const,
-    const struct unit_system* restrict us,
-    const struct cosmology* restrict cosmo,
-    const struct hydro_props* hydro_props,
-    const struct entropy_floor_properties* floor_props,
-    const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, double dt,
-    double dt_therm) {
-
-  /* Nothing to do here? */
-  if (dt == 0.) return;
-
-  /* Current energy */
-  const float u_old = hydro_get_physical_internal_energy(p, xp, cosmo);
-
-  /* Current du_dt in physical coordinates (internal units) */
-  const float hydro_du_dt = hydro_get_physical_internal_energy_dt(p, cosmo);
-
-  /* Calculate energy after dt */
-  gr_float u_new =
-      cooling_new_energy(phys_const, us, cosmo, cooling, p, xp, dt);
-
-  float delta_u = u_new - u_old + hydro_du_dt * dt_therm;
-
-  /* We now need to check that we are not going to go below any of the limits */
-
-  /* First, check whether we may end up below the minimal energy after
-   * this step 1/2 kick + another 1/2 kick that could potentially be for
-   * a time-step twice as big. We hence check for 1.5 delta_u. */
-  if (u_old + 1.5 * delta_u < hydro_props->minimal_internal_energy) {
-    delta_u = (hydro_props->minimal_internal_energy - u_old) / 1.5;
-  }
-
-  /* Second, check whether the energy used in the prediction could get negative.
-   * We need to check for the 1/2 dt kick followed by a full time-step drift
-   * that could potentially be for a time-step twice as big. We hence check
-   * for 2.5 delta_u but this time against 0 energy not the minimum.
-   * To avoid numerical rounding bringing us below 0., we add a tiny tolerance.
-   */
-  const float rounding_tolerance = 1.0e-4;
-
-  if (u_old + 2.5 * delta_u < 0.) {
-    delta_u = -u_old / (2.5 + rounding_tolerance);
-  }
-
-  /* Turn this into a rate of change (including cosmology term) */
-  const float cooling_du_dt = delta_u / dt_therm;
-
-  /* Update the internal energy time derivative */
-  hydro_set_physical_internal_energy_dt(p, cosmo, cooling_du_dt);
-
-  /* Store the radiated energy */
-  xp->cooling_data.radiated_energy -= hydro_get_mass(p) * cooling_du_dt * dt;
-}
+    const struct part* restrict p, struct xpart* restrict xp, double dt);
 
-/**
- * @brief Compute the temperature of a #part based on the cooling function.
- *
- * @param phys_const #phys_const data structure.
- * @param hydro_props The properties of the hydro scheme.
- * @param us The internal system of units.
- * @param cosmo #cosmology data structure.
- * @param cooling #cooling_function_data struct.
- * @param p #part data.
- * @param xp Pointer to the #xpart data.
- */
-static INLINE float cooling_get_temperature(
+gr_float cooling_time(const struct phys_const* restrict phys_const,
+                      const struct unit_system* restrict us,
+                      const struct hydro_props* hydro_properties,
+                      const struct cosmology* restrict cosmo,
+                      const struct cooling_function_data* restrict cooling,
+                      const struct part* restrict p, struct xpart* restrict xp);
+void cooling_apply(struct part* restrict p, struct xpart* restrict xp,
+                   const struct cosmology* restrict cosmo, float cooling_du_dt,
+                   gr_float u_new);
+
+void cooling_cool_part(const struct phys_const* restrict phys_const,
+                       const struct unit_system* restrict us,
+                       const struct cosmology* restrict cosmo,
+                       const struct hydro_props* hydro_properties,
+                       const struct entropy_floor_properties* floor_props,
+                       const struct cooling_function_data* restrict cooling,
+                       struct part* restrict p, struct xpart* restrict xp,
+                       const double time, const double dt,
+                       const double dt_therm);
+
+float cooling_get_temperature(
     const struct phys_const* restrict phys_const,
-    const struct hydro_props* restrict hydro_props,
+    const struct hydro_props* hydro_properties,
     const struct unit_system* restrict us,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* restrict cooling,
-    const struct part* restrict p, const struct xpart* restrict xp) {
-  // TODO use the grackle library
-
-  /* Physical constants */
-  const double m_H = phys_const->const_proton_mass;
-  const double k_B = phys_const->const_boltzmann_k;
-
-  /* Gas properties */
-  const double T_transition = hydro_props->hydrogen_ionization_temperature;
-  const double mu_neutral = hydro_props->mu_neutral;
-  const double mu_ionised = hydro_props->mu_ionised;
-
-  /* Particle temperature */
-  const double u = hydro_get_physical_internal_energy(p, xp, cosmo);
-
-  /* Temperature over mean molecular weight */
-  const double T_over_mu = hydro_gamma_minus_one * u * m_H / k_B;
-
-  /* Are we above or below the HII -> HI transition? */
-  if (T_over_mu > (T_transition + 1.) / mu_ionised)
-    return T_over_mu * mu_ionised;
-  else if (T_over_mu < (T_transition - 1.) / mu_neutral)
-    return T_over_mu * mu_neutral;
-  else
-    return T_transition;
-}
-
-/**
- * @brief Computes the cooling time-step.
- *
- * We return FLT_MAX so as to impose no limit on the time-step.
- *
- * @param cooling The #cooling_function_data used in the run.
- * @param phys_const The physical constants in internal units.
- * @param cosmo The current cosmological model.
- * @param us The internal system of units.
- * @param p Pointer to the particle data.
- */
-__attribute__((always_inline)) INLINE static float cooling_timestep(
-    const struct cooling_function_data* restrict cooling,
-    const struct phys_const* restrict phys_const,
-    const struct cosmology* restrict cosmo,
-    const struct unit_system* restrict us,
-    const struct hydro_props* hydro_props, const struct part* restrict p,
-    const struct xpart* restrict xp) {
-
-  return FLT_MAX;
-}
-
-/**
- * @brief Split the coolong content of a particle into n pieces
- *
- * @param p The #part.
- * @param xp The #xpart.
- * @param n The number of pieces to split into.
- */
-INLINE static void cooling_split_part(struct part* p, struct xpart* xp,
-                                      double n) {
-
-  error("Loic: to be implemented");
-}
-
-/**
- * @brief Initialises the cooling unit system.
- *
- * @param us The current internal system of units.
- * @param cooling The cooling properties to initialize
- */
-__attribute__((always_inline)) INLINE static void cooling_init_units(
-    const struct unit_system* us, struct cooling_function_data* cooling) {
-
-  /* These are conversions from code units to cgs. */
-
-  /* first cosmo */
-  cooling->units.a_units = 1.0;  // units for the expansion factor
-  cooling->units.a_value = 1.0;
-
-  /* We assume here all physical quantities to
-     be in proper coordinate (not comobile)  */
-  cooling->units.comoving_coordinates = 0;
-
-  /* then units */
-  cooling->units.density_units =
-      units_cgs_conversion_factor(us, UNIT_CONV_DENSITY);
-  cooling->units.length_units =
-      units_cgs_conversion_factor(us, UNIT_CONV_LENGTH);
-  cooling->units.time_units = units_cgs_conversion_factor(us, UNIT_CONV_TIME);
-  cooling->units.velocity_units =
-      units_cgs_conversion_factor(us, UNIT_CONV_VELOCITY);
-}
-
-/**
- * @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;
-
-  /* Create a chemistry object for parameters and rate data. */
-  if (set_default_chemistry_parameters(chemistry) == 0) {
-    error("Error in set_default_chemistry_parameters.");
-  }
-
-  // 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 = COOLING_GRACKLE_MODE;
-  chemistry->metal_cooling = cooling->with_metal_cooling;
-  chemistry->UVbackground = cooling->with_uv_background;
-  chemistry->grackle_data_file = cooling->cloudy_table;
-
-  /* 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 hydro_props The properties of the hydro scheme.
- * @param cooling The cooling properties to initialize
- */
-__attribute__((always_inline)) INLINE static void cooling_init_backend(
-    struct swift_params* parameter_file, const struct unit_system* us,
-    const struct phys_const* phys_const, const struct hydro_props* hydro_props,
-    struct cooling_function_data* cooling) {
-
-  if (GRACKLE_NPART != 1)
-    error("Grackle with multiple particles not implemented");
-
-  /* read parameters */
-  cooling_read_parameters(parameter_file, cooling);
-
-  /* Set up the units system. */
-  cooling_init_units(us, cooling);
-
-  /* Set up grackle */
-  cooling_init_grackle(cooling);
-}
-
-/**
- * @brief Clean-up the memory allocated for the cooling routines
- *
- * @param cooling the cooling data structure.
- */
-static INLINE void cooling_clean(struct cooling_function_data* cooling) {
-
-  // MATTHIEU: To do: free stuff here
-}
-
-/**
- * @brief Write a cooling struct to the given FILE as a stream of bytes.
- *
- * Nothing to do beyond writing the structure from the stream.
- *
- * @param cooling the struct
- * @param stream the file stream
- */
-static INLINE void cooling_struct_dump(
-    const struct cooling_function_data* cooling, FILE* stream) {
-  restart_write_blocks((void*)cooling, sizeof(struct cooling_function_data), 1,
-                       stream, "cooling", "cooling function");
-}
-
-/**
- * @brief Restore a hydro_props struct from the given FILE as a stream of
- * bytes.
- *
- * Nothing to do beyond reading the structure from the stream.
- *
- * @param cooling the struct
- * @param stream the file stream
- * @param cosmo #cosmology structure
- */
-static INLINE void cooling_struct_restore(struct cooling_function_data* cooling,
-                                          FILE* stream,
-                                          const struct cosmology* cosmo) {
-  restart_read_blocks((void*)cooling, sizeof(struct cooling_function_data), 1,
-                      stream, NULL, "cooling function");
-
-  /* Set up grackle */
-  cooling_init_grackle(cooling);
-}
-
+    const struct part* restrict p, const struct xpart* restrict xp);
+float cooling_timestep(const struct cooling_function_data* restrict cooling,
+                       const struct phys_const* restrict phys_const,
+                       const struct cosmology* restrict cosmo,
+                       const struct unit_system* restrict us,
+                       const struct hydro_props* hydro_properties,
+                       const struct part* restrict p,
+                       const struct xpart* restrict xp);
+
+void cooling_split_part(struct part* p, struct xpart* xp, double n);
+
+void cooling_init_units(const struct unit_system* us,
+                        const struct phys_const* phys_const,
+                        struct cooling_function_data* cooling);
+void cooling_init_grackle(struct cooling_function_data* cooling);
+
+void cooling_init_backend(struct swift_params* parameter_file,
+                          const struct unit_system* us,
+                          const struct phys_const* phys_const,
+                          const struct hydro_props* hydro_props,
+                          struct cooling_function_data* cooling);
+
+void cooling_clean(struct cooling_function_data* cooling);
+void cooling_struct_dump(const struct cooling_function_data* cooling,
+                         FILE* stream);
+void cooling_struct_restore(struct cooling_function_data* cooling, FILE* stream,
+                            const struct cosmology* cosmo);
 #endif /* SWIFT_COOLING_GRACKLE_H */
diff --git a/src/cooling/grackle/cooling_io.h b/src/cooling/grackle/cooling_io.h
index 18712f4cf6..613f507868 100644
--- a/src/cooling/grackle/cooling_io.h
+++ b/src/cooling/grackle/cooling_io.h
@@ -136,34 +136,58 @@ __attribute__((always_inline)) INLINE static int cooling_write_particles(
  * @param cooling The cooling properties to initialize
  */
 __attribute__((always_inline)) INLINE static void cooling_read_parameters(
-    struct swift_params* parameter_file,
-    struct cooling_function_data* cooling) {
+    struct swift_params* parameter_file, struct cooling_function_data* cooling,
+    const struct phys_const* phys_const) {
 
-  parser_get_param_string(parameter_file, "GrackleCooling:CloudyTable",
+  parser_get_param_string(parameter_file, "GrackleCooling:cloudy_table",
                           cooling->cloudy_table);
+
+  cooling->primordial_chemistry = parser_get_opt_param_int(
+      parameter_file, "GrackleCooling:primordial_chemistry",
+      COOLING_GRACKLE_MODE);
+
+  if (cooling->primordial_chemistry < 0)
+    error("Primordial chemistry cannot be below 0");
+
+  if (cooling->primordial_chemistry > COOLING_GRACKLE_MODE)
+    error("Cannot run primordial chemistry %i when compiled with %i",
+          cooling->primordial_chemistry, COOLING_GRACKLE_MODE);
+
   cooling->with_uv_background =
-      parser_get_param_int(parameter_file, "GrackleCooling:WithUVbackground");
+      parser_get_param_int(parameter_file, "GrackleCooling:with_UV_background");
 
   cooling->redshift =
-      parser_get_param_double(parameter_file, "GrackleCooling:Redshift");
+      parser_get_param_double(parameter_file, "GrackleCooling:redshift");
 
   cooling->with_metal_cooling =
-      parser_get_param_int(parameter_file, "GrackleCooling:WithMetalCooling");
+      parser_get_param_int(parameter_file, "GrackleCooling:with_metal_cooling");
 
   cooling->provide_volumetric_heating_rates = parser_get_opt_param_int(
-      parameter_file, "GrackleCooling:ProvideVolumetricHeatingRates", 0);
+      parameter_file, "GrackleCooling:provide_volumetric_heating_rates", 0);
 
   cooling->provide_specific_heating_rates = parser_get_opt_param_int(
-      parameter_file, "GrackleCooling:ProvideSpecificHeatingRates", 0);
+      parameter_file, "GrackleCooling:provide_specific_heating_rates", 0);
 
+  /* Self shielding */
   cooling->self_shielding_method = parser_get_opt_param_int(
-      parameter_file, "GrackleCooling:SelfShieldingMethod", 0);
+      parameter_file, "GrackleCooling:self_shielding_method", 0);
+
+  if (cooling->self_shielding_method == -1) {
+    cooling->self_shielding_threshold = parser_get_param_float(
+        parameter_file, "GrackleCooling:self_shielding_threshold_atom_per_cm3");
+  }
 
+  /* Initial step convergence */
   cooling->max_step = parser_get_opt_param_int(
-      parameter_file, "GrackleCooling:MaxSteps", 10000);
+      parameter_file, "GrackleCooling:max_steps", 10000);
 
   cooling->convergence_limit = parser_get_opt_param_double(
-      parameter_file, "GrackleCooling:ConvergenceLimit", 1e-2);
+      parameter_file, "GrackleCooling:convergence_limit", 1e-2);
+
+  /* Thermal time */
+  cooling->thermal_time =
+      parser_get_param_float(parameter_file, "GrackleCooling:thermal_time_myr");
+  cooling->thermal_time *= phys_const->const_year * 1e6;
 }
 
 #endif /* SWIFT_COOLING_GRACKLE_IO_H */
diff --git a/src/cooling/grackle/cooling_struct.h b/src/cooling/grackle/cooling_struct.h
index 66c385234c..fb1e745242 100644
--- a/src/cooling/grackle/cooling_struct.h
+++ b/src/cooling/grackle/cooling_struct.h
@@ -32,41 +32,51 @@
  */
 struct cooling_function_data {
 
-  /* Filename of the Cloudy Table */
+  /*! Filename of the Cloudy Table */
   char cloudy_table[200];
 
-  /* Enable/Disable UV backgroud */
+  /*! Enable/Disable UV backgroud */
   int with_uv_background;
 
-  /* Redshift to use for the UV backgroud (-1 to use cosmological one) */
+  /*! Chemistry network */
+  int primordial_chemistry;
+
+  /*! Redshift to use for the UV backgroud (-1 to use cosmological one) */
   double redshift;
 
-  /* unit system */
+  /*! unit system */
   code_units units;
 
-  /* grackle chemistry data */
+  /*! grackle chemistry data */
   chemistry_data chemistry;
 
-  /* Enable/Disable metal cooling */
+  /*! Enable/Disable metal cooling */
   int with_metal_cooling;
 
-  /* User provide volumetric heating rates */
+  /*! User provide volumetric heating rates */
   int provide_volumetric_heating_rates;
 
-  /* User provide specific heating rates */
+  /*! User provide specific heating rates */
   int provide_specific_heating_rates;
 
-  /* Self shielding method (<= 3) means grackle modes */
+  /*! Self shielding method (1 -> 3 for grackle's ones, 0 for none and -1 for
+   * GEAR) */
   int self_shielding_method;
 
-  /* convergence limit for first init */
+  /*! Self shielding threshold */
+  float self_shielding_threshold;
+
+  /*! convergence limit for first init */
   float convergence_limit;
 
-  /* number of step max for first init */
+  /*! number of step max for first init */
   int max_step;
 
-  /* over relaxation parameter */
+  /*! over relaxation parameter */
   float omega;
+
+  /*! Duration for switching off cooling after an event (e.g. supernovae) */
+  float thermal_time;
 };
 
 /**
@@ -77,6 +87,9 @@ struct cooling_xpart_data {
   /*! Energy radiated away by this particle since the start of the run */
   float radiated_energy;
 
+  /*! Last time the cooling was switch off */
+  float time_last_event;
+
 /* here all fractions are mass fraction */
 #if COOLING_GRACKLE_MODE >= 1
   /* primordial chemistry >= 1 */
diff --git a/src/cooling/none/cooling.h b/src/cooling/none/cooling.h
index 27c7451517..107ea4b88a 100644
--- a/src/cooling/none/cooling.h
+++ b/src/cooling/none/cooling.h
@@ -63,6 +63,7 @@ INLINE static void cooling_update(const struct cosmology* cosmo,
  * @param cooling The #cooling_function_data used in the run.
  * @param p Pointer to the particle data.
  * @param xp Pointer to the extended particle data.
+ * @param time The current time.
  * @param dt The time-step of this particle.
  * @param dt_therm The time-step operator used for thermal quantities.
  */
@@ -73,8 +74,8 @@ __attribute__((always_inline)) INLINE static void cooling_cool_part(
     const struct hydro_props* hydro_props,
     const struct entropy_floor_properties* floor_props,
     const struct cooling_function_data* restrict cooling,
-    struct part* restrict p, struct xpart* restrict xp, const float dt,
-    const float dt_therm) {}
+    struct part* restrict p, struct xpart* restrict xp, const double time,
+    const float dt, const float dt_therm) {}
 
 /**
  * @brief Computes the cooling time-step.
@@ -108,6 +109,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
  *
  * @param phys_const The physical constant in internal units.
  * @param us The unit system.
+ * @param hydro_props The properties of the hydro scheme.
  * @param cosmo The current cosmological model.
  * @param data The properties of the cooling function.
  * @param p Pointer to the particle data.
@@ -116,6 +118,7 @@ __attribute__((always_inline)) INLINE static float cooling_timestep(
 __attribute__((always_inline)) INLINE static void cooling_first_init_part(
     const struct phys_const* restrict phys_const,
     const struct unit_system* restrict us,
+    const struct hydro_props* hydro_props,
     const struct cosmology* restrict cosmo,
     const struct cooling_function_data* data, const struct part* restrict p,
     struct xpart* restrict xp) {}
diff --git a/src/engine.c b/src/engine.c
index b38738b9f5..c1e294a442 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -3173,7 +3173,7 @@ void engine_collect_stars_counter(struct engine *e) {
 
   /* Reset counters */
   for (size_t i = 0; i < e->s->nr_sparts_foreign; i++) {
-    e->s->sparts_foreign[i].num_ngb_force = 0;
+    e->s->sparts_foreign[i].num_ngb_feedback = 0;
   }
 
   /* Update counters */
@@ -3193,7 +3193,7 @@ void engine_collect_stars_counter(struct engine *e) {
               id_j, j, displs[engine_rank], n_sparts_int[engine_rank]);
         }
 
-        local_sparts[i].num_ngb_force += sparts[j].num_ngb_force;
+        local_sparts[i].num_ngb_feedback += sparts[j].num_ngb_feedback;
       }
     }
   }
diff --git a/src/feedback.h b/src/feedback.h
index ee2934c764..0b44e41bcd 100644
--- a/src/feedback.h
+++ b/src/feedback.h
@@ -29,6 +29,9 @@
 #elif defined(FEEDBACK_EAGLE)
 #include "./feedback/EAGLE/feedback.h"
 #include "./feedback/EAGLE/feedback_iact.h"
+#elif defined(FEEDBACK_GEAR)
+#include "./feedback/GEAR/feedback.h"
+#include "./feedback/GEAR/feedback_iact.h"
 #else
 #error "Invalid choice of feedback model"
 #endif
diff --git a/src/feedback/EAGLE/feedback.h b/src/feedback/EAGLE/feedback.h
index 03144fcc2d..c3e7c76df1 100644
--- a/src/feedback/EAGLE/feedback.h
+++ b/src/feedback/EAGLE/feedback.h
@@ -33,6 +33,20 @@ void compute_stellar_evolution(const struct feedback_props* feedback_props,
                                const struct unit_system* us, const double age,
                                const double dt);
 
+/**
+ * @brief Update the properties of a particle fue to feedback effects after
+ * the cooling was applied.
+ *
+ * Nothing to do here in the EAGLE model.
+ *
+ * @param p The #part to consider.
+ * @param xp The #xpart to consider.
+ * @param cosmo The #cosmology.
+ */
+__attribute__((always_inline)) INLINE static void feedback_update_part(
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct engine* restrict e) {}
+
 /**
  * @brief Should this particle be doing any feedback-related operation?
  *
@@ -157,16 +171,19 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_spart(
  * @param feedback_props The #feedback_props structure.
  * @param cosmo The current cosmological model.
  * @param us The unit system.
+ * @param phys_const The physical constants in internal units.
  * @param star_age_beg_step The age of the star at the star of the time-step in
  * internal units.
  * @param dt The time-step size of this star in internal units.
  * @param time The physical time in internal units.
+ * @param ti_begin The integer time at the beginning of the step.
  * @param with_cosmology Are we running with cosmology on?
  */
 __attribute__((always_inline)) INLINE static void feedback_evolve_spart(
     struct spart* restrict sp, const struct feedback_props* feedback_props,
     const struct cosmology* cosmo, const struct unit_system* us,
-    const double star_age_beg_step, const double dt, const double time,
+    const struct phys_const* phys_const, const double star_age_beg_step,
+    const double dt, const double time, const integertime_t ti_begin,
     const int with_cosmology) {
 
 #ifdef SWIFT_DEBUG_CHECKS
@@ -269,4 +286,16 @@ void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream);
 
 void feedback_struct_restore(struct feedback_props* feedback, FILE* stream);
 
+#ifdef HAVE_HDF5
+/**
+ * @brief Writes the current model of feedback to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+INLINE static void feedback_write_flavour(struct feedback_props* feedback,
+                                          hid_t h_grp) {
+
+  io_write_attribute_s(h_grp, "Feedback Model", "EAGLE");
+};
+#endif  // HAVE_HDF5
+
 #endif /* SWIFT_FEEDBACK_EAGLE_H */
diff --git a/src/feedback/EAGLE/feedback_iact.h b/src/feedback/EAGLE/feedback_iact.h
index a7da4b2267..7715e3a8b3 100644
--- a/src/feedback/EAGLE/feedback_iact.h
+++ b/src/feedback/EAGLE/feedback_iact.h
@@ -280,7 +280,7 @@ runner_iact_nonsym_feedback_apply(const float r2, const float *dx,
 
     /* Draw a random number (Note mixing both IDs) */
     const float rand = random_unit_interval(si->id + pj->id, ti_current,
-                                            random_number_stellar_feedback);
+                                            random_number_stellar_feedback_1);
     /* Are we lucky? */
     if (rand < prob) {
 
diff --git a/src/feedback/EAGLE/feedback_struct.h b/src/feedback/EAGLE/feedback_struct.h
index ad6d46da46..000a144719 100644
--- a/src/feedback/EAGLE/feedback_struct.h
+++ b/src/feedback/EAGLE/feedback_struct.h
@@ -21,6 +21,11 @@
 
 #include "chemistry_struct.h"
 
+/**
+ * @brief Feedback fields carried by each hydro particles
+ */
+struct feedback_part_data {};
+
 /**
  * @brief Feedback fields carried by each star particles
  */
diff --git a/src/feedback/GEAR/feedback.c b/src/feedback/GEAR/feedback.c
new file mode 100644
index 0000000000..7b498fd2ca
--- /dev/null
+++ b/src/feedback/GEAR/feedback.c
@@ -0,0 +1,279 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Include header */
+#include "feedback.h"
+
+/* Local includes */
+#include "cosmology.h"
+#include "engine.h"
+#include "error.h"
+#include "feedback_properties.h"
+#include "hydro_properties.h"
+#include "part.h"
+#include "stellar_evolution.h"
+#include "units.h"
+
+#include <strings.h>
+
+/**
+ * @brief Update the properties of the particle due to a supernovae.
+ *
+ * @param p The #part to consider.
+ * @param xp The #xpart to consider.
+ * @param cosmo The #cosmology.
+ */
+void feedback_update_part(struct part* restrict p, struct xpart* restrict xp,
+                          const struct engine* restrict e) {
+
+  /* Did the particle receive a supernovae */
+  if (xp->feedback_data.delta_mass == 0) return;
+
+  const struct cosmology* cosmo = e->cosmology;
+
+  /* Turn off the cooling */
+  xp->cooling_data.time_last_event = e->time;
+
+  /* Update mass */
+  const float old_mass = hydro_get_mass(p);
+  const float new_mass = old_mass + xp->feedback_data.delta_mass;
+
+  if (xp->feedback_data.delta_mass < 0.) {
+    error("Delta mass smaller than 0");
+  }
+
+  hydro_set_mass(p, new_mass);
+
+  xp->feedback_data.delta_mass = 0;
+
+  /* Update the density */
+  p->rho *= new_mass / old_mass;
+
+  /* Update internal energy */
+  const float u = hydro_get_physical_internal_energy(p, xp, cosmo);
+  const float u_new = u + xp->feedback_data.delta_u;
+
+  hydro_set_physical_internal_energy(p, xp, cosmo, u_new);
+  hydro_set_drifted_physical_internal_energy(p, cosmo, u_new);
+
+  xp->feedback_data.delta_u = 0.;
+
+  /* Update the velocities */
+  for (int i = 0; i < 3; i++) {
+    const float dv = xp->feedback_data.delta_p[i] / new_mass;
+
+    xp->v_full[i] += dv;
+    p->v[i] += dv;
+
+    xp->feedback_data.delta_p[i] = 0;
+  }
+}
+
+/**
+ * @Brief Should we do feedback for this star?
+ *
+ * @param sp The star to consider.
+ */
+int feedback_will_do_feedback(const struct spart* sp,
+                              const struct feedback_props* feedback_props,
+                              const int with_cosmology,
+                              const struct cosmology* cosmo,
+                              const double time) {
+
+  return (sp->birth_time != -1.);
+}
+
+/**
+ * @brief Should this particle be doing any feedback-related operation?
+ *
+ * @param sp The #spart.
+ * @param time The current simulation time (Non-cosmological runs).
+ * @param cosmo The cosmological model (cosmological runs).
+ * @param with_cosmology Are we doing a cosmological run?
+ */
+int feedback_is_active(const struct spart* sp, const double time,
+                       const struct cosmology* cosmo,
+                       const int with_cosmology) {
+
+  if (sp->birth_time == -1.) return 0;
+
+  if (with_cosmology) {
+    return ((double)cosmo->a) > sp->birth_scale_factor;
+  } else {
+    return time > sp->birth_time;
+  }
+}
+
+/**
+ * @brief Returns the length of time since the particle last did
+ * enrichment/feedback.
+ *
+ * @param sp The #spart.
+ * @param with_cosmology Are we running with cosmological time integration on?
+ * @param cosmo The cosmological model.
+ * @param time The current time (since the Big Bang / start of the run) in
+ * internal units.
+ * @param dt_star the length of this particle's time-step in internal units.
+ * @return The length of the enrichment step in internal units.
+ */
+double feedback_get_enrichment_timestep(const struct spart* sp,
+                                        const int with_cosmology,
+                                        const struct cosmology* cosmo,
+                                        const double time,
+                                        const double dt_star) {
+  return dt_star;
+}
+
+/**
+ * @brief Prepares a s-particle for its feedback interactions
+ *
+ * @param sp The particle to act upon
+ */
+void feedback_init_spart(struct spart* sp) {
+
+  sp->feedback_data.enrichment_weight = 0.f;
+}
+
+/**
+ * @brief Prepares a star's feedback field before computing what
+ * needs to be distributed.
+ */
+void feedback_reset_feedback(struct spart* sp,
+                             const struct feedback_props* feedback_props) {
+
+  /* Zero the energy of supernovae */
+  sp->feedback_data.energy_ejected = 0;
+}
+
+/**
+ * @brief Initialises the s-particles feedback props for the first time
+ *
+ * This function is called only once just after the ICs have been
+ * read in to do some conversions.
+ *
+ * @param sp The particle to act upon.
+ * @param feedback_props The properties of the feedback model.
+ */
+void feedback_first_init_spart(struct spart* sp,
+                               const struct feedback_props* feedback_props) {
+
+  feedback_init_spart(sp);
+
+  feedback_reset_feedback(sp, feedback_props);
+}
+
+/**
+ * @brief Initialises the s-particles feedback props for the first time
+ *
+ * This function is called only once just after the ICs have been
+ * read in to do some conversions.
+ *
+ * @param sp The particle to act upon.
+ * @param feedback_props The properties of the feedback model.
+ */
+void feedback_prepare_spart(struct spart* sp,
+                            const struct feedback_props* feedback_props) {}
+
+/**
+ * @brief Evolve the stellar properties of a #spart.
+ *
+ * This function compute the SN rate and yields before sending
+ * this information to a different MPI rank.
+ *
+ * @param sp The particle to act upon
+ * @param feedback_props The #feedback_props structure.
+ * @param cosmo The current cosmological model.
+ * @param us The unit system.
+ * @param phys_const The #phys_const.
+ * @param star_age_beg_step The age of the star at the star of the time-step in
+ * internal units.
+ * @param dt The time-step size of this star in internal units.
+ * @param time The physical time in internal units.
+ * @param ti_begin The integer time at the beginning of the step.
+ * @param with_cosmology Are we running with cosmology on?
+ */
+void feedback_evolve_spart(struct spart* restrict sp,
+                           const struct feedback_props* feedback_props,
+                           const struct cosmology* cosmo,
+                           const struct unit_system* us,
+                           const struct phys_const* phys_const,
+                           const double star_age_beg_step, const double dt,
+                           const double time, const integertime_t ti_begin,
+                           const int with_cosmology) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (sp->birth_time == -1.) error("Evolving a star particle that should not!");
+#endif
+
+  /* Reset the feedback */
+  feedback_reset_feedback(sp, feedback_props);
+
+  /* Add missing h factor */
+  const float hi_inv = 1.f / sp->h;
+  const float hi_inv_dim = pow_dimension(hi_inv); /* 1/h^d */
+
+  sp->feedback_data.enrichment_weight *= hi_inv_dim;
+
+  /* Compute the stellar evolution */
+  stellar_evolution_evolve_spart(sp, &feedback_props->stellar_model, cosmo, us,
+                                 phys_const, ti_begin, star_age_beg_step, dt);
+
+  /* Transform the number of SN to the energy */
+  sp->feedback_data.energy_ejected =
+      sp->feedback_data.number_sn * feedback_props->energy_per_supernovae;
+}
+
+/**
+ * @brief Write a feedback struct to the given FILE as a stream of bytes.
+ *
+ * @param feedback the struct
+ * @param stream the file stream
+ */
+void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream) {
+
+  restart_write_blocks((void*)feedback, sizeof(struct feedback_props), 1,
+                       stream, "feedback", "feedback function");
+
+  stellar_evolution_dump(&feedback->stellar_model, stream);
+}
+
+/**
+ * @brief Restore a feedback struct from the given FILE as a stream of
+ * bytes.
+ *
+ * @param feedback the struct
+ * @param stream the file stream
+ */
+void feedback_struct_restore(struct feedback_props* feedback, FILE* stream) {
+
+  restart_read_blocks((void*)feedback, sizeof(struct feedback_props), 1, stream,
+                      NULL, "feedback function");
+
+  stellar_evolution_restore(&feedback->stellar_model, stream);
+}
+
+/**
+ * @brief Clean the allocated memory.
+ *
+ * @param feedback the #feedback_props.
+ */
+void feedback_clean(struct feedback_props* feedback) {
+
+  stellar_evolution_clean(&feedback->stellar_model);
+}
diff --git a/src/feedback/GEAR/feedback.h b/src/feedback/GEAR/feedback.h
new file mode 100644
index 0000000000..1f333b39f7
--- /dev/null
+++ b/src/feedback/GEAR/feedback.h
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_FEEDBACK_GEAR_H
+#define SWIFT_FEEDBACK_GEAR_H
+
+#include "cosmology.h"
+#include "error.h"
+#include "feedback_properties.h"
+#include "hydro_properties.h"
+#include "part.h"
+#include "stellar_evolution.h"
+#include "units.h"
+
+#include <strings.h>
+
+void feedback_update_part(struct part* restrict p, struct xpart* restrict xp,
+                          const struct engine* restrict e);
+
+int feedback_will_do_feedback(const struct spart* sp,
+                              const struct feedback_props* feedback_props,
+                              const int with_cosmology,
+                              const struct cosmology* cosmo, const double time);
+
+int feedback_is_active(const struct spart* sp, const double time,
+                       const struct cosmology* cosmo, const int with_cosmology);
+double feedback_get_enrichment_timestep(const struct spart* sp,
+                                        const int with_cosmology,
+                                        const struct cosmology* cosmo,
+                                        const double time,
+                                        const double dt_star);
+void feedback_init_spart(struct spart* sp);
+
+void feedback_reset_feedback(struct spart* sp,
+                             const struct feedback_props* feedback_props);
+void feedback_first_init_spart(struct spart* sp,
+                               const struct feedback_props* feedback_props);
+void feedback_prepare_spart(struct spart* sp,
+                            const struct feedback_props* feedback_props);
+void feedback_evolve_spart(struct spart* restrict sp,
+                           const struct feedback_props* feedback_props,
+                           const struct cosmology* cosmo,
+                           const struct unit_system* us,
+                           const struct phys_const* phys_const,
+                           const double star_age_beg_step, const double dt,
+                           const double time, const integertime_t ti_begin,
+                           const int with_cosmology);
+void feedback_struct_dump(const struct feedback_props* feedback, FILE* stream);
+void feedback_struct_restore(struct feedback_props* feedback, FILE* stream);
+void feedback_clean(struct feedback_props* feedback);
+
+/**
+ * @brief Writes the current model of feedback to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+INLINE static void feedback_write_flavour(struct feedback_props* feedback,
+                                          hid_t h_grp) {
+
+  io_write_attribute_s(h_grp, "Feedback Model", "GEAR");
+
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    char buffer[20];
+    sprintf(buffer, "Element %d", (int)i);
+    io_write_attribute_s(
+        h_grp, buffer,
+        stellar_evolution_get_element_name(&feedback->stellar_model, i));
+  }
+};
+
+#endif /* SWIFT_FEEDBACK_GEAR_H */
diff --git a/src/feedback/GEAR/feedback_iact.h b/src/feedback/GEAR/feedback_iact.h
new file mode 100644
index 0000000000..93c37cadc2
--- /dev/null
+++ b/src/feedback/GEAR/feedback_iact.h
@@ -0,0 +1,148 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_GEAR_FEEDBACK_IACT_H
+#define SWIFT_GEAR_FEEDBACK_IACT_H
+
+/* Local includes */
+#include "hydro.h"
+#include "random.h"
+#include "timestep_sync_part.h"
+
+/**
+ * @brief Density interaction between two particles (non-symmetric).
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (pi - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param si First sparticle.
+ * @param pj Second particle (not updated).
+ * @param xpj Extra particle data (not updated).
+ * @param cosmo The cosmological model.
+ * @param ti_current Current integer time value
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_feedback_density(const float r2, const float *dx,
+                                    const float hi, const float hj,
+                                    struct spart *restrict si,
+                                    const struct part *restrict pj,
+                                    const struct xpart *restrict xpj,
+                                    const struct cosmology *restrict cosmo,
+                                    const integertime_t ti_current) {
+
+  /* Get the gas mass. */
+  const float mj = hydro_get_mass(pj);
+
+  /* Get r and 1/r. */
+  const float r_inv = 1.0f / sqrtf(r2);
+  const float r = r2 * r_inv;
+
+  /* Compute the kernel function */
+  const float hi_inv = 1.0f / hi;
+  const float ui = r * hi_inv;
+  float wi;
+  kernel_eval(ui, &wi);
+
+  /* Add contribution of pj to normalisation of density weighted fraction
+   * which determines how much mass to distribute to neighbouring
+   * gas particles */
+
+  /* The normalization by 1 / h^d is done in feedback.h */
+  si->feedback_data.enrichment_weight += mj * wi;
+}
+
+/**
+ * @brief Feedback interaction between two particles (non-symmetric).
+ * Used for updating properties of gas particles neighbouring a star particle
+ *
+ * @param r2 Comoving square distance between the two particles.
+ * @param dx Comoving vector separating both particles (si - pj).
+ * @param hi Comoving smoothing-length of particle i.
+ * @param hj Comoving smoothing-length of particle j.
+ * @param si First (star) particle (not updated).
+ * @param pj Second (gas) particle.
+ * @param xpj Extra particle data
+ * @param cosmo The cosmological model.
+ * @param ti_current Current integer time used value for seeding random number
+ * generator
+ */
+__attribute__((always_inline)) INLINE static void
+runner_iact_nonsym_feedback_apply(const float r2, const float *dx,
+                                  const float hi, const float hj,
+                                  struct spart *si, struct part *pj,
+                                  struct xpart *xpj,
+                                  const struct cosmology *cosmo,
+                                  const integertime_t ti_current) {
+
+  const double e_sn = si->feedback_data.energy_ejected;
+
+  /* Do we have supernovae? */
+  if (e_sn == 0) {
+    return;
+  }
+
+  const float mj = hydro_get_mass(pj);
+  const float r = sqrtf(r2);
+
+  /* Get the kernel for hi. */
+  float hi_inv = 1.0f / hi;
+  float hi_inv_dim = pow_dimension(hi_inv); /* 1/h^d */
+  float xi = r * hi_inv;
+  float wi, wi_dx;
+  kernel_deval(xi, &wi, &wi_dx);
+  wi *= hi_inv_dim;
+
+  /* Compute inverse enrichment weight */
+  const double si_inv_weight = si->feedback_data.enrichment_weight == 0
+                                   ? 0.
+                                   : 1. / si->feedback_data.enrichment_weight;
+
+  /* Mass received */
+  const double m_ej = si->feedback_data.mass_ejected;
+  // TODO compute inverse before feedback loop
+  const double weight = mj * wi * si_inv_weight;
+  const double dm = m_ej * weight;
+  const double new_mass = mj + dm;
+
+  /* Energy received */
+  // TODO compute inverse before feedback loop
+  const double du = e_sn * weight / new_mass;
+
+  xpj->feedback_data.delta_mass += dm;
+  xpj->feedback_data.delta_u += du;
+
+  /* Compute momentum received. */
+  for (int i = 0; i < 3; i++) {
+    xpj->feedback_data.delta_p[i] += dm * (si->v[i] - xpj->v_full[i]);
+  }
+
+  /* Add the metals */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    pj->chemistry_data.metal_mass[i] +=
+        weight * si->feedback_data.metal_mass_ejected[i];
+  }
+
+  /* Impose maximal viscosity */
+  hydro_diffusive_feedback_reset(pj);
+
+  /* Synchronize the particle on the timeline */
+  timestep_sync_part(pj);
+}
+
+#endif /* SWIFT_GEAR_FEEDBACK_IACT_H */
diff --git a/src/feedback/GEAR/feedback_properties.h b/src/feedback/GEAR/feedback_properties.h
new file mode 100644
index 0000000000..4b2d4f207b
--- /dev/null
+++ b/src/feedback/GEAR/feedback_properties.h
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Coypright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_GEAR_FEEDBACK_PROPERTIES_H
+#define SWIFT_GEAR_FEEDBACK_PROPERTIES_H
+
+#include "chemistry.h"
+#include "hydro_properties.h"
+#include "stellar_evolution.h"
+#include "stellar_evolution_struct.h"
+
+/**
+ * @brief Properties of the GEAR feedback model.
+ */
+struct feedback_props {
+  /*! Energy per supernovae */
+  float energy_per_supernovae;
+
+  /*! filename of the chemistry table */
+  char filename[PARSER_MAX_LINE_SIZE];
+
+  /*! The stellar model */
+  struct stellar_model stellar_model;
+};
+
+/**
+ * @brief Print the feedback model.
+ *
+ * @param feedback_props The #feedback_props
+ */
+__attribute__((always_inline)) INLINE static void feedback_props_print(
+    const struct feedback_props* feedback_props) {
+
+  /* Only the master print */
+  if (engine_rank != 0) {
+    return;
+  }
+
+  /* Print the feedback properties */
+  message("Energy per supernovae = %.2g",
+          feedback_props->energy_per_supernovae);
+  message("Yields table = %s", feedback_props->filename);
+
+  /* Print the stellar model */
+  stellar_model_print(&feedback_props->stellar_model);
+}
+
+/**
+ * @brief Initialize the global properties of the feedback scheme.
+ *
+ * By default, takes the values provided by the hydro.
+ *
+ * @param fp The #feedback_props.
+ * @param phys_const The physical constants in the internal unit system.
+ * @param us The internal unit system.
+ * @param params The parsed parameters.
+ * @param hydro_props The already read-in properties of the hydro scheme.
+ * @param cosmo The cosmological model.
+ */
+__attribute__((always_inline)) INLINE static void feedback_props_init(
+    struct feedback_props* fp, const struct phys_const* phys_const,
+    const struct unit_system* us, struct swift_params* params,
+    const struct hydro_props* hydro_props, const struct cosmology* cosmo) {
+
+  /* Supernovae energy */
+  double e_feedback =
+      parser_get_param_double(params, "GEARFeedback:supernovae_energy_erg");
+  e_feedback /= units_cgs_conversion_factor(us, UNIT_CONV_ENERGY);
+  fp->energy_per_supernovae = e_feedback;
+
+  /* filename of the chemistry table */
+  parser_get_param_string(params, "GEARFeedback:yields_table", fp->filename);
+
+  /* Initialize the stellar model */
+  stellar_evolution_props_init(&fp->stellar_model, phys_const, us, params,
+                               cosmo);
+
+  /* Print the stellar properties */
+  feedback_props_print(fp);
+
+  /* Print a final message. */
+  if (engine_rank == 0) {
+    message("Stellar feedback initialized");
+  }
+}
+
+#endif /* SWIFT_GEAR_FEEDBACK_PROPERTIES_H */
diff --git a/src/feedback/GEAR/feedback_struct.h b/src/feedback/GEAR/feedback_struct.h
new file mode 100644
index 0000000000..cd6e336ed7
--- /dev/null
+++ b/src/feedback/GEAR/feedback_struct.h
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Matthieu Schaller (schaller@strw.leidenuniv.nl)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_FEEDBACK_STRUCT_GEAR_H
+#define SWIFT_FEEDBACK_STRUCT_GEAR_H
+
+#include "chemistry_struct.h"
+
+/**
+ * @brief Feedback fields carried by each hydro particles
+ */
+
+struct feedback_part_data {
+  /*! mass received from supernovae */
+  float delta_mass;
+
+  /*! specific energy received from supernovae */
+  float delta_u;
+
+  /*! Momemtum received from a supernovae */
+  float delta_p[3];
+};
+
+/**
+ * @brief Feedback fields carried by each star particles
+ */
+struct feedback_spart_data {
+
+  /*! Inverse of normalisation factor used for the enrichment. */
+  float enrichment_weight;
+
+  /* Assign two different names for clarification */
+  union {
+    /*! Number of supernovae */
+    float number_sn;
+
+    /*! Energy injected in the surrounding particles */
+    float energy_ejected;
+  };
+
+  /*! Total mass ejected by the supernovae */
+  float mass_ejected;
+
+  /*! Chemical composition of the mass ejected */
+  float metal_mass_ejected[GEAR_CHEMISTRY_ELEMENT_COUNT];
+};
+
+#endif /* SWIFT_FEEDBACK_STRUCT_GEAR_H */
diff --git a/src/feedback/GEAR/hdf5_functions.h b/src/feedback/GEAR/hdf5_functions.h
new file mode 100644
index 0000000000..3d7e9befa0
--- /dev/null
+++ b/src/feedback/GEAR/hdf5_functions.h
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_HDF5_FUNCTIONS_GEAR_H
+#define SWIFT_HDF5_FUNCTIONS_GEAR_H
+
+/* Local includes. */
+#include "chemistry.h"
+#include "inline.h"
+
+/**
+ * @brief Reads a string attribute (array) from a given HDF5 group.
+ *
+ * @param grp The group from which to read.
+ * @param name The name of the attribute to read.
+ * @param data (output) The attribute read from the HDF5 group (need to be
+ * allocated).
+ * @param number_element Number of elements in the attribute.
+ * @param size_per_element Maximal size per element in data.
+ *
+ * Calls #error() if an error occurs.
+ */
+__attribute__((always_inline)) INLINE static void
+io_read_string_array_attribute(hid_t grp, const char *name, void *data,
+                               hsize_t number_element,
+                               hsize_t size_per_element) {
+
+  /* Open attribute */
+  const hid_t h_attr = H5Aopen(grp, name, H5P_DEFAULT);
+  if (h_attr < 0) error("Error while opening attribute '%s'", name);
+
+  /* Get the number of elements */
+  hsize_t count = io_get_number_element_in_attribute(h_attr);
+
+  /* Check if correct number of element */
+  if (count != number_element) {
+    error(
+        "Error found a different number of elements than expected (%lli != "
+        "%lli) in attribute %s",
+        count, number_element, name);
+  }
+
+  /* Get the string length */
+  const hid_t type = H5Aget_type(h_attr);
+  if (type < 0) error("Failed to get attribute type");
+
+  size_t sdim = H5Tget_size(type);
+
+  /* Check if the size is correct */
+  if (sdim > size_per_element) {
+    error("Cannot read string longer than %lli in %s", size_per_element, name);
+  }
+
+  /* Allocate the temporary array */
+  char *tmp = malloc(sizeof(char) * sdim * number_element);
+  if (tmp == NULL) {
+    error("Failed to allocate the temporary array.");
+  }
+
+  /* Read attribute */
+  const hid_t h_err = H5Aread(h_attr, type, tmp);
+  if (h_err < 0) error("Error while reading attribute '%s'", name);
+
+  /* Copy the attribute correctly */
+  for (hsize_t i = 0; i < number_element; i++) {
+    char *src = tmp + i * sdim;
+    char *dest = data + i * size_per_element;
+    memcpy(dest, src, sdim);
+  }
+
+  /* Cleanup */
+  free(tmp);
+  H5Aclose(h_attr);
+}
+
+/**
+ * @brief Open a group in the yields table (#h5_close_group needs to be called).
+ *
+ * @param params The @swift_params.
+ * @param group_name The name of the group to open.
+ * @param file_id (output) The id of the file opened.
+ * @param group_id (output) The id of the group opened.
+ *
+ */
+__attribute__((always_inline)) INLINE static void h5_open_group(
+    struct swift_params *params, char *group_name, hid_t *file_id,
+    hid_t *group_id) {
+
+  /* Get filename. */
+  char filename[256];
+  parser_get_param_string(params, "GEARFeedback:yields_table", filename);
+
+  /* Open file. */
+  *file_id = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT);
+  if (*file_id < 0) error("unable to open file %s.\n", filename);
+
+  /* Open group. */
+  *group_id = H5Gopen(*file_id, group_name, H5P_DEFAULT);
+  if (*group_id < 0) error("unable to open group %s.\n", group_name);
+}
+
+/**
+ * @brief Close a group in the yields table.
+ *
+ * @param file_id The id of the file opened.
+ * @param group_id The id of the group opened.
+ *
+ */
+__attribute__((always_inline)) INLINE static void h5_close_group(
+    hid_t file_id, hid_t group_id) {
+
+  /* Close group */
+  hid_t status = H5Gclose(group_id);
+  if (status < 0) error("error closing group.");
+
+  /* Close file */
+  status = H5Fclose(file_id);
+  if (status < 0) error("error closing file.");
+}
+#endif  // SWIFT_HDF5_FUNCTIONS_GEAR_H
diff --git a/src/feedback/GEAR/initial_mass_function.c b/src/feedback/GEAR/initial_mass_function.c
new file mode 100644
index 0000000000..cb165648d2
--- /dev/null
+++ b/src/feedback/GEAR/initial_mass_function.c
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Include header */
+#include "initial_mass_function.h"
+
+/* local headers */
+#include "hdf5_functions.h"
+#include "stellar_evolution_struct.h"
+
+/**
+ * @brief Get the IMF exponent in between mass_min and mass_max.
+ */
+float initial_mass_function_get_exponent(
+    const struct initial_mass_function *imf, float mass_min, float mass_max) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (mass_max > imf->mass_max)
+    error("Cannot have mass larger than the largest one in the IMF");
+  if (mass_min < imf->mass_min)
+    error("Cannot have mass smaller than the smallest one in the IMF");
+  if (mass_max < mass_min) error("Cannot have mass_min larger than mass_max");
+#endif
+
+  for (int i = 0; i < imf->n_parts; i++) {
+
+    /* Check if in the correct part of the IMF */
+    if (mass_min < imf->mass_limits[i + 1]) {
+
+      /* Check if in only one segment */
+      if (mass_max > imf->mass_limits[i + 1]) {
+        error("Cannot get a single exponent for the interval [%g, %g]",
+              mass_min, mass_max);
+      }
+
+      return imf->exp[i];
+    }
+  }
+
+  error("Masses outside IMF ranges");
+
+  return -1;
+}
+
+/** @brief Print the initial mass function */
+void initial_mass_function_print(const struct initial_mass_function *imf) {
+
+  message("Number of parts: %i", imf->n_parts);
+  message("Mass interval: [%g, %g]", imf->mass_min, imf->mass_max);
+  for (int i = 0; i < imf->n_parts; i++) {
+    message("[%g, %g]: %.2g * m^{%g}", imf->mass_limits[i],
+            imf->mass_limits[i + 1], imf->coef[i], imf->exp[i]);
+  }
+}
+
+/**
+ * @brief Integrate the #interpolation_1d data with the initial mass function.
+ *
+ * The x are supposed to be linear in log.
+ *
+ * @param imf The #initial_mass_function.
+ * @param interp The #interpolation_1d.
+ */
+void initial_mass_function_integrate(const struct initial_mass_function *imf,
+                                     struct interpolation_1d *interp) {
+
+  /* Index in the data */
+  int j = 1;
+  const float mass_min = pow(10, interp->xmin);
+  const float mass_max = pow(10, interp->xmin + (interp->N - 1) * interp->dx);
+
+  float m = mass_min;
+
+  float *tmp = (float *)malloc(sizeof(float) * interp->N);
+
+  /* Set lower limit */
+  tmp[0] = 0;
+  for (int i = 0; i < imf->n_parts; i++) {
+
+    /* Check if already in the correct part */
+    if (mass_min > imf->mass_limits[i + 1]) {
+      continue;
+    }
+
+    /* Check if already above the maximal mass */
+    if (mass_max < imf->mass_limits[i]) {
+      break;
+    }
+
+    /* Integrate the data */
+    while (m < imf->mass_limits[i + 1] && j < interp->N) {
+
+      /* Compute the masses */
+      const float log_m1 = interp->xmin + (j - 1) * interp->dx;
+      const float m1 = pow(10, log_m1);
+      const float log_m2 = interp->xmin + j * interp->dx;
+      const float m2 = pow(10, log_m2);
+      const float dm = m2 - m1;
+      const float imf_1 = imf->coef[i] * pow(m1, imf->exp[i]);
+
+      /* Get the imf of the upper limit  */
+      float imf_2;
+      if (m2 > imf->mass_limits[i + 1]) {
+        imf_2 = imf->coef[i + 1] * pow(m2, imf->exp[i + 1]);
+      } else {
+        imf_2 = imf->coef[i] * pow(m2, imf->exp[i]);
+      }
+
+      /* Compute the integral */
+      tmp[j] =
+          tmp[j - 1] +
+          0.5 * (imf_1 * interp->data[j - 1] + imf_2 * interp->data[j]) * dm;
+
+      /* Update j and m */
+      j += 1;
+      m = m2;
+    }
+  }
+
+  /* The rest is extrapolated with 0 */
+  for (int k = j; k < interp->N; k++) {
+    tmp[k] = tmp[k - 1];
+  }
+
+  /* Copy temporary array */
+  memcpy(interp->data, tmp, interp->N * sizeof(float));
+
+  /* Update the boundary conditions */
+  interp->boundary_condition = boundary_condition_zero_const;
+
+  /* clean everything */
+  free(tmp);
+}
+
+/**
+ * @brief Get the IMF coefficient in between mass_min and mass_max.
+ *
+ * @param imf The #initial_mass_function.
+ * @param mass_min The minimal mass of the requested interval.
+ * @param mass_max The maximal mass of the requested interval.
+ *
+ * @return The imf's coefficient of the interval.
+ */
+float initial_mass_function_get_coefficient(
+    const struct initial_mass_function *imf, float mass_min, float mass_max) {
+
+  for (int i = 0; i < imf->n_parts; i++) {
+
+    /* Check if in the correct part of the IMF */
+    if (mass_min < imf->mass_limits[i + 1]) {
+
+      /* Check if in only one segment */
+      if (mass_max > imf->mass_limits[i + 1]) {
+        error("Cannot get a single coefficient for the interval [%g, %g]",
+              mass_min, mass_max);
+      }
+
+      return imf->coef[i];
+    }
+  }
+
+  error("Masses outside IMF ranges");
+
+  return -1;
+}
+
+/**
+ * @brief Compute the integral of the fraction number of the initial mass
+ * function.
+ *
+ * @param imf The #initial_mass_function.
+ * @param m1 The lower mass to evaluate.
+ * @param m2 The upper mass to evaluate.
+ *
+ * @return The number fraction.
+ */
+float initial_mass_function_get_integral_xi(
+    const struct initial_mass_function *imf, float m1, float m2) {
+
+  int k = -1;
+  /* Find the correct part */
+  for (int i = 0; i < imf->n_parts; i++) {
+    if (m1 <= imf->mass_limits[i + 1]) {
+      k = i;
+      break;
+    }
+  }
+
+  /* Check if found a part */
+  if (k == -1) {
+    error("Failed to find the correct function part: %g %g", m1, m2);
+  }
+
+  /* Check if m2 is inside the part */
+  if (m2 < imf->mass_limits[k] || m2 > imf->mass_limits[k + 1]) {
+    error("This function is not able to integrate in two different parts %g %g",
+          m1, m2);
+  }
+
+  /* Compute the integral */
+  const float int_xi1 = pow(m1, imf->exp[k]);
+  const float int_xi2 = pow(m2, imf->exp[k]);
+
+  return imf->coef[k] * (int_xi2 - int_xi1) / imf->exp[k];
+};
+
+/**
+ * @brief Compute the mass fraction of the initial mass function.
+ *
+ * @param imf The #initial_mass_function.
+ * @param m The mass to evaluate.
+ *
+ * @return The mass fraction.
+ */
+float initial_mass_function_get_imf(const struct initial_mass_function *imf,
+                                    float m) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (m > imf->mass_max || m < imf->mass_min)
+    error("Mass below or above limits expecting %g < %g < %g.", imf->mass_min,
+          m, imf->mass_max);
+#endif
+
+  for (int i = 0; i < imf->n_parts; i++) {
+    if (m <= imf->mass_limits[i + 1]) {
+      return imf->coef[i] * pow(m, imf->exp[i]);
+    }
+  }
+
+  error("Failed to find correct function part: %g larger than mass max %g.", m,
+        imf->mass_max);
+  return 0.;
+};
+
+/**
+ * @brief Compute the integral of the mass fraction of the initial mass
+ * function.
+ *
+ * @param imf The #initial_mass_function.
+ * @param m1 The lower mass to evaluate.
+ * @param m2 The upper mass to evaluate.
+ *
+ * @return The integral of the mass fraction.
+ */
+float initial_mass_function_get_integral_imf(
+    const struct initial_mass_function *imf, const float m1, const float m2) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (m1 > imf->mass_max || m1 < imf->mass_min)
+    error("Mass 1 below or above limits expecting %g < %g < %g.", imf->mass_min,
+          m1, imf->mass_max);
+  if (m2 > imf->mass_max || m2 < imf->mass_min)
+    error("Mass 2 below or above limits expecting %g < %g < %g.", imf->mass_min,
+          m2, imf->mass_max);
+#endif
+
+  for (int i = 0; i < imf->n_parts; i++) {
+    if (m1 <= imf->mass_limits[i + 1]) {
+      if (m2 < imf->mass_limits[i] || m2 > imf->mass_limits[i + 1]) {
+        error(
+            "The code does not support the integration over multiple parts of "
+            "the IMF");
+      }
+      const float exp = imf->exp[i] + 1.;
+      return imf->coef[i] * (pow(m2, exp) - pow(m1, exp)) / exp;
+    }
+  }
+
+  error("Failed to find correct function part: %g, %g larger than mass max %g.",
+        m1, m2, imf->mass_max);
+  return 0.;
+};
+
+/**
+ * @brief Compute the coefficients of the initial mass function.
+ *
+ * @param imf The #initial_mass_function.
+ */
+void initial_mass_function_compute_coefficients(
+    struct initial_mass_function *imf) {
+
+  /* Allocate memory */
+  if ((imf->coef = (float *)malloc(sizeof(float) * imf->n_parts)) == NULL)
+    error("Failed to allocate the IMF coefficients.");
+
+  /* Suppose that the first coefficients is 1 (will be corrected later) */
+  imf->coef[0] = 1.;
+
+  /* Use the criterion of continuity for the IMF */
+  for (int i = 1; i < imf->n_parts; i++) {
+    float exp = imf->exp[i - 1] - imf->exp[i];
+    imf->coef[i] = imf->coef[i - 1] * pow(imf->mass_limits[i], exp);
+  }
+
+  /* Use the criterion on the integral = 1 */
+  float integral = 0;
+  for (int i = 0; i < imf->n_parts; i++) {
+    const float exp = imf->exp[i] + 1.;
+    const float m_i = pow(imf->mass_limits[i], exp);
+    const float m_i1 = pow(imf->mass_limits[i + 1], exp);
+    integral += imf->coef[i] * (m_i1 - m_i) / exp;
+  }
+
+  /* Normalize the coefficients (fix initial supposition) */
+  for (int i = 0; i < imf->n_parts; i++) {
+    imf->coef[i] /= integral;
+  }
+}
+
+/**
+ * @brief Reads the initial mass function parameters from the tables.
+ *
+ * @param imf The #initial_mass_function.
+ * @param params The #swift_params.
+ */
+void initial_mass_function_read_from_table(struct initial_mass_function *imf,
+                                           struct swift_params *params) {
+
+  hid_t file_id, group_id;
+
+  /* Open IMF group */
+  h5_open_group(params, "Data/IMF", &file_id, &group_id);
+
+  /* Read number of parts */
+  io_read_attribute(group_id, "n", INT, &imf->n_parts);
+
+  /* The tables have a different definition of n */
+  imf->n_parts += 1;
+
+  /* Allocate the memory for the exponents */
+  if ((imf->exp = (float *)malloc(sizeof(float) * imf->n_parts)) == NULL)
+    error("Failed to allocate the IMF exponents.");
+
+  /* Read the exponents */
+  io_read_array_attribute(group_id, "as", FLOAT, imf->exp, imf->n_parts);
+
+  /* Allocate the memory for the temporary mass limits */
+  if ((imf->mass_limits =
+           (float *)malloc(sizeof(float) * (imf->n_parts + 1))) == NULL)
+    error("Failed to allocate the IMF masses.");
+
+  /* Read the mass limits */
+  io_read_array_attribute(group_id, "ms", FLOAT, imf->mass_limits,
+                          imf->n_parts - 1);
+
+  /* Copy the data (need to shift for mass_min) */
+  for (int i = imf->n_parts - 1; i > 0; i--) {
+    imf->mass_limits[i] = imf->mass_limits[i - 1];
+  }
+
+  /* Read the minimal mass limit */
+  io_read_attribute(group_id, "Mmin", FLOAT, &imf->mass_limits[0]);
+
+  /* Read the maximal mass limit */
+  io_read_attribute(group_id, "Mmax", FLOAT, &imf->mass_limits[imf->n_parts]);
+
+  /* Close everything */
+  h5_close_group(file_id, group_id);
+}
+
+/**
+ * @brief Reads the parameters file and if required overwrites the parameters
+ * found in the yields table.
+ *
+ * @param imf The #initial_mass_function.
+ * @param params The #swift_params.
+ */
+void initial_mass_function_read_from_params(struct initial_mass_function *imf,
+                                            struct swift_params *params) {
+
+  /* Read the number of elements */
+  const int n_parts = parser_get_opt_param_int(
+      params, "GEARInitialMassFunction:number_function_part", imf->n_parts);
+
+  const int n_parts_changed = n_parts != imf->n_parts;
+  imf->n_parts = n_parts;
+
+  /* Reallocate the exponent memory */
+  if (n_parts_changed) {
+    free(imf->exp);
+    if ((imf->exp = (float *)malloc(sizeof(float) * imf->n_parts)) == NULL)
+      error("Failed to allocate the IMF exponents.");
+  }
+
+  /* Read the exponents */
+  const char *exponent_name = "GEARInitialMassFunction:exponents";
+  if (n_parts_changed) {
+    parser_get_param_float_array(params, exponent_name, imf->n_parts, imf->exp);
+  } else {
+    parser_get_opt_param_float_array(params, exponent_name, imf->n_parts,
+                                     imf->exp);
+  }
+
+  /* Reallocate the mass limits memory */
+  if (n_parts_changed) {
+    free(imf->mass_limits);
+    if ((imf->mass_limits =
+             (float *)malloc(sizeof(float) * (imf->n_parts + 1))) == NULL)
+      error("Failed to allocate the IMF masses.");
+  }
+
+  /* Read the mass limits */
+  const char *mass_limits_name = "GEARInitialMassFunction:mass_limits_msun";
+  if (n_parts_changed) {
+    parser_get_param_float_array(params, mass_limits_name, imf->n_parts + 1,
+                                 imf->mass_limits);
+  } else {
+    parser_get_opt_param_float_array(params, mass_limits_name, imf->n_parts + 1,
+                                     imf->mass_limits);
+  }
+}
+
+/**
+ * @brief Initialize the initial mass function.
+ *
+ * @param imf The #initial_mass_function.
+ * @param phys_const The #phys_const.
+ * @param us The #unit_system.
+ * @param params The #swift_params.
+ */
+void initial_mass_function_init(struct initial_mass_function *imf,
+                                const struct phys_const *phys_const,
+                                const struct unit_system *us,
+                                struct swift_params *params) {
+
+  /* Read the parameters from the yields table */
+  initial_mass_function_read_from_table(imf, params);
+
+  /* Overwrites the parameters if found in the params file */
+  initial_mass_function_read_from_params(imf, params);
+
+  /* Write the masses in the correct attributes */
+  imf->mass_min = imf->mass_limits[0];
+  imf->mass_max = imf->mass_limits[imf->n_parts];
+
+  /* Compute the coefficients */
+  initial_mass_function_compute_coefficients(imf);
+}
+
+/**
+ * @brief Write a initial_mass_function struct to the given FILE as a stream of
+ * bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param imf the struct
+ * @param stream the file stream
+ * @param sm The #stellar_model.
+ */
+void initial_mass_function_dump(const struct initial_mass_function *imf,
+                                FILE *stream, const struct stellar_model *sm) {
+
+  /* Dump the mass limits. */
+  if (imf->mass_limits != NULL) {
+    restart_write_blocks((void *)imf->mass_limits, sizeof(float),
+                         imf->n_parts + 1, stream, "imf_mass_limits",
+                         "imf_mass_limits");
+  }
+
+  /*! Dump the exponents. */
+  if (imf->exp != NULL) {
+    restart_write_blocks((void *)imf->exp, sizeof(float), imf->n_parts, stream,
+                         "imf_exponents", "imf_exponents");
+  }
+
+  /*! Dump the coefficients. */
+  if (imf->coef != NULL) {
+    restart_write_blocks((void *)imf->coef, sizeof(float), imf->n_parts, stream,
+                         "imf_coef", "imf_coef");
+  }
+}
+
+/**
+ * @brief Restore a initial_mass_function struct from the given FILE as a stream
+ * of bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param imf the struct
+ * @param stream the file stream
+ * @param sm The #stellar_model.
+ */
+void initial_mass_function_restore(struct initial_mass_function *imf,
+                                   FILE *stream,
+                                   const struct stellar_model *sm) {
+
+  /* Restore the mass limits */
+  if (imf->mass_limits != NULL) {
+    imf->mass_limits = (float *)malloc(sizeof(float) * imf->n_parts + 1);
+    restart_read_blocks((void *)imf->mass_limits, sizeof(float),
+                        imf->n_parts + 1, stream, NULL, "imf_mass_limits");
+  }
+
+  /* Restore the exponents */
+  if (imf->exp != NULL) {
+    imf->exp = (float *)malloc(sizeof(float) * imf->n_parts);
+    restart_read_blocks((void *)imf->exp, sizeof(float), imf->n_parts, stream,
+                        NULL, "imf_exponents");
+  }
+
+  /* Restore the coefficients */
+  if (imf->coef != NULL) {
+    imf->coef = (float *)malloc(sizeof(float) * imf->n_parts);
+    restart_read_blocks((void *)imf->coef, sizeof(float), imf->n_parts, stream,
+                        NULL, "imf_coef");
+  }
+}
+
+/**
+ * @brief Clean the allocated memory.
+ *
+ * @param imf the #initial_mass_function.
+ */
+void initial_mass_function_clean(struct initial_mass_function *imf) {
+
+  /* Free the pointers */
+  free(imf->mass_limits);
+  imf->mass_limits = NULL;
+
+  free(imf->exp);
+  imf->exp = NULL;
+
+  free(imf->coef);
+  imf->coef = NULL;
+}
diff --git a/src/feedback/GEAR/initial_mass_function.h b/src/feedback/GEAR/initial_mass_function.h
new file mode 100644
index 0000000000..bf29e7bada
--- /dev/null
+++ b/src/feedback/GEAR/initial_mass_function.h
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_INITIAL_MASS_FUNCTION_GEAR_H
+#define SWIFT_INITIAL_MASS_FUNCTION_GEAR_H
+
+#include "hdf5_functions.h"
+#include "stellar_evolution_struct.h"
+
+float initial_mass_function_get_exponent(
+    const struct initial_mass_function *imf, float mass_min, float mass_max);
+void initial_mass_function_print(const struct initial_mass_function *imf);
+
+void initial_mass_function_integrate(const struct initial_mass_function *imf,
+                                     struct interpolation_1d *interp);
+float initial_mass_function_get_coefficient(
+    const struct initial_mass_function *imf, float mass_min, float mass_max);
+float initial_mass_function_get_integral_xi(
+    const struct initial_mass_function *imf, float m1, float m2);
+float initial_mass_function_get_imf(const struct initial_mass_function *imf,
+                                    float m);
+float initial_mass_function_get_integral_imf(
+    const struct initial_mass_function *imf, const float m1, const float m2);
+void initial_mass_function_compute_coefficients(
+    struct initial_mass_function *imf);
+
+void initial_mass_function_read_from_table(struct initial_mass_function *imf,
+                                           struct swift_params *params);
+void initial_mass_function_read_from_params(struct initial_mass_function *imf,
+                                            struct swift_params *params);
+
+void initial_mass_function_init(struct initial_mass_function *imf,
+                                const struct phys_const *phys_const,
+                                const struct unit_system *us,
+                                struct swift_params *params);
+
+void initial_mass_function_dump(const struct initial_mass_function *imf,
+                                FILE *stream, const struct stellar_model *sm);
+
+void initial_mass_function_restore(struct initial_mass_function *imf,
+                                   FILE *stream,
+                                   const struct stellar_model *sm);
+
+void initial_mass_function_clean(struct initial_mass_function *imf);
+#endif  // SWIFT_INITIAL_MASS_FUNCTION_GEAR_H
diff --git a/src/feedback/GEAR/interpolation.h b/src/feedback/GEAR/interpolation.h
new file mode 100644
index 0000000000..37706cda73
--- /dev/null
+++ b/src/feedback/GEAR/interpolation.h
@@ -0,0 +1,207 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_GEAR_INTERPOLATION_H
+#define SWIFT_GEAR_INTERPOLATION_H
+
+enum interpolate_boundary_condition {
+  /* No extrapolation => raise errors */
+  boundary_condition_error,
+
+  /* Zero as boundary conditions */
+  boundary_condition_zero,
+
+  /* Zero (left boundary) and constant (right boundary) boundary conditions */
+  boundary_condition_zero_const,
+};
+
+struct interpolation_1d {
+  /* Data to interpolate */
+  float *data;
+
+  /* Minimal x */
+  float xmin;
+
+  /* Step size between x points */
+  float dx;
+
+  /* Number of element in the data */
+  int N;
+
+  /* Type of boundary conditions. */
+  enum interpolate_boundary_condition boundary_condition;
+};
+
+/**
+ * @brief Initialize the #interpolation_1d.
+ *
+ * Assumes x are linear in log.
+ *
+ * @params interp The #interpolation_1d.
+ * @params xmin Minimal value of x (in log).
+ * @params xmax Maximal value of x (in log).
+ * @params N Requested number of values.
+ * @params log_data_xmin The minimal value of the data (in log).
+ * @params step_size The size of the x steps (in log).
+ * @params N_data The number of element in the data.
+ * @params data The data to interpolate (y).
+ * @params N The number of element in data.
+ * @params boundary_condition The type of #interpolate_boundary_condition.
+ */
+__attribute__((always_inline)) static INLINE void interpolate_1d_init(
+    struct interpolation_1d *interp, float xmin, float xmax, int N,
+    float log_data_xmin, float step_size, int N_data, const float *data,
+    enum interpolate_boundary_condition boundary_condition) {
+
+  /* Save the variables */
+  interp->N = N;
+  interp->xmin = xmin;
+  interp->dx = (xmax - xmin) / (N - 1.f);
+  interp->boundary_condition = boundary_condition;
+
+  /* Allocate the memory */
+  interp->data = malloc(sizeof(float) * N);
+  if (interp->data == NULL)
+    error("Failed to allocate memory for the interpolation");
+
+  /* Interpolate the data */
+  for (int i = 0; i < N; i++) {
+    const float log_x = xmin + i * interp->dx;
+    const float x_j = (log_x - log_data_xmin) / step_size;
+
+    /* Check boundaries */
+    if (x_j < 0) {
+      switch (boundary_condition) {
+        case boundary_condition_error:
+          error("Cannot extrapolate");
+          break;
+        case boundary_condition_zero:
+          interp->data[i] = 0;
+          break;
+        case boundary_condition_zero_const:
+          interp->data[i] = 0;
+          break;
+        default:
+          error("Interpolation type not implemented");
+      }
+      continue;
+    } else if (x_j >= N_data) {
+      switch (boundary_condition) {
+        case boundary_condition_error:
+          error("Cannot extrapolate");
+          break;
+        case boundary_condition_zero:
+          interp->data[i] = 0;
+          break;
+        case boundary_condition_zero_const:
+          interp->data[i] = interp->data[i - 1];
+          break;
+        default:
+          error("Interpolation type not implemented");
+      }
+      continue;
+    }
+
+    /* Interpolate i */
+    const int j = x_j;
+    const float f = x_j - j;
+    interp->data[i] = (1. - f) * data[j] + f * data[j + 1];
+  }
+}
+
+/**
+ * @brief Interpolate the data.
+ *
+ * @params interp The #interpolation_1d.
+ * @params x The x value where to interpolate.
+ *
+ * @return The interpolated value y.
+ */
+__attribute__((always_inline)) static INLINE float interpolate_1d(
+    const struct interpolation_1d *interp, float x) {
+
+  /* Find indice */
+  const float i = (x - interp->xmin) / interp->dx;
+  const int idx = i;
+  const float dx = i - idx;
+
+  /* Should we extrapolate? */
+  if (i < 0) {
+    switch (interp->boundary_condition) {
+      case boundary_condition_error:
+        error("Cannot extrapolate");
+        break;
+      case boundary_condition_zero:
+      case boundary_condition_zero_const:
+        return 0;
+      default:
+        error("Interpolation type not implemented");
+    }
+  } else if (i >= interp->N - 1) {
+    switch (interp->boundary_condition) {
+      case boundary_condition_error:
+        error("Cannot extrapolate");
+        break;
+      case boundary_condition_zero:
+        return 0;
+      case boundary_condition_zero_const:
+        return interp->data[interp->N - 1];
+      default:
+        error("Interpolation type not implemented");
+    }
+  }
+
+  /* interpolate */
+  return interp->data[idx] * (1. - dx) + interp->data[idx + 1] * dx;
+}
+
+/**
+ * @brief Print the data.
+ *
+ * @params interp The #interpolation_1d.
+ */
+__attribute__((always_inline)) static INLINE void interpolate_1d_print(
+    const struct interpolation_1d *interp) {
+
+  message("Interpolation between %g and %g", interp->xmin,
+          interp->xmin + interp->dx * interp->N);
+
+  message("Contains %i values and use the boundary condition %i", interp->N,
+          interp->boundary_condition);
+
+  /* Print values */
+  for (int i = 0; i < interp->N; i++) {
+    float x = interp->xmin + i * interp->dx;
+    message("%.2g: %g", x, interp->data[i]);
+  }
+}
+
+/**
+ * @brief Cleanup the #interpolation_1d structure.
+ *
+ * @params interp The #interpolation_1d.
+ */
+__attribute__((always_inline)) static INLINE void interpolate_1d_free(
+    struct interpolation_1d *interp) {
+
+  /* Free the allocated memory */
+  free(interp->data);
+  interp->data = NULL;
+}
+
+#endif  // SWIFT_GEAR_INTERPOLATION_H
diff --git a/src/feedback/GEAR/lifetime.h b/src/feedback/GEAR/lifetime.h
new file mode 100644
index 0000000000..78a7ac16b2
--- /dev/null
+++ b/src/feedback/GEAR/lifetime.h
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_LIFETIME_GEAR_H
+#define SWIFT_LIFETIME_GEAR_H
+
+#include "hdf5_functions.h"
+#include "stellar_evolution_struct.h"
+
+/**
+ * @brief Print the lifetime model.
+ *
+ * @param lf The #lifetime.
+ */
+__attribute__((always_inline)) INLINE static void lifetime_print(
+    const struct lifetime* lf) {
+
+  /* Only the master print */
+  if (engine_rank != 0) {
+    return;
+  }
+
+  message("Quadratic terms: %.2g %.2g %.2g", lf->quadratic[0], lf->quadratic[1],
+          lf->quadratic[2]);
+
+  message("Linear terms: %.2g %.2g %.2g", lf->linear[0], lf->linear[1],
+          lf->linear[2]);
+
+  message("Constant terms: %.2g %.2g %.2g", lf->constant[0], lf->constant[1],
+          lf->constant[2]);
+}
+
+/**
+ * @brief Compute the lifetime of a star.
+ *
+ * @param life The #lifetime model.
+ * @param log_mass The star's mass (in log10(solMass)).
+ * @param metallicity The star's metallicity.
+ *
+ * @return The star's lifetime (in log10(Myr)).
+ */
+__attribute__((always_inline)) INLINE static float
+lifetime_get_log_lifetime_from_mass(const struct lifetime* life, float log_mass,
+                                    float metallicity) {
+
+  /* Compute quadratic term */
+  const float quadratic =
+      (life->quadratic[0] * metallicity + life->quadratic[1]) * metallicity +
+      life->quadratic[2];
+  /* Compute linear term */
+  const float linear =
+      (life->linear[0] * metallicity + life->linear[1]) * metallicity +
+      life->linear[2];
+  /* Compute constant term */
+  const float constant =
+      (life->constant[0] * metallicity + life->constant[1]) * metallicity +
+      life->constant[2];
+
+  /* Compute lifetime */
+  return (quadratic * log_mass + linear) * log_mass + constant;
+}
+
+/**
+ * @brief Compute the mass of a star with a given lifetime
+ *
+ * @param life The #lifetime model.
+ * @param log_time The star's lifetime (in log10(Myr)).
+ * @param metallicity The star's metallicity.
+ *
+ * @return The star's mass (in log10(solMass))
+ */
+__attribute__((always_inline)) INLINE static float
+lifetime_get_log_mass_from_lifetime(const struct lifetime* life, float log_time,
+                                    float metallicity) {
+
+  /* Compute quadratic term */
+  const float quadratic =
+      (life->quadratic[0] * metallicity + life->quadratic[1]) * metallicity +
+      life->quadratic[2];
+  /* Compute linear term */
+  const float linear =
+      (life->linear[0] * metallicity + life->linear[1]) * metallicity +
+      life->linear[2];
+  /* Compute constant term */
+  const float constant =
+      (life->constant[0] * metallicity + life->constant[1]) * metallicity +
+      life->constant[2];
+
+  /* Compute the "c" with the time */
+  const float c_t = constant - log_time;
+
+  /* Use the quadratic formula to find the mass */
+  if (quadratic != 0) {
+    const float delta = linear * linear - 4 * quadratic * c_t;
+
+    /* Avoid complex number should not happen in real simulation */
+    if (delta < 0) {
+      return -linear / (2. * quadratic);
+    } else {
+      return (-linear - sqrt(delta)) / (2. * quadratic);
+    }
+  } else {
+    return -c_t / linear;
+  }
+}
+
+/**
+ * @brief Read lifetime parameters from tables.
+ *
+ * @param lt The #lifetime.
+ * @param params The #swift_params.
+ */
+__attribute__((always_inline)) INLINE static void lifetime_read_from_tables(
+    struct lifetime* lt, struct swift_params* params) {
+
+  hid_t file_id, group_id;
+
+  /* Open IMF group */
+  h5_open_group(params, "Data/LiveTimes", &file_id, &group_id);
+
+  /* Allocate the temporary array */
+  float* tmp;
+  if ((tmp = (float*)malloc(sizeof(float) * 9)) == NULL)
+    error("Failed to allocate the temporary array.");
+
+  /* Read the coefficients */
+  io_read_array_dataset(group_id, "coeff_z", FLOAT, tmp, 9);
+
+  /* Copy the coefficents */
+  const int dim = 3;
+  for (int i = 0; i < dim; i++) {
+    lt->quadratic[i] = tmp[i];
+    lt->linear[i] = tmp[i + dim];
+    lt->constant[i] = tmp[i + 2 * dim];
+  }
+
+  /* Change units from yr into Myr */
+  lt->constant[dim - 1] -= 6;
+
+  /* Cleanup everything */
+  free(tmp);
+  h5_close_group(file_id, group_id);
+}
+
+/**
+ * @brief Read lifetime parameters from params.
+ *
+ * @param lt The #lifetime.
+ * @param params The #swift_params.
+ */
+__attribute__((always_inline)) INLINE static void lifetime_read_from_params(
+    struct lifetime* lt, struct swift_params* params) {
+
+  /* Read quadratic terms */
+  parser_get_opt_param_float_array(params, "GEARLifetime:quadratic", 3,
+                                   lt->quadratic);
+
+  /* Read linear terms */
+  parser_get_opt_param_float_array(params, "GEARLifetime:linear", 3,
+                                   lt->linear);
+
+  /* Read constant terms */
+  parser_get_opt_param_float_array(params, "GEARLifetime:constant", 3,
+                                   lt->constant);
+}
+
+/**
+ * @brief Inititialize the Lifetime.
+ *
+ * @param lt The #lifetime.
+ * @param phys_const The #phys_const.
+ * @param us The #unit_system.
+ * @param params The #swift_params.
+ */
+__attribute__((always_inline)) INLINE static void lifetime_init(
+    struct lifetime* lt, const struct phys_const* phys_const,
+    const struct unit_system* us, struct swift_params* params) {
+
+  /* Read params from yields table */
+  lifetime_read_from_tables(lt, params);
+
+  /* overwrite the parameters if found in the params */
+  lifetime_read_from_params(lt, params);
+}
+
+/**
+ * @brief Write a lifetime struct to the given FILE as a stream of bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param lt the struct
+ * @param stream the file stream
+ * @param sm The #stellar_model.
+ */
+__attribute__((always_inline)) INLINE static void lifetime_dump(
+    const struct lifetime* lt, FILE* stream, const struct stellar_model* sm) {
+
+  /* Nothing to do here */
+}
+
+/**
+ * @brief Restore a lifetime struct from the given FILE as a stream of
+ * bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param lt the struct
+ * @param stream the file stream
+ * @param sm The #stellar_model.
+ */
+__attribute__((always_inline)) INLINE static void lifetime_restore(
+    struct lifetime* lt, FILE* stream, const struct stellar_model* sm) {
+
+  /* Nothing to do here */
+}
+
+/**
+ * @brief Clean the allocated memory.
+ *
+ * @param lifetime the #lifetime.
+ */
+__attribute__((always_inline)) INLINE static void lifetime_clean(
+    struct lifetime* lifetime) {}
+
+#endif  // SWIFT_LIFETIME_GEAR_H
diff --git a/src/feedback/GEAR/stellar_evolution.c b/src/feedback/GEAR/stellar_evolution.c
new file mode 100644
index 0000000000..15853d0ff9
--- /dev/null
+++ b/src/feedback/GEAR/stellar_evolution.c
@@ -0,0 +1,502 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Include header */
+#include "stellar_evolution.h"
+
+/* Include local headers */
+#include "hdf5_functions.h"
+#include "initial_mass_function.h"
+#include "lifetime.h"
+#include "random.h"
+#include "stellar_evolution_struct.h"
+#include "supernovae_ia.h"
+#include "supernovae_ii.h"
+
+#include <math.h>
+#include <stddef.h>
+
+/**
+ * @brief Print the stellar model.
+ *
+ * @param sm The #stellar_model.
+ */
+void stellar_model_print(const struct stellar_model* sm) {
+
+  /* Only the master print */
+  if (engine_rank != 0) {
+    return;
+  }
+
+  /* Print the type of yields */
+  message("Discrete yields? %i", sm->discrete_yields);
+
+  /* Print the sub properties */
+  initial_mass_function_print(&sm->imf);
+  lifetime_print(&sm->lifetime);
+  supernovae_ia_print(&sm->snia);
+  supernovae_ii_print(&sm->snii);
+}
+
+/**
+ * @brief Compute the integer number of supernovae from the floating number.
+ *
+ * @param sp The particle to act upon
+ * @param number_supernovae_f Floating number of supernovae during this step.
+ * @param ti_begin The #integertime_t at the begining of the step.
+ * @param random_type The categorie of random.
+ *
+ * @return The integer number of supernovae.
+ */
+int stellar_evolution_compute_integer_number_supernovae(
+    struct spart* restrict sp, float number_supernovae_f,
+    const integertime_t ti_begin, enum random_number_type random_type) {
+
+  const int number_supernovae_i = floor(number_supernovae_f);
+
+  /* Get the random number for the decimal part */
+  const float rand_sn = random_unit_interval(sp->id, ti_begin, random_type);
+
+  /* Get the fraction part */
+  const float frac_sn = number_supernovae_f - number_supernovae_i;
+
+  /* Get the integer number of SN */
+  return number_supernovae_i + ((rand_sn < frac_sn) ? 1 : 0);
+}
+
+/**
+ * @brief Compute the feedback properties.
+ *
+ * @param sp The particle to act upon
+ * @param sm The #stellar_model structure.
+ * @param phys_const The physical constants in the internal unit system.
+ * @param log_m_beg_step Mass of a star ending its life at the begining of the
+ * step (log10(solMass))
+ * @param log_m_end_step Mass of a star ending its life at the end of the step
+ * (log10(solMass))
+ * @param m_beg_step Mass of a star ending its life at the begining of the step
+ * (solMass)
+ * @param m_end_step Mass of a star ending its life at the end of the step
+ * (solMass)
+ * @param number_snia Number of SNIa produced by the stellar particle.
+ * @param number_snii Number of SNII produced by the stellar particle.
+ *
+ */
+void stellar_evolution_compute_continuous_feedback_properties(
+    struct spart* restrict sp, const struct stellar_model* sm,
+    const struct phys_const* phys_const, const float log_m_beg_step,
+    const float log_m_end_step, const float m_beg_step, const float m_end_step,
+    const float m_init, const int number_snia, const int number_snii) {
+
+  /* Compute the mass ejected */
+  /* SNIa */
+  const float mass_frac_snia =
+      supernovae_ia_get_ejected_mass_processed(&sm->snia) *
+      supernovae_ia_get_number(&sm->snia, m_end_step, m_beg_step);
+
+  /* SNII */
+  const float mass_frac_snii =
+      supernovae_ii_get_ejected_mass_fraction_processed(
+          &sm->snii, log_m_end_step, log_m_beg_step);
+
+  sp->feedback_data.mass_ejected = (mass_frac_snia + mass_frac_snii) * m_init;
+
+  /* Transform into internal units */
+  sp->feedback_data.mass_ejected *= phys_const->const_solar_mass;
+
+  if (sp->mass <= sp->feedback_data.mass_ejected) {
+    error("Stars cannot have negative mass. (%g <= %g). Initial mass = %g",
+          sp->mass, sp->feedback_data.mass_ejected, sp->birth.mass);
+  }
+
+  /* Update the mass */
+  sp->mass -= sp->feedback_data.mass_ejected;
+
+  /* Now deal with the metals */
+
+  /* Get the SNIa yields */
+  const float* snia_yields = supernovae_ia_get_yields(&sm->snia);
+
+  /* Compute the SNII yields */
+  float snii_yields[GEAR_CHEMISTRY_ELEMENT_COUNT];
+  supernovae_ii_get_yields(&sm->snii, log_m_end_step, log_m_beg_step,
+                           snii_yields);
+
+  /* Compute the mass fraction of non processed elements */
+  const float non_processed = supernovae_ii_get_ejected_mass_fraction(
+      &sm->snii, log_m_end_step, log_m_beg_step);
+
+  /* Set the yields */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    /* Compute the mass fraction of metals */
+    sp->feedback_data.metal_mass_ejected[i] =
+        /* Supernovae Ia yields */
+        snia_yields[i] * number_snia +
+        /* Supernovae II yields */
+        snii_yields[i] +
+        /* Gas contained in stars initial metallicity */
+        chemistry_get_metal_mass_fraction_for_feedback(sp)[i] * non_processed;
+
+    /* Convert it to total mass */
+    sp->feedback_data.metal_mass_ejected[i] *= sp->birth.mass;
+  }
+}
+
+/**
+ * @brief Compute the feedback properties.
+ *
+ * @param sp The particle to act upon
+ * @param sm The #stellar_model structure.
+ * @param phys_const The physical constants in the internal unit system.
+ * @param log_m_beg_step Mass of a star ending its life at the begining of the
+ * step (log10(solMass))
+ * @param log_m_end_step Mass of a star ending its life at the end of the step
+ * (log10(solMass))
+ * @param m_beg_step Mass of a star ending its life at the begining of the step
+ * (solMass)
+ * @param m_end_step Mass of a star ending its life at the end of the step
+ * (solMass)
+ * @param number_snia Number of SNIa produced by the stellar particle.
+ * @param number_snii Number of SNII produced by the stellar particle.
+ *
+ */
+void stellar_evolution_compute_discrete_feedback_properties(
+    struct spart* restrict sp, const struct stellar_model* sm,
+    const struct phys_const* phys_const, const float log_m_beg_step,
+    const float log_m_end_step, const float m_beg_step, const float m_end_step,
+    const float m_init, const int number_snia, const int number_snii) {
+
+  /* Get the normalization to the average */
+  const float normalization =
+      number_snii == 0
+          ? 0.
+          : number_snii /
+                (supernovae_ii_get_number(&sm->snii, m_end_step, m_beg_step) *
+                 m_init);
+
+  /* Compute the mass ejected */
+  /* SNIa */
+  const float mass_snia =
+      (number_snia == 0)
+          ? 0
+          : (supernovae_ia_get_ejected_mass_processed(&sm->snia) * number_snia);
+
+  /* SNII */
+  const float mass_snii =
+      normalization * supernovae_ii_get_ejected_mass_fraction_processed(
+                          &sm->snii, log_m_end_step, log_m_beg_step);
+
+  sp->feedback_data.mass_ejected = mass_snia + mass_snii;
+
+  /* Transform into internal units */
+  sp->feedback_data.mass_ejected *= phys_const->const_solar_mass;
+
+  if (sp->mass <= sp->feedback_data.mass_ejected) {
+    error("Stars cannot have negative mass. (%g <= %g). Initial mass = %g",
+          sp->mass, sp->feedback_data.mass_ejected, sp->birth.mass);
+  }
+
+  /* Update the mass */
+  sp->mass -= sp->feedback_data.mass_ejected;
+
+  /* Get the SNIa yields */
+  const float* snia_yields = supernovae_ia_get_yields(&sm->snia);
+
+  /* Compute the SNII yields (without the normalization) */
+  float snii_yields[GEAR_CHEMISTRY_ELEMENT_COUNT];
+  supernovae_ii_get_yields(&sm->snii, log_m_end_step, log_m_beg_step,
+                           snii_yields);
+
+  /* Compute the mass fraction of non processed elements */
+  const float non_processed =
+      normalization * supernovae_ii_get_ejected_mass_fraction(
+                          &sm->snii, log_m_end_step, log_m_beg_step);
+
+  /* Set the yields */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    /* Compute the mass fraction of metals */
+    sp->feedback_data.metal_mass_ejected[i] =
+        /* Supernovae Ia yields */
+        snia_yields[i] * number_snia +
+        /* Supernovae II yields */
+        normalization * snii_yields[i] +
+        /* Gas contained in stars initial metallicity */
+        chemistry_get_metal_mass_fraction_for_feedback(sp)[i] * non_processed;
+
+    /* Convert it to total mass */
+    sp->feedback_data.metal_mass_ejected[i] *= sp->birth.mass;
+  }
+}
+
+/**
+ * @brief Evolve the stellar properties of a #spart.
+ *
+ * This function compute the SN rate and yields before sending
+ * this information to a different MPI rank.
+ *
+ * Here I am using Myr-solar mass units internally in order to
+ * avoid numerical errors.
+ *
+ * @param sp The particle to act upon
+ * @param sm The #stellar_model structure.
+ * @param cosmo The current cosmological model.
+ * @param us The unit system.
+ * @param phys_const The physical constants in the internal unit system.
+ * @param ti_begin The #integertime_t at the begining of the step.
+ * @param star_age_beg_step The age of the star at the star of the time-step in
+ * internal units.
+ * @param dt The time-step size of this star in internal units.
+ */
+void stellar_evolution_evolve_spart(
+    struct spart* restrict sp, const struct stellar_model* sm,
+    const struct cosmology* cosmo, const struct unit_system* us,
+    const struct phys_const* phys_const, const integertime_t ti_begin,
+    const double star_age_beg_step, const double dt) {
+
+  /* Convert the inputs */
+  const double conversion_to_myr = phys_const->const_year * 1e6;
+  const double star_age_beg_step_myr = star_age_beg_step / conversion_to_myr;
+  const double dt_myr = dt / conversion_to_myr;
+
+  /* Get the metallicity */
+  const float metallicity =
+      chemistry_get_total_metal_mass_fraction_for_feedback(sp);
+
+  /* Compute masses range */
+  const float log_m_beg_step =
+      star_age_beg_step == 0.
+          ? FLT_MAX
+          : lifetime_get_log_mass_from_lifetime(
+                &sm->lifetime, log10(star_age_beg_step_myr), metallicity);
+  const float log_m_end_step = lifetime_get_log_mass_from_lifetime(
+      &sm->lifetime, log10(star_age_beg_step_myr + dt_myr), metallicity);
+
+  const float m_beg_step =
+      star_age_beg_step == 0. ? FLT_MAX : pow(10, log_m_beg_step);
+  const float m_end_step = pow(10, log_m_end_step);
+
+  /* Check if the star can produce a supernovae */
+  const int can_produce_snia =
+      supernovae_ia_can_explode(&sm->snia, m_end_step, m_beg_step);
+  const int can_produce_snii =
+      supernovae_ii_can_explode(&sm->snii, m_end_step, m_beg_step);
+
+  /* Is it possible to generate a supernovae? */
+  if (!can_produce_snia && !can_produce_snii) return;
+
+  /* Compute the initial mass */
+  const float m_init = sp->birth.mass / phys_const->const_solar_mass;
+
+  /* Compute number of SNIa */
+  int number_snia = 0;
+  if (can_produce_snia) {
+    /* Compute rates */
+    const float number_snia_f =
+        supernovae_ia_get_number(&sm->snia, m_end_step, m_beg_step) * m_init;
+
+    /* Get the integer number of supernovae */
+    number_snia = stellar_evolution_compute_integer_number_supernovae(
+        sp, number_snia_f, ti_begin, random_number_stellar_feedback_1);
+  }
+
+  /* Compute number of SNII */
+  int number_snii = 0;
+  if (can_produce_snii) {
+    /* Compute rates */
+    const float number_snii_f =
+        supernovae_ii_get_number(&sm->snii, m_end_step, m_beg_step) * m_init;
+
+    /* Get the integer number of supernovae */
+    number_snii = stellar_evolution_compute_integer_number_supernovae(
+        sp, number_snii_f, ti_begin, random_number_stellar_feedback_2);
+  }
+
+  /* Does this star produce a supernovae? */
+  if (number_snia == 0 && number_snii == 0) return;
+
+  sp->feedback_data.number_sn = number_snia + number_snii;
+
+  /* Compute the properties of the feedback (e.g. yields) */
+  if (sm->discrete_yields) {
+    stellar_evolution_compute_discrete_feedback_properties(
+        sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step,
+        m_end_step, m_init, number_snia, number_snii);
+  } else {
+    stellar_evolution_compute_continuous_feedback_properties(
+        sp, sm, phys_const, log_m_beg_step, log_m_end_step, m_beg_step,
+        m_end_step, m_init, number_snia, number_snii);
+  }
+}
+
+/**
+ * @brief Get the name of the element i.
+ *
+ * @param sm The #stellar_model.
+ * @param i The element indice.
+ */
+const char* stellar_evolution_get_element_name(const struct stellar_model* sm,
+                                               int i) {
+
+  return sm->elements_name + i * GEAR_LABELS_SIZE;
+}
+
+/**
+ * @brief Read the name of all the elements present in the tables.
+ *
+ * @param sm The #stellar_model.
+ */
+void stellar_evolution_read_elements(struct stellar_model* sm,
+                                     struct swift_params* params) {
+
+  hid_t file_id, group_id;
+
+  /* Open IMF group */
+  h5_open_group(params, "Data", &file_id, &group_id);
+
+  /* Read the elements */
+  io_read_string_array_attribute(group_id, "elts", sm->elements_name,
+                                 GEAR_CHEMISTRY_ELEMENT_COUNT,
+                                 GEAR_LABELS_SIZE);
+
+  /* Check that we received correctly the metals */
+  if (strcmp(stellar_evolution_get_element_name(
+                 sm, GEAR_CHEMISTRY_ELEMENT_COUNT - 1),
+             "Metals") != 0) {
+    error(
+        "The chemistry table should contain the metals in the last column "
+        "(found %s)",
+        stellar_evolution_get_element_name(sm,
+                                           GEAR_CHEMISTRY_ELEMENT_COUNT - 1));
+  }
+
+  /* Print the name of the elements */
+  char txt[GEAR_CHEMISTRY_ELEMENT_COUNT * (GEAR_LABELS_SIZE + 2)] = "";
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    if (i != 0) {
+      strcat(txt, ", ");
+    }
+    strcat(txt, stellar_evolution_get_element_name(sm, i));
+  }
+
+  if (engine_rank == 0) {
+    message("Chemistry elements: %s", txt);
+  }
+
+  /* Cleanup everything */
+  h5_close_group(file_id, group_id);
+}
+
+/**
+ * @brief Initialize the global properties of the stellar evolution scheme.
+ *
+ * @param sm The #stellar_model.
+ * @param phys_const The physical constants in the internal unit system.
+ * @param us The internal unit system.
+ * @param params The parsed parameters.
+ * @param hydro_props The already read-in properties of the hydro scheme.
+ * @param cosmo The cosmological model.
+ */
+void stellar_evolution_props_init(struct stellar_model* sm,
+                                  const struct phys_const* phys_const,
+                                  const struct unit_system* us,
+                                  struct swift_params* params,
+                                  const struct cosmology* cosmo) {
+
+  /* Read the list of elements */
+  stellar_evolution_read_elements(sm, params);
+
+  /* Use the discrete yields approach? */
+  sm->discrete_yields =
+      parser_get_param_int(params, "GEARFeedback:discrete_yields");
+
+  /* Initialize the initial mass function */
+  initial_mass_function_init(&sm->imf, phys_const, us, params);
+
+  /* Initialize the lifetime model */
+  lifetime_init(&sm->lifetime, phys_const, us, params);
+
+  /* Initialize the supernovae Ia model */
+  supernovae_ia_init(&sm->snia, phys_const, us, params, sm);
+
+  /* Initialize the supernovae II model */
+  supernovae_ii_init(&sm->snii, phys_const, us, params, sm);
+}
+
+/**
+ * @brief Write a stellar_evolution struct to the given FILE as a stream of
+ * bytes.
+ *
+ * Here we are only writing the arrays, everything has been copied in the
+ * feedback.
+ *
+ * @param sm the struct
+ * @param stream the file stream
+ */
+void stellar_evolution_dump(const struct stellar_model* sm, FILE* stream) {
+
+  /* Dump the initial mass function */
+  initial_mass_function_dump(&sm->imf, stream, sm);
+
+  /* Dump the lifetime model */
+  lifetime_dump(&sm->lifetime, stream, sm);
+
+  /* Dump the supernovae Ia model */
+  supernovae_ia_dump(&sm->snia, stream, sm);
+
+  /* Dump the supernovae II model */
+  supernovae_ii_dump(&sm->snii, stream, sm);
+}
+
+/**
+ * @brief Restore a stellar_evolution struct from the given FILE as a stream of
+ * bytes.
+ *
+ * Here we are only writing the arrays, everything has been copied in the
+ * feedback.
+ *
+ * @param sm the struct
+ * @param stream the file stream
+ */
+void stellar_evolution_restore(struct stellar_model* sm, FILE* stream) {
+
+  /* Restore the initial mass function */
+  initial_mass_function_restore(&sm->imf, stream, sm);
+
+  /* Restore the lifetime model */
+  lifetime_restore(&sm->lifetime, stream, sm);
+
+  /* Restore the supernovae Ia model */
+  supernovae_ia_restore(&sm->snia, stream, sm);
+
+  /* Restore the supernovae II model */
+  supernovae_ii_restore(&sm->snii, stream, sm);
+}
+
+/**
+ * @brief Clean the allocated memory.
+ *
+ * @param sm the #stellar_model.
+ */
+void stellar_evolution_clean(struct stellar_model* sm) {
+
+  initial_mass_function_clean(&sm->imf);
+  lifetime_clean(&sm->lifetime);
+  supernovae_ia_clean(&sm->snia);
+  supernovae_ii_clean(&sm->snii);
+}
diff --git a/src/feedback/GEAR/stellar_evolution.h b/src/feedback/GEAR/stellar_evolution.h
new file mode 100644
index 0000000000..9ffda5ff67
--- /dev/null
+++ b/src/feedback/GEAR/stellar_evolution.h
@@ -0,0 +1,70 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_STELLAR_EVOLUTION_GEAR_H
+#define SWIFT_STELLAR_EVOLUTION_GEAR_H
+
+#include "hdf5_functions.h"
+#include "initial_mass_function.h"
+#include "lifetime.h"
+#include "random.h"
+#include "stellar_evolution_struct.h"
+#include "supernovae_ia.h"
+#include "supernovae_ii.h"
+
+#include <math.h>
+#include <stddef.h>
+
+void stellar_model_print(const struct stellar_model* sm);
+int stellar_evolution_compute_integer_number_supernovae(
+    struct spart* restrict sp, float number_supernovae_f,
+    const integertime_t ti_begin, enum random_number_type random_type);
+
+void stellar_evolution_compute_continuous_feedback_properties(
+    struct spart* restrict sp, const struct stellar_model* sm,
+    const struct phys_const* phys_const, const float log_m_beg_step,
+    const float log_m_end_step, const float m_beg_step, const float m_end_step,
+    const float m_init, const int number_snia, const int number_snii);
+void stellar_evolution_compute_discrete_feedback_properties(
+    struct spart* restrict sp, const struct stellar_model* sm,
+    const struct phys_const* phys_const, const float log_m_beg_step,
+    const float log_m_end_step, const float m_beg_step, const float m_end_step,
+    const float m_init, const int number_snia, const int number_snii);
+
+void stellar_evolution_evolve_spart(
+    struct spart* restrict sp, const struct stellar_model* sm,
+    const struct cosmology* cosmo, const struct unit_system* us,
+    const struct phys_const* phys_const, const integertime_t ti_begin,
+    const double star_age_beg_step, const double dt);
+
+const char* stellar_evolution_get_element_name(const struct stellar_model* sm,
+                                               int i);
+void stellar_evolution_read_elements(struct stellar_model* sm,
+                                     struct swift_params* params);
+void stellar_evolution_props_init(struct stellar_model* sm,
+                                  const struct phys_const* phys_const,
+                                  const struct unit_system* us,
+                                  struct swift_params* params,
+                                  const struct cosmology* cosmo);
+
+void stellar_evolution_dump(const struct stellar_model* sm, FILE* stream);
+void stellar_evolution_restore(struct stellar_model* sm, FILE* stream);
+
+void stellar_evolution_clean(struct stellar_model* sm);
+
+#endif  // SWIFT_STELLAR_EVOLUTION_GEAR_H
diff --git a/src/feedback/GEAR/stellar_evolution_struct.h b/src/feedback/GEAR/stellar_evolution_struct.h
new file mode 100644
index 0000000000..cb6280c65c
--- /dev/null
+++ b/src/feedback/GEAR/stellar_evolution_struct.h
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_STELLAR_EVOLUTION_STRUCT_GEAR_H
+#define SWIFT_STELLAR_EVOLUTION_STRUCT_GEAR_H
+
+#include "interpolation.h"
+
+/* Number of different type of companion.
+   If changed, the IO needs to be updated.
+ */
+#define GEAR_NUMBER_TYPE_OF_COMPANION 2
+#define GEAR_LABELS_SIZE 10
+
+/**
+ * @brief Model for the initial mass function.
+ *
+ * Describe a model such as Kroupa 2001:
+ *
+ * f(m) = coef[i] * pow(m, exp[i])
+ */
+struct initial_mass_function {
+
+  /*! Mass limits between IMF parts (n_parts + 1 elements). */
+  float *mass_limits;
+
+  /*! Exponent of each IMF parts (n_parts elements). */
+  float *exp;
+
+  /*! Coefficient of each IMF parts (n_parts elements). */
+  float *coef;
+
+  /*! Number of parts in the function. */
+  int n_parts;
+
+  /*! Minimal mass contained in mass_limits, copied for more clarity. */
+  float mass_min;
+
+  /*! Maximal mass contained in mass_limits, copied for more clarity. */
+  float mass_max;
+};
+
+/**
+ * @brief Model for the stellar lifetime.
+ */
+struct lifetime {
+
+  /*! Coefficients for the log10(m)^2 term */
+  float quadratic[3];
+
+  /*! Coefficients for the log10(m) term */
+  float linear[3];
+
+  /*! Coefficients for the constant term */
+  float constant[3];
+};
+
+/**
+ * @brief Model for SNIa.
+ */
+struct supernovae_ia {
+  /*! Mass of each element ejected by a single supernovae */
+  float yields[GEAR_CHEMISTRY_ELEMENT_COUNT];
+
+  /*! White dwarf's mass */
+  float mass_white_dwarf;
+
+  /*! Minimal mass of the progenitor */
+  float mass_min_progenitor;
+
+  /*! Maximal mass of the progenitor */
+  float mass_max_progenitor;
+
+  /*! coefficient of the initial mass function for progenitor divided by
+   * progenitor_exponent */
+  float progenitor_coef_exp;
+
+  /*! exponent of the initial mass function for progenitor */
+  float progenitor_exponent;
+
+  /*! exponent of the initial mass function for binaries */
+  float companion_exponent;
+
+  struct {
+    /*! Initial mass function's coeffcients */
+    float coef;
+
+    /*! Maximal mass of the companion */
+    float mass_max;
+
+    /*! Minimal mass of the companion */
+    float mass_min;
+  } companion[GEAR_NUMBER_TYPE_OF_COMPANION];
+};
+
+/**
+ * @brief Model for SNII.
+ */
+struct supernovae_ii {
+
+  /*! Integrated (over the IMF) mass fraction of metals ejected by a supernovae
+   */
+  struct interpolation_1d integrated_yields[GEAR_CHEMISTRY_ELEMENT_COUNT];
+
+  /*! Total mass fraction ejected (integrated over the IMF) */
+  struct interpolation_1d integrated_ejected_mass_processed;
+
+  /*! Mass fraction ejected and not processed (=> with the star metallicity) */
+  struct interpolation_1d integrated_ejected_mass;
+
+  /*! Minimal mass for a SNII */
+  float mass_min;
+
+  /*! Maximal mass for a SNII */
+  float mass_max;
+
+  /*! exponent of the IMF */
+  float exponent;
+
+  /*! coefficient of the IMF over the exponent */
+  float coef_exp;
+};
+
+/**
+ * @brief The complete stellar model.
+ */
+struct stellar_model {
+
+  /*! Name of the different elements */
+  char elements_name[GEAR_CHEMISTRY_ELEMENT_COUNT * GEAR_LABELS_SIZE];
+
+  /*! The initial mass function */
+  struct initial_mass_function imf;
+
+  /*! The stellar lifetime */
+  struct lifetime lifetime;
+
+  /*! The supernovae type Ia */
+  struct supernovae_ia snia;
+
+  /*! The supernovae type II */
+  struct supernovae_ii snii;
+
+  /*! Use a discrete yields approach */
+  char discrete_yields;
+};
+
+#endif  // SWIFT_STELLAR_EVOLUTION_STRUCT_GEAR_H
diff --git a/src/feedback/GEAR/supernovae_ia.c b/src/feedback/GEAR/supernovae_ia.c
new file mode 100644
index 0000000000..7d42694747
--- /dev/null
+++ b/src/feedback/GEAR/supernovae_ia.c
@@ -0,0 +1,418 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Include header */
+#include "supernovae_ia.h"
+
+/* Local headers */
+#include "hdf5_functions.h"
+#include "stellar_evolution.h"
+#include "stellar_evolution_struct.h"
+
+/**
+ * @brief Print the supernovae Ia model.
+ *
+ * @param snia The #supernovae_ia.
+ */
+void supernovae_ia_print(const struct supernovae_ia *snia) {
+
+  /* Only the master print */
+  if (engine_rank != 0) {
+    return;
+  }
+
+  message("Mass of the white dwarf = %g", snia->mass_white_dwarf);
+  message("Mass range of the progenitor = [%g, %g]", snia->mass_min_progenitor,
+          snia->mass_max_progenitor);
+
+  for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) {
+    message("Mass range of the companion %i = [%g, %g]", i,
+            snia->companion[i].mass_min, snia->companion[i].mass_max);
+  }
+}
+
+/**
+ * @brief Check if the given mass is able to produce a SNIa.
+ *
+ * @param snia The #supernovae_ia model.
+ * @param m_low The lower mass.
+ * @param m_high The higher mass.
+ *
+ * @return If the mass is in the range of SNIa.
+ */
+int supernovae_ia_can_explode(const struct supernovae_ia *snia, float m_low,
+                              float m_high) {
+
+  if (m_low > snia->mass_max_progenitor) return 0;
+
+  for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) {
+    if (m_low < snia->companion[i].mass_max &&
+        m_high > snia->companion[i].mass_min) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+/**
+ * @brief Get the yields of a supernovae Ia.
+ *
+ * @param snia The #supernovae_ia model.
+ */
+const float *supernovae_ia_get_yields(const struct supernovae_ia *snia) {
+  return snia->yields;
+}
+
+/**
+ * @brief Get the processed mass ejected of a supernovae Ia.
+ *
+ * @param snia The #supernovae_ia model.
+ */
+float supernovae_ia_get_ejected_mass_processed(
+    const struct supernovae_ia *snia) {
+  return snia->mass_white_dwarf;
+}
+
+/**
+ * @brief Compute the companion integral (second integral in equation 3.46 in
+ * Poirier 2004)
+ *
+ * @param snia The #supernovae_ia model.
+ * @param m1 The lower mass limit.
+ * @param m2 The upper mass limit.
+ * @param companion_type The type of companion (e.g. index of snia->companion).
+ *
+ * @return The fraction of companion.
+ */
+float supernovae_ia_get_companion_fraction(const struct supernovae_ia *snia,
+                                           float m1, float m2,
+                                           int companion_type) {
+#ifdef SWIFT_DEBUG_CHECKS
+  if (m1 > m2) error("Mass 1 larger than mass 2 %g > %g.", m1, m2);
+#endif
+
+  const float tmp =
+      pow(m2, snia->companion_exponent) - pow(m1, snia->companion_exponent);
+  return snia->companion[companion_type].coef * tmp / snia->companion_exponent;
+}
+
+/**
+ * @brief Compute the number of supernovae Ia per unit of mass (equation 3.46 in
+ * Poirier 2004).
+ *
+ * @param snia The #supernovae_ia model.
+ * @param m1 The lower mass limit.
+ * @param m2 The upper mass limit.
+ *
+ * @return The number of supernovae Ia per unit of mass.
+ */
+float supernovae_ia_get_number(const struct supernovae_ia *snia, float m1,
+                               float m2) {
+
+#ifdef SWIFT_DEBUG_CHECKS
+  if (m1 > m2) error("Mass 1 larger than mass 2 %g > %g.", m1, m2);
+#endif
+
+  /* Do we have white dwarf? */
+  if (m1 > snia->mass_max_progenitor) {
+    return 0.;
+  }
+
+  float number_companion = 0.;
+  for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) {
+    /* Check if we are in the possible interval */
+    if (m1 > snia->companion[i].mass_max || m2 < snia->companion[i].mass_min)
+      continue;
+
+    /* Get mass limits */
+    const float mass_min = max(m1, snia->companion[i].mass_min);
+    const float mass_max = min(m2, snia->companion[i].mass_max);
+
+    /* Compute number of companions */
+    number_companion +=
+        supernovae_ia_get_companion_fraction(snia, mass_min, mass_max, i);
+  }
+
+  /* Use only the white dwarf already created */
+  const float mass_min = max(m1, snia->mass_min_progenitor);
+
+  /* Compute number of white dwarf */
+  float number_white_dwarf =
+      pow(snia->mass_max_progenitor, snia->progenitor_exponent);
+  number_white_dwarf -= pow(mass_min, snia->progenitor_exponent);
+  number_white_dwarf *= snia->progenitor_coef_exp;
+
+  return number_companion * number_white_dwarf;
+};
+
+/**
+ * @brief Read the SNIa yields from the table.
+ *
+ * @param snia The #supernovae_ia model.
+ * @param params The #swift_params.
+ * @param sm The #stellar_model.
+ */
+void supernovae_ia_read_yields(struct supernovae_ia *snia,
+                               struct swift_params *params,
+                               const struct stellar_model *sm) {
+
+  hid_t file_id, group_id;
+  const int number_labels = GEAR_CHEMISTRY_ELEMENT_COUNT + 2;
+
+  /* Open IMF group */
+  h5_open_group(params, "Data/SNIa/Metals", &file_id, &group_id);
+
+  /* Read the yields */
+  float *yields = (float *)malloc(sizeof(float) * number_labels);
+  io_read_array_attribute(group_id, "data", FLOAT, yields, number_labels);
+
+  /* Read the labels */
+  char labels[(GEAR_CHEMISTRY_ELEMENT_COUNT + 2) * GEAR_LABELS_SIZE] = "";
+  io_read_string_array_attribute(group_id, "elts", labels, number_labels,
+                                 GEAR_LABELS_SIZE);
+
+  /* Save the yields */
+  /* Loop over the elements in sm */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    int found = 0;
+    /* Loop over SNIa yields labels */
+    for (int j = 0; j < number_labels; j++) {
+      const char *s1 = labels + j * GEAR_LABELS_SIZE;
+      const char *s2 = stellar_evolution_get_element_name(sm, i);
+      if (strcmp(s1, s2) == 0) {
+        found = 1;
+        snia->yields[i] = yields[j];
+        break;
+      }
+    }
+
+    /* Check if found an element */
+    if (!found) {
+      error("Cannot find element %s in SNIa yields",
+            stellar_evolution_get_element_name(sm, i));
+    }
+  }
+
+  /* Cleanup everything */
+  free(yields);
+  h5_close_group(file_id, group_id);
+};
+
+/**
+ * @brief Initialize the companion structure in the #supernovae_ia.
+ */
+void supernovae_ia_init_companion(struct supernovae_ia *snia) {
+
+  for (int i = 0; i < GEAR_NUMBER_TYPE_OF_COMPANION; i++) {
+    /* Compute the integral */
+    float integral = supernovae_ia_get_companion_fraction(
+        snia, snia->companion[i].mass_min, snia->companion[i].mass_max, i);
+
+    /* Update the coefficient for a normalization to 1 of the IMF */
+    snia->companion[i].coef *= snia->companion[i].coef / integral;
+  }
+}
+
+/**
+ * @brief Reads the supernovae Ia parameters from the tables.
+ *
+ * @param snia The #supernovae_ia model.
+ * @param params The simulation parameters.
+ */
+void supernovae_ia_read_from_tables(struct supernovae_ia *snia,
+                                    struct swift_params *params) {
+
+  hid_t file_id, group_id;
+
+  /* Open IMF group */
+  h5_open_group(params, "Data/SNIa", &file_id, &group_id);
+
+  /* Read the exponent of the IMF for companion */
+  io_read_attribute(group_id, "a", FLOAT, &snia->companion_exponent);
+
+  /* Read the minimal mass for a white dwarf progenitor */
+  io_read_attribute(group_id, "Mpl", FLOAT, &snia->mass_min_progenitor);
+
+  /* Read the maximal mass for a white dwarf */
+  io_read_attribute(group_id, "Mpu", FLOAT, &snia->mass_max_progenitor);
+
+  /* Read the maximal mass of a red giant companion */
+  io_read_attribute(group_id, "Mdu1", FLOAT, &snia->companion[0].mass_max);
+
+  /* Read the minimal mass of a red giant companion */
+  io_read_attribute(group_id, "Mdl1", FLOAT, &snia->companion[0].mass_min);
+
+  /* Read the coefficient of the main sequence companion */
+  io_read_attribute(group_id, "bb1", FLOAT, &snia->companion[0].coef);
+
+  /* Read the maximal mass of a main sequence companion */
+  io_read_attribute(group_id, "Mdu2", FLOAT, &snia->companion[1].mass_max);
+
+  /* Read the minimal mass of a main sequence companion */
+  io_read_attribute(group_id, "Mdl2", FLOAT, &snia->companion[1].mass_min);
+
+  /* Read the coefficient of the main sequence companion */
+  io_read_attribute(group_id, "bb2", FLOAT, &snia->companion[1].coef);
+
+  /* Cleanup everything */
+  h5_close_group(file_id, group_id);
+
+  /* Read the white dwarf mass */
+
+  /* Open IMF group */
+  h5_open_group(params, "Data", &file_id, &group_id);
+
+  /* Read the white dwarf mass */
+  io_read_attribute(group_id, "MeanWDMass", FLOAT, &snia->mass_white_dwarf);
+
+  /* Cleanup everything */
+  h5_close_group(file_id, group_id);
+}
+
+/**
+ * @brief Reads the supernovae Ia parameters from the parameters file.
+ *
+ * @param snia The #supernovae_ia model.
+ * @param params The simulation parameters.
+ */
+void supernovae_ia_read_from_params(struct supernovae_ia *snia,
+                                    struct swift_params *params) {
+
+  /* Read the exponent of the IMF for companion */
+  snia->companion_exponent = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:exponent", snia->companion_exponent);
+
+  /* Read the minimal mass for a white dwarf */
+  snia->mass_min_progenitor = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:min_mass_white_dwarf_progenitor",
+      snia->mass_min_progenitor);
+
+  /* Read the maximal mass for a white dwarf */
+  snia->mass_max_progenitor = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:max_mass_white_dwarf_progenitor",
+      snia->mass_max_progenitor);
+
+  /* Read the maximal mass of a red giant companion */
+  snia->companion[0].mass_max =
+      parser_get_opt_param_float(params, "GEARSupernovaeIa:max_mass_red_giant",
+                                 snia->companion[0].mass_max);
+
+  /* Read the minimal mass of a red giant companion */
+  snia->companion[0].mass_min =
+      parser_get_opt_param_float(params, "GEARSupernovaeIa:min_mass_red_giant",
+                                 snia->companion[0].mass_min);
+
+  /* Read the coefficient of the main sequence companion */
+  snia->companion[0].coef = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:coef_red_giant", snia->companion[0].coef);
+
+  /* Read the maximal mass of a main sequence companion */
+  snia->companion[1].mass_max = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:max_mass_main_sequence",
+      snia->companion[1].mass_max);
+
+  /* Read the minimal mass of a main sequence companion */
+  snia->companion[1].mass_min = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:min_mass_main_sequence",
+      snia->companion[1].mass_min);
+
+  /* Read the coefficient of the main sequence companion */
+  snia->companion[1].coef = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:coef_main_sequence", snia->companion[1].coef);
+
+  /* Read the mass of a white dwarf */
+  snia->mass_white_dwarf = parser_get_opt_param_float(
+      params, "GEARSupernovaeIa:white_dwarf_mass", snia->mass_white_dwarf);
+}
+
+/**
+ * @brief Initialize the #supernovae_ia structure.
+ *
+ * @param snia The #supernovae_ia model.
+ * @param phys_const The #phys_const.
+ * @param us The #unit_system.
+ * @param params The simulation parameters.
+ * @param sm The #stellar_model.
+ */
+void supernovae_ia_init(struct supernovae_ia *snia,
+                        const struct phys_const *phys_const,
+                        const struct unit_system *us,
+                        struct swift_params *params,
+                        const struct stellar_model *sm) {
+
+  /* Read the parameters from the tables */
+  supernovae_ia_read_from_tables(snia, params);
+
+  /* Read the parameters from the params file */
+  supernovae_ia_read_from_params(snia, params);
+
+  /* Read the yields */
+  supernovae_ia_read_yields(snia, params, sm);
+
+  /* Get the IMF parameters */
+  snia->progenitor_exponent = initial_mass_function_get_exponent(
+      &sm->imf, snia->mass_min_progenitor, snia->mass_max_progenitor);
+  snia->progenitor_coef_exp = initial_mass_function_get_coefficient(
+      &sm->imf, snia->mass_min_progenitor, snia->mass_max_progenitor);
+  snia->progenitor_coef_exp /= snia->progenitor_exponent;
+
+  /* Compute the normalization coefficients of the companion IMF */
+  supernovae_ia_init_companion(snia);
+}
+
+/**
+ * @brief Write a supernovae_ia struct to the given FILE as a stream of bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param snia the struct
+ * @param stream the file stream
+ * @param sm The #stellar_model.
+ */
+void supernovae_ia_dump(const struct supernovae_ia *snia, FILE *stream,
+                        const struct stellar_model *sm) {
+
+  /* Nothing to do here */
+}
+
+/**
+ * @brief Restore a supernovae_ia struct from the given FILE as a stream of
+ * bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param snia the struct
+ * @param stream the file stream
+ * @param sm The #stellar_model.
+ */
+void supernovae_ia_restore(struct supernovae_ia *snia, FILE *stream,
+                           const struct stellar_model *sm) {
+
+  /* Nothing to do here */
+}
+
+/**
+ * @brief Clean the allocated memory.
+ *
+ * @param snia the #supernovae_ia.
+ */
+void supernovae_ia_clean(struct supernovae_ia *snia) {}
diff --git a/src/feedback/GEAR/supernovae_ia.h b/src/feedback/GEAR/supernovae_ia.h
new file mode 100644
index 0000000000..19a6923c2d
--- /dev/null
+++ b/src/feedback/GEAR/supernovae_ia.h
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_SUPERNOVAE_IA_GEAR_H
+#define SWIFT_SUPERNOVAE_IA_GEAR_H
+
+#include "hdf5_functions.h"
+#include "stellar_evolution.h"
+#include "stellar_evolution_struct.h"
+
+void supernovae_ia_print(const struct supernovae_ia *snia);
+int supernovae_ia_can_explode(const struct supernovae_ia *snia, float m_low,
+                              float m_high);
+const float *supernovae_ia_get_yields(const struct supernovae_ia *snia);
+float supernovae_ia_get_ejected_mass_processed(
+    const struct supernovae_ia *snia);
+float supernovae_ia_get_companion_fraction(const struct supernovae_ia *snia,
+                                           float m1, float m2,
+                                           int companion_type);
+float supernovae_ia_get_number(const struct supernovae_ia *snia, float m1,
+                               float m2);
+
+void supernovae_ia_read_yields(struct supernovae_ia *snia,
+                               struct swift_params *params,
+                               const struct stellar_model *sm);
+
+void supernovae_ia_init_companion(struct supernovae_ia *snia);
+
+void supernovae_ia_read_from_tables(struct supernovae_ia *snia,
+                                    struct swift_params *params);
+
+void supernovae_ia_read_from_params(struct supernovae_ia *snia,
+                                    struct swift_params *params);
+
+void supernovae_ia_init(struct supernovae_ia *snia,
+                        const struct phys_const *phys_const,
+                        const struct unit_system *us,
+                        struct swift_params *params,
+                        const struct stellar_model *sm);
+
+void supernovae_ia_dump(const struct supernovae_ia *snia, FILE *stream,
+                        const struct stellar_model *sm);
+
+void supernovae_ia_restore(struct supernovae_ia *snia, FILE *stream,
+                           const struct stellar_model *sm);
+void supernovae_ia_clean(struct supernovae_ia *snia);
+
+#endif  // SWIFT_SUPERNOVAE_IA_GEAR_H
diff --git a/src/feedback/GEAR/supernovae_ii.c b/src/feedback/GEAR/supernovae_ii.c
new file mode 100644
index 0000000000..8b44afe834
--- /dev/null
+++ b/src/feedback/GEAR/supernovae_ii.c
@@ -0,0 +1,432 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+
+/* Include header */
+#include "supernovae_ii.h"
+
+/* Local headers */
+#include "hdf5_functions.h"
+#include "interpolation.h"
+#include "stellar_evolution.h"
+#include "stellar_evolution_struct.h"
+
+/**
+ * @brief Print the supernovae II model.
+ *
+ * @param snii The #supernovae_ii.
+ */
+void supernovae_ii_print(const struct supernovae_ii *snii) {
+
+  /* Only the master print */
+  if (engine_rank != 0) {
+    return;
+  }
+
+  message("Mass range for SNII = [%g, %g]", snii->mass_min, snii->mass_max);
+}
+
+/**
+ * @brief Check if the given mass is able to produce a SNII.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param m_low The lower mass.
+ * @param m_high The higher mass
+ *
+ * @return If the mass is in the range of SNII.
+ */
+int supernovae_ii_can_explode(const struct supernovae_ii *snii, float m_low,
+                              float m_high) {
+
+  if (m_high < snii->mass_min || m_low > snii->mass_max) return 0;
+
+  return 1;
+}
+
+/**
+ * @brief Compute the number of supernovae II per unit of mass (equation 3.47 in
+ * Poirier 2004).
+ *
+ * @param snii The #supernovae_ii model.
+ * @param m1 The lower mass limit.
+ * @param m2 The upper mass limit.
+ *
+ * @return The number of supernovae II per unit of mass.
+ */
+float supernovae_ii_get_number(const struct supernovae_ii *snii, float m1,
+                               float m2) {
+#ifdef SWIFT_DEBUG_CHECKS
+  if (m1 > m2) error("Mass 1 larger than mass 2 %g > %g.", m1, m2);
+#endif
+
+  /* Can we explode SNII? */
+  if (!supernovae_ii_can_explode(snii, m1, m2)) {
+    return 0.;
+  }
+
+  const float mass_min = max(m1, snii->mass_min);
+  const float mass_max = min(m2, snii->mass_max);
+
+  const float pow_mass =
+      pow(mass_max, snii->exponent) - pow(mass_min, snii->exponent);
+
+  return snii->coef_exp * pow_mass;
+};
+
+/**
+ * @brief Get the SNII yields per mass (Poirier version).
+ *
+ * @param snii The #supernovae_ii model.
+ * @param m1 The lower mass in log.
+ * @param m2 The upper mass in log.
+ * @param yields The elements ejected (needs to be allocated).
+ */
+void supernovae_ii_get_yields(const struct supernovae_ii *snii, float log_m1,
+                              float log_m2, float *yields) {
+
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    float yields_1 = interpolate_1d(&snii->integrated_yields[i], log_m1);
+    float yields_2 = interpolate_1d(&snii->integrated_yields[i], log_m2);
+
+    yields[i] = yields_2 - yields_1;
+  }
+};
+
+/**
+ * @brief Get the ejected mass per mass unit.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param m1 The lower mass in log.
+ * @param m2 The upper mass in log.
+ *
+ * @return mass_ejected_processed The mass of processsed elements.
+ */
+float supernovae_ii_get_ejected_mass_fraction(const struct supernovae_ii *snii,
+                                              float log_m1, float log_m2) {
+
+  float mass_ejected_1 = interpolate_1d(&snii->integrated_ejected_mass, log_m1);
+  float mass_ejected_2 = interpolate_1d(&snii->integrated_ejected_mass, log_m2);
+
+  return mass_ejected_2 - mass_ejected_1;
+};
+
+/**
+ * @brief Get the ejected mass (processed) per mass.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param log_m1 The lower mass in log.
+ * @param log_m2 The upper mass in log.
+ *
+ * @return mass_ejected The mass of non processsed elements.
+ */
+float supernovae_ii_get_ejected_mass_fraction_processed(
+    const struct supernovae_ii *snii, float log_m1, float log_m2) {
+
+  float mass_ejected_1 =
+      interpolate_1d(&snii->integrated_ejected_mass_processed, log_m1);
+  float mass_ejected_2 =
+      interpolate_1d(&snii->integrated_ejected_mass_processed, log_m2);
+
+  return mass_ejected_2 - mass_ejected_1;
+};
+
+/**
+ * @brief Read an array of SNII yields from the table.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param sm The #stellar_model.
+ */
+void supernovae_ii_read_yields_array(
+    struct supernovae_ii *snii, struct interpolation_1d *interp,
+    const struct phys_const *phys_const, const struct stellar_model *sm,
+    hid_t group_id, const char *hdf5_dataset_name, hsize_t *previous_count,
+    int interpolation_size) {
+
+  /* Now let's get the number of elements */
+  /* Open attribute */
+  const hid_t h_dataset = H5Dopen(group_id, hdf5_dataset_name, H5P_DEFAULT);
+  if (h_dataset < 0)
+    error("Error while opening attribute '%s'", hdf5_dataset_name);
+
+  /* Get the number of elements */
+  hsize_t count = io_get_number_element_in_dataset(h_dataset);
+
+  /* Check that all the arrays have the same size */
+  if (*previous_count != 0 && count != *previous_count) {
+    error("The code is not able to deal with yields arrays of different size");
+  }
+  *previous_count = count;
+
+  /* Read the minimal mass (in log) */
+  float log_mass_min = 0;
+  io_read_attribute(h_dataset, "min", FLOAT, &log_mass_min);
+
+  /* Read the step size (log step) */
+  float step_size = 0;
+  io_read_attribute(h_dataset, "step", FLOAT, &step_size);
+
+  /* Close the attribute */
+  H5Dclose(h_dataset);
+
+  /* Allocate the memory */
+  float *data = (float *)malloc(sizeof(float) * count);
+  if (data == NULL)
+    error("Failed to allocate the SNII yields for %s.", hdf5_dataset_name);
+
+  /* Read the dataset */
+  io_read_array_dataset(group_id, hdf5_dataset_name, FLOAT, data, count);
+
+  /* Initialize the interpolation */
+  interpolate_1d_init(interp, log10(snii->mass_min), log10(snii->mass_max),
+                      interpolation_size, log_mass_min, step_size, count, data,
+                      boundary_condition_zero);
+
+  /* Integrate the yields */
+  initial_mass_function_integrate(&sm->imf, interp);
+
+  /* Cleanup the memory */
+  free(data);
+}
+
+/**
+ * @brief Read the SNII yields from the table.
+ *
+ * The tables are in internal units at the end of this function.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param params The simulation parameters.
+ * @param sm The #stellar_model.
+ */
+void supernovae_ii_read_yields(struct supernovae_ii *snii,
+                               struct swift_params *params,
+                               const struct phys_const *phys_const,
+                               const struct stellar_model *sm) {
+
+  hid_t file_id, group_id;
+
+  hsize_t previous_count = 0;
+
+  const int interpolation_size = parser_get_opt_param_int(
+      params, "GEARSupernovaeII:interpolation_size", 200);
+
+  /* Open IMF group */
+  h5_open_group(params, "Data/SNII", &file_id, &group_id);
+
+  /* Do all the elements */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+
+    /* Get the element name */
+    const char *name = stellar_evolution_get_element_name(sm, i);
+
+    /* Read the array */
+    supernovae_ii_read_yields_array(snii, &snii->integrated_yields[i],
+                                    phys_const, sm, group_id, name,
+                                    &previous_count, interpolation_size);
+  }
+
+  /* Read the mass ejected */
+  supernovae_ii_read_yields_array(
+      snii, &snii->integrated_ejected_mass_processed, phys_const, sm, group_id,
+      "Ej", &previous_count, interpolation_size);
+
+  /* Read the mass ejected of non processed gas */
+  supernovae_ii_read_yields_array(snii, &snii->integrated_ejected_mass,
+                                  phys_const, sm, group_id, "Ejnp",
+                                  &previous_count, interpolation_size);
+
+  /* Cleanup everything */
+  h5_close_group(file_id, group_id);
+};
+
+/**
+ * @brief Reads the supernovae II parameters from parameters file.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param params The simulation parameters.
+ */
+void supernovae_ii_read_from_params(struct supernovae_ii *snii,
+                                    struct swift_params *params) {
+
+  /* Read the minimal mass of a supernovae */
+  snii->mass_min = parser_get_opt_param_float(
+      params, "GEARSupernovaeII:min_mass", snii->mass_min);
+
+  /* Read the maximal mass of a supernovae */
+  snii->mass_max = parser_get_opt_param_float(
+      params, "GEARSupernovaeII:max_mass", snii->mass_max);
+}
+
+/**
+ * @brief Reads the supernovae II parameters from tables.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param params The simulation parameters.
+ */
+void supernovae_ii_read_from_tables(struct supernovae_ii *snii,
+                                    struct swift_params *params) {
+
+  hid_t file_id, group_id;
+
+  /* Open IMF group */
+  h5_open_group(params, "Data/SNII", &file_id, &group_id);
+
+  /* Read the minimal mass of a supernovae */
+  io_read_attribute(group_id, "Mmin", FLOAT, &snii->mass_min);
+
+  /* Read the maximal mass of a supernovae */
+  io_read_attribute(group_id, "Mmax", FLOAT, &snii->mass_max);
+
+  /* Cleanup everything */
+  h5_close_group(file_id, group_id);
+}
+
+/**
+ * @brief Initialize the #supernovae_ii structure.
+ *
+ * @param snii The #supernovae_ii model.
+ * @param phys_const The #phys_const.
+ * @param us The #unit_system.
+ * @param params The simulation parameters.
+ * @param sm The #stellar_model.
+ */
+void supernovae_ii_init(struct supernovae_ii *snii,
+                        const struct phys_const *phys_const,
+                        const struct unit_system *us,
+                        struct swift_params *params,
+                        const struct stellar_model *sm) {
+
+  /* Read the parameters from the tables */
+  supernovae_ii_read_from_tables(snii, params);
+
+  /* Read the parameters from the params file */
+  supernovae_ii_read_from_tables(snii, params);
+
+  /* Read the supernovae yields (and apply the units) */
+  supernovae_ii_read_yields(snii, params, phys_const, sm);
+
+  /* Get the IMF parameters */
+  snii->exponent = initial_mass_function_get_exponent(&sm->imf, snii->mass_min,
+                                                      snii->mass_max);
+  snii->coef_exp = initial_mass_function_get_coefficient(
+      &sm->imf, snii->mass_min, snii->mass_max);
+  snii->coef_exp /= snii->exponent;
+}
+
+/**
+ * @brief Write a supernovae_ii struct to the given FILE as a stream of bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param snii the struct
+ * @param stream the file stream
+ */
+void supernovae_ii_dump(const struct supernovae_ii *snii, FILE *stream,
+                        const struct stellar_model *sm) {
+
+  /* Dump the yields. */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    if (snii->integrated_yields[i].data == NULL) {
+      continue;
+    }
+
+    const char *name = stellar_evolution_get_element_name(sm, i);
+    restart_write_blocks((void *)snii->integrated_yields[i].data, sizeof(float),
+                         snii->integrated_yields[i].N, stream, name, name);
+  }
+
+  /*! Dump the processed mass. */
+  if (snii->integrated_ejected_mass_processed.data != NULL) {
+    restart_write_blocks((void *)snii->integrated_ejected_mass_processed.data,
+                         sizeof(float),
+                         snii->integrated_ejected_mass_processed.N, stream,
+                         "processed_mass", "processed_mass");
+  }
+
+  /*! Dump the non processed mass. */
+  if (snii->integrated_ejected_mass.data != NULL) {
+    restart_write_blocks((void *)snii->integrated_ejected_mass.data,
+                         sizeof(float), snii->integrated_ejected_mass.N, stream,
+                         "non_processed_mass", "non_processed_mass");
+  }
+}
+
+/**
+ * @brief Restore a supernovae_ii struct from the given FILE as a stream of
+ * bytes.
+ *
+ * Here we are only writing the arrays, everything else has been copied in the
+ * feedback.
+ *
+ * @param snii the struct
+ * @param stream the file stream
+ */
+void supernovae_ii_restore(struct supernovae_ii *snii, FILE *stream,
+                           const struct stellar_model *sm) {
+
+  /* Restore the yields */
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    if (snii->integrated_yields[i].data == NULL) {
+      continue;
+    }
+
+    const char *name = stellar_evolution_get_element_name(sm, i);
+    snii->integrated_yields[i].data =
+        (float *)malloc(sizeof(float) * snii->integrated_yields[i].N);
+
+    restart_read_blocks((void *)snii->integrated_yields[i].data, sizeof(float),
+                        snii->integrated_yields[i].N, stream, NULL, name);
+  }
+
+  /* Restore the processed mass */
+
+  if (snii->integrated_ejected_mass_processed.data != NULL) {
+    snii->integrated_ejected_mass_processed.data = (float *)malloc(
+        sizeof(float) * snii->integrated_ejected_mass_processed.N);
+
+    restart_read_blocks((void *)snii->integrated_ejected_mass_processed.data,
+                        sizeof(float),
+                        snii->integrated_ejected_mass_processed.N, stream, NULL,
+                        "processed_mass");
+  }
+
+  /* Restore the non processed mass */
+  if (snii->integrated_ejected_mass.data != NULL) {
+    snii->integrated_ejected_mass.data =
+        (float *)malloc(sizeof(float) * snii->integrated_ejected_mass.N);
+
+    restart_read_blocks((void *)snii->integrated_ejected_mass.data,
+                        sizeof(float), snii->integrated_ejected_mass.N, stream,
+                        NULL, "non_processed_mass");
+  }
+}
+
+/**
+ * @brief Clean the allocated memory.
+ *
+ * @param snii the #supernovae_ii.
+ */
+void supernovae_ii_clean(struct supernovae_ii *snii) {
+
+  for (int i = 0; i < GEAR_CHEMISTRY_ELEMENT_COUNT; i++) {
+    interpolate_1d_free(&snii->integrated_yields[i]);
+  }
+
+  interpolate_1d_free(&snii->integrated_ejected_mass_processed);
+  interpolate_1d_free(&snii->integrated_ejected_mass);
+}
diff --git a/src/feedback/GEAR/supernovae_ii.h b/src/feedback/GEAR/supernovae_ii.h
new file mode 100644
index 0000000000..515e320b58
--- /dev/null
+++ b/src/feedback/GEAR/supernovae_ii.h
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2019 Loic Hausammann (loic.hausammann@epfl.ch)
+ *
+ * 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_SUPERNOVAE_II_GEAR_H
+#define SWIFT_SUPERNOVAE_II_GEAR_H
+
+#include "hdf5_functions.h"
+#include "interpolation.h"
+#include "stellar_evolution_struct.h"
+
+/**
+ * @brief Print the supernovae II model.
+ *
+ * @param snii The #supernovae_ii.
+ */
+void supernovae_ii_print(const struct supernovae_ii *snii);
+int supernovae_ii_can_explode(const struct supernovae_ii *snii, float m_low,
+                              float m_high);
+float supernovae_ii_get_number(const struct supernovae_ii *snii, float m1,
+                               float m2);
+void supernovae_ii_get_yields(const struct supernovae_ii *snii, float log_m1,
+                              float log_m2, float *yields);
+float supernovae_ii_get_ejected_mass_fraction(const struct supernovae_ii *snii,
+                                              float log_m1, float log_m2);
+
+float supernovae_ii_get_ejected_mass_fraction_processed(
+    const struct supernovae_ii *snii, float log_m1, float log_m2);
+void supernovae_ii_read_yields_array(
+    struct supernovae_ii *snii, struct interpolation_1d *interp,
+    const struct phys_const *phys_const, const struct stellar_model *sm,
+    hid_t group_id, const char *hdf5_dataset_name, hsize_t *previous_count,
+    int interpolation_size);
+void supernovae_ii_read_yields(struct supernovae_ii *snii,
+                               struct swift_params *params,
+                               const struct phys_const *phys_const,
+                               const struct stellar_model *sm);
+void supernovae_ii_read_from_params(struct supernovae_ii *snii,
+                                    struct swift_params *params);
+
+void supernovae_ii_read_from_tables(struct supernovae_ii *snii,
+                                    struct swift_params *params);
+void supernovae_ii_init(struct supernovae_ii *snii,
+                        const struct phys_const *phys_const,
+                        const struct unit_system *us,
+                        struct swift_params *params,
+                        const struct stellar_model *sm);
+void supernovae_ii_dump(const struct supernovae_ii *snii, FILE *stream,
+                        const struct stellar_model *sm);
+void supernovae_ii_restore(struct supernovae_ii *snii, FILE *stream,
+                           const struct stellar_model *sm);
+void supernovae_ii_clean(struct supernovae_ii *snii);
+#endif  // SWIFT_SUPERNOVAE_II_GEAR_H
diff --git a/src/feedback/none/feedback.h b/src/feedback/none/feedback.h
index 85a8cf9566..7c83e9ed5d 100644
--- a/src/feedback/none/feedback.h
+++ b/src/feedback/none/feedback.h
@@ -26,6 +26,20 @@
 #include "part.h"
 #include "units.h"
 
+/**
+ * @brief Update the properties of a particle fue to feedback effects after
+ * the cooling was applied.
+ *
+ * Nothing to do here.
+ *
+ * @param p The #part to consider.
+ * @param xp The #xpart to consider.
+ * @param cosmo The #cosmology.
+ */
+__attribute__((always_inline)) INLINE static void feedback_update_part(
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct engine* restrict e) {}
+
 /**
  * @brief Prepares a s-particle for its feedback interactions
  *
@@ -128,16 +142,19 @@ __attribute__((always_inline)) INLINE static void feedback_prepare_spart(
  * @param feedback_props The #feedback_props structure.
  * @param cosmo The current cosmological model.
  * @param us The unit system.
+ * @param phys_const The #phys_const.
  * @param star_age_beg_step The age of the star at the star of the time-step in
  * internal units.
  * @param dt The time-step size of this star in internal units.
  * @param time The physical time in internal units.
+ * @param ti_begin The integer time at the beginning of the step.
  * @param with_cosmology Are we running with cosmology on?
  */
 __attribute__((always_inline)) INLINE static void feedback_evolve_spart(
     struct spart* restrict sp, const struct feedback_props* feedback_props,
     const struct cosmology* cosmo, const struct unit_system* us,
-    const double star_age_beg_step, const double dt, const double time,
+    const struct phys_const* phys_const, const double star_age_beg_step,
+    const double dt, const double time, const integertime_t ti_begin,
     const int with_cosmology) {}
 
 /**
@@ -185,4 +202,16 @@ static INLINE void feedback_struct_dump(const struct feedback_props* feedback,
 static INLINE void feedback_struct_restore(struct feedback_props* feedback,
                                            FILE* stream) {}
 
+#ifdef HAVE_HDF5
+/**
+ * @brief Writes the current model of feedback to the file
+ * @param h_grpsph The HDF5 group in which to write
+ */
+INLINE static void feedback_write_flavour(struct feedback_props* feedback,
+                                          hid_t h_grp) {
+
+  io_write_attribute_s(h_grp, "Feedback Model", "None");
+};
+#endif
+
 #endif /* SWIFT_FEEDBACK_NONE_H */
diff --git a/src/feedback/none/feedback_struct.h b/src/feedback/none/feedback_struct.h
index 0dde77bbd7..23506c34ac 100644
--- a/src/feedback/none/feedback_struct.h
+++ b/src/feedback/none/feedback_struct.h
@@ -21,6 +21,11 @@
 
 #include "chemistry_struct.h"
 
+/**
+ * @brief Feedback fields carried by each hydro particles
+ */
+struct feedback_part_data {};
+
 /**
  * @brief Feedback fields carried by each star particles
  *
diff --git a/src/feedback_properties.h b/src/feedback_properties.h
index 83afbb1eea..32729203a4 100644
--- a/src/feedback_properties.h
+++ b/src/feedback_properties.h
@@ -27,6 +27,8 @@
 #include "./feedback/none/feedback_properties.h"
 #elif defined(FEEDBACK_EAGLE)
 #include "./feedback/EAGLE/feedback_properties.h"
+#elif defined(FEEDBACK_GEAR)
+#include "./feedback/GEAR/feedback_properties.h"
 #else
 #error "Invalid choice of feedback model"
 #endif
diff --git a/src/feedback_struct.h b/src/feedback_struct.h
index be6d480e7a..09009b2ba6 100644
--- a/src/feedback_struct.h
+++ b/src/feedback_struct.h
@@ -32,6 +32,8 @@
 #include "./feedback/none/feedback_struct.h"
 #elif defined(FEEDBACK_EAGLE)
 #include "./feedback/EAGLE/feedback_struct.h"
+#elif defined(FEEDBACK_GEAR)
+#include "./feedback/GEAR/feedback_struct.h"
 #else
 #error "Invalid choice of feedback function."
 #endif
diff --git a/src/hydro/AnarchyPU/hydro_part.h b/src/hydro/AnarchyPU/hydro_part.h
index 87113504c9..c0b300e3f5 100644
--- a/src/hydro/AnarchyPU/hydro_part.h
+++ b/src/hydro/AnarchyPU/hydro_part.h
@@ -32,6 +32,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
@@ -69,6 +70,9 @@ struct xpart {
   /* Additional data used by the tracers */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /**
diff --git a/src/hydro/Default/hydro_part.h b/src/hydro/Default/hydro_part.h
index a236b14534..6437e5c5c4 100644
--- a/src/hydro/Default/hydro_part.h
+++ b/src/hydro/Default/hydro_part.h
@@ -30,6 +30,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
@@ -67,6 +68,9 @@ struct xpart {
   /* Additional data used by the tracers */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /**
diff --git a/src/hydro/Gadget2/hydro_part.h b/src/hydro/Gadget2/hydro_part.h
index d34d51a603..1916410885 100644
--- a/src/hydro/Gadget2/hydro_part.h
+++ b/src/hydro/Gadget2/hydro_part.h
@@ -34,6 +34,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "logger.h"
 #include "pressure_floor_struct.h"
 #include "star_formation_struct.h"
@@ -67,6 +68,9 @@ struct xpart {
   /* Additional data used by the star formation */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 #ifdef WITH_LOGGER
   /* Additional data for the particle logger */
   struct logger_part_data logger_data;
diff --git a/src/hydro/Gizmo/hydro_part.h b/src/hydro/Gizmo/hydro_part.h
index 7003396da3..c1228f576e 100644
--- a/src/hydro/Gizmo/hydro_part.h
+++ b/src/hydro/Gizmo/hydro_part.h
@@ -22,6 +22,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
@@ -50,6 +51,9 @@ struct xpart {
   /* Additional data used by the star formation */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /* Import the right hydro particle definition */
diff --git a/src/hydro/Minimal/hydro_part.h b/src/hydro/Minimal/hydro_part.h
index 4d4b1a09f4..9b31c4976d 100644
--- a/src/hydro/Minimal/hydro_part.h
+++ b/src/hydro/Minimal/hydro_part.h
@@ -35,6 +35,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
@@ -72,6 +73,9 @@ struct xpart {
   /* Additional data used by the tracers */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /**
diff --git a/src/hydro/Planetary/hydro_part.h b/src/hydro/Planetary/hydro_part.h
index c3cdf3879c..4c0c4b786c 100644
--- a/src/hydro/Planetary/hydro_part.h
+++ b/src/hydro/Planetary/hydro_part.h
@@ -37,6 +37,7 @@
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
 #include "equation_of_state.h"  // For enum material_id
+#include "feedback_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
@@ -74,6 +75,9 @@ struct xpart {
   /* Additional data used by the star formation */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /**
diff --git a/src/hydro/PressureEnergy/hydro.h b/src/hydro/PressureEnergy/hydro.h
index 2ed22ac672..fddafa52a9 100644
--- a/src/hydro/PressureEnergy/hydro.h
+++ b/src/hydro/PressureEnergy/hydro.h
@@ -442,6 +442,10 @@ hydro_set_drifted_physical_internal_energy(struct part *p,
 
   /* Update variables. */
   hydro_update_soundspeed(p, cosmo);
+
+  const float comoving_pressure_with_floor =
+      pressure_floor_get_comoving_pressure(p, p->pressure_bar, cosmo);
+  p->force.pressure_bar_with_floor = comoving_pressure_with_floor;
 }
 
 /**
diff --git a/src/hydro/PressureEnergy/hydro_part.h b/src/hydro/PressureEnergy/hydro_part.h
index 0f19f968b8..82a6275f9c 100644
--- a/src/hydro/PressureEnergy/hydro_part.h
+++ b/src/hydro/PressureEnergy/hydro_part.h
@@ -34,6 +34,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "pressure_floor_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
@@ -72,6 +73,9 @@ struct xpart {
   /*! Additional data used by the star formation */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /**
diff --git a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h
index 09b838b65e..b21dbc6e81 100644
--- a/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h
+++ b/src/hydro/PressureEnergyMorrisMonaghanAV/hydro_part.h
@@ -35,6 +35,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
@@ -72,6 +73,9 @@ struct xpart {
   /*! Additional data used by the star formation */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /**
diff --git a/src/hydro/PressureEntropy/hydro_part.h b/src/hydro/PressureEntropy/hydro_part.h
index 586976e4dc..7c77bf88a7 100644
--- a/src/hydro/PressureEntropy/hydro_part.h
+++ b/src/hydro/PressureEntropy/hydro_part.h
@@ -33,6 +33,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
@@ -64,6 +65,9 @@ struct xpart {
   /*! Additional data used by the star formation */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /* Data of a single particle. */
diff --git a/src/hydro/SPHENIX/hydro_part.h b/src/hydro/SPHENIX/hydro_part.h
index 3865ad539f..e7f9a60545 100644
--- a/src/hydro/SPHENIX/hydro_part.h
+++ b/src/hydro/SPHENIX/hydro_part.h
@@ -29,6 +29,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "pressure_floor_struct.h"
 #include "star_formation_struct.h"
 #include "timestep_limiter_struct.h"
@@ -67,6 +68,9 @@ struct xpart {
   /* Additional data used by the tracers */
   struct star_formation_xpart_data sf_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /**
diff --git a/src/hydro/Shadowswift/hydro_part.h b/src/hydro/Shadowswift/hydro_part.h
index 3bd1a6af2e..bd43ab43db 100644
--- a/src/hydro/Shadowswift/hydro_part.h
+++ b/src/hydro/Shadowswift/hydro_part.h
@@ -24,6 +24,7 @@
 #include "black_holes_struct.h"
 #include "chemistry_struct.h"
 #include "cooling_struct.h"
+#include "feedback_struct.h"
 #include "timestep_limiter_struct.h"
 #include "tracers_struct.h"
 #include "voronoi_cell.h"
@@ -49,6 +50,9 @@ struct xpart {
   /* Additional data used by the tracers */
   struct tracers_xpart_data tracers_data;
 
+  /* Additional data used by the feedback */
+  struct feedback_part_data feedback_data;
+
 } SWIFT_STRUCT_ALIGN;
 
 /* Data of a single particle. */
diff --git a/src/parallel_io.c b/src/parallel_io.c
index f073104e89..5a8605dbcd 100644
--- a/src/parallel_io.c
+++ b/src/parallel_io.c
@@ -45,6 +45,7 @@
 #include "engine.h"
 #include "entropy_floor.h"
 #include "error.h"
+#include "feedback.h"
 #include "fof_io.h"
 #include "gravity_io.h"
 #include "gravity_properties.h"
@@ -1170,6 +1171,7 @@ void prepare_file(struct engine* e, const char* baseName, long long N_total[6],
   cooling_write_flavour(h_grp, e->cooling_func);
   chemistry_write_flavour(h_grp);
   tracers_write_flavour(h_grp);
+  feedback_write_flavour(e->feedback_props, h_grp);
   H5Gclose(h_grp);
 
   /* Print the gravity parameters */
diff --git a/src/pressure_floor/GEAR/pressure_floor.h b/src/pressure_floor/GEAR/pressure_floor.h
index cb88659cb6..2d00b9b6c2 100644
--- a/src/pressure_floor/GEAR/pressure_floor.h
+++ b/src/pressure_floor/GEAR/pressure_floor.h
@@ -130,7 +130,7 @@ __attribute__((always_inline)) static INLINE void pressure_floor_init(
 
   /* Read the Jeans factor */
   props->n_jeans =
-      parser_get_param_float(params, "GEARPressureFloor:Jeans_factor");
+      parser_get_param_float(params, "GEARPressureFloor:jeans_factor");
 
   /* Compute the constants */
   props->constants =
diff --git a/src/random.h b/src/random.h
index 8618f77ff7..afe0b99b2c 100644
--- a/src/random.h
+++ b/src/random.h
@@ -42,11 +42,12 @@
  * generator.
  * In case new numbers need to be added other possible
  * numbers could be:
- * 5947309451, 6977309513
+ * 5947309451
  */
 enum random_number_type {
   random_number_star_formation = 0LL,
-  random_number_stellar_feedback = 3947008991LL,
+  random_number_stellar_feedback_1 = 3947008991LL,
+  random_number_stellar_feedback_2 = 6977309513LL,
   random_number_stellar_enrichment = 2936881973LL,
   random_number_BH_feedback = 1640531371LL,
   random_number_BH_swallow = 4947009007LL
diff --git a/src/runner_ghost.c b/src/runner_ghost.c
index a4113b5b89..2aaf172ea4 100644
--- a/src/runner_ghost.c
+++ b/src/runner_ghost.c
@@ -74,6 +74,7 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
   struct spart *restrict sparts = c->stars.parts;
   const struct engine *e = r->e;
   const struct unit_system *us = e->internal_units;
+  const struct phys_const *phys_const = e->physical_constants;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
   const struct cosmology *cosmo = e->cosmology;
   const struct feedback_props *feedback_props = e->feedback_props;
@@ -239,9 +240,9 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
                     star_age_end_of_step - dt_enrichment;
 
                 /* Compute the stellar evolution  */
-                feedback_evolve_spart(sp, feedback_props, cosmo, us,
+                feedback_evolve_spart(sp, feedback_props, cosmo, us, phys_const,
                                       star_age_beg_of_step, dt_enrichment,
-                                      e->time, with_cosmology);
+                                      e->time, ti_begin, with_cosmology);
               } else {
 
                 /* Reset the feedback fields of the star particle */
@@ -382,9 +383,9 @@ void runner_do_stars_ghost(struct runner *r, struct cell *c, int timer) {
                 star_age_end_of_step - dt_enrichment;
 
             /* Compute the stellar evolution  */
-            feedback_evolve_spart(sp, feedback_props, cosmo, us,
+            feedback_evolve_spart(sp, feedback_props, cosmo, us, phys_const,
                                   star_age_beg_of_step, dt_enrichment, e->time,
-                                  with_cosmology);
+                                  ti_begin, with_cosmology);
           } else {
 
             /* Reset the feedback fields of the star particle */
@@ -1054,7 +1055,7 @@ void runner_do_ghost(struct runner *r, struct cell *c, int timer) {
           hydro_end_density(p, cosmo);
           chemistry_end_density(p, chemistry, cosmo);
           pressure_floor_end_density(p, cosmo);
-          star_formation_end_density(p, star_formation, cosmo);
+          star_formation_end_density(p, xp, star_formation, cosmo);
 
           /* Are we using the alternative definition of the
              number of neighbours? */
diff --git a/src/runner_others.c b/src/runner_others.c
index 60e86bc039..2f7a82e2ff 100644
--- a/src/runner_others.c
+++ b/src/runner_others.c
@@ -44,6 +44,7 @@
 #include "cooling.h"
 #include "engine.h"
 #include "error.h"
+#include "feedback.h"
 #include "gravity.h"
 #include "hydro.h"
 #include "logger.h"
@@ -52,6 +53,7 @@
 #include "star_formation.h"
 #include "star_formation_logger.h"
 #include "stars.h"
+#include "task_order.h"
 #include "timers.h"
 #include "timestep_limiter.h"
 #include "tracers.h"
@@ -143,7 +145,6 @@ void runner_do_grav_mesh(struct runner *r, struct cell *c, int timer) {
  * @param timer 1 if the time is to be recorded.
  */
 void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
-
   const struct engine *e = r->e;
   const struct cosmology *cosmo = e->cosmology;
   const int with_cosmology = (e->policy & engine_policy_cosmology);
@@ -157,11 +158,14 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
   struct part *restrict parts = c->hydro.parts;
   struct xpart *restrict xparts = c->hydro.xparts;
   const int count = c->hydro.count;
+  const double time = e->time;
 
   TIMER_TIC;
 
-  /* Anything to do here? */
-  if (!cell_is_active_hydro(c, e)) return;
+  /* Anything to do here? (i.e. does this cell need updating?) */
+  if (!((task_order_cooling_after_timestep && cell_is_starting_hydro(c, e)) ||
+        (!task_order_cooling_after_timestep && cell_is_active_hydro(c, e))))
+    return;
 
   /* Recurse? */
   if (c->split) {
@@ -176,7 +180,9 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
       struct part *restrict p = &parts[i];
       struct xpart *restrict xp = &xparts[i];
 
-      if (part_is_active(p, e)) {
+      /* Anything to do here? (i.e. does this particle need updating?) */
+      if ((task_order_cooling_after_timestep && part_is_starting(p, e)) ||
+          (!task_order_cooling_after_timestep && part_is_active(p, e))) {
 
         double dt_cool, dt_therm;
         if (with_cosmology) {
@@ -196,8 +202,13 @@ void runner_do_cooling(struct runner *r, struct cell *c, int timer) {
 
         /* Let's cool ! */
         cooling_cool_part(constants, us, cosmo, hydro_props,
-                          entropy_floor_props, cooling_func, p, xp, dt_cool,
-                          dt_therm);
+                          entropy_floor_props, cooling_func, p, xp, time,
+                          dt_cool, dt_therm);
+
+        /* Apply the effects of feedback on this particle
+         * (Note: Only used in schemes that have a delayed feedback mechanism
+         * otherwise just an empty function) */
+        feedback_update_part(p, xp, e);
       }
     }
   }
diff --git a/src/runner_recv.c b/src/runner_recv.c
index 6486f7621c..a00e3d2dc5 100644
--- a/src/runner_recv.c
+++ b/src/runner_recv.c
@@ -232,7 +232,7 @@ void runner_do_recv_spart(struct runner *r, struct cell *c, int clear_sorts,
     /* Collect everything... */
     for (size_t k = 0; k < nr_sparts; k++) {
 #ifdef DEBUG_INTERACTIONS_STARS
-      sparts[k].num_ngb_force = 0;
+      sparts[k].num_ngb_feedback = 0;
 #endif
       if (sparts[k].time_bin == time_bin_inhibited) continue;
       time_bin_min = min(time_bin_min, sparts[k].time_bin);
diff --git a/src/serial_io.c b/src/serial_io.c
index dc033b95c4..d028d79dc2 100644
--- a/src/serial_io.c
+++ b/src/serial_io.c
@@ -45,6 +45,7 @@
 #include "engine.h"
 #include "entropy_floor.h"
 #include "error.h"
+#include "feedback.h"
 #include "fof_io.h"
 #include "gravity_io.h"
 #include "gravity_properties.h"
@@ -1035,6 +1036,7 @@ void write_output_serial(struct engine* e, const char* baseName,
     cooling_write_flavour(h_grp, e->cooling_func);
     chemistry_write_flavour(h_grp);
     tracers_write_flavour(h_grp);
+    feedback_write_flavour(e->feedback_props, h_grp);
     H5Gclose(h_grp);
 
     /* Print the gravity parameters */
diff --git a/src/single_io.c b/src/single_io.c
index 05ccfa8fde..2a8a589757 100644
--- a/src/single_io.c
+++ b/src/single_io.c
@@ -44,6 +44,7 @@
 #include "engine.h"
 #include "entropy_floor.h"
 #include "error.h"
+#include "feedback.h"
 #include "fof_io.h"
 #include "gravity_io.h"
 #include "gravity_properties.h"
@@ -880,6 +881,7 @@ void write_output_single(struct engine* e, const char* baseName,
   cooling_write_flavour(h_grp, e->cooling_func);
   chemistry_write_flavour(h_grp);
   tracers_write_flavour(h_grp);
+  feedback_write_flavour(e->feedback_props, h_grp);
   H5Gclose(h_grp);
 
   /* Print the gravity parameters */
diff --git a/src/space.c b/src/space.c
index 7596ed637d..3fffafc4b7 100644
--- a/src/space.c
+++ b/src/space.c
@@ -4286,7 +4286,8 @@ void space_first_init_parts_mapper(void *restrict map_data, int count,
                                    &xp[k]);
 
     /* And the cooling */
-    cooling_first_init_part(phys_const, us, cosmo, cool_func, &p[k], &xp[k]);
+    cooling_first_init_part(phys_const, us, hydro_props, cosmo, cool_func,
+                            &p[k], &xp[k]);
 
     /* And the tracers */
     tracers_first_init_xpart(&p[k], &xp[k], us, phys_const, cosmo, hydro_props,
diff --git a/src/star_formation/EAGLE/star_formation.h b/src/star_formation/EAGLE/star_formation.h
index 143984ebf8..ce680d4ec1 100644
--- a/src/star_formation/EAGLE/star_formation.h
+++ b/src/star_formation/EAGLE/star_formation.h
@@ -664,12 +664,13 @@ INLINE static void starformation_print_backend(
  * density loop for the EAGLE star formation model.
  *
  * @param p The particle to act upon
+ * @param xp The extra particle to act upon
  * @param cd The global star_formation information.
  * @param cosmo The current cosmological model.
  */
 __attribute__((always_inline)) INLINE static void star_formation_end_density(
-    struct part* restrict p, const struct star_formation* cd,
-    const struct cosmology* cosmo) {}
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct star_formation* cd, const struct cosmology* cosmo) {}
 
 /**
  * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
diff --git a/src/star_formation/GEAR/star_formation.h b/src/star_formation/GEAR/star_formation.h
index c2682f1643..8eb60bf38f 100644
--- a/src/star_formation/GEAR/star_formation.h
+++ b/src/star_formation/GEAR/star_formation.h
@@ -59,6 +59,11 @@ INLINE static int star_formation_is_star_forming(
     const struct cooling_function_data* restrict cooling,
     const struct entropy_floor_properties* restrict entropy_floor) {
 
+  /* Check if collapsing particles */
+  if (xp->sf_data.div_v > 0) {
+    return 0;
+  }
+
   const float temperature = cooling_get_temperature(phys_const, hydro_props, us,
                                                     cosmo, cooling, p, xp);
 
@@ -73,7 +78,7 @@ INLINE static int star_formation_is_star_forming(
   const float sigma2 = p->pressure_floor_data.sigma2 * cosmo->a * cosmo->a;
   const float n_jeans_2_3 = starform->n_jeans_2_3;
 
-  const float h = p->h;
+  const float h = p->h * kernel_gamma;
   const float density = hydro_get_physical_density(p, cosmo);
 
   // TODO use GRACKLE */
@@ -200,10 +205,6 @@ INLINE static void star_formation_copy_properties(
     sp->birth_time = e->time;
   }
 
-  // TODO copy only metals
-  /* Store the chemistry struct in the star particle */
-  sp->chemistry_data = p->chemistry_data;
-
   /* Store the tracers data */
   sp->tracers_data = xp->tracers_data;
 
@@ -213,6 +214,9 @@ INLINE static void star_formation_copy_properties(
   /* Store the birth temperature*/
   sp->birth.temperature = cooling_get_temperature(phys_const, hydro_props, us,
                                                   cosmo, cooling, p, xp);
+
+  /* Copy the chemistry properties */
+  chemistry_copy_star_formation_properties(p, xp, sp);
 }
 
 /**
@@ -231,12 +235,17 @@ INLINE static void starformation_print_backend(
  * Nothing to do here.
  *
  * @param p The particle to act upon
+ * @param xp The extra particle to act upon
  * @param cd The global star_formation information.
  * @param cosmo The current cosmological model.
  */
 __attribute__((always_inline)) INLINE static void star_formation_end_density(
-    struct part* restrict p, const struct star_formation* cd,
-    const struct cosmology* cosmo) {}
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct star_formation* cd, const struct cosmology* cosmo) {
+
+  /* Copy the velocity divergence */
+  xp->sf_data.div_v = p->density.div_v;
+}
 
 /**
  * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
diff --git a/src/star_formation/GEAR/star_formation_struct.h b/src/star_formation/GEAR/star_formation_struct.h
index 50a735ff45..9d6d78df5c 100644
--- a/src/star_formation/GEAR/star_formation_struct.h
+++ b/src/star_formation/GEAR/star_formation_struct.h
@@ -23,7 +23,10 @@
  * @brief Star-formation-related properties stored in the extended particle
  * data.
  */
-struct star_formation_xpart_data {};
+struct star_formation_xpart_data {
+  /*! Particle velocity divergence. */
+  float div_v;
+};
 
 /**
  * @brief Global star formation properties
diff --git a/src/star_formation/none/star_formation.h b/src/star_formation/none/star_formation.h
index a48155b05b..6155d41564 100644
--- a/src/star_formation/none/star_formation.h
+++ b/src/star_formation/none/star_formation.h
@@ -171,12 +171,13 @@ INLINE static void starformation_print_backend(
  * Nothing to do here.
  *
  * @param p The particle to act upon
+ * @param xp The extra particle to act upon
  * @param cd The global star_formation information.
  * @param cosmo The current cosmological model.
  */
 __attribute__((always_inline)) INLINE static void star_formation_end_density(
-    struct part* restrict p, const struct star_formation* cd,
-    const struct cosmology* cosmo) {}
+    struct part* restrict p, struct xpart* restrict xp,
+    const struct star_formation* cd, const struct cosmology* cosmo) {}
 
 /**
  * @brief Sets all particle fields to sensible values when the #part has 0 ngbs.
diff --git a/src/stars/EAGLE/stars.h b/src/stars/EAGLE/stars.h
index 644acfcbf7..9567af1bce 100644
--- a/src/stars/EAGLE/stars.h
+++ b/src/stars/EAGLE/stars.h
@@ -167,7 +167,7 @@ __attribute__((always_inline)) INLINE static void stars_reset_acceleration(
     struct spart* restrict p) {
 
 #ifdef DEBUG_INTERACTIONS_STARS
-  p->num_ngb_force = 0;
+  p->num_ngb_feedback = 0;
 #endif
 }
 
@@ -184,8 +184,8 @@ __attribute__((always_inline)) INLINE static void stars_reset_feedback(
 
 #ifdef DEBUG_INTERACTIONS_STARS
   for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
-    p->ids_ngbs_force[i] = -1;
-  p->num_ngb_force = 0;
+    p->ids_ngbs_feedback[i] = -1;
+  p->num_ngb_feedback = 0;
 #endif
 }
 
diff --git a/src/stars/EAGLE/stars_part.h b/src/stars/EAGLE/stars_part.h
index 13f0783674..15613d077a 100644
--- a/src/stars/EAGLE/stars_part.h
+++ b/src/stars/EAGLE/stars_part.h
@@ -132,11 +132,11 @@ struct spart {
   /*! List of interacting particles in the density SELF and PAIR */
   long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS];
 
-  /*! Number of interactions in the force SELF and PAIR */
-  int num_ngb_force;
+  /*! Number of interactions in the feedback SELF and PAIR */
+  int num_ngb_feedback;
 
-  /*! List of interacting particles in the force SELF and PAIR */
-  long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_STARS];
+  /*! List of interacting particles in the feedback SELF and PAIR */
+  long long ids_ngbs_feedback[MAX_NUM_OF_NEIGHBOURS_STARS];
 #endif
 
 } SWIFT_STRUCT_ALIGN;
diff --git a/src/stars/GEAR/stars.h b/src/stars/GEAR/stars.h
index 2fe4428b81..1c5bdf5916 100644
--- a/src/stars/GEAR/stars.h
+++ b/src/stars/GEAR/stars.h
@@ -33,26 +33,6 @@ __attribute__((always_inline)) INLINE static float stars_compute_timestep(
   return FLT_MAX;
 }
 
-/**
- * @brief Initialises the s-particles for the first time
- *
- * This function is called only once just after the ICs have been
- * read in to do some conversions.
- *
- * @param sp The particle to act upon
- * @param stars_properties The properties of the stellar model.
- * @param with_cosmology Are we running with cosmological time integration.
- * @param scale_factor The current scale-factor (used if running with
- * cosmology).
- * @param time The current time (used if running without cosmology).
- */
-__attribute__((always_inline)) INLINE static void stars_first_init_spart(
-    struct spart* sp, const struct stars_props* stars_properties,
-    const int with_cosmology, const double scale_factor, const double time) {
-
-  sp->time_bin = 0;
-}
-
 /**
  * @brief Prepares a s-particle for its interactions
  *
@@ -71,6 +51,26 @@ __attribute__((always_inline)) INLINE static void stars_init_spart(
   sp->density.wcount_dh = 0.f;
 }
 
+/**
+ * @brief Initialises the s-particles for the first time
+ *
+ * This function is called only once just after the ICs have been
+ * read in to do some conversions.
+ *
+ * @param sp The particle to act upon.
+ * @param stars_properties Properties of the stars model.
+ */
+__attribute__((always_inline)) INLINE static void stars_first_init_spart(
+    struct spart* sp, const struct stars_props* stars_properties,
+    const int with_cosmology, const double scale_factor, const double time) {
+
+  sp->time_bin = 0;
+  sp->birth.density = 0.f;
+  // sp->birth_time = 0.;
+
+  stars_init_spart(sp);
+}
+
 /**
  * @brief Predict additional particle fields forward in time when drifting
  *
@@ -92,6 +92,8 @@ __attribute__((always_inline)) INLINE static void stars_reset_predicted_values(
 /**
  * @brief Finishes the calculation of (non-gravity) forces acting on stars
  *
+ * Multiplies the forces and accelerations by the appropiate constants
+ *
  * @param sp The particle to act upon
  */
 __attribute__((always_inline)) INLINE static void stars_end_feedback(
@@ -136,13 +138,8 @@ __attribute__((always_inline)) INLINE static void stars_end_density(
 __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours(
     struct spart* restrict sp, const struct cosmology* cosmo) {
 
-  /* Some smoothing length multiples. */
-  const float h = sp->h;
-  const float h_inv = 1.0f / h;                 /* 1/h */
-  const float h_inv_dim = pow_dimension(h_inv); /* 1/h^d */
-
   /* Re-set problematic values */
-  sp->density.wcount = kernel_root * h_inv_dim;
+  sp->density.wcount = 0.f;
   sp->density.wcount_dh = 0.f;
 }
 
@@ -154,24 +151,30 @@ __attribute__((always_inline)) INLINE static void stars_spart_has_no_neighbours(
  *
  * @param p The particle to act upon
  */
-__attribute__((always_inline)) INLINE static void stars_reset_feedback(
+__attribute__((always_inline)) INLINE static void stars_reset_acceleration(
     struct spart* restrict p) {
 
 #ifdef DEBUG_INTERACTIONS_STARS
-  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
-    p->ids_ngbs_force[i] = -1;
-  p->num_ngb_force = 0;
+  p->num_ngb_feedback = 0;
 #endif
 }
 
 /**
- * @brief Initializes constants related to stellar evolution, initializes imf,
- * reads and processes yield tables
+ * @brief Reset acceleration fields of a particle
+ *
+ * This is the equivalent of hydro_reset_acceleration.
+ * We do not compute the acceleration on star, therefore no need to use it.
  *
- * @param params swift_params parameters structure
- * @param stars stars_props data structure
+ * @param p The particle to act upon
  */
-inline static void stars_evolve_init(struct swift_params* params,
-                                     struct stars_props* restrict stars) {}
+__attribute__((always_inline)) INLINE static void stars_reset_feedback(
+    struct spart* restrict p) {
+
+#ifdef DEBUG_INTERACTIONS_STARS
+  for (int i = 0; i < MAX_NUM_OF_NEIGHBOURS_STARS; ++i)
+    p->ids_ngbs_feedback[i] = -1;
+  p->num_ngb_feedback = 0;
+#endif
+}
 
 #endif /* SWIFT_GEAR_STARS_H */
diff --git a/src/stars/GEAR/stars_iact.h b/src/stars/GEAR/stars_iact.h
index c7bda43fc0..024ac7a9b9 100644
--- a/src/stars/GEAR/stars_iact.h
+++ b/src/stars/GEAR/stars_iact.h
@@ -65,13 +65,14 @@ runner_iact_nonsym_stars_density(const float r2, const float *dx,
 
 /**
  * @brief Feedback interaction between two particles (non-symmetric).
+ * Used for updating properties of gas particles neighbouring a star particle
  *
  * @param r2 Comoving square distance between the two particles.
- * @param dx Comoving vector separating both particles (pi - pj).
+ * @param dx Comoving vector separating both particles (si - pj).
  * @param hi Comoving smoothing-length of particle i.
  * @param hj Comoving smoothing-length of particle j.
- * @param si First sparticle.
- * @param pj Second particle (not updated).
+ * @param si First (star) particle (not updated).
+ * @param pj Second (gas) particle.
  * @param a Current scale factor.
  * @param H Current Hubble parameter.
  */
@@ -91,4 +92,4 @@ runner_iact_nonsym_stars_feedback(const float r2, const float *dx,
 #endif
 }
 
-#endif
+#endif /* SWIFT_GEAR_STARS_IACT_H */
diff --git a/src/stars/GEAR/stars_io.h b/src/stars/GEAR/stars_io.h
index addde2424b..a5a6e7368b 100644
--- a/src/stars/GEAR/stars_io.h
+++ b/src/stars/GEAR/stars_io.h
@@ -34,7 +34,7 @@ INLINE static void stars_read_particles(struct spart *sparts,
                                         int *num_fields) {
 
   /* Say how much we want to read */
-  *num_fields = 5;
+  *num_fields = 7;
 
   /* List what we want to read */
   list[0] = io_make_input_field("Coordinates", DOUBLE, 3, COMPULSORY,
@@ -47,6 +47,15 @@ INLINE static void stars_read_particles(struct spart *sparts,
                                 UNIT_CONV_NO_UNITS, sparts, id);
   list[4] = io_make_input_field("SmoothingLength", FLOAT, 1, OPTIONAL,
                                 UNIT_CONV_LENGTH, sparts, h);
+  // TODO take it from initial mass
+  list[5] = io_make_input_field("BirthMass", FLOAT, 1, COMPULSORY,
+                                UNIT_CONV_MASS, sparts, birth.mass);
+
+  // TODO make it optional
+  list[6] = io_make_input_field("BirthTime", FLOAT, 1, OPTIONAL, UNIT_CONV_MASS,
+                                sparts, birth_time);
+
+  // TODO read birth density
 }
 
 INLINE static void convert_spart_pos(const struct engine *e,
diff --git a/src/stars/GEAR/stars_part.h b/src/stars/GEAR/stars_part.h
index bf68a580ef..bee9b5c6a3 100644
--- a/src/stars/GEAR/stars_part.h
+++ b/src/stars/GEAR/stars_part.h
@@ -55,22 +55,9 @@ struct spart {
   /*! Star mass */
   float mass;
 
-  /* Particle cutoff radius. */
+  /*! Particle smoothing length. */
   float h;
 
-  /*! Union for the birth time and birth scale factor */
-  union {
-
-    /*! Birth time */
-    float birth_time;
-
-    /*! Birth scale factor */
-    float birth_scale_factor;
-  };
-
-  /*! Particle time bin */
-  timebin_t time_bin;
-
   struct {
 
     /* Number of neighbours. */
@@ -92,6 +79,16 @@ struct spart {
     float mass;
   } birth;
 
+  /*! Union for the birth time and birth scale factor */
+  union {
+
+    /*! Birth time */
+    float birth_time;
+
+    /*! Birth scale factor */
+    float birth_scale_factor;
+  };
+
   /*! Feedback structure */
   struct feedback_spart_data feedback_data;
 
@@ -99,7 +96,10 @@ struct spart {
   struct tracers_xpart_data tracers_data;
 
   /*! Chemistry structure */
-  struct chemistry_part_data chemistry_data;
+  struct chemistry_spart_data chemistry_data;
+
+  /*! Particle time bin */
+  timebin_t time_bin;
 
 #ifdef SWIFT_DEBUG_CHECKS
 
@@ -118,11 +118,11 @@ struct spart {
   /*! List of interacting particles in the density SELF and PAIR */
   long long ids_ngbs_density[MAX_NUM_OF_NEIGHBOURS_STARS];
 
-  /*! Number of interactions in the force SELF and PAIR */
-  int num_ngb_force;
+  /*! Number of interactions in the feedback SELF and PAIR */
+  int num_ngb_feedback;
 
   /*! List of interacting particles in the force SELF and PAIR */
-  long long ids_ngbs_force[MAX_NUM_OF_NEIGHBOURS_STARS];
+  long long ids_ngbs_feedback[MAX_NUM_OF_NEIGHBOURS_STARS];
 #endif
 
 } SWIFT_STRUCT_ALIGN;
@@ -135,7 +135,7 @@ struct stars_props {
   /*! Resolution parameter */
   float eta_neighbours;
 
-  /*! Target weightd number of neighbours (for info only)*/
+  /*! Target weighted number of neighbours (for info only)*/
   float target_neighbours;
 
   /*! Smoothing length tolerance */
diff --git a/src/task_order.h b/src/task_order.h
index da1f8032a3..7924bc2ee3 100644
--- a/src/task_order.h
+++ b/src/task_order.h
@@ -21,6 +21,9 @@
 
 #include "../config.h"
 
+/* Local includes */
+#include "scheduler.h"
+
 #ifdef TASK_ORDER_NONE
 #include "task_order/none/task_order.h"
 #elif TASK_ORDER_GEAR
diff --git a/src/task_order/EAGLE/task_order.h b/src/task_order/EAGLE/task_order.h
index e6dff8610e..b3769d65e8 100644
--- a/src/task_order/EAGLE/task_order.h
+++ b/src/task_order/EAGLE/task_order.h
@@ -19,8 +19,16 @@
 #ifndef SWIFT_TASK_ORDER_EAGLE_H
 #define SWIFT_TASK_ORDER_EAGLE_H
 
+/**
+ * Is the star-formation task running before the feedback task?
+ */
 #define task_order_star_formation_before_feedback 1
 
+/**
+ * Is the cooling task running after the time-step calculation task?
+ */
+#define task_order_cooling_after_timestep 0
+
 /**
  * @brief Place the star formation cell at the right place in the dependency
  * graph.
diff --git a/src/task_order/GEAR/task_order.h b/src/task_order/GEAR/task_order.h
index 3708881db6..461068c6e4 100644
--- a/src/task_order/GEAR/task_order.h
+++ b/src/task_order/GEAR/task_order.h
@@ -19,8 +19,16 @@
 #ifndef SWIFT_TASK_ORDER_GEAR_H
 #define SWIFT_TASK_ORDER_GEAR_H
 
+/**
+ * Is the star-formation task running before the feedback task?
+ */
 #define task_order_star_formation_before_feedback 0
 
+/**
+ * Is the cooling task running after the time-step calculation task?
+ */
+#define task_order_cooling_after_timestep 1
+
 /**
  * @brief Place the star formation cell at the right place in the dependency
  * graph.
diff --git a/src/task_order/none/task_order.h b/src/task_order/none/task_order.h
index 3bb7975f43..826999f9dc 100644
--- a/src/task_order/none/task_order.h
+++ b/src/task_order/none/task_order.h
@@ -19,8 +19,22 @@
 #ifndef SWIFT_TASK_ORDER_NONE_H
 #define SWIFT_TASK_ORDER_NONE_H
 
+/**
+ * @file task_order/none/task_order.h
+ * @brief Defines the order of the subgrid tasks when no subgrid model has
+ * been specified. Defaults to the EAGLE model.
+ */
+
+/**
+ * Is the star-formation task running before the feedback task?
+ */
 #define task_order_star_formation_before_feedback 1
 
+/**
+ * Is the cooling task running after the time-step calculation task?
+ */
+#define task_order_cooling_after_timestep 0
+
 /**
  * @brief Place the star formation cell at the right place in the dependency
  * graph.
-- 
GitLab