diff --git a/.gitignore b/.gitignore
index 1a2e83a9d8c62f48cfa9db9f45b1b40bec80b449..27fa7cf814b81dc5ba4bf697a8a4e0e6a1393333 100644
--- a/.gitignore
+++ b/.gitignore
@@ -111,6 +111,8 @@ tests/benchmarkInteractions
 tests/testGravityDerivatives
 tests/testPotentialSelf
 tests/testPotentialPair
+tests/testEOS
+tests/testEOS*.txt
 
 theory/latex/swift.pdf
 theory/SPH/Kernels/kernels.pdf
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 643d89aaf05e31b6ea1c304c4fac6c28f7e89d03..cead0cc9cd16ae06a08e051c913db36d8cb43558 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,16 +1,16 @@
 # This file is part of SWIFT.
 # Copyright (c) 2015 matthieu.schaller@durham.ac.uk.
-# 
+#
 # 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/>.
 
@@ -27,7 +27,7 @@ TESTS = testGreetings testMaths testReading.sh testSingle testKernel testSymmetr
         testMatrixInversion testThreadpool testDump testLogger testInteractions.sh \
         testVoronoi1D testVoronoi2D testVoronoi3D testGravityDerivatives \
 	testPeriodicBC.sh testPeriodicBCPerturbed.sh testPotentialSelf \
-	testPotentialPair 
+	testPotentialPair testEOS
 
 # List of test programs to compile
 check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \
@@ -37,7 +37,7 @@ check_PROGRAMS = testGreetings testReading testSingle testTimeIntegration \
                  testAdiabaticIndex testRiemannExact testRiemannTRRS \
                  testRiemannHLLC testMatrixInversion testDump testLogger \
 		 testVoronoi1D testVoronoi2D testVoronoi3D testPeriodicBC \
-		 testGravityDerivatives testPotentialSelf testPotentialPair
+		 testGravityDerivatives testPotentialSelf testPotentialPair testEOS
 
 # Rebuild tests when SWIFT is updated.
 $(check_PROGRAMS): ../src/.libs/libswiftsim.a
@@ -105,6 +105,8 @@ testPotentialSelf_SOURCES = testPotentialSelf.c
 
 testPotentialPair_SOURCES = testPotentialPair.c
 
+testEOS_SOURCES = testEOS.c
+
 # Files necessary for distribution
 EXTRA_DIST = testReading.sh makeInput.py testActivePair.sh \
 	     test27cells.sh test27cellsPerturbed.sh testParser.sh testPeriodicBC.sh \
diff --git a/tests/testEOS.c b/tests/testEOS.c
new file mode 100644
index 0000000000000000000000000000000000000000..aa04a819825ecf3ec257731ab6fec7b60f2629d4
--- /dev/null
+++ b/tests/testEOS.c
@@ -0,0 +1,241 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2012 Pedro Gonnet (pedro.gonnet@durham.ac.uk),
+ *                    Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *               2018 Jacob Kegerreis (jacob.kegerreis@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/>.
+ *
+ ******************************************************************************/
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <fenv.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Conditional headers. */
+#ifdef HAVE_LIBZ
+#include <zlib.h>
+#endif
+
+/* Local headers. */
+#include "swift.h"
+#include "equation_of_state.h"
+
+/* Engine policy flags. */
+#ifndef ENGINE_POLICY
+#define ENGINE_POLICY engine_policy_none
+#endif
+
+/**
+ * @brief Write a list of densities, energies, and resulting pressures to file
+ *  from an equation of state.
+ *
+ * So far only has the Hubbard & MacFarlane (1980) equations of state.
+ *
+ * Sys args:
+ *      mat_id | int | Material ID, see equation_of_state.h for the options.
+ *          Default: 21 (= HM80_ice).
+ *
+ *      do_output | int | Set 1 to write the output file of rho, u, P values,
+ *          set 0 for no output. Default: 0.
+ *
+ * Output text file contains:
+ *  header
+ *  num_rho  num_u  mat_id                      # Header info
+ *  rho_0   rho_1   rho_2   ...   rho_num_rho   # Array of densities, rho
+ *  u_0     u_1     u_2     ...   u_num_u       # Array of energies, u
+ *  P_0_0   P_0_1   ...     P_0_num_u           # Array of pressures, P(rho, u)
+ *  P_1_0   ...     ...     P_1_num_u
+ *  ...     ...     ...     ...
+ *  P_num_rho_0     ...     P_num_rho_num_u
+ *
+ */
+
+int main(int argc, char *argv[]) {
+    int mat_id, do_output;
+    struct HM80_params mat;
+    float rho, log_rho, log_u, P;
+    int num_rho, num_u;
+    struct unit_system us;
+    const struct phys_const *phys_const = 0; // Unused placeholder
+    const struct swift_params *params = 0; // Unused placeholder
+
+    // Check the number of system arguments and set defaults if not provided
+    switch(argc) {
+        case 1:
+            // Default both
+            mat_id = HM80_ice;
+            do_output = 0;
+            break;
+
+        case 2:
+            // Read mat_id, default do_output
+            mat_id = atoi(argv[1]);
+            do_output = 0;
+            break;
+
+        case 3:
+            // Read both
+            mat_id = atoi(argv[1]);
+            do_output = atoi(argv[2]);
+            break;
+
+        default:
+            error("Invalid number of system arguments!\n");
+            mat_id = HM80_ice; // Ignored, just here to keep the compiler happy
+            do_output = 0;
+    };
+
+    /* Greeting message */
+    printf("This is %s\n", package_description());
+
+    // Select the material parameters
+    switch(mat_id) {
+        case HM80_HHe:
+            printf("HM80_HHe \n");
+            set_HM80_HHe(&mat);
+            load_HM80_table(&mat, HM80_HHe_table_file);
+            break;
+
+        case HM80_ice:
+            printf("HM80_ice \n");
+            set_HM80_ice(&mat);
+            load_HM80_table(&mat, HM80_ice_table_file);
+            break;
+
+        case HM80_rock:
+            printf("HM80_rock \n");
+            set_HM80_rock(&mat);
+            load_HM80_table(&mat, HM80_rock_table_file);
+            break;
+
+        default:
+            error("Unknown material ID! mat_id = %d", mat_id);
+            set_HM80_rock(&mat); // Ignored, just here to keep the compiler happy
+            load_HM80_table(&mat, HM80_rock_table_file);
+    };
+
+    // Convert to internal units
+    units_init(&us, 5.9724e27, 6.3710e8, 1, 1, 1);
+    convert_units_HM80(&mat, &us);
+
+    eos_init(&eos, phys_const, &us, params);
+
+    // Output file
+    FILE *f = fopen("testEOS_rho_u_P.txt", "w");
+    if (f == NULL) {
+        printf("Could not open output file!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    num_rho = (mat.log_rho_max - mat.log_rho_min) / mat.log_rho_step;
+    num_u = (mat.log_u_max - mat.log_u_min) / mat.log_u_step;
+    if (do_output == 1) {
+        fprintf(f, "Density  Sp.Int.Energy  mat_id \n");
+        fprintf(f, "%d      %d            %d \n",
+                num_rho, num_u, mat_id);
+    }
+
+    // Arrays of densities and energies
+    float A1_rho[num_rho], A1_u[num_u];
+
+    log_rho = mat.log_rho_min;
+    for (int i = 0; i < num_rho; i++) {
+        A1_rho[i] = exp(log_rho);
+        log_rho += mat.log_rho_step;
+
+        if (do_output == 1)
+            fprintf(f, "%.6e ", A1_rho[i] *
+                    units_cgs_conversion_factor(&us, UNIT_CONV_DENSITY));
+    }
+
+    if (do_output == 1) fprintf(f, "\n");
+    log_u = mat.log_u_min;
+    for (int i = 0; i < num_u; i++) {
+        A1_u[i] = exp(log_u);
+        log_u += mat.log_u_step;
+
+        if (do_output == 1)
+            fprintf(f, "%.6e ", A1_u[i] *
+                    units_cgs_conversion_factor(&us, UNIT_CONV_ENERGY_PER_UNIT_MASS));
+    }
+
+    // Pressures P(rho, u)
+    if (do_output == 1) fprintf(f, "\n");
+    for (int i = 0; i < num_rho; i++) {
+        rho = A1_rho[i];
+
+        for (int j = 0; j < num_u; j++) {
+            P = gas_pressure_from_internal_energy(rho, A1_u[j], mat.mat_id);
+
+            if (do_output == 1)
+                fprintf(f, "%.6e ", P *
+                        units_cgs_conversion_factor(&us, UNIT_CONV_PRESSURE));
+        }
+
+        if (do_output == 1) fprintf(f, "\n");
+    }
+    fclose(f);
+
+    return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/testEOS.sh b/tests/testEOS.sh
new file mode 100644
index 0000000000000000000000000000000000000000..4ffb43eecf03770f87220f4ee5a0d18e52ea5cf3
--- /dev/null
+++ b/tests/testEOS.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+echo ""
+
+rm -f testEOS_rho_u_P.txt
+
+echo "Running  ./testEOS  21  1  0"
+
+./testEOS  21  1  0
+
+exit $?
diff --git a/tests/testEOS_plot.py b/tests/testEOS_plot.py
new file mode 100644
index 0000000000000000000000000000000000000000..83b512683ed9c2a0027c8792a0ef3c4c269b365e
--- /dev/null
+++ b/tests/testEOS_plot.py
@@ -0,0 +1,153 @@
+###############################################################################
+ # This file is part of SWIFT.
+ # Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ #               2018 Jacob Kegerreis (jacob.kegerreis@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/>.
+ #
+ ##############################################################################
+"""
+Plot the output of testEOS to show how the equation of state pressure varies
+with density and specific internal energy.
+
+Text file contains:
+    header
+    num_rho  num_u  mat_id                      # Header info
+    rho_0   rho_1   rho_2   ...   rho_num_rho   # Array of densities, rho
+    u_0     u_1     u_2     ...   u_num_u       # Array of energies, u
+    P_0_0   P_0_1   ...     P_0_num_u           # Array of pressures, P(rho, u)
+    P_1_0   ...     ...     P_1_num_u
+    ...     ...     ...     ...
+    P_num_rho_0     ...     P_num_rho_num_u
+"""
+
+# ========
+# Modules and constants
+# ========
+from __future__ import division
+import numpy as np
+import matplotlib
+import matplotlib.pyplot as plt
+
+filename = "testEOS_rho_u_P.txt"
+
+# Material types (copied from src/equation_of_state/planetary/equation_of_state.h)
+type_factor = 10
+Di_type = {
+    'Till'   : 1,
+    'HM80'   : 2,
+    'ANEOS'  : 3,
+    'SESAME' : 4,
+}
+Di_material = {
+    # Tillotson
+    'Til_iron'      : Di_type['Till']*type_factor,
+    'Til_granite'   : Di_type['Till']*type_factor + 1,
+    'Til_water'     : Di_type['Till']*type_factor + 2,
+    # Hubbard & MacFarlane (1980) Uranus/Neptune
+    'HM80_HHe'      : Di_type['HM80']*type_factor,      # Hydrogen-helium atmosphere
+    'HM80_ice'      : Di_type['HM80']*type_factor + 1,  # H20-CH4-NH3 ice mix
+    'HM80_rock'     : Di_type['HM80']*type_factor + 2,  # SiO2-MgO-FeS-FeO rock mix
+    # ANEOS
+    'ANEOS_iron'        : Di_type['ANEOS']*type_factor,
+    'MANEOS_forsterite' : Di_type['ANEOS']*type_factor + 1,
+    # SESAME
+    'SESAME_iron'   : Di_type['SESAME']*type_factor,
+}
+# Invert so the mat_id are the keys
+Di_mat_id = {mat_id : mat for mat, mat_id in Di_material.iteritems()}
+
+Ba_to_Mbar = 1e-12
+erg_g_to_J_kg = 1e-4
+
+# ========
+# Main
+# ========
+# Load the header info and density and energy arrays
+with open(filename) as f:
+    f.readline()
+    num_rho, num_u, mat_id = np.array(f.readline().split(), dtype=int)
+    A1_rho = np.array(f.readline().split(), dtype=float)
+    A1_u = np.array(f.readline().split(), dtype=float)
+
+# Load pressure array
+A2_P = np.loadtxt(filename, skiprows=4)
+
+# Convert pressures from cgs Barye to Mbar
+A2_P *= Ba_to_Mbar
+# Convert energies from cgs to SI
+A1_u *= erg_g_to_J_kg
+
+# Check that the numbers are right
+assert A1_rho.shape == (num_rho,)
+assert A1_u.shape == (num_u,)
+assert A2_P.shape == (num_rho, num_u)
+try:
+    mat = Di_mat_id[mat_id]
+except KeyError:
+    print "Error: unknown material ID! mat_id = %d" % mat_id
+    print "Materials:"
+    for mat_id, mat in sorted(Di_mat_id.iteritems()):
+        print "  %s%s%d" % (mat, (20 - len("%s" % mat))*" ", mat_id)
+
+# Plot
+plt.figure(figsize=(7, 7))
+ax = plt.gca()
+
+# P(rho) at fixed u
+num_u_fix = 9
+A1_idx = np.floor(np.linspace(0, num_u - 1, num_u_fix)).astype(int)
+A1_colour = matplotlib.cm.rainbow(np.linspace(0, 1, num_u_fix))
+
+for i, idx in enumerate(A1_idx):
+    plt.plot(A1_rho, A2_P[:, idx], c=A1_colour[i],
+             label=r"%.2e" % A1_u[idx])
+
+plt.legend(title="Sp. Int. Energy (J kg$^{-1}$)")
+plt.xscale('log')
+plt.yscale('log')
+plt.xlabel(r"Density (g cm$^{-3}$)")
+plt.ylabel(r"Pressure (Mbar)")
+plt.title(mat)
+plt.tight_layout()
+
+plt.show()
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+