diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 6251ddd24e369f246e64c15b92c399bcf5dca2a0..8098ff3743c52eb01cb9537a59ab734e07929c58 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,11 +1,40 @@
+# you can find different images here https://hub.docker.com/
+image: ubuntu:latest
+
+
 before_script:
-  # compile swift
-  - git clone https://gitlab.cosma.dur.ac.uk/swift/swiftsim.git
+  - apt-get update -qq && apt-get install -y -qq build-essential libhdf5-serial-dev gfortran csh git libtool-bin autoconf
+  - export LHOME=`pwd`
+# Compile grackle
+  - mkdir grackle
+  - git clone https://github.com/grackle-project/grackle.git
+  - cd grackle
+  - ./configure
+  - cd src/clib
+  - sed -i 's/LOCAL_HDF5_INSTALL = \/usr\/local\/hdf5\/1.8.2s/LOCAL_HDF5_INSTALL = \/usr\/lib\/x86_64-linux-gnu\/hdf5\/serial\/lib/' Make.mach.linux-gnu
+  - sed -i 's/MACH_INSTALL_PREFIX = $(HOME)\/local/MACH_INSTALL_PREFIX = $(LHOME)\/grackle/' Make.mach.linux-gnu
+  - make machine-linux-gnu
+  - make && make install
+  - export GRACKLE_ROOT=$LHOME/grackle
+  - cd $LHOME
+
+# compile swift
+  - git clone https://gitlab.cosma.dur.ac.uk/swift/swiftsim.git --single-branch --branch master
   - cd swiftsim
-  - ./autogen
-  - ./configure --with-subgrid=GEAR
+  - ./autogen.sh
+  - ./configure --with-subgrid=GEAR --with-grackle=$GRACKLE_ROOT
   - make -j 4
+  - cd ..
+
+# compile pyswiftsim
+  - python setup.py install --user
+
+# compile the docs
+  - cd docs
+  - make html
 
-test_build:
+test:
   script:
-    - python setup.py build
\ No newline at end of file
+    - cd test
+    - ./getCoolingTable.sh
+    - ./test_cooling.py
\ No newline at end of file
diff --git a/README b/README
deleted file mode 120000
index 602d87dfd9405502dcd988b9dab5bb724dc4471d..0000000000000000000000000000000000000000
--- a/README
+++ /dev/null
@@ -1 +0,0 @@
-Readme.md
\ No newline at end of file
diff --git a/Readme.md b/Readme.md
index 35a4f470363f3463f9600b28f85ce3bf5a6d001c..4f4c47bc88a31b1aadd99adc92d102415c503904 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,72 +1,16 @@
+[![pipeline status](https://gitlab.com/clastro/pyswiftsim/badges/master/pipeline.svg)](https://gitlab.com/clastro/pyswiftsim/commits/master)
+[![Documentation Status](https://readthedocs.org/projects/pyswiftsim/badge/?version=latest)](https://pyswiftsim.readthedocs.io/en/latest/?badge=latest)
+
 Welcome to PySWIFTsim
 =====================
 
 This code is a python wrapper around the cosmological hydrodynamical code SWIFT hosted at https://gitlab.cosma.dur.ac.uk/swift/swiftsim.
 
-
-A few examples are given in test and the API is trying to follow the SWIFT API, therefore a developer of SWIFT should be able to quickly learn how to use it.
+You can find a few examples in test and examples and a documentation in docs.
 
 Install
 =======
 
 To install PySWIFT, you can run the following command: `python3 setup.py install --with-swift SWIFT_PATH --user`.
 The setup script will read all your parameters in the config file of swift, therefore you need to reinstall PySWIFT with each configuration of SWIFT.
-You may need to remove the build directory to recompile PySWIFT.
-
-
-Adding a new structure
-======================
-
-In pyswiftsim/structure.py, a list of all SWIFT structure are described in python.
-When adding a new structure, a new class should be added to this file using the following example:
-
-```
-class SwiftParams(SwiftStruct):
-   """ All object should inherit from SwiftStruct """ 
-    _format = "{sec}s{data}sii{line_size}c".format(
-            sec=struct.calcsize(Section._format)*PARSER_MAX_NO_OF_SECTIONS,
-            data=struct.calcsize(Parameter._format)*PARSER_MAX_NO_OF_PARAMS,
-            line_size=PARSER_MAX_LINE_SIZE
-        )
-    """ _format describes the type of the object contained in a C structure.
-        see struct python package for a list of all possible types.
-	For an undefined/complex object, the letter 's' is used and for string 'c'.
-    """
-
-    _name = [
-            "section",
-            "data_params",
-            "sectionCount",
-            "paramCount",
-            "filename"
-        ]
-    """ _name defines the name of each variable """
-
-    def __init__(self, data, parent=None):
-        """ defines the constructor
-	    data is a list of bytes and parent is the structure in which this one is included
-	"""
-        super().__init__(self.struct_format, data, parent)
-
-    @property
-    def struct_substruct(self):
-    	""" This method should be defined if a substructure is present in this one.
-	    It returns a dictionary where the key is the name of the object (defined in _name)
-	    and the value is the array size (1 if this structure is not in an array).
-	"""
-        sec = {
-            "class": Section,
-            "size": PARSER_MAX_NO_OF_SECTIONS
-        }
-
-        param = {
-            "class": Parameter,
-            "size": PARSER_MAX_NO_OF_PARAMS
-        }
-        return {
-            "section": sec,
-            "data_params": param
-        }
-```
-
-If the new structure is an option (e.g. Part, Cooling, ...) you can use an if to define the _format and _name variables.
\ No newline at end of file
+You may need to remove the build directory to recompile PySWIFT.
\ No newline at end of file
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..298ea9e213e8c4c11f0431077510d4e325733c65
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,19 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
\ No newline at end of file
diff --git a/docs/RST/examples.rst b/docs/RST/examples.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0912efdbcafde647c6059e69b21fbedb34a6d76f
--- /dev/null
+++ b/docs/RST/examples.rst
@@ -0,0 +1,10 @@
+Examples
+========
+
+Here you will find the image generated by every example in the ``examples`` directory.
+If you wish to generate them, you will need to first install PySWIFTsim and then run the corresponding example.
+
+.. toctree::
+   :maxdepth: 2
+
+   examples/cooling.rst
diff --git a/docs/RST/examples/cooling.rst b/docs/RST/examples/cooling.rst
new file mode 100644
index 0000000000000000000000000000000000000000..1cc6f4db5c44493cdf7792754084760d5b89954b
--- /dev/null
+++ b/docs/RST/examples/cooling.rst
@@ -0,0 +1,27 @@
+Cooling Examples
+================
+
+
+Cooling Rate
+~~~~~~~~~~~~
+
+This example is generated in ``examples/cooling_rate`` with the Grackle cooling.
+The plot are the same than in [Smith2016]_.
+
+The code has two different modes 1D or 2D.
+In the first mode, the code generates particles at different temperatures but same density and computes the cooling rate associated to theses conditions.
+
+.. image:: https://obswww.unige.ch/~lhausamm/PySWIFTsim/examples/cooling_rate.png
+
+In the second mode, the code generates a grid of particles at different density and temperatures and then computes the cooling.
+
+.. image:: https://obswww.unige.ch/~lhausamm/PySWIFTsim/examples/cooling_rate_2d.png
+
+Cooling Box
+~~~~~~~~~~~
+
+.. image:: https://obswww.unige.ch/~lhausamm/PySWIFTsim/examples/cooling_box.png
+
+
+
+.. [Smith2016] `Grackle: a Chemistry and Cooling Library for Astrophysics <https://arxiv.org/abs/1610.09591>`_
diff --git a/docs/RST/libcooling.rst b/docs/RST/libcooling.rst
new file mode 100644
index 0000000000000000000000000000000000000000..b83bf4409a206c2f0e27c83b51bb0a1fc7b2382a
--- /dev/null
+++ b/docs/RST/libcooling.rst
@@ -0,0 +1,11 @@
+Libcooling
+==========
+
+.. automodule:: pyswiftsim.libcooling
+    :members:
+    :undoc-members: 
+    :private-members:
+    :special-members:
+    :show-inheritance:
+    :inherited-members:
+
diff --git a/docs/RST/pyswiftsim.rst b/docs/RST/pyswiftsim.rst
new file mode 100644
index 0000000000000000000000000000000000000000..0e77bb58ed4a0765bdd419362edebdda15f60178
--- /dev/null
+++ b/docs/RST/pyswiftsim.rst
@@ -0,0 +1,11 @@
+PySWIFTsim
+==========
+
+.. automodule:: pyswiftsim
+    :members:
+    :undoc-members: 
+    :private-members:
+    :special-members:
+    :show-inheritance:
+    :inherited-members:
+
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..8d40b6e2f01cd0c989474cbfab4425561875bd84
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/master/config
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'PySWIFTsim'
+copyright = '2019, Loic Hausammann'
+author = 'Loic Hausammann'
+
+# The short X.Y version
+version = ''
+# The full version, including alpha/beta/rc tags
+release = ''
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.doctest',
+    'sphinx.ext.intersphinx',
+    'sphinx.ext.todo',
+    'sphinx.ext.coverage',
+    'sphinx.ext.mathjax',
+    'sphinx.ext.viewcode',
+    'numpydoc'
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = None
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself.  Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'PySWIFTsimdoc'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'PySWIFTsim.tex', 'PySWIFTsim Documentation',
+     'Loic Hausammann', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'pyswiftsim', 'PySWIFTsim Documentation',
+     [author], 1)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'PySWIFTsim', 'PySWIFTsim Documentation',
+     author, 'PySWIFTsim', 'One line description of project.',
+     'Miscellaneous'),
+]
+
+
+# -- Options for Epub output -------------------------------------------------
+
+# Bibliographic Dublin Core info.
+epub_title = project
+
+# The unique identifier of the text. This can be a ISBN number
+# or the project homepage.
+#
+# epub_identifier = ''
+
+# A unique identification for the text.
+#
+# epub_uid = ''
+
+# A list of files that should not be packed into the epub file.
+epub_exclude_files = ['search.html']
+
+
+# -- Extension configuration -------------------------------------------------
+
+# -- Options for intersphinx extension ---------------------------------------
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {
+    'python': ('https://docs.python.org/', None),
+    'numpy': ('https://docs.scipy.org/doc/numpy', None),
+}
+
+# -- Options for todo extension ----------------------------------------------
+
+# If true, `todo` and `todoList` produce output, else they produce nothing.
+todo_include_todos = True
+
+# -- Options for numpy extension ----------------------------------------------
+
+numpydoc_use_plots = True
+
+numpydoc_xref_param_type = True
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..d11523e49bcf892c03137a516830300e753817cc
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,26 @@
+.. PySWIFTsim documentation master file, created by
+   sphinx-quickstart on Wed May 22 08:34:27 2019.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to PySWIFTsim's documentation!
+======================================
+
+PySWIFTsim is a python wrapper around the gravity and SPH solver SWIFT.
+The aim is to provide a python tool that benefits from HPC code and allows to directly use some feature of SWIFT in order to either test it or analyze a simulation.
+
+.. toctree::
+   :maxdepth: 2
+
+   RST/pyswiftsim.rst
+   RST/libcooling.rst
+   RST/examples.rst
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000000000000000000000000000000000000..27f573b87af11e2cbbd9f54eb1ee285a58550146
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd
diff --git a/examples/cooling_box/cooling.yml b/examples/cooling_box/cooling.yml
new file mode 100644
index 0000000000000000000000000000000000000000..415f22ce7fadc74f042ad91d30079aa8ce0ffbea
--- /dev/null
+++ b/examples/cooling_box/cooling.yml
@@ -0,0 +1,28 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.989e43   # Grams
+  UnitLength_in_cgs:   3.085e21   # Centimeters
+  UnitVelocity_in_cgs: 1e5   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# 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.
+
+# Cooling with Grackle 2.0
+GrackleCooling:
+  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table
+  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: 0 # Write in output corresponding primordial chemistry mode
+  MaxSteps: 1000
+  ConvergenceLimit: 1e-2
+
+GearChemistry:
+  InitialMetallicity: 0.01295
\ No newline at end of file
diff --git a/examples/cooling_box/plot_cooling.py b/examples/cooling_box/plot_cooling.py
new file mode 100644
index 0000000000000000000000000000000000000000..13c2e4e514fd14b4f749a8a7d7f39a1e19414088
--- /dev/null
+++ b/examples/cooling_box/plot_cooling.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+# ########################################################################
+# This file is part of PYSWIFT.
+# 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/>.
+# ########################################################################
+
+from pyswiftsim.libcooling import coolingRate
+from pyswiftsim import downloadCoolingTable
+
+import numpy as np
+import matplotlib.pyplot as plt
+import yaml
+from astropy import units, constants
+from time import strftime, gmtime
+
+plt.rc('text', usetex=True)
+
+downloadCoolingTable()
+
+# PARAMETERS
+
+# swift params filename
+filename = "cooling.yml"
+
+with_cosmo = False
+
+# particle data
+# in atom/cc
+part_density = np.array([1.])
+part_temperature = np.array([1e4])  # in K
+gamma = 5./3.
+
+# time in Myr
+t_end = 1.
+N_time = 100000
+
+# SCRIPT
+
+
+def generate_initial_condition(us):
+    print("Generating default initial conditions")
+    # get units
+    unit_length = float(us["UnitLength_in_cgs"])
+    unit_vel = float(us["UnitVelocity_in_cgs"])
+    unit_mass = float(us["UnitMass_in_cgs"])
+    unit_temp = float(us["UnitTemp_in_cgs"])
+
+    d = {}
+    # Deal with units
+    m_p = constants.m_p.to("g") / (unit_mass * units.g)
+    rho = part_density * unit_length**3 * m_p
+    d["density"] = rho.to("")
+
+    unit_time = unit_length / unit_vel
+    unit_energy = unit_mass * unit_length**2
+    unit_energy /= unit_time**2
+    k_B = constants.k_B.to("erg / K") / (unit_energy / unit_temp)
+    k_B *= units.K / units.erg
+    energy = k_B * part_temperature / unit_temp
+    energy /= (gamma - 1.) * m_p
+    d["energy"] = energy
+
+    t = t_end * units.Myr.to("s") / unit_time
+    d["time"] = np.linspace(0, t, N_time)
+
+    return d
+
+
+def plot_solution(energy, data, us):
+    print("Plotting solution")
+    rho = data["density"]
+    time = data["time"]
+    unit_length = float(us["UnitLength_in_cgs"])
+    unit_vel = float(us["UnitVelocity_in_cgs"])
+    unit_mass = float(us["UnitMass_in_cgs"])
+    unit_time = unit_length / unit_vel
+
+    # change units => cgs
+    rho *= unit_mass / unit_length**3
+
+    energy *= unit_length**2 / unit_time**2
+
+    time *= unit_time
+
+    # do plot
+
+    plt.figure()
+    date = strftime("%d %b %Y", gmtime())
+    plt.title("Date: {}".format(date))
+    plt.plot(time, energy)
+    plt.xlabel("Time [s]")
+    plt.ylabel("Energy [erg]")
+    plt.show()
+
+
+def getParser(filename):
+    with open(filename, "r") as stream:
+        stream = yaml.load(stream)
+        return stream
+
+
+if __name__ == "__main__":
+
+    us = getParser(filename)["InternalUnitSystem"]
+
+    d = generate_initial_condition(us)
+
+    t = d["time"]
+    dt = t[1] - t[0]
+
+    # du / dt
+    print("Computing cooling...")
+
+    N = len(t)
+    energy = np.zeros(t.shape)
+    energy[0] = d["energy"]
+    rho = np.array(d["density"], dtype=np.float32)
+    for i in range(N-1):
+        ene = np.array([energy[i]], dtype=np.float32)
+        rate = coolingRate(filename, rho, ene, with_cosmo, dt)
+        energy[i+1] = energy[i] + rate * dt
+
+    plot_solution(energy, d, us)
diff --git a/examples/cooling_box/run.sh b/examples/cooling_box/run.sh
new file mode 100644
index 0000000000000000000000000000000000000000..ca4ed090d7161d18ce520af16a81caaaebd04cf3
--- /dev/null
+++ b/examples/cooling_box/run.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./plot_cooling.py
diff --git a/examples/cooling_rate/cooling.yml b/examples/cooling_rate/cooling.yml
new file mode 100644
index 0000000000000000000000000000000000000000..166fa0401a5870b941e503152d9843a8e2417e69
--- /dev/null
+++ b/examples/cooling_rate/cooling.yml
@@ -0,0 +1,41 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.989e43   # Grams
+  UnitLength_in_cgs:   3.085e21   # Centimeters
+  UnitVelocity_in_cgs: 1e5   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# 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.
+  minimal_temperature:   0        # (Optional) Minimal temperature (in internal units) allowed for the gas particles. Value is ignored if set to 0.
+
+# Cosmological parameters
+Cosmology:
+  h:              0.6777        # Reduced Hubble constant
+  a_begin:        0.0078125     # Initial scale-factor of the simulation
+  a_end:          1.0           # Final scale factor of the simulation
+  Omega_m:        0.307         # Matter density parameter
+  Omega_lambda:   0.693         # Dark-energy density parameter
+  Omega_b:        0.0455        # Baryon density parameter
+  Omega_r:        0.            # (Optional) Radiation density parameter
+  w_0:            -1.0          # (Optional) Dark-energy equation-of-state parameter at z=0.
+  w_a:            0.            # (Optional) Dark-energy equation-of-state time evolution parameter.
+
+# Cooling with Grackle 2.0
+GrackleCooling:
+  CloudyTable: CloudyData_UVB=HM2012.h5 # Name of the Cloudy Table
+  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
+  ConvergenceLimit: 1e-4
+  MaxSteps: 20000
+  Omega: 0.8
+
+GearChemistry:
+  InitialMetallicity: 0.01295
\ No newline at end of file
diff --git a/examples/cooling_rate/plot_cooling.py b/examples/cooling_rate/plot_cooling.py
new file mode 100644
index 0000000000000000000000000000000000000000..55d21ee1790177aa1755a7cc2815cdb26414502a
--- /dev/null
+++ b/examples/cooling_rate/plot_cooling.py
@@ -0,0 +1,206 @@
+#!/usr/bin/env python3
+# ########################################################################
+# This file is part of PYSWIFT.
+# 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/>.
+# ########################################################################
+
+from pyswiftsim.libcooling import coolingRate
+from pyswiftsim import downloadCoolingTable
+
+from copy import deepcopy
+import numpy as np
+import matplotlib.pyplot as plt
+from astropy import units, constants
+import yaml
+from time import strftime, gmtime
+
+plt.rc('text', usetex=True)
+
+downloadCoolingTable()
+
+# PARAMETERS
+# cosmology
+with_cosmo = False
+
+# swift params filename
+filename = "cooling.yml"
+
+# hydrogen mass fraction
+h_mass_frac = 0.76
+
+# density in atom / cm3
+N_rho = 100
+if N_rho == 1:
+    density = np.array([1.])
+else:
+    density = np.logspace(-6, 4, N_rho)
+
+# temperature in K
+N_T = 1000
+temperature = np.logspace(1, 9, N_T)
+
+# time step in s
+dt = units.Myr * 1e-8
+dt = dt.to("s") / units.s
+
+# adiabatic index
+gamma = 5. / 3.
+
+
+def mu(T):
+    # define constants
+    T_transition = 1e4
+    mu_neutral = 4. / (1. + 3. * h_mass_frac)
+    mu_ion = 4. / (8. - 5. * (1 - h_mass_frac))
+
+    # Find correct mu
+    ind = T > T_transition
+    mu = np.zeros(T.shape)
+    mu[ind] = mu_ion
+    mu[~ind] = mu_neutral
+    return mu
+
+
+# SCRIPT
+def generate_initial_condition(us):
+    print("Generating default initial conditions")
+    # get units
+    unit_length = float(us["UnitLength_in_cgs"])
+    unit_vel = float(us["UnitVelocity_in_cgs"])
+    unit_mass = float(us["UnitMass_in_cgs"])
+    unit_temp = float(us["UnitTemp_in_cgs"])
+
+    d = {}
+    # generate grid
+    rho, T = np.meshgrid(density, temperature)
+    rho = deepcopy(rho.reshape(-1))
+    T = T.reshape(-1)
+    d["temperature"] = T
+
+    # Deal with units
+    m_p = constants.m_p.to("g") / (unit_mass * units.g)
+    rho = rho * unit_length**3 * m_p
+    d["density"] = rho.to("")
+
+    unit_time = unit_length / unit_vel
+    unit_energy = unit_mass * unit_length**2
+    unit_energy /= unit_time**2
+    k_B = constants.k_B.to("erg / K") / (unit_energy / unit_temp)
+    k_B *= units.K / units.erg
+    energy = k_B * T / (unit_temp * mu(T))
+    energy /= (gamma - 1.) * m_p
+    d["energy"] = energy.to("")
+
+    time_step = dt / unit_time
+    d["time_step"] = time_step
+
+    return d
+
+
+def getParser(filename):
+    with open(filename, "r") as stream:
+        stream = yaml.load(stream)
+        return stream
+
+
+def plot_solution(rate, data, us):
+    print("Plotting solution")
+    energy = data["energy"]
+    rho = data["density"]
+    T = data["temperature"]
+
+    T *= float(us["UnitTemp_in_cgs"])
+
+    # change units => cgs
+    u_m = float(us["UnitMass_in_cgs"])
+    u_l = float(us["UnitLength_in_cgs"])
+    u_v = float(us["UnitVelocity_in_cgs"])
+    u_t = u_l / u_v
+
+    rho *= u_m / u_l**3
+
+    energy *= u_v**2
+
+    rate *= u_v**2 / u_t
+
+    # lambda cooling
+    lam = rate * rho
+
+    # do plot
+    if N_rho == 1:
+        # plot Lambda vs T
+        plt.figure()
+        plt.loglog(T, np.abs(lam),
+                   label="SWIFT")
+        plt.xlabel("Temperature [K]")
+        plt.ylabel("$\\Lambda$ [erg s$^{-1}$ cm$^{3}$]")
+
+    else:
+        m_p = constants.m_p.to("g") / units.g
+        rho /= m_p
+
+        shape = [N_rho, N_T]
+        cooling_time = energy / rate
+        cooling_length = np.sqrt(gamma * (gamma-1.) * energy) * cooling_time
+
+        cooling_length = np.log10(np.abs(cooling_length) / units.kpc.to('cm'))
+
+        # reshape
+        rho = rho.reshape(shape)
+        T = T.reshape(shape)
+        energy = energy.reshape(shape)
+        cooling_length = cooling_length.reshape(shape)
+
+        _min = -7
+        _max = 7
+        N_levels = 100
+        levels = np.linspace(_min, _max, N_levels)
+        plt.figure()
+        plt.contourf(rho, T, cooling_length, levels)
+        plt.xlabel("Density [atom/cm3]")
+        plt.ylabel("Temperature [K]")
+
+        ax = plt.gca()
+        ax.set_xscale("log")
+        ax.set_yscale("log")
+
+        cbar = plt.colorbar()
+        tc = np.arange(_min, _max, 1.5)
+        cbar.set_ticks(tc)
+        cbar.set_ticklabels(tc)
+        cbar.ax.set_ylabel("Log( Cooling Length / kpc )")
+
+    date = strftime("%d %b %Y", gmtime())
+    plt.title("Date: {}".format(date))
+    plt.show()
+
+
+if __name__ == "__main__":
+
+    parser = getParser(filename)
+    us = parser["InternalUnitSystem"]
+
+    d = generate_initial_condition(us)
+
+    # du / dt
+    print("Computing cooling...")
+
+    rate = coolingRate(filename,
+                       d["density"].astype(np.float32),
+                       d["energy"].astype(np.float32),
+                       with_cosmo, d["time_step"])
+
+    plot_solution(rate, d, us)
diff --git a/examples/cooling_rate/run.sh b/examples/cooling_rate/run.sh
new file mode 100644
index 0000000000000000000000000000000000000000..ca4ed090d7161d18ce520af16a81caaaebd04cf3
--- /dev/null
+++ b/examples/cooling_rate/run.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./plot_cooling.py
diff --git a/pyswiftsim/__init__.py b/pyswiftsim/__init__.py
index 526dbce334dd759ae3af7299c78404f02c44fa8d..8c6a5748ae78c94a83aee7c1d808247ac8fd102e 100644
--- a/pyswiftsim/__init__.py
+++ b/pyswiftsim/__init__.py
@@ -1,19 +1,30 @@
-"""
-This file is part of PYSWIFT.
-Copyright (c) 2018 Loic Hausammann (loic.hausammann@epfl.ch)
+# ########################################################################
+# This file is part of PYSWIFT.
+# 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/>.
+# ########################################################################
 
-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.
+import os
+import urllib.request
 
-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/>.
-"""
+def downloadCoolingTable():
+    url = "http://virgodb.cosma.dur.ac.uk/"
+    url += "swift-webstorage/CoolingTables/CloudyData_UVB=HM2012.h5"
+    filename = "CloudyData_UVB=HM2012.h5"
 
-from pyswiftsim import *
+    if not os.path.isfile(filename):
+        # Download the file from `url` and save it locally under `file_name`:
+        urllib.request.urlretrieve(url, filename)
diff --git a/setup.py b/setup.py
index 919adee2849f4fc5dcc55e8dd88d25a6b2ccf337..1c76bc94e8463de8a2bf4af34c8a9c61a2505365 100644
--- a/setup.py
+++ b/setup.py
@@ -118,7 +118,7 @@ data_files = []
 ##############
 
 c_src = glob("src/*.c")
-ext_modules = Extension("pyswiftsim.wrapper",
+ext_modules = Extension("pyswiftsim.libcooling",
                         c_src,
                         include_dirs=include,
                         libraries=lib,
diff --git a/src/Makefile.am b/src/Makefile.am
deleted file mode 100644
index a77473fd9cf45fb642c0175a3d22490b9b7f9084..0000000000000000000000000000000000000000
--- a/src/Makefile.am
+++ /dev/null
@@ -1,68 +0,0 @@
-# This file is part of PySWIFTsim.
-# 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 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 General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-# Add the non-standard paths to the included library headers
-AM_CFLAGS = -I$(top_srcdir)/../src $(HDF5_CPPFLAGS) $(GSL_INCS) $(FFTW_INCS) $(NUMA_INCS) $(GRACKLE_INCS) $(PYTHON_INCS)
-
-# Assign a "safe" version number
-AM_LDFLAGS = $(HDF5_LDFLAGS) $(FFTW_LIBS) -version-info 0:0:0
-
-# The git command, if available.
-GIT_CMD = @GIT_CMD@
-
-# Additional dependencies for shared libraries.
-EXTRA_LIBS = $(HDF5_LIBS) $(FFTW_LIBS) $(NUMA_LIBS) $(PROFILER_LIBS) $(TCMALLOC_LIBS) \
-	$(JEMALLOC_LIBS) $(TBBMALLOC_LIBS) $(GRACKLE_LIBS) $(GSL_LIBS) $(PYTHON_LIBS) \
-	$(top_srcdir)/src/.libs
-
-# MPI libraries.
-MPI_LIBS = $(PARMETIS_LIBS) $(METIS_LIBS) $(MPI_THREAD_LIBS)
-MPI_FLAGS = -DWITH_MPI $(PARMETIS_INCS) $(METIS_INCS)
-
-# Build the libswiftsim library
-lib_LTLIBRARIES = libpyswiftsim.la
-# Build a MPI-enabled version too?
-if HAVEMPI
-lib_LTLIBRARIES += libpyswiftsim_mpi.la
-endif
-
-# List required headers
-include_HEADERS = chemistry_wrapper.h cooling_wrapper.h \
-	cosmology_wrapper.h parser_wrapper.h part_wrapper.h \
-	pyswiftsim_tools.h units_wrapper.h
-
-
-# Common source files
-AM_SOURCES = chemistry_wrapper.c cooling_wrapper.c cosmology_wrapper.c \
-	parser_wrapper.c part_wrapper.c pyswiftsim_tools.c \
-	units_wrapper.c wrapper.c
-
-# Include files for distribution, not installation.
-nobase_noinst_HEADERS = 
-
-# Sources and flags for regular library
-libpyswiftsim_la_SOURCES = $(AM_SOURCES)
-libpyswiftsim_la_CFLAGS = $(AM_CFLAGS)
-libpyswiftsim_la_LDFLAGS = $(AM_LDFLAGS) $(EXTRA_LIBS) -lswiftsim
-libpyswiftsim_la_LIBADD = $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS)
-
-# Sources and flags for MPI library
-libpyswiftsim_mpi_la_SOURCES = $(AM_SOURCES)
-libpyswiftsim_mpi_la_CFLAGS = $(AM_CFLAGS) $(MPI_FLAGS)
-libpyswiftsim_mpi_la_LDFLAGS = $(AM_LDFLAGS) $(MPI_LIBS) $(EXTRA_LIBS) -lswiftsim_mpi
-libpyswiftsim_mpi_la_SHORTNAME = mpi
-libpyswiftsim_mpi_la_LIBADD = $(GRACKLE_LIBS) $(VELOCIRAPTOR_LIBS)
-
diff --git a/src/cooling_wrapper.c b/src/cooling_wrapper.c
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..47dd1ccb52857d1794719eaac9ba2bdcedded1c2 100644
--- a/src/cooling_wrapper.c
+++ b/src/cooling_wrapper.c
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * This file is part of PYSWIFTSIM.
+ * 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 "cooling_wrapper.h"
+#include "pyswiftsim_tools.h"
+
+/* TODO Remove this */
+struct cooling_function_data cooling;
+int initialized;
+
+
+/**
+ * @brief Set the cooling element fractions
+ *
+ * @param xp The #xpart to set
+ * @param frac The numpy array containing the fractions (id, element)
+ * @param idx The id (in frac) of the particle to set
+ */
+void pycooling_set_fractions(struct xpart *xp, PyArrayObject* frac, const int idx) {
+#ifdef COOLING_GRACKLE
+  struct cooling_xpart_data *data = &xp->cooling_data;
+  data->metal_frac = *(float*)PyArray_GETPTR2(frac, idx, 0);
+
+#ifdef COOLING_GRACKLE
+#if COOLING_GRACKLE_MODE > 0
+  data->HI_frac = *(float*)PyArray_GETPTR2(frac, idx, 1);
+  data->HII_frac = *(float*)PyArray_GETPTR2(frac, idx, 2);
+  data->HeI_frac = *(float*)PyArray_GETPTR2(frac, idx, 3);
+  data->HeII_frac = *(float*)PyArray_GETPTR2(frac, idx, 4);
+  data->HeIII_frac = *(float*)PyArray_GETPTR2(frac, idx, 5);
+  data->e_frac = *(float*)PyArray_GETPTR2(frac, idx, 6);
+#endif // COOLING_GRACKLE_MODE
+#if COOLING_GRACKLE_MODE > 1
+  data->HM_frac = *(float*)PyArray_GETPTR2(frac, idx, 7);
+  data->H2I_frac = *(float*)PyArray_GETPTR2(frac, idx, 8);
+  data->H2II_frac = *(float*)PyArray_GETPTR2(frac, idx, 9);
+#endif // COOLING_GRACKLE_MODE
+#if COOLING_GRACKLE_MODE > 2
+  data->DI_frac = *(float*)PyArray_GETPTR2(frac, idx, 10);
+  data->DII_frac = *(float*)PyArray_GETPTR2(frac, idx, 11);
+  data->HDI_frac = *(float*)PyArray_GETPTR2(frac, idx, 12);
+#endif // COOLING_GRACKLE_MODE
+#endif // COOLING_GRACKLE
+
+#else
+  error("Not implemented");
+#endif
+}
+
+/**
+ * @brief Compute the cooling rate
+ *
+ * args is expecting:
+ *   - a parameter filename,
+ *   - a numpy array of the densities,
+ *   - a numpy array of the specific energies,
+ *   - an optional int if the cosmology should be used,
+ *   - an optional float for the time step,
+ *   - an optional array with the element fractions.
+ *
+ * @param self calling object
+ * @param args arguments
+ * @return cooling rate
+ */
+PyObject* pycooling_rate(PyObject* self, PyObject* args) {
+  import_array();
+  
+
+  char *filename;
+
+  PyArrayObject *rho;
+  PyArrayObject *energy;
+  PyArrayObject *fractions = NULL;
+
+  float dt = 1e-3;
+  int with_cosmo = 0;
+
+  /* parse argument */
+  if (!PyArg_ParseTuple(
+        args, "sOO|ifO", &filename, &rho, &energy,
+	&with_cosmo, &dt, &fractions))
+    return NULL;
+
+  /* check numpy array */
+  if (pytools_check_array(energy, 1, NPY_FLOAT, "Energy") != SWIFT_SUCCESS)
+    return NULL;
+
+  if (pytools_check_array(rho, 1, NPY_FLOAT, "Density") != SWIFT_SUCCESS)
+    return NULL;
+
+  if (fractions != NULL &&
+      pytools_check_array(fractions, 2, NPY_FLOAT, "Fractions") != SWIFT_SUCCESS)
+    return NULL;
+
+  if (PyArray_DIM(energy, 0) != PyArray_DIM(rho, 0))
+    error("Density and energy should have the same dimension");
+
+  if (fractions != NULL &&
+      PyArray_DIM(fractions, 0) != PyArray_DIM(rho,0))
+    error("Fractions should have the same first dimension than the others");
+
+  size_t N = PyArray_DIM(energy, 0);
+
+
+  /* Initialize parser */
+  struct swift_params params;
+  parser_read_file(filename, &params);
+
+  /* Init unit system */
+  struct unit_system us;
+  units_init_from_params(&us, &params, "InternalUnitSystem");
+
+  /* Init physical constant */
+  struct phys_const pconst;
+  phys_const_init(&us, &params, &pconst);
+
+  /* init cooling */
+  if (initialized == 0) {
+    // struct cooling_function_data cooling;
+    cooling_init_backend(&params, &us, &pconst, &cooling);
+    initialized = 1;
+  }
+  else if (initialized == 1) {
+    message("WARNING: not reinitializing the cooling, need to be fixed");
+    initialized = 2;
+  }
+
+  /* Init chemistry */
+  struct chemistry_global_data chemistry;
+  chemistry_init_backend(&params, &us, &pconst, &chemistry);
+
+  /* Init cosmo */
+  struct cosmology cosmo;
+
+  if (with_cosmo)
+    cosmology_init(&params, &us, &pconst, &cosmo);
+  else
+    cosmology_init_no_cosmo(&cosmo);
+
+  /* Init hydro prop */
+  struct hydro_props hydro_props;
+  hydro_props_init(&hydro_props, &pconst, &us, &params);
+
+  /* Init entropy floor */
+  struct entropy_floor_properties floor_props;
+  entropy_floor_init(&floor_props, &pconst, &us,
+		     &hydro_props, &params);
+
+  struct part p;
+  struct xpart xp;
+
+  hydro_first_init_part(&p, &xp);
+    
+  /* return object */
+  PyArrayObject *rate = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(energy), PyArray_DIMS(energy), NPY_FLOAT);
+
+  /* loop over all particles */
+  for(size_t i = 0; i < N; i++)
+    {
+      /* set particle data */
+      p.rho = *(float*) PyArray_GETPTR1(rho, i);
+      float u = *(float*) PyArray_GETPTR1(energy, i);
+      hydro_set_physical_internal_energy(&p, &xp, &cosmo, u);
+
+      if (fractions != NULL)
+	pycooling_set_fractions(&xp, fractions, i);
+      else
+	cooling_first_init_part(&pconst, &us, &cosmo, &cooling, &p, &xp);
+
+      chemistry_first_init_part(&pconst, &us, &cosmo, &chemistry, &p, &xp);	
+
+      hydro_set_physical_internal_energy_dt(&p, &cosmo, 0);
+
+      /* compute cooling rate */
+      cooling_cool_part(&pconst, &us, &cosmo, &hydro_props,
+			&floor_props, &cooling, &p, &xp, dt, dt);
+      float *tmp = PyArray_GETPTR1(rate, i);
+      *tmp = hydro_get_physical_internal_energy_dt(&p, &cosmo);
+
+    }
+
+  /* Cleanup */
+  cosmology_clean(&cosmo);
+
+  return (PyObject *) rate;
+  
+}
+
diff --git a/src/cooling_wrapper.h b/src/cooling_wrapper.h
index 86841cff6fcfed69a26a4fa89e42bd0ab9d1ab0e..bd9017f8abb4c31084db4ace60fa52372f943006 100644
--- a/src/cooling_wrapper.h
+++ b/src/cooling_wrapper.h
@@ -21,6 +21,11 @@
 
 #include "pyswiftsim_includes.h"
 
+/* A few global variables */
+extern struct cooling_function_data cooling;
+extern int initialized;
+
+
 PyObject* pycooling_rate(PyObject* self, PyObject* args);
 
 #endif // __PYSWIFTSIM_COOLING_H__
diff --git a/src/wrapper.c b/src/libcooling.c
similarity index 67%
rename from src/wrapper.c
rename to src/libcooling.c
index af15b99499ce9bd9ca7ea085da4ad418cf9bc856..6248d437d0ad9519b033c81acf8f80760f54280b 100644
--- a/src/wrapper.c
+++ b/src/libcooling.c
@@ -22,30 +22,38 @@
 #include <math.h>
 #include <numpy/arrayobject.h>
 
-
 /* definition of the method table */      
       
-static PyMethodDef wrapper_methods[] = {
+static PyMethodDef libcooling_methods[] = {
 
   {"coolingRate", pycooling_rate, METH_VARARGS,
    "Compute the cooling rate.\n\n"
    "Parameters\n"
    "----------\n\n"
-   "pyconst: swift physical constant\n"
-   "pyus: swift unit system\n"
-   "cooling: swift cooling structure\n"
-   "rho: np.array\n"
-   "\t Mass density in pyus units\n"
-   "energy: np.array\n"
-   "\t Internal energy in pyus units\n"
+   "filename: str\n"
+   "\t Parameter file\n"
+   "rho: array\n"
+   "\t Mass density in internal units\n"
+   "energy: array\n"
+   "\t Internal energy in internal units\n"
    "dt: float, optional\n"
-   "\t Time step in pyus units\n"
-   "fractions: np.array, optional\n"
+   "\t Time step in internal units\n"
+   "with_cosmo: int, optional\n"
+   "\t Use cosmology?\n"
+   "fractions: array, optional\n"
    "\t Fraction of each cooling element (including metals)\n"
+   "\n"
    "Returns\n"
-   "-------\n\n"
-   "rate: np.array\n"
+   "-------\n"
+   "rate: array\n"
    "\t Cooling rate\n"
+   "\n"
+   "Examples\n"
+   "--------\n"
+   ">>> rho = np.array([1])\n"
+   ">>> u = np.array([1000])\n"
+   ">>> filename = 'params.yml'\n"
+   ">>> rate = coolingRate(filename, rho, u)\n"
   },
 
 
@@ -54,12 +62,12 @@ static PyMethodDef wrapper_methods[] = {
       
       
 
-static struct PyModuleDef wrapper_cmodule = {
+static struct PyModuleDef libcooling_cmodule = {
   PyModuleDef_HEAD_INIT,
-  "wrapper",
-  "Wrapper around the SPH cosmological simulation code SWIFT",
+  "libcooling",
+  "Wrapper around the cooling of SWIFT",
   -1,
-  wrapper_methods,
+  libcooling_methods,
   NULL, /* m_slots */
   NULL, /* m_traverse */
   NULL, /* m_clear */
@@ -67,7 +75,7 @@ static struct PyModuleDef wrapper_cmodule = {
 };
 
 
-PyMODINIT_FUNC PyInit_libpyswiftsim(void)
+PyMODINIT_FUNC PyInit_libcooling(void)
 {
   PyObject *m;
 
@@ -76,7 +84,7 @@ PyMODINIT_FUNC PyInit_libpyswiftsim(void)
   
   import_array();
   Py_Initialize();
-  m = PyModule_Create(&wrapper_cmodule);
+  m = PyModule_Create(&libcooling_cmodule);
 
   return m;
 }
diff --git a/src/pyswiftsim_tools.c b/src/pyswiftsim_tools.c
index 4fd232542387b913e7604f99cb95eb18ae3f8e70..e9c8f3d3196cbca65265e7f9d62073d902c31529 100644
--- a/src/pyswiftsim_tools.c
+++ b/src/pyswiftsim_tools.c
@@ -35,21 +35,21 @@ int pytools_check_array(PyArrayObject *obj, int dim, int type, const char* name)
   /* check if array */
   if (!PyArray_Check(obj))
     {
-      pyerror("Expecting a numpy array for %s", name);
+      error("Expecting a numpy array for %s", name);
     }
 
   /* check if required dim */
   if (PyArray_NDIM(obj) != dim)
     {
-      pyerror("Array should be a %i dimensional object for %s", dim, name);
+      error("Array should be a %i dimensional object for %s", dim, name);
     }
 
   /* check data type */
   if (PyArray_TYPE(obj) != type)
     {
-      pyerror("Wrong array type for %s", name);
+      error("Wrong array type for %s", name);
     }
 
-  return SUCCESS;
+  return SWIFT_SUCCESS;
 
 }
diff --git a/src/pyswiftsim_tools.h b/src/pyswiftsim_tools.h
index b7dd07b2b5972cb8b77d1afe0ffdb0b1dc7855fc..567314f028f17bf34e395ea77292763d1373447e 100644
--- a/src/pyswiftsim_tools.h
+++ b/src/pyswiftsim_tools.h
@@ -19,7 +19,7 @@
 #ifndef __PYSWIFTSIM_TOOLS_H__
 #define __PYSWIFTSIM_TOOLS_H__
 
-#include "pyswiftsim.h"
+#include "pyswiftsim_includes.h"
 
 #define DIM 3
 #define STRING_SIZE 200
@@ -33,7 +33,14 @@
   }
 
 #define IMPORT_ARRAY() IMPORT_ARRAY1(NUMPY_IMPORT_ARRAY_RETVAL);
-	
+
+/* error code in pyswiftsim */
+enum error_code {
+  SWIFT_FAIL,
+  SWIFT_SUCCESS,
+};
+
+
 
 int pytools_check_array(PyArrayObject *obj, int dim, int type, const char* name);
 
diff --git a/test/test_cooling.py b/test/test_cooling.py
new file mode 100644
index 0000000000000000000000000000000000000000..94e3a46917f74659c8b8502f2922d316f14d5c41
--- /dev/null
+++ b/test/test_cooling.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# ########################################################################
+# This file is part of PYSWIFT.
+# 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/>.
+# ########################################################################
+
+from pyswiftsim.libcooling import coolingRate
+from pyswiftsim import downloadCoolingTable
+import numpy as np
+from astropy import units, constants
+import yaml
+
+# define parameters
+filename = "test_cooling.yml"
+# adiabatic index
+gamma = 5. / 3.
+
+# download cooling table
+downloadCoolingTable()
+
+# Read parameter file
+f = open(filename, "r")
+data = yaml.load(f)
+f.close()
+
+# Get the units
+swift_units = data["InternalUnitSystem"]
+# length unit
+u_l = float(swift_units["UnitLength_in_cgs"])
+# velocity unit
+u_v = float(swift_units["UnitVelocity_in_cgs"])
+# mass unit
+u_m = float(swift_units["UnitMass_in_cgs"])
+# time unit
+u_t = u_l / u_v
+# energy unit
+u_e = u_m * u_l**2 / u_t**2
+
+# boltzman constant
+k_B = constants.k_B.to("erg / K") * units.K / (units.erg * u_e)
+k_B = float(k_B)
+# proton mass
+m_p = constants.m_p.to("g") / (units.g * u_m)
+m_p = float(m_p)
+
+
+def internalEnergy(T):
+    u = (k_B / m_p) * T
+    u /= (gamma - 1.)
+    return u
+
+
+# generates data
+# time step in Myr
+dt = 1e-3
+dt = float(units.Myr.to('s')) / u_t
+
+# density in atom / cm3
+density = np.array([1.], dtype=np.float32)
+density *= m_p * u_l**3
+
+# Temperature in K
+T = np.array([1e4], dtype=np.float32)
+u = internalEnergy(T)
+
+# compute the cooling rate
+with_cosmo = False
+rate = coolingRate(filename, density, u, with_cosmo, dt)
+
+# go back to cgs
+rate /= u_e / u_t
+
+print("Cooling done: {} erg/s".format(rate))
diff --git a/test/test_cooling.yml b/test/test_cooling.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a30e9edf8a5fa99564e288e5029c038af1fb2b32
--- /dev/null
+++ b/test/test_cooling.yml
@@ -0,0 +1,25 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.989e43   # Grams
+  UnitLength_in_cgs:   3.085e21   # Centimeters
+  UnitVelocity_in_cgs: 1e5   # Centimeters per second
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# 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
+  OutputMode: 0                         # (optional) Write in output corresponding primordial chemistry mode
+  MaxSteps: 10000                       # (optional) Max number of step when computing the initial composition
+  ConvergenceLimit: 1e-2                # (optional) Convergence threshold (relative) for initial composition
+
+# Parameters 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.