From c808c1bf13b986be2d5a11a866dafe580b84d98a Mon Sep 17 00:00:00 2001
From: Folkert Nobels <nobels@strw.leidenuniv.nl>
Date: Tue, 6 Nov 2018 09:08:31 +0000
Subject: [PATCH] Add Hernquist and NFW potential to SWIFT

---
 configure.ac                                  |   8 +-
 doc/RTD/source/ExternalPotentials/index.rst   |  80 ++++++
 doc/RTD/source/index.rst                      |   1 +
 .../Hernquist_circularorbit/hernquistcirc.yml |  38 +++
 examples/Hernquist_circularorbit/makeIC.py    |  81 ++++++
 examples/Hernquist_circularorbit/plotprog.py  |  77 ++++++
 examples/Hernquist_circularorbit/run.sh       |  23 ++
 examples/Hernquist_radialinfall/README        |   3 +
 examples/Hernquist_radialinfall/hernquist.yml |  39 +++
 examples/Hernquist_radialinfall/makeIC.py     | 167 +++++++++++
 examples/Hernquist_radialinfall/plotprog.py   |  50 ++++
 examples/Hernquist_radialinfall/run.sh        |  24 ++
 examples/IsothermalPotential/energy_plot.py   |   2 +-
 examples/IsothermalPotential/isothermal.yml   |   2 +
 examples/NFW_Halo/README                      |   5 +
 examples/NFW_Halo/makeIC.py                   |  75 +++++
 examples/NFW_Halo/makePlots.py                |  73 +++++
 examples/NFW_Halo/run.sh                      |  20 ++
 examples/NFW_Halo/test.yml                    |  41 +++
 examples/parameter_example.yml                |  20 ++
 src/potential.h                               |   4 +
 src/potential/hernquist/potential.h           | 223 +++++++++++++++
 src/potential/isothermal/potential.h          |  16 +-
 src/potential/nfw/potential.h                 | 260 ++++++++++++++++++
 src/potential/point_mass/potential.h          |  15 +-
 src/potential/point_mass_softened/potential.h |  13 +
 26 files changed, 1353 insertions(+), 7 deletions(-)
 create mode 100644 doc/RTD/source/ExternalPotentials/index.rst
 create mode 100755 examples/Hernquist_circularorbit/hernquistcirc.yml
 create mode 100755 examples/Hernquist_circularorbit/makeIC.py
 create mode 100755 examples/Hernquist_circularorbit/plotprog.py
 create mode 100755 examples/Hernquist_circularorbit/run.sh
 create mode 100644 examples/Hernquist_radialinfall/README
 create mode 100644 examples/Hernquist_radialinfall/hernquist.yml
 create mode 100644 examples/Hernquist_radialinfall/makeIC.py
 create mode 100755 examples/Hernquist_radialinfall/plotprog.py
 create mode 100755 examples/Hernquist_radialinfall/run.sh
 create mode 100755 examples/NFW_Halo/README
 create mode 100755 examples/NFW_Halo/makeIC.py
 create mode 100755 examples/NFW_Halo/makePlots.py
 create mode 100755 examples/NFW_Halo/run.sh
 create mode 100755 examples/NFW_Halo/test.yml
 create mode 100644 src/potential/hernquist/potential.h
 create mode 100644 src/potential/nfw/potential.h

diff --git a/configure.ac b/configure.ac
index 345f732742..ab2ed97305 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1518,7 +1518,7 @@ esac
 #  External potential
 AC_ARG_WITH([ext-potential],
    [AS_HELP_STRING([--with-ext-potential=<pot>],
-      [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, softened-isothermal, disc-patch, sine-wave, default: none@:>@]
+      [external potential @<:@none, point-mass, point-mass-ring, point-mass-softened, isothermal, softened-isothermal, nfw, hernquist, disc-patch, sine-wave, default: none@:>@]
    )],
    [with_potential="$withval"],
    [with_potential="none"]
@@ -1533,6 +1533,12 @@ case "$with_potential" in
    isothermal)
       AC_DEFINE([EXTERNAL_POTENTIAL_ISOTHERMAL], [1], [Isothermal external potential])
    ;;
+   hernquist)
+      AC_DEFINE([EXTERNAL_POTENTIAL_HERNQUIST], [1], [Hernquist external potential])
+   ;;
+   nfw)
+      AC_DEFINE([EXTERNAL_POTENTIAL_NFW], [1], [Navarro-Frenk-White external potential])
+   ;;
    disc-patch)
       AC_DEFINE([EXTERNAL_POTENTIAL_DISC_PATCH], [1], [Disc-patch external potential])
    ;;
diff --git a/doc/RTD/source/ExternalPotentials/index.rst b/doc/RTD/source/ExternalPotentials/index.rst
new file mode 100644
index 0000000000..e313816246
--- /dev/null
+++ b/doc/RTD/source/ExternalPotentials/index.rst
@@ -0,0 +1,80 @@
+.. External potentials in SWIFT
+   Folkert Nobels, 25th October 2018
+   
+External Potentials 
+===================
+
+SWIFT can be run with an external potential on this page we will summarize the
+current potentials which can be run with SWIFT and how to implement your own 
+potential in SWIFT.
+
+Implemented External Potentials
+-------------------------------
+
+Currently there are several potentials implemented in SWIFT. On this page we 
+give a short overview of the potentials that are implemented in the code:
+
+1. No potential (none)
+2. Point mass potential (point-mass): classical point mass, can be placed at
+   a position with a mass.
+3. Plummer potential (point-mass-softened): in the code a softended point mass 
+   corresponds to a Plummer potential, can be placed at a position with a mass.
+4. Isothermal potential (isothermal): An isothermal potential which corresponds 
+   to a density profile which is :math:`\propto r^{-2}` and a potential which is 
+   logarithmic. This potential has as free parameters the rotation velocity 
+   and the position.
+5. Hernquist potential (hernquist): A potential that is given by the Hernquist 
+   potential: 
+   
+   :math:`\Phi(r) = - \frac{GM}{r+a}.`
+
+   The free paramters of Hernquist potential are mass, scale length,
+   and softening. The potential can be set at any position in the box.
+6. NFW potential (nfw): The most used potential to describe dark matter halos, the  
+   potential is given by:
+
+   :math:`\Phi(r) = - \frac{4\pi G \rho_0 R_s^3}{r} \ln \left( 1+ 
+   \frac{r}{R_s} \right).`
+
+   This potential has as free paramters the concentration of the DM halo, the
+   virial mass (:math:`M_{200}`) and the critical density.
+7. Sine wave (sine-wave)
+8. Point mass ring (point-mass-ring)
+9. Disc Patch (disc-patch)
+
+
+How to implement your own potential
+-----------------------------------
+
+The first step in implementing your own potential is making a directory of your
+potential in the ``src/potential`` folder and creating a file in the folder 
+called ``potential.h``.
+
+Configuring the potential 
+^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To get started you can copy a ``potential.h`` file from an already implemented 
+potential. In this potential the header guards (e.g. ``#IFDEF <>``) need to be 
+changed to the specific potential and the ``struct`` and 
+``potential_init_backend`` need to be  changed such that it uses your potential 
+and reads the correct potential from the parameter file during running the 
+program.
+
+Add the potential to the ``potential.h`` file in the ``src`` directory such that
+the program knows that it is possible to run with this potential.
+
+Furthermore during the configuration of the code it also needs to be clear for 
+the program that the code can be configured to run with the different 
+potentials. This means that the ``configure.ac`` file needs to be changed.
+This can be done to add an other case in the potential::
+
+  case "$with_potential" in
+     none)
+        AC_DEFINE([EXTERNAL_POTENTIAL_NONE], [1], [No external potential])
+     ;;
+     newpotential)
+        AC_DEFINE([EXTERNAL_POTENTIAL_NEWPOTENTIAL], [1], [New external potential])
+     ;;
+
+After this change it is possible to configure the code to use your new potential.
+
diff --git a/doc/RTD/source/index.rst b/doc/RTD/source/index.rst
index 343713f5f8..d148398c1b 100644
--- a/doc/RTD/source/index.rst
+++ b/doc/RTD/source/index.rst
@@ -21,6 +21,7 @@ difference is the parameter file that will need to be adapted for SWIFT.
    HydroSchemes/index
    Cooling/index
    EquationOfState/index
+   ExternalPotentials/index
    NewOption/index
    Task/index
    VELOCIraptorInterface/index
diff --git a/examples/Hernquist_circularorbit/hernquistcirc.yml b/examples/Hernquist_circularorbit/hernquistcirc.yml
new file mode 100755
index 0000000000..5e81d18000
--- /dev/null
+++ b/examples/Hernquist_circularorbit/hernquistcirc.yml
@@ -0,0 +1,38 @@
+# Define the system of units to use internally.
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.988e+33    # Grams
+  UnitLength_in_cgs:   3.086e+21 # Centimeters
+  UnitVelocity_in_cgs: 1e5       # Centimeters per second
+  UnitCurrent_in_cgs:  1         # Amperes
+  UnitTemp_in_cgs:     1         # Kelvin
+
+# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.)
+TimeIntegration:
+  time_begin:          0.      # The starting time of the simulation (in internal units).
+  time_end:            2.0     # 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:              1e0    # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            output  # Common part of the name of output files
+  time_first:          0.      # Time of the first output (in internal units)
+  delta_time:          1e-3    # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e0    # Time between statistics output
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:          circularorbitshernquist.hdf5 # The file to read
+  periodic:           0
+
+# Hernquist potential parameters
+HernquistPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
+  position:        [0.,0.,0.]    # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units)
+  mass:            2e12     # Mass of the Hernquist potential
+  scalelength:     10.0     # Scale length of the potential
+  timestep_mult:   0.005     # Dimensionless pre-factor for the time-step condition
+  epsilon:         0.1      # Softening size (internal units)
diff --git a/examples/Hernquist_circularorbit/makeIC.py b/examples/Hernquist_circularorbit/makeIC.py
new file mode 100755
index 0000000000..474450f0e2
--- /dev/null
+++ b/examples/Hernquist_circularorbit/makeIC.py
@@ -0,0 +1,81 @@
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@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/>.
+#
+################################################################################
+from galpy.potential import NFWPotential
+from galpy.orbit import Orbit
+from galpy.util import bovy_conversion
+import numpy as np
+import matplotlib.pyplot as plt
+from astropy import units
+import h5py as h5
+
+C = 8.0
+M_200 = 2.0
+N_PARTICLES = 3
+print("Initial conditions written to 'test_nfw.hdf5'")
+
+pos = np.zeros((3, 3))
+pos[0, 2] = 50.0
+pos[1, 2] = 10.0
+pos[2, 2] = 2.0
+pos = pos + 500.0
+vel = np.zeros((3, 3))
+vel[0, 1] = 348.0
+vel[1, 1] = 466.9
+vel[2, 1] = 348.0
+ids = np.array([1.0, 2.0, 3.0])
+mass = np.array([1.0, 1.0, 1.0])
+
+# File
+file = h5.File("circularorbitshernquist.hdf5", "w")
+
+# Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 3.086e21
+grp.attrs["Unit mass in cgs (U_M)"] = 1.988e33
+grp.attrs["Unit time in cgs (U_t)"] = 3.086e16
+grp.attrs["Unit current in cgs (U_I)"] = 1.0
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.0
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = 1000.0
+grp.attrs["NumPart_Total"] = [0, N_PARTICLES, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [0, N_PARTICLES, 0, 0, 0, 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, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+# Particle group
+grp1 = file.create_group("/PartType1")
+ds = grp1.create_dataset("Velocities", (N_PARTICLES, 3), "f", data=vel)
+
+ds = grp1.create_dataset("Masses", (N_PARTICLES,), "f", data=mass)
+
+ds = grp1.create_dataset("ParticleIDs", (N_PARTICLES,), "L", data=ids)
+
+ds = grp1.create_dataset("Coordinates", (N_PARTICLES, 3), "d", data=pos)
+
+file.close()
diff --git a/examples/Hernquist_circularorbit/plotprog.py b/examples/Hernquist_circularorbit/plotprog.py
new file mode 100755
index 0000000000..849ae4e365
--- /dev/null
+++ b/examples/Hernquist_circularorbit/plotprog.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+import numpy as np
+import h5py
+import matplotlib.pyplot as plt
+from scipy.integrate import odeint
+
+t = np.linspace(0, 40, 1e5)
+y0 = [0, 10]
+a = 30.0
+G = 4.300927e-06
+M = 1e15
+GM = G * M
+
+
+lengthrun = 2001
+numbpar = 3
+
+radius = np.zeros((numbpar, lengthrun))
+xx = np.zeros((numbpar, lengthrun))
+yy = np.zeros((numbpar, lengthrun))
+zz = np.zeros((numbpar, lengthrun))
+time = np.zeros(lengthrun)
+for i in range(0, lengthrun):
+    Data = h5py.File("output_%04d.hdf5" % i, "r")
+    header = Data["Header"]
+    time[i] = header.attrs["Time"]
+    particles = Data["PartType1"]
+    positions = particles["Coordinates"]
+    xx[:, i] = positions[:, 0] - 500.0
+    yy[:, i] = positions[:, 1] - 500.0
+    zz[:, i] = positions[:, 2] - 500.0
+
+col = ["b", "r", "c", "y", "k"]
+print(np.shape(xx), np.shape(yy), np.shape(zz))
+
+for i in range(0, numbpar):
+    plt.plot(xx[i, :], yy[i, :], col[i])
+
+plt.ylabel("y (kpc)")
+plt.xlabel("x (kpc)")
+plt.savefig("xyplot.png")
+plt.close()
+
+
+for i in range(0, numbpar):
+    plt.plot(xx[i, :], zz[i, :], col[i])
+
+plt.ylabel("z (kpc)")
+plt.xlabel("x (kpc)")
+plt.savefig("xzplot.png")
+plt.close()
+
+for i in range(0, numbpar):
+    plt.plot(yy[i, :], zz[i, :], col[i])
+
+plt.ylabel("z (kpc)")
+plt.xlabel("y (kpc)")
+plt.savefig("yzplot.png")
+plt.close()
diff --git a/examples/Hernquist_circularorbit/run.sh b/examples/Hernquist_circularorbit/run.sh
new file mode 100755
index 0000000000..a2fedd0914
--- /dev/null
+++ b/examples/Hernquist_circularorbit/run.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+if [ ! -e circularorbitshernquist.hdf5 ]
+then 
+    echo "Generate initial conditions for circular orbits"
+    if command -v python3 &>/dev/null; then
+        python3 makeIC.py
+    else 
+        python makeIC.py
+    fi
+
+fi
+
+# self gravity G, external potential g, hydro s, threads t and high verbosity v
+../swift -g -t 6 hernquistcirc.yml 2>&1 | tee output.log
+
+
+echo "Save plots of the circular orbits"
+if command -v python3 &>/dev/null; then
+    python3 plotprog.py
+else 
+    python plotprog.py
+fi
diff --git a/examples/Hernquist_radialinfall/README b/examples/Hernquist_radialinfall/README
new file mode 100644
index 0000000000..be22a1a11a
--- /dev/null
+++ b/examples/Hernquist_radialinfall/README
@@ -0,0 +1,3 @@
+This example generates 5 particles at radii of 10, 20, 30, 40 and 50 kpc
+without velocitiy and follows the evolution of these particles in an Hernquist
+potential as they are free falling.
diff --git a/examples/Hernquist_radialinfall/hernquist.yml b/examples/Hernquist_radialinfall/hernquist.yml
new file mode 100644
index 0000000000..adea54ed9a
--- /dev/null
+++ b/examples/Hernquist_radialinfall/hernquist.yml
@@ -0,0 +1,39 @@
+# Define the system of units to use internally. 
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.98848e33    # M_sun
+  UnitLength_in_cgs:   3.08567758e21 # kpc
+  UnitVelocity_in_cgs: 1e5           # km/s
+  UnitCurrent_in_cgs:  1   # Amperes
+  UnitTemp_in_cgs:     1   # Kelvin
+
+# Parameters governing the time integration
+TimeIntegration:
+  time_begin: 0.    # The starting time of the simulation (in internal units).
+  time_end:   40.    # The end time of the simulation (in internal units).
+  dt_min:     9e-10  # The minimal time-step size of the simulation (in internal units).
+  dt_max:     1e-2  # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3 # Time between statistics output
+  
+# Parameters governing the snapshots
+Snapshots:
+  basename:            hernquist # Common part of the name of output files
+  time_first:          0.         # Time of the first output (in internal units)
+  delta_time:          0.02       # Time difference between consecutive outputs (in internal units)
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:  Hernquist.hdf5       # The file to read
+  periodic:   1
+  shift:      [200.,200.,200.]      # Shift all particles to be in the potential
+ 
+# External potential parameters
+HernquistPotential:
+  useabspos:       0          # Whether to use absolute position (1) or relative potential to centre of box (0)
+  position:        [0.,0.,0.]
+  mass:            1e9
+  scalelength:     1.0
+  timestep_mult:   0.01      # controls time step
+  epsilon:         2.0         # No softening at the centre of the halo
diff --git a/examples/Hernquist_radialinfall/makeIC.py b/examples/Hernquist_radialinfall/makeIC.py
new file mode 100644
index 0000000000..567e15a953
--- /dev/null
+++ b/examples/Hernquist_radialinfall/makeIC.py
@@ -0,0 +1,167 @@
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import h5py
+import sys
+import numpy
+import math
+import random
+import numpy as np
+
+# Generates N particles in a spherical distribution centred on [0,0,0], to be moved in an isothermal potential
+# usage: python makeIC.py 1000 0 : generate 1000 particles on circular orbits
+#        python makeIC.py 1000 1 : generate 1000 particles with Lz/L uniform in [0,1]
+# all particles move in the xy plane, and start at y=0
+
+# physical constants in cgs
+NEWTON_GRAVITY_CGS = 6.67408e-8
+SOLAR_MASS_IN_CGS = 1.98848e33
+PARSEC_IN_CGS = 3.08567758e18
+YEAR_IN_CGS = 3.15569252e7
+
+# choice of units
+const_unit_length_in_cgs = 1000 * PARSEC_IN_CGS
+const_unit_mass_in_cgs = SOLAR_MASS_IN_CGS
+const_unit_velocity_in_cgs = 1e5
+
+
+# Properties of the Hernquist potential
+Mass = 1e15
+scaleLength = 30.0  # kpc
+
+
+# derived units
+const_unit_time_in_cgs = const_unit_length_in_cgs / const_unit_velocity_in_cgs
+const_G = (
+    NEWTON_GRAVITY_CGS
+    * const_unit_mass_in_cgs
+    * const_unit_time_in_cgs
+    * const_unit_time_in_cgs
+    / (const_unit_length_in_cgs * const_unit_length_in_cgs * const_unit_length_in_cgs)
+)
+print("G=", const_G)
+
+
+def hernquistcircvel(r, M, a):
+    """ Function that calculates the circular velocity in a 
+    Hernquist potential.
+    @param r: radius from centre of potential
+    @param M: mass of the Hernquist potential
+    @param a: Scale length of the potential
+    @return: circular velocity
+    """
+    return (const_G * M * r) ** 0.5 / (r + a)
+
+
+# Parameters
+periodic = 1  # 1 For periodic box
+boxSize = 400.0  #  [kpc]
+Radius = 100.0  # maximum radius of particles [kpc]
+G = const_G
+
+N = 5
+L = N ** (1.0 / 3.0)
+
+fileName = "Hernquist.hdf5"
+
+
+# ---------------------------------------------------
+numPart = N
+mass = 1
+
+# --------------------------------------------------
+
+# File
+file = h5py.File(fileName, "w")
+
+# Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = const_unit_length_in_cgs
+grp.attrs["Unit mass in cgs (U_M)"] = const_unit_mass_in_cgs
+grp.attrs["Unit time in cgs (U_t)"] = (
+    const_unit_length_in_cgs / const_unit_velocity_in_cgs
+)
+grp.attrs["Unit current in cgs (U_I)"] = 1.0
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.0
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = boxSize
+grp.attrs["NumPart_Total"] = [0, numPart, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [0, numPart, 0, 0, 0, 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, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# set seed for random number
+numpy.random.seed(1234)
+
+# Particle group
+grp1 = file.create_group("/PartType1")
+# generate particle positions
+# radius = Radius * (numpy.random.rand(N))**(1./3.) + 10.
+radius = np.zeros(N)
+radius[0] = 10
+radius[1] = 20
+radius[2] = 30
+radius[3] = 40
+radius[4] = 50
+# this part is not even used:
+# ctheta = -1. + 2 * numpy.random.rand(N)
+# stheta = numpy.sqrt(1.-ctheta**2)
+# phi    =  2 * math.pi * numpy.random.rand(N)
+# end
+r = numpy.zeros((numPart, 3))
+r[:, 0] = radius
+
+# import matplotlib.pyplot as plt
+# plt.plot(r[:,0],'.')
+# plt.show()
+
+# print('Mass = ', Mass)
+# print('radius = ', radius)
+# print('scaleLength = ',scaleLength)
+#
+v = numpy.zeros((numPart, 3))
+# v[:,0] = hernquistcircvel(radius,Mass,scaleLength)
+omega = v[:, 0] / radius
+period = 2.0 * math.pi / omega
+print("period = minimum = ", min(period), " maximum = ", max(period))
+print("Circular velocity = minimum =", min(v[:, 0]), " maximum = ", max(v[:, 0]))
+
+omegav = omega
+
+v[:, 0] = -omegav * r[:, 1]
+v[:, 1] = omegav * r[:, 0]
+
+ds = grp1.create_dataset("Velocities", (numPart, 3), "f", data=v)
+
+m = numpy.full((numPart,), mass, dtype="f")
+ds = grp1.create_dataset("Masses", (numPart,), "f", data=m)
+
+ids = 1 + numpy.linspace(0, numPart, numPart, endpoint=False)
+ds = grp1.create_dataset("ParticleIDs", (numPart,), "L", data=ids)
+
+ds = grp1.create_dataset("Coordinates", (numPart, 3), "d", data=r)
+
+
+file.close()
diff --git a/examples/Hernquist_radialinfall/plotprog.py b/examples/Hernquist_radialinfall/plotprog.py
new file mode 100755
index 0000000000..d8de00a6b6
--- /dev/null
+++ b/examples/Hernquist_radialinfall/plotprog.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+###############################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+import numpy as np
+import h5py
+import matplotlib.pyplot as plt
+from scipy.integrate import odeint
+
+
+lengthrun = 2001
+numbpar = 5
+
+radius = np.zeros((numbpar, lengthrun))
+time = np.zeros(lengthrun)
+for i in range(0, lengthrun):
+    Data = h5py.File("hernquist_%04d.hdf5" % i, "r")
+    header = Data["Header"]
+    time[i] = header.attrs["Time"]
+    particles = Data["PartType1"]
+    positions = particles["Coordinates"]
+    radius[:, i] = positions[:, 0] - 200.0
+
+col = ["b", "r", "c", "y", "k"]
+
+for i in range(0, numbpar):
+    plt.plot(time, radius[i, :], col[i])
+    plt.axhline(np.max(radius[i, :]), color=col[i], linestyle="--")
+    plt.axhline(-np.max(radius[i, :]), color=col[i], linestyle="--")
+
+
+plt.ylabel("Radial distance (kpc)")
+plt.xlabel("Simulation time (internal units)")
+plt.savefig("radial_infall.png")
+plt.close()
diff --git a/examples/Hernquist_radialinfall/run.sh b/examples/Hernquist_radialinfall/run.sh
new file mode 100755
index 0000000000..b2bcd99f9e
--- /dev/null
+++ b/examples/Hernquist_radialinfall/run.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+# Generate the initial conditions if they are not present.
+if [ ! -e Hernquist.hdf5 ]
+then
+    echo "Generate initial conditions for radial orbits"
+    if command -v python3 &>/dev/null; then
+        python3 makeIC.py 
+    else 
+        python makeIC.py
+    fi
+fi
+
+rm -rf hernquist_*.hdf5
+../swift -g -t 1 hernquist.yml 2>&1 | tee output.log
+
+
+
+echo "Make plots of the radially free falling particles" 
+if command -v python3 &>/dev/null; then
+    python3 plotprog.py 
+else 
+    python plotprog.py
+fi
diff --git a/examples/IsothermalPotential/energy_plot.py b/examples/IsothermalPotential/energy_plot.py
index dab30715fb..d157e4233c 100644
--- a/examples/IsothermalPotential/energy_plot.py
+++ b/examples/IsothermalPotential/energy_plot.py
@@ -86,7 +86,7 @@ for i in range(402):
 
     time_snap[i] = f["Header"].attrs["Time"]
     E_kin_snap[i] = np.sum(0.5 * mass * (vel_x[:]**2 + vel_y[:]**2 + vel_z[:]**2))
-    E_pot_snap[i] = np.sum(-mass * Vrot**2 *  log(r))
+    E_pot_snap[i] = np.sum(mass * Vrot**2 *  log(r))
     E_tot_snap[i] = E_kin_snap[i] + E_pot_snap[i]
     Lz_snap[i] = np.sum(Lz)
 
diff --git a/examples/IsothermalPotential/isothermal.yml b/examples/IsothermalPotential/isothermal.yml
index f9cae4d3b4..4f8d98a1f7 100644
--- a/examples/IsothermalPotential/isothermal.yml
+++ b/examples/IsothermalPotential/isothermal.yml
@@ -31,6 +31,8 @@ InitialConditions:
  
 # External potential parameters
 IsothermalPotential:
+  useabspos:       0          # Whether to use absolute position (1) or relative potential to centre of box (0)
+  position:        [0.,0.,0.]
   vrot:            200.       # rotation speed of isothermal potential in internal units
   timestep_mult:   0.01       # controls time step
   epsilon:         0.         # No softening at the centre of the halo
diff --git a/examples/NFW_Halo/README b/examples/NFW_Halo/README
new file mode 100755
index 0000000000..059d35c9a9
--- /dev/null
+++ b/examples/NFW_Halo/README
@@ -0,0 +1,5 @@
+This just provides a test that the NFW potential is giving the correct orbit 
+for an elliptical orbit as calculated by Jo Bovy's galpy package. If 
+galpy is not installed on your system you can install it by using:
+pp install galpy --user
+
diff --git a/examples/NFW_Halo/makeIC.py b/examples/NFW_Halo/makeIC.py
new file mode 100755
index 0000000000..68d8108f84
--- /dev/null
+++ b/examples/NFW_Halo/makeIC.py
@@ -0,0 +1,75 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Ashley Kelly ()
+#                    Folkert Nobels (nobels@strw.leidenuniv.nl)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+################################################################################
+
+import numpy as np
+import matplotlib.pyplot as plt
+from astropy import units
+import h5py as h5
+
+C = 8.0
+M_200 = 2.0
+N_PARTICLES = 1
+
+
+print("\nInitial conditions written to 'test_nfw.hdf5'")
+
+pos = np.array([8.0, 0.0, 0.0]) + 500.0
+vel = np.array([0.0, 240.0, 5.0])
+ids = np.array([1.0])
+mass = np.array([1.0])
+
+# File
+file = h5.File("test_nfw.hdf5", "w")
+
+# Units
+grp = file.create_group("/Units")
+grp.attrs["Unit length in cgs (U_L)"] = 3.086e21
+grp.attrs["Unit mass in cgs (U_M)"] = 1.988e33
+grp.attrs["Unit time in cgs (U_t)"] = 3.086e16
+grp.attrs["Unit current in cgs (U_I)"] = 1.0
+grp.attrs["Unit temperature in cgs (U_T)"] = 1.0
+
+# Header
+grp = file.create_group("/Header")
+grp.attrs["BoxSize"] = 1000.0
+grp.attrs["NumPart_Total"] = [0, N_PARTICLES, 0, 0, 0, 0]
+grp.attrs["NumPart_Total_HighWord"] = [0, 0, 0, 0, 0, 0]
+grp.attrs["NumPart_ThisFile"] = [0, N_PARTICLES, 0, 0, 0, 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, 0, 0, 0, 0, 0]
+grp.attrs["Dimension"] = 3
+
+# Runtime parameters
+grp = file.create_group("/RuntimePars")
+grp.attrs["PeriodicBoundariesOn"] = 1
+
+# Particle group
+grp1 = file.create_group("/PartType1")
+ds = grp1.create_dataset("Velocities", (N_PARTICLES, 3), "f", data=vel)
+
+ds = grp1.create_dataset("Masses", (N_PARTICLES,), "f", data=mass)
+
+ds = grp1.create_dataset("ParticleIDs", (N_PARTICLES,), "L", data=ids)
+
+ds = grp1.create_dataset("Coordinates", (N_PARTICLES, 3), "d", data=pos)
+
+file.close()
diff --git a/examples/NFW_Halo/makePlots.py b/examples/NFW_Halo/makePlots.py
new file mode 100755
index 0000000000..5e6f24d7a7
--- /dev/null
+++ b/examples/NFW_Halo/makePlots.py
@@ -0,0 +1,73 @@
+################################################################################
+# This file is part of SWIFT.
+# Copyright (c) 2018 Ashley Kelly ()
+#                    Folkert Nobels (nobels@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/>.
+#
+################################################################################
+from galpy.potential import NFWPotential
+from galpy.orbit import Orbit
+import numpy as np
+import matplotlib.pyplot as plt
+from astropy import units
+import h5py as h5
+
+C = 8.0
+M_200 = 2.0
+
+
+def read_data():
+    R = np.array([])
+    z = np.array([])
+    for frame in range(0, 599, 1):
+        try:
+            sim = h5.File("output_%04d.hdf5" % frame, "r")
+        except IOError:
+            break
+
+        boxSize = sim["/Header"].attrs["BoxSize"][0]
+        pos = sim["/PartType1/Coordinates"][:, :] - boxSize / 2.0
+        R = np.append(R, np.sqrt(pos[0, 0] ** 2 + pos[0, 1] ** 2))
+        z = np.append(z, pos[0, 2])
+    return (R, z)
+
+
+def galpy_nfw_orbit():
+    # Setting up the potential
+    nfw = NFWPotential(conc=C, mvir=M_200, H=70.0, wrtcrit=True, overdens=200)
+    nfw.turn_physical_on()
+    vxvv = [
+        8.0 * units.kpc,
+        0.0 * units.km / units.s,
+        240.0 * units.km / units.s,
+        0.0 * units.pc,
+        5.0 * units.km / units.s,
+    ]
+
+    # Calculating the orbit
+    ts = np.linspace(0.0, 0.58, 1000) * units.Gyr
+    o = Orbit(vxvv=vxvv)
+    o.integrate(ts, nfw, method="odeint")
+
+    return o
+
+
+o = galpy_nfw_orbit()
+(R, z) = read_data()
+
+o.plot()
+plt.scatter(R, z, s=1, color="black", marker="x")
+plt.savefig("comparison.png")
+plt.close()
diff --git a/examples/NFW_Halo/run.sh b/examples/NFW_Halo/run.sh
new file mode 100755
index 0000000000..5501f09ef4
--- /dev/null
+++ b/examples/NFW_Halo/run.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+if [ ! -e test_nfw.hdf5 ]
+then
+    echo "Generate initial conditions for NFW example"	
+    if command -v python3 &>/dev/null; then
+        python3 makeIC.py
+    else 
+        python makeIC.py
+    fi
+fi
+
+# self gravity G, external potential g, hydro s, threads t and high verbosity v
+../swift -g -t 6 test.yml 2>&1 | tee output.log
+
+if command -v python3 &>/dev/null; then
+    python3 makePlots.py
+else 
+    python makePlots.py
+fi
diff --git a/examples/NFW_Halo/test.yml b/examples/NFW_Halo/test.yml
new file mode 100755
index 0000000000..73831af307
--- /dev/null
+++ b/examples/NFW_Halo/test.yml
@@ -0,0 +1,41 @@
+# Define the system of units to use internally.
+InternalUnitSystem:
+  UnitMass_in_cgs:     1.988e+33 # Solar mass
+  UnitLength_in_cgs:   3.086e+21 # kpc
+  UnitVelocity_in_cgs: 1e5       # km / s
+  UnitCurrent_in_cgs:  1         # Amperes
+  UnitTemp_in_cgs:     1         # Kelvin
+
+# Parameters governing the time integration (Set dt_min and dt_max to the same value for a fixed time-step run.)
+TimeIntegration:
+  time_begin:          0.      # The starting time of the simulation (in internal units).
+  time_end:            0.6     # The end time of the simulation (in internal units).
+  dt_min:              1e-8    # The minimal time-step size of the simulation (in internal units).
+  dt_max:              1e-1    # The maximal time-step size of the simulation (in internal units).
+
+# Parameters governing the snapshots
+Snapshots:
+  basename:            output  # Common part of the name of output files
+  time_first:          0.      # Time of the first output (in internal units)
+  delta_time:          1e-3    # Time difference between consecutive outputs (in internal units)
+
+# Parameters governing the conserved quantities statistics
+Statistics:
+  delta_time:          1e-3    # Time between statistics output
+
+# Parameters related to the initial conditions
+InitialConditions:
+  file_name:          test_nfw.hdf5 # The file to read
+  shift_x:            0.          # (Optional) A shift to apply to all particles read from the ICs (in internal units).
+  shift_y:            0.
+  shift_z:            0.
+  periodic:           0
+
+# Isothermal potential parameters
+NFWPotential:
+  useabspos:          0
+  position:           [0.0,0.0,0.0]      # Location of centre of potential with respect to centre of the box (internal units)
+  concentration:      8.
+  M_200:              2.0e+12  # Virial mass (internal units)
+  critical_density:   140      # Critical density (internal units)
+  timestep_mult:      0.01     # Dimensionless pre-factor for the time-step condition
diff --git a/examples/parameter_example.yml b/examples/parameter_example.yml
index 64e31b97d0..63a61af3e7 100644
--- a/examples/parameter_example.yml
+++ b/examples/parameter_example.yml
@@ -175,6 +175,7 @@ EoS:
 
 # Point mass external potentials
 PointMassPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
   position:        [50.,50.0,50.]      # location of external point mass (internal units)
   mass:            1e10                # mass of external point mass (internal units)
   timestep_mult:   0.03                # Dimensionless pre-factor for the time-step condition
@@ -182,10 +183,29 @@ PointMassPotential:
 
 # Isothermal potential parameters
 IsothermalPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
   position:        [100.,100.,100.]    # Location of centre of isothermal potential with respect to centre of the box (internal units)
   vrot:            200.     # Rotation speed of isothermal potential (internal units)
   timestep_mult:   0.03     # Dimensionless pre-factor for the time-step condition
   epsilon:         0.1      # Softening size (internal units)
+  
+# Hernquist potential parameters
+HernquistPotential:
+  useabspos:       0        # 0 -> positions based on centre, 1 -> absolute positions 
+  position:        [100.,100.,100.]    # Location of centre of isothermal potential with respect to centre of the box (if 0) otherwise absolute (if 1) (internal units)
+  mass:            1e10     # Mass of the Hernquist potential
+  scalelength:     10.0     # Scale length of the potential
+  timestep_mult:   0.01     # Dimensionless pre-factor for the time-step condition, basically determines the fraction of the orbital time we use to do the time integration
+  epsilon:         0.1      # Softening size (internal units)
+ 
+# Isothermal potential parameters
+NFWPotential:
+  useabspos:          0
+  position:           [0.0,0.0,0.0]      # Location of centre of isothermal potential with respect to centre of the box (internal units) if useabspos=0 otherwise with respect to the 0,0,0, coordinates.
+  concentration:      8.       # Concentration of the halo
+  M_200:              2.0e+12  # Mass of the halo (M_200 in internal units)
+  critical_density:   127.4    # Critical density (internal units).
+  timestep_mult:      0.01     # Dimensionless pre-factor for the time-step condition, basically determines fraction of orbital time we need to do an integration step
 
 # Disk-patch potential parameters
 DiscPatchPotential:
diff --git a/src/potential.h b/src/potential.h
index 814b83c691..59567fe922 100644
--- a/src/potential.h
+++ b/src/potential.h
@@ -34,6 +34,10 @@
 #include "./potential/point_mass/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_ISOTHERMAL)
 #include "./potential/isothermal/potential.h"
+#elif defined(EXTERNAL_POTENTIAL_HERNQUIST)
+#include "./potential/hernquist/potential.h"
+#elif defined(EXTERNAL_POTENTIAL_NFW)
+#include "./potential/nfw/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_DISC_PATCH)
 #include "./potential/disc_patch/potential.h"
 #elif defined(EXTERNAL_POTENTIAL_SINE_WAVE)
diff --git a/src/potential/hernquist/potential.h b/src/potential/hernquist/potential.h
new file mode 100644
index 0000000000..d0ec339d91
--- /dev/null
+++ b/src/potential/hernquist/potential.h
@@ -0,0 +1,223 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018 Folkert Nobels (nobels@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_POTENTIAL_HERNQUIST_H
+#define SWIFT_POTENTIAL_HERNQUIST_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local includes. */
+#include "error.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "space.h"
+#include "units.h"
+
+/**
+ * @brief External Potential Properties - Hernquist potential
+ */
+struct external_potential {
+
+  /*! Position of the centre of potential */
+  double x[3];
+
+  /*! Mass of the halo */
+  double mass;
+
+  /*! Scale length (often as a, to prevent confusion with the cosmological
+   * scale-factor we use al) */
+  double al;
+
+  /*! Square of the softening length. Acceleration tends to zero within this
+   * distance from the origin */
+  double epsilon2;
+
+  /* Minimum timestep of the potential given by the timestep multiple
+   * times the orbital time at the softening length */
+  double mintime;
+
+  /*! Time-step condition pre-factor, is multiplied times the circular orbital
+   * time to get the time steps */
+  double timestep_mult;
+};
+
+/**
+ * @brief Computes the time-step in a Hernquist potential based on a
+ *        fraction of the circular orbital time
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static float external_gravity_timestep(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
+
+  const float G_newton = phys_const->const_newton_G;
+
+  /* Calculate the relative potential with respect to the centre of the
+   * potential */
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  /* calculate the radius  */
+  const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
+  const float sqrtgm_inv = 1.f / sqrtf(G_newton * potential->mass);
+
+  /* Calculate the circular orbital period */
+  const float period = 2.f * M_PI * sqrtf(r) * potential->al *
+                       (1 + r / potential->al) * sqrtgm_inv;
+
+  /* Time-step as a fraction of the cirecular orbital time */
+  const float time_step = potential->timestep_mult * period;
+
+  return max(time_step, potential->mintime);
+}
+
+/**
+ * @brief Computes the gravitational acceleration from an Hernquist potential.
+ *
+ * Note that the accelerations are multiplied by Newton's G constant
+ * later on.
+ *
+ * a_x = - GM / (a+r)^2 * x/r
+ * a_y = - GM / (a+r)^2 * y/r
+ * a_z = - GM / (a+r)^2 * z/r
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static void external_gravity_acceleration(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, struct gpart* g) {
+
+  /* Determine the position relative to the centre of the potential */
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  /* Calculate the acceleration */
+  const float r = sqrtf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
+  const float r_plus_a_inv = 1.f / (r + potential->al);
+  const float r_plus_a_inv2 = r_plus_a_inv * r_plus_a_inv;
+  const float term = -potential->mass * r_plus_a_inv2 / r;
+
+  g->a_grav[0] += term * dx;
+  g->a_grav[1] += term * dy;
+  g->a_grav[2] += term * dz;
+}
+
+/**
+ * @brief Computes the gravitational potential energy of a particle in an
+ * Hernquist potential.
+ *
+ * phi = - GM/(r+a)
+ *
+ * @param time The current time (unused here).
+ * @param potential The #external_potential used in the run.
+ * @param phys_const Physical constants in internal units.
+ * @param g Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+external_gravity_get_potential_energy(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+  const float r = sqrtf(dx * dx + dy * dy + dz * dz);
+  const float r_plus_alinv = 1.f / (r + potential->al);
+  return -phys_const->const_newton_G * potential->mass * r_plus_alinv;
+}
+
+/**
+ * @brief Initialises the external potential properties in the internal system
+ * of units.
+ *
+ * @param parameter_file The parsed parameter file
+ * @param phys_const Physical constants in internal units
+ * @param us The current internal system of units
+ * @param potential The external potential properties to initialize
+ */
+static INLINE void potential_init_backend(
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  /* Read in the position of the centre of potential */
+  parser_get_param_double_array(parameter_file, "HernquistPotential:position",
+                                3, potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "HernquistPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
+  potential->mass =
+      parser_get_param_double(parameter_file, "HernquistPotential:mass");
+  potential->al =
+      parser_get_param_double(parameter_file, "HernquistPotential:scalelength");
+  potential->timestep_mult = parser_get_param_float(
+      parameter_file, "HernquistPotential:timestep_mult");
+  const float epsilon =
+      parser_get_param_double(parameter_file, "HernquistPotential:epsilon");
+  potential->epsilon2 = epsilon * epsilon;
+
+  /* Compute the minimal time-step. */
+  /* This is the circular orbital time at the softened radius */
+  const float sqrtgm = sqrtf(phys_const->const_newton_G * potential->mass);
+  potential->mintime = 2.f * sqrtf(epsilon) * potential->al * M_PI *
+                       (1 + epsilon / potential->al) / sqrtgm *
+                       potential->timestep_mult;
+}
+
+/**
+ * @brief prints the properties of the external potential to stdout.
+ *
+ * @param  potential the external potential properties.
+ */
+static inline void potential_print_backend(
+    const struct external_potential* potential) {
+
+  message(
+      "external potential is 'hernquist' with properties are (x,y,z) = (%e, "
+      "%e, %e), mass = %e "
+      "scale length = %e , minimum time = %e "
+      "timestep multiplier = %e",
+      potential->x[0], potential->x[1], potential->x[2], potential->mass,
+      potential->al, potential->mintime, potential->timestep_mult);
+}
+
+#endif /* SWIFT_POTENTIAL_HERNQUIST_H */
diff --git a/src/potential/isothermal/potential.h b/src/potential/isothermal/potential.h
index b5f8d7c397..160372210e 100644
--- a/src/potential/isothermal/potential.h
+++ b/src/potential/isothermal/potential.h
@@ -148,7 +148,7 @@ external_gravity_get_potential_energy(
   const float dy = g->x[1] - potential->x[1];
   const float dz = g->x[2] - potential->x[2];
 
-  return -0.5f * potential->vrot * potential->vrot *
+  return 0.5f * potential->vrot * potential->vrot *
          logf(dx * dx + dy * dy + dz * dz + potential->epsilon2);
 }
 
@@ -166,11 +166,19 @@ static INLINE void potential_init_backend(
     const struct unit_system* us, const struct space* s,
     struct external_potential* potential) {
 
+  /* Read in the position of the centre of potential */
   parser_get_param_double_array(parameter_file, "IsothermalPotential:position",
                                 3, potential->x);
-  potential->x[0] += s->dim[0] / 2.;
-  potential->x[1] += s->dim[1] / 2.;
-  potential->x[2] += s->dim[2] / 2.;
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "IsothermalPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
 
   potential->vrot =
       parser_get_param_double(parameter_file, "IsothermalPotential:vrot");
diff --git a/src/potential/nfw/potential.h b/src/potential/nfw/potential.h
new file mode 100644
index 0000000000..28bafd439a
--- /dev/null
+++ b/src/potential/nfw/potential.h
@@ -0,0 +1,260 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2018   Ashley Kelly ()
+ *                      Folkert Nobels (nobels@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_POTENTIAL_NFW_H
+#define SWIFT_POTENTIAL_NFW_H
+
+/* Config parameters. */
+#include "../config.h"
+
+/* Some standard headers. */
+#include <float.h>
+#include <math.h>
+
+/* Local includes. */
+#include "error.h"
+#include "parser.h"
+#include "part.h"
+#include "physical_constants.h"
+#include "space.h"
+#include "units.h"
+
+/**
+ * @brief External Potential Properties - NFW Potential
+                rho(r) = rho_0 / ( (r/R_s)*(1+r/R_s)^2 )
+
+        We however parameterise this in terms of c and virial_mass
+ */
+struct external_potential {
+
+  /*! Position of the centre of potential */
+  double x[3];
+
+  /*! The scale radius of the NFW potential */
+  double r_s;
+
+  /*! The pre-factor \f$ 4 \pi G \rho_0 \r_s^3 \f$ */
+  double pre_factor;
+
+  /*! The critical density of the universe */
+  double rho_c;
+
+  /*! The concentration parameter */
+  double c_200;
+
+  /*! The virial mass */
+  double M_200;
+
+  /*! Time-step condition pre_factor, this factor is used to multiply times the
+   * orbital time, so in the case of 0.01 we take 1% of the orbital time as
+   * the time integration steps */
+  double timestep_mult;
+
+  /*! Minimum time step based on the orbital time at the softening times
+   * the timestep_mult */
+  double mintime;
+
+  /*! Common log term \f$ \ln(1+c_{200}) - \frac{c_{200}}{1 + c_{200}} \f$ */
+  double log_c200_term;
+
+  /*! Softening length */
+  double eps;
+};
+
+/**
+ * @brief Computes the time-step due to the acceleration from the NFW potential
+ *        as a fraction (timestep_mult) of the circular orbital time of that
+ *        particle.
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static float external_gravity_timestep(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const,
+    const struct gpart* restrict g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  const float r =
+      sqrtf(dx * dx + dy * dy + dz * dz + potential->eps * potential->eps);
+
+  const float mr = potential->M_200 *
+                   (logf(1.f + r / potential->r_s) - r / (r + potential->r_s)) /
+                   potential->log_c200_term;
+
+  const float period =
+      2 * M_PI * r * sqrtf(r / (phys_const->const_newton_G * mr));
+
+  /* Time-step as a fraction of the circular period */
+  const float time_step = potential->timestep_mult * period;
+
+  return max(time_step, potential->mintime);
+}
+
+/**
+ * @brief Computes the gravitational acceleration from an NFW Halo potential.
+ *
+ * Note that the accelerations are multiplied by Newton's G constant
+ * later on.
+ *
+ * a_x = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * x
+ * a_y = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * y
+ * a_z = 4 pi \rho_0 r_s^3 ( 1/((r+rs)*r^2) - log(1+r/rs)/r^3) * z
+ *
+ * @param time The current time.
+ * @param potential The #external_potential used in the run.
+ * @param phys_const The physical constants in internal units.
+ * @param g Pointer to the g-particle data.
+ */
+__attribute__((always_inline)) INLINE static void external_gravity_acceleration(
+    double time, const struct external_potential* restrict potential,
+    const struct phys_const* restrict phys_const, struct gpart* restrict g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  const float r =
+      sqrtf(dx * dx + dy * dy + dz * dz + potential->eps * potential->eps);
+  const float term1 = potential->pre_factor;
+  const float term2 = (1.0f / ((r + potential->r_s) * r * r) -
+                       logf(1.0f + r / potential->r_s) / (r * r * r));
+
+  g->a_grav[0] += term1 * term2 * dx;
+  g->a_grav[1] += term1 * term2 * dy;
+  g->a_grav[2] += term1 * term2 * dz;
+}
+
+/**
+ * @brief Computes the gravitational potential energy of a particle in an
+ * NFW potential.
+ *
+ * phi = -4 * pi * G * rho_0 * r_s^3 * ln(1+r/r_s)
+ *
+ * @param time The current time (unused here).
+ * @param potential The #external_potential used in the run.
+ * @param phys_const Physical constants in internal units.
+ * @param g Pointer to the particle data.
+ */
+__attribute__((always_inline)) INLINE static float
+external_gravity_get_potential_energy(
+    double time, const struct external_potential* potential,
+    const struct phys_const* const phys_const, const struct gpart* g) {
+
+  const float dx = g->x[0] - potential->x[0];
+  const float dy = g->x[1] - potential->x[1];
+  const float dz = g->x[2] - potential->x[2];
+
+  const float r =
+      sqrtf(dx * dx + dy * dy + dz * dz + potential->eps * potential->eps);
+  const float term1 = -potential->pre_factor / r;
+  const float term2 = logf(1.0f + r / potential->r_s);
+
+  return term1 * term2;
+}
+
+/**
+ * @brief Initialises the external potential properties in the internal system
+ * of units.
+ *
+ * @param parameter_file The parsed parameter file
+ * @param phys_const Physical constants in internal units
+ * @param us The current internal system of units
+ * @param potential The external potential properties to initialize
+ */
+static INLINE void potential_init_backend(
+    struct swift_params* parameter_file, const struct phys_const* phys_const,
+    const struct unit_system* us, const struct space* s,
+    struct external_potential* potential) {
+
+  /* Read in the position of the centre of potential */
+  parser_get_param_double_array(parameter_file, "NFWPotential:position", 3,
+                                potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "NFWPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
+  potential->timestep_mult =
+      parser_get_param_double(parameter_file, "NFWPotential:timestep_mult");
+  potential->c_200 =
+      parser_get_param_double(parameter_file, "NFWPotential:concentration");
+  potential->M_200 =
+      parser_get_param_double(parameter_file, "NFWPotential:M_200");
+  potential->rho_c =
+      parser_get_param_double(parameter_file, "NFWPotential:critical_density");
+  potential->eps = 0.05;
+
+  /* Compute R_200 */
+  const double R_200 =
+      cbrtf(3.0 * potential->M_200 / (4. * M_PI * 200.0 * potential->rho_c));
+
+  /* NFW scale-radius */
+  potential->r_s = R_200 / potential->c_200;
+  const double r_s3 = potential->r_s * potential->r_s * potential->r_s;
+
+  /* Log(c_200) term appearing in many expressions */
+  potential->log_c200_term =
+      log(1. + potential->c_200) - potential->c_200 / (1. + potential->c_200);
+
+  const double rho_0 =
+      potential->M_200 / (4.f * M_PI * r_s3 * potential->log_c200_term);
+
+  /* Pre-factor for the accelerations (note G is multiplied in later on) */
+  potential->pre_factor = 4.0f * M_PI * rho_0 * r_s3;
+
+  /* Compute the orbital time at the softening radius */
+  const double sqrtgm = sqrt(phys_const->const_newton_G * potential->M_200);
+  const double epslnthing = log(1.f + potential->eps / potential->r_s) -
+                            potential->eps / (potential->eps + potential->r_s);
+
+  potential->mintime = 2. * M_PI * potential->eps * sqrtf(potential->eps) *
+                       sqrtf(potential->log_c200_term / epslnthing) / sqrtgm *
+                       potential->timestep_mult;
+}
+
+/**
+ * @brief Prints the properties of the external potential to stdout.
+ *
+ * @param  potential The external potential properties.
+ */
+static INLINE void potential_print_backend(
+    const struct external_potential* potential) {
+
+  message(
+      "External potential is 'NFW' with properties are (x,y,z) = (%e, "
+      "%e, %e), scale radius = %e "
+      "timestep multiplier = %e, mintime = %e",
+      potential->x[0], potential->x[1], potential->x[2], potential->r_s,
+      potential->timestep_mult, potential->mintime);
+}
+
+#endif /* SWIFT_POTENTIAL_NFW_H */
diff --git a/src/potential/point_mass/potential.h b/src/potential/point_mass/potential.h
index f9d56a1ff1..5ae03f8637 100644
--- a/src/potential/point_mass/potential.h
+++ b/src/potential/point_mass/potential.h
@@ -137,7 +137,7 @@ external_gravity_get_potential_energy(
   const float dx = g->x[0] - potential->x[0];
   const float dy = g->x[1] - potential->x[1];
   const float dz = g->x[2] - potential->x[2];
-  const float rinv = 1. / sqrtf(dx * dx + dy * dy + dz * dz);
+  const float rinv = 1.f / sqrtf(dx * dx + dy * dy + dz * dz);
   return -phys_const->const_newton_G * potential->mass * rinv;
 }
 
@@ -156,8 +156,21 @@ static INLINE void potential_init_backend(
     const struct unit_system* us, const struct space* s,
     struct external_potential* potential) {
 
+  /* Read in the position of the centre of potential */
   parser_get_param_double_array(parameter_file, "PointMassPotential:position",
                                 3, potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "PointMassPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
   potential->mass =
       parser_get_param_double(parameter_file, "PointMassPotential:mass");
   potential->timestep_mult = parser_get_param_float(
diff --git a/src/potential/point_mass_softened/potential.h b/src/potential/point_mass_softened/potential.h
index 0e35e7bb98..050bc1a00c 100644
--- a/src/potential/point_mass_softened/potential.h
+++ b/src/potential/point_mass_softened/potential.h
@@ -183,8 +183,21 @@ static INLINE void potential_init_backend(
     const struct unit_system* us, const struct space* s,
     struct external_potential* potential) {
 
+  /* Read in the position of the centre of potential */
   parser_get_param_double_array(parameter_file, "PointMassPotential:position",
                                 3, potential->x);
+
+  /* Is the position absolute or relative to the centre of the box? */
+  const int useabspos =
+      parser_get_param_int(parameter_file, "PointMassPotential:useabspos");
+
+  if (!useabspos) {
+    potential->x[0] += s->dim[0] / 2.;
+    potential->x[1] += s->dim[1] / 2.;
+    potential->x[2] += s->dim[2] / 2.;
+  }
+
+  /* Read the other parameters of the model */
   potential->mass =
       parser_get_param_double(parameter_file, "PointMassPotential:mass");
   potential->timestep_mult = parser_get_param_float(
-- 
GitLab