diff --git a/src/Makefile.am b/src/Makefile.am
index 3e759d8fd15cfad6b38fe4ed469fb3b745aff786..2fc7ac5134c793f9476136759676a3ffaaf9816d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,7 +45,7 @@ include_HEADERS = space.h runner.h queue.h task.h lock.h cell.h part.h const.h \
     physical_constants.h physical_constants_cgs.h potential.h version.h \
     hydro_properties.h riemann.h threadpool.h cooling.h cooling_struct.h sourceterms.h \
     sourceterms_struct.h statistics.h memswap.h cache.h runner_doiact_vec.h profiler.h \
-    dump.h logger.h active.h timeline.h xmf.h gravity_properties.h
+    dump.h logger.h active.h timeline.h xmf.h gravity_properties.h gravity_derivatives.h
 
 # Common source files
 AM_SOURCES = space.c runner.c queue.c task.c cell.c engine.c \
diff --git a/src/cell.c b/src/cell.c
index 126e6c7e3d1fd9f73a49055c565b0d4fde937321..941ee7a7623b0006f0291878da58f7c8fa966565 100644
--- a/src/cell.c
+++ b/src/cell.c
@@ -1073,50 +1073,27 @@ int cell_are_neighbours(const struct cell *restrict ci,
 void cell_check_multipole(struct cell *c, void *data) {
 
   struct multipole ma;
+  const double tolerance = 1e-5; /* Relative */
+
+  /* First recurse */
+  if (c->split)
+    for (int k = 0; k < 8; k++)
+      if (c->progeny[k] != NULL) cell_check_multipole(c->progeny[k], NULL);
 
   if (c->gcount > 0) {
 
     /* Brute-force calculation */
-    multipole_init(&ma, c->gparts, c->gcount);
-
-    /* Compare with recursive one */
-    struct multipole mb = c->multipole;
-
-    if (fabsf(ma.mass - mb.mass) / fabsf(ma.mass + mb.mass) > 1e-5)
-      error("Multipole masses are different (%12.15e vs. %12.15e)", ma.mass,
-            mb.mass);
-
-    for (int k = 0; k < 3; ++k)
-      if (fabs(ma.CoM[k] - mb.CoM[k]) / fabs(ma.CoM[k] + mb.CoM[k]) > 1e-5)
-        error("Multipole CoM are different (%12.15e vs. %12.15e", ma.CoM[k],
-              mb.CoM[k]);
-
-#if const_gravity_multipole_order >= 2
-    if (fabsf(ma.I_xx - mb.I_xx) / fabsf(ma.I_xx + mb.I_xx) > 1e-5 &&
-        ma.I_xx > 1e-9)
-      error("Multipole I_xx are different (%12.15e vs. %12.15e)", ma.I_xx,
-            mb.I_xx);
-    if (fabsf(ma.I_yy - mb.I_yy) / fabsf(ma.I_yy + mb.I_yy) > 1e-5 &&
-        ma.I_yy > 1e-9)
-      error("Multipole I_yy are different (%12.15e vs. %12.15e)", ma.I_yy,
-            mb.I_yy);
-    if (fabsf(ma.I_zz - mb.I_zz) / fabsf(ma.I_zz + mb.I_zz) > 1e-5 &&
-        ma.I_zz > 1e-9)
-      error("Multipole I_zz are different (%12.15e vs. %12.15e)", ma.I_zz,
-            mb.I_zz);
-    if (fabsf(ma.I_xy - mb.I_xy) / fabsf(ma.I_xy + mb.I_xy) > 1e-5 &&
-        ma.I_xy > 1e-9)
-      error("Multipole I_xy are different (%12.15e vs. %12.15e)", ma.I_xy,
-            mb.I_xy);
-    if (fabsf(ma.I_xz - mb.I_xz) / fabsf(ma.I_xz + mb.I_xz) > 1e-5 &&
-        ma.I_xz > 1e-9)
-      error("Multipole I_xz are different (%12.15e vs. %12.15e)", ma.I_xz,
-            mb.I_xz);
-    if (fabsf(ma.I_yz - mb.I_yz) / fabsf(ma.I_yz + mb.I_yz) > 1e-5 &&
-        ma.I_yz > 1e-9)
-      error("Multipole I_yz are different (%12.15e vs. %12.15e)", ma.I_yz,
-            mb.I_yz);
-#endif
+    multipole_P2M(&ma, c->gparts, c->gcount);
+
+    /* Now  compare the multipole expansion */
+    if (!multipole_equal(&ma, c->multipole, tolerance)) {
+      message("Multipoles are not equal at depth=%d!", c->depth);
+      message("Correct answer:");
+      multipole_print(&ma);
+      message("Recursive multipole:");
+      multipole_print(c->multipole);
+      error("Aborting");
+    }
   }
 }
 
diff --git a/src/cell.h b/src/cell.h
index 86748161c79b39e038adc455e58376229f49a841..0d5a46006bd34cce4e65e9c2f9761292c31f40be 100644
--- a/src/cell.h
+++ b/src/cell.h
@@ -94,9 +94,6 @@ struct pcell {
  */
 struct cell {
 
-  /*! This cell's multipole. */
-  struct multipole multipole;
-
   /*! The cell location on the grid. */
   double loc[3];
 
@@ -106,6 +103,9 @@ struct cell {
   /*! Max smoothing length in this cell. */
   double h_max;
 
+  /*! This cell's multipole. */
+  struct multipole *multipole;
+
   /*! Linking pointer for "memory management". */
   struct cell *next;
 
diff --git a/src/engine.c b/src/engine.c
index 284b9cfe83bee77948b3f0c741bedc4ef3697136..531b84c471514341736b0e846efec4e6c734e43e 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -3448,6 +3448,7 @@ void engine_unpin() {
  * @param internal_units The system of units used internally.
  * @param physical_constants The #phys_const used for this run.
  * @param hydro The #hydro_props used for this run.
+ * @param gravity The #gravity_props used for this run.
  * @param potential The properties of the external potential.
  * @param cooling_func The properties of the cooling function.
  * @param sourceterms The properties of the source terms function.
diff --git a/src/gravity_derivatives.h b/src/gravity_derivatives.h
new file mode 100644
index 0000000000000000000000000000000000000000..4730d9df5dc573de74fde422e1b7dafc0ee0994a
--- /dev/null
+++ b/src/gravity_derivatives.h
@@ -0,0 +1,72 @@
+/*******************************************************************************
+ * This file is part of SWIFT.
+ * Copyright (c) 2016 Matthieu Schaller (matthieu.schaller@durham.ac.uk)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ ******************************************************************************/
+#ifndef SWIFT_GRAVITY_DERIVATIVE_H
+#define SWIFT_GRAVITY_DERIVATIVE_H
+
+/* Some standard headers. */
+#include <math.h>
+
+/* Local headers. */
+#include "inline.h"
+
+/**
+ * @brief \f$ \phi(r_x, r_y, r_z) \f$.
+ */
+__attribute__((always_inline)) INLINE static double D_000(double r_x,
+                                                          double r_y,
+                                                          double r_z,
+                                                          double r_inv) {
+
+  return r_inv;
+}
+
+/**
+ * @brief \f$ \frac{\partial\phi(r_x, r_y, r_z)}{\partial r_x} \f$.
+ */
+__attribute__((always_inline)) INLINE static double D_100(double r_x,
+                                                          double r_y,
+                                                          double r_z,
+                                                          double r_inv) {
+
+  return -r_x * r_inv * r_inv * r_inv;
+}
+
+/**
+ * @brief \f$ \frac{\partial\phi(r_x, r_y, r_z)}{\partial r_x} \f$.
+ */
+__attribute__((always_inline)) INLINE static double D_010(double r_x,
+                                                          double r_y,
+                                                          double r_z,
+                                                          double r_inv) {
+
+  return -r_y * r_inv * r_inv * r_inv;
+}
+
+/**
+ * @brief \f$ \frac{\partial\phi(r_x, r_y, r_z)}{\partial r_x} \f$.
+ */
+__attribute__((always_inline)) INLINE static double D_001(double r_x,
+                                                          double r_y,
+                                                          double r_z,
+                                                          double r_inv) {
+
+  return -r_z * r_inv * r_inv * r_inv;
+}
+
+#endif /* SWIFT_GRAVITY_DERIVATIVE_H */
diff --git a/src/multipole.c b/src/multipole.c
index f0d0c7084fd9bec366f2185cb24887da40591b17..bd5c6d6546fa0546108dcd53d7fe4060293c37a7 100644
--- a/src/multipole.c
+++ b/src/multipole.c
@@ -20,205 +20,3 @@
 
 /* Config parameters. */
 #include "../config.h"
-
-/* Some standard headers. */
-#include <strings.h>
-
-/* This object's header. */
-#include "multipole.h"
-
-/**
-* @brief Reset the data of a #multipole.
-*
-* @param m The #multipole.
-*/
-void multipole_reset(struct multipole *m) {
-
-  /* Just bzero the struct. */
-  bzero(m, sizeof(struct multipole));
-}
-
-/**
-* @brief Init a multipole from a set of particles.
-*
-* @param m The #multipole.
-* @param gparts The #gpart.
-* @param gcount The number of particles.
-*/
-void multipole_init(struct multipole *m, const struct gpart *gparts,
-                    int gcount) {
-
-#if const_gravity_multipole_order > 2
-#error "Multipoles of order >2 not yet implemented."
-#endif
-
-  /* Zero everything */
-  multipole_reset(m);
-
-  /* Temporary variables */
-  double mass = 0.0;
-  double com[3] = {0.0, 0.0, 0.0};
-
-#if const_gravity_multipole_order >= 2
-  double I_xx = 0.0, I_yy = 0.0, I_zz = 0.0;
-  double I_xy = 0.0, I_xz = 0.0, I_yz = 0.0;
-#endif
-
-  /* Collect the particle data. */
-  for (int k = 0; k < gcount; k++) {
-    const float w = gparts[k].mass;
-
-    mass += w;
-    com[0] += gparts[k].x[0] * w;
-    com[1] += gparts[k].x[1] * w;
-    com[2] += gparts[k].x[2] * w;
-
-#if const_gravity_multipole_order >= 2
-    I_xx += gparts[k].x[0] * gparts[k].x[0] * w;
-    I_yy += gparts[k].x[1] * gparts[k].x[1] * w;
-    I_zz += gparts[k].x[2] * gparts[k].x[2] * w;
-    I_xy += gparts[k].x[0] * gparts[k].x[1] * w;
-    I_xz += gparts[k].x[0] * gparts[k].x[2] * w;
-    I_yz += gparts[k].x[1] * gparts[k].x[2] * w;
-#endif
-  }
-
-  const double imass = 1.0 / mass;
-
-  /* Store the data on the multipole. */
-  m->mass = mass;
-  m->CoM[0] = com[0] * imass;
-  m->CoM[1] = com[1] * imass;
-  m->CoM[2] = com[2] * imass;
-
-#if const_gravity_multipole_order >= 2
-  m->I_xx = I_xx - imass * com[0] * com[0];
-  m->I_yy = I_yy - imass * com[1] * com[1];
-  m->I_zz = I_zz - imass * com[2] * com[2];
-  m->I_xy = I_xy - imass * com[0] * com[1];
-  m->I_xz = I_xz - imass * com[0] * com[2];
-  m->I_yz = I_yz - imass * com[1] * com[2];
-#endif
-}
-
-/**
- * @brief Add the second multipole to the first one.
- *
- * @param ma The #multipole which will contain the sum.
- * @param mb The #multipole to add.
- */
-
-void multipole_add(struct multipole *ma, const struct multipole *mb) {
-
-#if const_gravity_multipole_order > 2
-#error "Multipoles of order >2 not yet implemented."
-#endif
-
-  /* Correct the position. */
-  const double ma_mass = ma->mass;
-  const double mb_mass = mb->mass;
-  const double M_tot = ma_mass + mb_mass;
-  const double M_tot_inv = 1.0 / M_tot;
-
-  const double ma_CoM[3] = {ma->CoM[0], ma->CoM[1], ma->CoM[2]};
-  const double mb_CoM[3] = {mb->CoM[0], mb->CoM[1], mb->CoM[2]};
-
-#if const_gravity_multipole_order >= 2
-  const double ma_I_xx = (double)ma->I_xx + ma_mass * ma_CoM[0] * ma_CoM[0];
-  const double ma_I_yy = (double)ma->I_yy + ma_mass * ma_CoM[1] * ma_CoM[1];
-  const double ma_I_zz = (double)ma->I_zz + ma_mass * ma_CoM[2] * ma_CoM[2];
-  const double ma_I_xy = (double)ma->I_xy + ma_mass * ma_CoM[0] * ma_CoM[1];
-  const double ma_I_xz = (double)ma->I_xz + ma_mass * ma_CoM[0] * ma_CoM[2];
-  const double ma_I_yz = (double)ma->I_yz + ma_mass * ma_CoM[1] * ma_CoM[2];
-
-  const double mb_I_xx = (double)mb->I_xx + mb_mass * mb_CoM[0] * mb_CoM[0];
-  const double mb_I_yy = (double)mb->I_yy + mb_mass * mb_CoM[1] * mb_CoM[1];
-  const double mb_I_zz = (double)mb->I_zz + mb_mass * mb_CoM[2] * mb_CoM[2];
-  const double mb_I_xy = (double)mb->I_xy + mb_mass * mb_CoM[0] * mb_CoM[1];
-  const double mb_I_xz = (double)mb->I_xz + mb_mass * mb_CoM[0] * mb_CoM[2];
-  const double mb_I_yz = (double)mb->I_yz + mb_mass * mb_CoM[1] * mb_CoM[2];
-#endif
-
-  /* New mass */
-  ma->mass = M_tot;
-
-  /* New CoM */
-  ma->CoM[0] = (ma_CoM[0] * ma_mass + mb_CoM[0] * mb_mass) * M_tot_inv;
-  ma->CoM[1] = (ma_CoM[1] * ma_mass + mb_CoM[1] * mb_mass) * M_tot_inv;
-  ma->CoM[2] = (ma_CoM[2] * ma_mass + mb_CoM[2] * mb_mass) * M_tot_inv;
-
-/* New quadrupole */
-#if const_gravity_multipole_order >= 2
-  ma->I_xx = (ma_I_xx + mb_I_xx) - M_tot * ma->CoM[0] * ma->CoM[0];
-  ma->I_yy = (ma_I_yy + mb_I_yy) - M_tot * ma->CoM[1] * ma->CoM[1];
-  ma->I_zz = (ma_I_zz + mb_I_zz) - M_tot * ma->CoM[2] * ma->CoM[2];
-  ma->I_xy = (ma_I_xy + mb_I_xy) - M_tot * ma->CoM[0] * ma->CoM[1];
-  ma->I_xz = (ma_I_xz + mb_I_xz) - M_tot * ma->CoM[0] * ma->CoM[2];
-  ma->I_yz = (ma_I_yz + mb_I_yz) - M_tot * ma->CoM[1] * ma->CoM[2];
-#endif
-}
-
-/**
- * @brief Add a particle to the given multipole.
- *
- * @param m The #multipole.
- * @param p The #gpart.
- */
-
-void multipole_addpart(struct multipole *m, struct gpart *p) {
-
-  /* #if const_gravity_multipole_order == 1 */
-
-  /*   /\* Correct the position. *\/ */
-  /*   float mm = m->coeffs[0], mp = p->mass; */
-  /*   float w = 1.0f / (mm + mp); */
-  /*   for (int k = 0; k < 3; k++) m->x[k] = (m->x[k] * mm + p->x[k] * mp) * w;
-   */
-
-  /*   /\* Add the particle to the moments. *\/ */
-  /*   m->coeffs[0] = mm + mp; */
-
-  /* #else */
-  /* #error( "Multipoles of order %i not yet implemented." ,
-   * const_gravity_multipole_order )
-   */
-  /* #endif */
-}
-
-/**
- * @brief Add a group of particles to the given multipole.
- *
- * @param m The #multipole.
- * @param p The #gpart array.
- * @param N Number of parts to add.
- */
-
-void multipole_addparts(struct multipole *m, struct gpart *p, int N) {
-
-  /* #if const_gravity_multipole_order == 1 */
-
-  /*   /\* Get the combined mass and positions. *\/ */
-  /*   double xp[3] = {0.0, 0.0, 0.0}; */
-  /*   float mp = 0.0f, w; */
-  /*   for (int k = 0; k < N; k++) { */
-  /*     w = p[k].mass; */
-  /*     mp += w; */
-  /*     xp[0] += p[k].x[0] * w; */
-  /*     xp[1] += p[k].x[1] * w; */
-  /*     xp[2] += p[k].x[2] * w; */
-  /*   } */
-
-  /*   /\* Correct the position. *\/ */
-  /*   float mm = m->coeffs[0]; */
-  /*   w = 1.0f / (mm + mp); */
-  /*   for (int k = 0; k < 3; k++) m->x[k] = (m->x[k] * mm + xp[k]) * w; */
-
-  /*   /\* Add the particle to the moments. *\/ */
-  /*   m->coeffs[0] = mm + mp; */
-
-  /* #else */
-  /* #error( "Multipoles of order %i not yet implemented." ,
-   * const_gravity_multipole_order )
-   */
-  /* #endif */
-}
diff --git a/src/multipole.h b/src/multipole.h
index dc1f914dd73969bc63f3beffca7f34d0f889edb6..69195f8ec72cb18c1f661f0e94802ad4a2014739 100644
--- a/src/multipole.h
+++ b/src/multipole.h
@@ -22,28 +22,286 @@
 
 /* Some standard headers. */
 #include <math.h>
+#include <string.h>
 
 /* Includes. */
+#include "align.h"
 #include "const.h"
+#include "error.h"
+#include "gravity_derivatives.h"
 #include "inline.h"
 #include "kernel_gravity.h"
+#include "minmax.h"
 #include "part.h"
 
-/* Multipole struct. */
+#define multipole_align 128
+
+/**
+ * @brief The multipole expansion of a mass distribution.
+ */
 struct multipole {
 
-  /* Multipole location. */
-  double CoM[3];
+  union {
+
+    /*! Linking pointer for "memory management". */
+    struct multipole *next;
+
+    /*! The actual content */
+    struct {
+
+      /*! Multipole mass */
+      float mass;
+
+      /*! Centre of mass of the matter dsitribution */
+      double CoM[3];
+
+      /*! Bulk velocity */
+      float vel[3];
+    };
+  };
+} SWIFT_STRUCT_ALIGN;
+
+struct acc_tensor {
+
+  double F_000;
+};
+
+struct pot_tensor {
+
+  double F_000;
+};
+
+struct field_tensors {
+
+  union {
+
+    /*! Linking pointer for "memory management". */
+    struct field_tensors *next;
+
+    /*! The actual content */
+    struct {
+
+      /*! Field tensor for acceleration along x */
+      struct acc_tensor a_x;
+
+      /*! Field tensor for acceleration along y */
+      struct acc_tensor a_y;
+
+      /*! Field tensor for acceleration along z */
+      struct acc_tensor a_z;
+
+      /*! Field tensor for the potential */
+      struct pot_tensor pot;
+    };
+  };
+
+} SWIFT_STRUCT_ALIGN;
+
+/**
+ * @brief Reset the data of a #multipole.
+ *
+ * @param m The #multipole.
+ */
+INLINE static void multipole_init(struct multipole *m) {
+
+  /* Just bzero the struct. */
+  bzero(m, sizeof(struct multipole));
+}
+
+/**
+ * @brief Prints the content of a #multipole to stdout.
+ *
+ * Note: Uses directly printf(), not a call to message().
+ *
+ * @param m The #multipole to print.
+ */
+INLINE static void multipole_print(const struct multipole *m) {
+
+  printf("CoM= [%12.5e %12.5e %12.5e\n", m->CoM[0], m->CoM[1], m->CoM[2]);
+  printf("Mass= %12.5e\n", m->mass);
+  printf("Vel= [%12.5e %12.5e %12.5e\n", m->vel[0], m->vel[1], m->vel[2]);
+}
+
+/**
+ * @brief Adds a #multipole to another one (i.e. does ma += mb).
+ *
+ * @param ma The multipole to add to.
+ * @param mb The multipole to add.
+ */
+INLINE static void multipole_add(struct multipole *ma,
+                                 const struct multipole *mb) {
+
+  const float mass = ma->mass + mb->mass;
+  const float imass = 1.f / mass;
+
+  /* Add the bulk velocities */
+  ma->vel[0] = (ma->vel[0] * ma->mass + mb->vel[0] * mb->mass) * imass;
+  ma->vel[1] = (ma->vel[1] * ma->mass + mb->vel[1] * mb->mass) * imass;
+  ma->vel[2] = (ma->vel[2] * ma->mass + mb->vel[2] * mb->mass) * imass;
+
+  /* Add the masses */
+  ma->mass = mass;
+}
+
+/**
+ * @brief Verifies whether two #multipole's are equal or not.
+ *
+ * @param ma The first #multipole.
+ * @param mb The second #multipole.
+ * @param tolerance The maximal allowed difference for the fields.
+ * @return 1 if the multipoles are equal 0 otherwise.
+ */
+INLINE static int multipole_equal(const struct multipole *ma,
+                                  const struct multipole *mb,
+                                  double tolerance) {
+
+  /* Check CoM */
+  if (fabs(ma->CoM[0] - mb->CoM[0]) / fabs(ma->CoM[0] + mb->CoM[0]) > tolerance)
+    return 0;
+  if (fabs(ma->CoM[1] - mb->CoM[1]) / fabs(ma->CoM[1] + mb->CoM[1]) > tolerance)
+    return 0;
+  if (fabs(ma->CoM[2] - mb->CoM[2]) / fabs(ma->CoM[2] + mb->CoM[2]) > tolerance)
+    return 0;
+
+  /* Check bulk velocity (if non-zero)*/
+  if (fabsf(ma->vel[0] + mb->vel[0]) > 0.f &&
+      fabsf(ma->vel[0] - mb->vel[0]) / fabsf(ma->vel[0] + mb->vel[0]) >
+          tolerance)
+    return 0;
+  if (fabsf(ma->vel[1] + mb->vel[1]) > 0.f &&
+      fabsf(ma->vel[1] - mb->vel[1]) / fabsf(ma->vel[1] + mb->vel[1]) >
+          tolerance)
+    return 0;
+  if (fabsf(ma->vel[2] + mb->vel[2]) > 0.f &&
+      fabsf(ma->vel[2] - mb->vel[2]) / fabsf(ma->vel[2] + mb->vel[2]) >
+          tolerance)
+    return 0;
+
+  /* Check mass */
+  if (fabsf(ma->mass - mb->mass) / fabsf(ma->mass + mb->mass) > tolerance)
+    return 0;
 
-  /* Multipole mass */
-  float mass;
+  /* All is good */
+  return 1;
+}
+
+/**
+ * @brief Applies the forces due to particles j onto particles i directly.
+ *
+ * @param gparts_i The #gpart to update.
+ * @param gcount_i The number of particles to update.
+ * @param gparts_j The #gpart that source the gravity field.
+ * @param gcount_j The number of sources.
+ */
+INLINE static void multipole_P2P(struct gpart *gparts_i, int gcount_i,
+                                 const struct gpart *gparts_j, int gcount_j) {}
+
+/**
+ * @brief Constructs the #multipole of a bunch of particles around their
+ * centre of mass.
+ *
+ * Corresponds to equation (28c).
+ *
+ * @param m The #multipole (content will  be overwritten).
+ * @param gparts The #gpart.
+ * @param gcount The number of particles.
+ */
+INLINE static void multipole_P2M(struct multipole *m,
+                                 const struct gpart *gparts, int gcount) {
 
 #if const_gravity_multipole_order >= 2
-  /* Quadrupole terms */
-  float I_xx, I_yy, I_zz;
-  float I_xy, I_xz, I_yz;
+#error "Implementation of P2M kernel missing for this order."
 #endif
-};
+
+  /* Temporary variables */
+  double mass = 0.0;
+  double com[3] = {0.0, 0.0, 0.0};
+  float vel[3] = {0.f, 0.f, 0.f};
+
+  /* Collect the particle data. */
+  for (int k = 0; k < gcount; k++) {
+    const float m = gparts[k].mass;
+
+    mass += m;
+    com[0] += gparts[k].x[0] * m;
+    com[1] += gparts[k].x[1] * m;
+    com[2] += gparts[k].x[2] * m;
+    vel[0] += gparts[k].v_full[0] * m;
+    vel[1] += gparts[k].v_full[1] * m;
+    vel[2] += gparts[k].v_full[2] * m;
+  }
+
+  const double imass = 1.0 / mass;
+
+  /* Store the data on the multipole. */
+  m->mass = mass;
+  m->CoM[0] = com[0] * imass;
+  m->CoM[1] = com[1] * imass;
+  m->CoM[2] = com[2] * imass;
+  m->vel[0] = vel[0] * imass;
+  m->vel[1] = vel[1] * imass;
+  m->vel[2] = vel[2] * imass;
+}
+
+/**
+ * @brief Creates a copy of #multipole shifted to a new location.
+ *
+ * Corresponds to equation (28d).
+ *
+ * @param m_a The #multipole copy (content will  be overwritten).
+ * @param m_b The #multipole to shift.
+ * @param pos_a The position to which m_b will be shifted.
+ * @param pos_b The current postion of the multipole to shift.
+ * @param periodic Is the calculation periodic ?
+ */
+INLINE static void multipole_M2M(struct multipole *m_a,
+                                 const struct multipole *m_b,
+                                 const double pos_a[3], const double pos_b[3],
+                                 int periodic) {
+
+  m_a->mass = m_b->mass;
+
+  m_a->vel[0] = m_b->vel[0];
+  m_a->vel[1] = m_b->vel[1];
+  m_a->vel[2] = m_b->vel[2];
+}
+/**
+ * @brief Compute the field tensors due to a multipole.
+ *
+ * Corresponds to equation (28b).
+ *
+ * @param l_a The field tensor to compute.
+ * @param m_b The multipole creating the field.
+ * @param pos_a The position of the field tensor.
+ * @param pos_b The position of the multipole.
+ * @param periodic Is the calculation periodic ?
+ */
+INLINE static void multipole_M2L(struct field_tensors *l_a,
+                                 const struct multipole m_b,
+                                 const double pos_a[3], const double pos_b[3],
+                                 int periodic) {
+
+  /* double dx, dy, dz; */
+  /* if (periodic) { */
+  /*   dx = box_wrap(pos_a[0] - pos_b[0], 0., 1.); */
+  /*   dy = box_wrap(pos_a[1] - pos_b[1], 0., 1.); */
+  /*   dz = box_wrap(pos_a[2] - pos_b[2], 0., 1.); */
+  /* } else { */
+  /*   dx = pos_a[0] - pos_b[0]; */
+  /*   dy = pos_a[1] - pos_b[1]; */
+  /*   dz = pos_a[2] - pos_b[2]; */
+  /* } */
+  /* const double r2 = dx * dx + dy * dy + dz * dz; */
+
+  /* const double r_inv = 1. / sqrt(r2); */
+
+  /* /\* 1st order multipole term *\/ */
+  /* l_a->x.F_000 =  D_100(dx, dy, dz, r_inv); */
+  /* l_a->y.F_000 =  D_010(dx, dy, dz, r_inv); */
+  /* l_a->z.F_000 =  D_001(dx, dy, dz, r_inv); */
+}
+
+#if 0
 
 /* Multipole function prototypes. */
 void multipole_add(struct multipole *m_sum, const struct multipole *m_term);
@@ -137,4 +395,6 @@ __attribute__((always_inline)) INLINE static void multipole_iact_mp(
   /* #endif */
 }
 
+#endif
+
 #endif /* SWIFT_MULTIPOLE_H */
diff --git a/src/space.c b/src/space.c
index 503727cfef81bffabd1d1cf568be6954bad175cf..68d96397a3c85ed11d5e506519af699dfdd2cecf 100644
--- a/src/space.c
+++ b/src/space.c
@@ -51,6 +51,7 @@
 #include "lock.h"
 #include "memswap.h"
 #include "minmax.h"
+#include "multipole.h"
 #include "runner.h"
 #include "stars.h"
 #include "threadpool.h"
@@ -179,14 +180,27 @@ int space_getsid(struct space *s, struct cell **ci, struct cell **cj,
  * @param rec_end Pointer to the end of the list of cells to recycle.
  */
 void space_rebuild_recycle_rec(struct space *s, struct cell *c,
-                               struct cell **rec_begin, struct cell **rec_end) {
+                               struct cell **cell_rec_begin,
+                               struct cell **cell_rec_end,
+                               struct multipole **multipole_rec_begin,
+                               struct multipole **multipole_rec_end) {
   if (c->split)
     for (int k = 0; k < 8; k++)
       if (c->progeny[k] != NULL) {
-        space_rebuild_recycle_rec(s, c->progeny[k], rec_begin, rec_end);
-        c->progeny[k]->next = *rec_begin;
-        *rec_begin = c->progeny[k];
-        if (*rec_end == NULL) *rec_end = *rec_begin;
+        space_rebuild_recycle_rec(s, c->progeny[k], cell_rec_begin,
+                                  cell_rec_end, multipole_rec_begin,
+                                  multipole_rec_end);
+
+        c->progeny[k]->next = *cell_rec_begin;
+        *cell_rec_begin = c->progeny[k];
+        c->progeny[k]->multipole->next = *multipole_rec_begin;
+        *multipole_rec_begin = c->progeny[k]->multipole;
+
+        if (*cell_rec_end == NULL) *cell_rec_end = *cell_rec_begin;
+        if (*multipole_rec_end == NULL)
+          *multipole_rec_end = *multipole_rec_begin;
+
+        c->progeny[k]->multipole = NULL;
         c->progeny[k] = NULL;
       }
 }
@@ -199,9 +213,13 @@ void space_rebuild_recycle_mapper(void *map_data, int num_elements,
 
   for (int k = 0; k < num_elements; k++) {
     struct cell *c = &cells[k];
-    struct cell *rec_begin = NULL, *rec_end = NULL;
-    space_rebuild_recycle_rec(s, c, &rec_begin, &rec_end);
-    if (rec_begin != NULL) space_recycle_list(s, rec_begin, rec_end);
+    struct cell *cell_rec_begin = NULL, *cell_rec_end = NULL;
+    struct multipole *multipole_rec_begin = NULL, *multipole_rec_end = NULL;
+    space_rebuild_recycle_rec(s, c, &cell_rec_begin, &cell_rec_end,
+                              &multipole_rec_begin, &multipole_rec_end);
+    if (cell_rec_begin != NULL)
+      space_recycle_list(s, cell_rec_begin, cell_rec_end, multipole_rec_begin,
+                         multipole_rec_end);
     c->sorts = NULL;
     c->nr_tasks = 0;
     c->density = NULL;
@@ -362,6 +380,7 @@ void space_regrid(struct space *s, int verbose) {
       threadpool_map(&s->e->threadpool, space_rebuild_recycle_mapper,
                      s->cells_top, s->nr_cells, sizeof(struct cell), 100, s);
       free(s->cells_top);
+      free(s->multipoles_top);
       s->maxdepth = 0;
     }
 
@@ -377,19 +396,35 @@ void space_regrid(struct space *s, int verbose) {
     s->tot_cells = s->nr_cells = cdim[0] * cdim[1] * cdim[2];
     if (posix_memalign((void *)&s->cells_top, cell_align,
                        s->nr_cells * sizeof(struct cell)) != 0)
-      error("Failed to allocate cells.");
+      error("Failed to allocate top-level cells.");
     bzero(s->cells_top, s->nr_cells * sizeof(struct cell));
 
+    /* Allocate the multipoles for the top-level cells. */
+    if (s->gravity) {
+      if (posix_memalign((void *)&s->multipoles_top, multipole_align,
+                         s->nr_cells * sizeof(struct multipole)) != 0)
+        error("Failed to allocate top-level multipoles.");
+      bzero(s->multipoles_top, s->nr_cells * sizeof(struct multipole));
+    }
+
     /* Set the cells' locks */
-    for (int k = 0; k < s->nr_cells; k++)
+    for (int k = 0; k < s->nr_cells; k++) {
       if (lock_init(&s->cells_top[k].lock) != 0)
-        error("Failed to init spinlock.");
+        error("Failed to init spinlock for hydro.");
+      if (lock_init(&s->cells_top[k].glock) != 0)
+        error("Failed to init spinlock for gravity.");
+      if (lock_init(&s->cells_top[k].mlock) != 0)
+        error("Failed to init spinlock for multipoles.");
+      if (lock_init(&s->cells_top[k].slock) != 0)
+        error("Failed to init spinlock for stars.");
+    }
 
     /* Set the cell location and sizes. */
     for (int i = 0; i < cdim[0]; i++)
       for (int j = 0; j < cdim[1]; j++)
         for (int k = 0; k < cdim[2]; k++) {
-          struct cell *restrict c = &s->cells_top[cell_getid(cdim, i, j, k)];
+          const size_t cid = cell_getid(cdim, i, j, k);
+          struct cell *restrict c = &s->cells_top[cid];
           c->loc[0] = i * s->width[0];
           c->loc[1] = j * s->width[1];
           c->loc[2] = k * s->width[2];
@@ -403,7 +438,7 @@ void space_regrid(struct space *s, int verbose) {
           c->scount = 0;
           c->super = c;
           c->ti_old = ti_current;
-          lock_init(&c->lock);
+          if (s->gravity) c->multipole = &s->multipoles_top[cid];
         }
 
     /* Be verbose about the change. */
@@ -900,6 +935,12 @@ void space_rebuild(struct space *s, int verbose) {
      sure that the parts in each cell are ok. */
   space_split(s, cells_top, s->nr_cells, verbose);
 
+#ifdef SWIFT_DEBUG_CHECKS
+  /* Check that the multipole construction went OK */
+  for (int k = 0; k < s->nr_cells; k++)
+    cell_check_multipole(&s->cells_top[k], NULL);
+#endif
+
   if (verbose)
     message("took %.3f %s.", clocks_from_ticks(getticks() - tic),
             clocks_getunit());
@@ -2011,7 +2052,7 @@ void space_split_recursive(struct space *s, struct cell *c,
     /* Remove any progeny with zero parts. */
     struct cell_buff *progeny_buff = buff, *progeny_gbuff = gbuff,
                      *progeny_sbuff = sbuff;
-    for (int k = 0; k < 8; k++)
+    for (int k = 0; k < 8; k++) {
       if (c->progeny[k]->count == 0 && c->progeny[k]->gcount == 0 &&
           c->progeny[k]->scount == 0) {
         space_recycle(s, c->progeny[k]);
@@ -2028,7 +2069,41 @@ void space_split_recursive(struct space *s, struct cell *c,
         if (c->progeny[k]->maxdepth > maxdepth)
           maxdepth = c->progeny[k]->maxdepth;
       }
+    }
 
+    /* Deal with multipole */
+    if (s->gravity) {
+
+      /* Reset everything */
+      multipole_init(c->multipole);
+
+      /* Compute CoM of all progenies */
+      double CoM[3] = {0., 0., 0.};
+      double mass = 0.;
+
+      for (int k = 0; k < 8; ++k) {
+        if (c->progeny[k] != NULL) {
+          const struct multipole *m = c->progeny[k]->multipole;
+          CoM[0] += m->CoM[0] * m->mass;
+          CoM[1] += m->CoM[1] * m->mass;
+          CoM[2] += m->CoM[2] * m->mass;
+          mass += m->mass;
+        }
+      }
+      c->multipole->CoM[0] = CoM[0] / mass;
+      c->multipole->CoM[1] = CoM[1] / mass;
+      c->multipole->CoM[2] = CoM[2] / mass;
+
+      /* Now shift progeny multipoles and add them up */
+      struct multipole temp;
+      for (int k = 0; k < 8; ++k) {
+        if (c->progeny[k] != NULL) {
+          const struct multipole *m = c->progeny[k]->multipole;
+          multipole_M2M(&temp, m, c->multipole->CoM, m->CoM, s->periodic);
+          multipole_add(c->multipole, &temp);
+        }
+      }
+    }
   }
 
   /* Otherwise, collect the data for this cell. */
@@ -2070,6 +2145,9 @@ void space_split_recursive(struct space *s, struct cell *c,
       if (ti_end < ti_end_min) ti_end_min = ti_end;
       if (ti_end > ti_end_max) ti_end_max = ti_end;
     }
+
+    /* Construct the multipole and the centre of mass*/
+    multipole_P2M(c->multipole, c->gparts, c->gcount);
   }
 
   /* Set the values for this cell. */
@@ -2137,8 +2215,9 @@ void space_split_mapper(void *map_data, int num_cells, void *extra_data) {
 void space_recycle(struct space *s, struct cell *c) {
 
   /* Clear the cell. */
-  if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0)
-    error("Failed to destroy spinlock.");
+  if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0 ||
+      lock_destroy(&c->mlock) != 0 || lock_destroy(&c->slock) != 0)
+    error("Failed to destroy spinlocks.");
 
   /* Clear this cell's sort arrays. */
   if (c->sort != NULL) free(c->sort);
@@ -2146,6 +2225,12 @@ void space_recycle(struct space *s, struct cell *c) {
   /* Lock the space. */
   lock_lock(&s->lock);
 
+  /* Hook the multipole back in the buffer */
+  if (s->gravity) {
+    c->multipole->next = s->multipoles_sub;
+    s->multipoles_sub = c->multipole;
+  }
+
   /* Hook this cell into the buffer. */
   c->next = s->cells_sub;
   s->cells_sub = c;
@@ -2159,22 +2244,32 @@ void space_recycle(struct space *s, struct cell *c) {
  * @brief Return a list of used cells to the buffer of unused sub-cells.
  *
  * @param s The #space.
- * @param list_begin Pointer to the first #cell in the linked list of
+ * @param cell_list_begin Pointer to the first #cell in the linked list of
  *        cells joined by their @c next pointers.
- * @param list_end Pointer to the last #cell in the linked list of
+ * @param cell_list_end Pointer to the last #cell in the linked list of
  *        cells joined by their @c next pointers. It is assumed that this
  *        cell's @c next pointer is @c NULL.
+ * @param multipole_list_begin Pointer to the first #multipole in the linked
+ * list of
+ *        multipoles joined by their @c next pointers.
+ * @param multipole_list_end Pointer to the last #multipole in the linked list
+ * of
+ *        multipoles joined by their @c next pointers. It is assumed that this
+ *        multipole's @c next pointer is @c NULL.
  */
-void space_recycle_list(struct space *s, struct cell *list_begin,
-                        struct cell *list_end) {
+void space_recycle_list(struct space *s, struct cell *cell_list_begin,
+                        struct cell *cell_list_end,
+                        struct multipole *multipole_list_begin,
+                        struct multipole *multipole_list_end) {
 
   int count = 0;
 
   /* Clean up the list of cells. */
-  for (struct cell *c = list_begin; c != NULL; c = c->next) {
+  for (struct cell *c = cell_list_begin; c != NULL; c = c->next) {
     /* Clear the cell. */
-    if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0)
-      error("Failed to destroy spinlock.");
+    if (lock_destroy(&c->lock) != 0 || lock_destroy(&c->glock) != 0 ||
+        lock_destroy(&c->mlock) != 0 || lock_destroy(&c->slock) != 0)
+      error("Failed to destroy spinlocks.");
 
     /* Clear this cell's sort arrays. */
     if (c->sort != NULL) free(c->sort);
@@ -2186,11 +2281,15 @@ void space_recycle_list(struct space *s, struct cell *list_begin,
   /* Lock the space. */
   lock_lock(&s->lock);
 
-  /* Hook this cell into the buffer. */
-  list_end->next = s->cells_sub;
-  s->cells_sub = list_begin;
+  /* Hook the cells into the buffer. */
+  cell_list_end->next = s->cells_sub;
+  s->cells_sub = cell_list_begin;
   s->tot_cells -= count;
 
+  /* Hook the multipoles into the buffer. */
+  multipole_list_end->next = s->multipoles_sub;
+  s->multipoles_sub = multipole_list_begin;
+
   /* Unlock the space. */
   lock_unlock_blind(&s->lock);
 }
@@ -2214,7 +2313,7 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) {
   /* For each requested cell... */
   for (int j = 0; j < nr_cells; j++) {
 
-    /* Is the buffer empty? */
+    /* Is the cell buffer empty? */
     if (s->cells_sub == NULL) {
       if (posix_memalign((void *)&s->cells_sub, cell_align,
                          space_cellallocchunk * sizeof(struct cell)) != 0)
@@ -2226,10 +2325,28 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) {
       s->cells_sub[space_cellallocchunk - 1].next = NULL;
     }
 
+    /* Is the multipole buffer empty? */
+    if (s->gravity && s->multipoles_sub == NULL) {
+      if (posix_memalign((void *)&s->multipoles_sub, multipole_align,
+                         space_cellallocchunk * sizeof(struct multipole)) != 0)
+        error("Failed to allocate more multipoles.");
+
+      /* Constructed a linked list */
+      for (int k = 0; k < space_cellallocchunk - 1; k++)
+        s->multipoles_sub[k].next = &s->multipoles_sub[k + 1];
+      s->multipoles_sub[space_cellallocchunk - 1].next = NULL;
+    }
+
     /* Pick off the next cell. */
     cells[j] = s->cells_sub;
     s->cells_sub = cells[j]->next;
     s->tot_cells += 1;
+
+    /* Hook the multipole */
+    if (s->gravity) {
+      cells[j]->multipole = s->multipoles_sub;
+      s->multipoles_sub = cells[j]->multipole->next;
+    }
   }
 
   /* Unlock the space. */
@@ -2237,9 +2354,12 @@ void space_getcells(struct space *s, int nr_cells, struct cell **cells) {
 
   /* Init some things in the cell we just got. */
   for (int j = 0; j < nr_cells; j++) {
+    struct multipole *temp = cells[j]->multipole;
     bzero(cells[j], sizeof(struct cell));
+    cells[j]->multipole = temp;
     cells[j]->nodeID = -1;
-    if (lock_init(&cells[j]->lock) != 0 || lock_init(&cells[j]->glock) != 0)
+    if (lock_init(&cells[j]->lock) != 0 || lock_init(&cells[j]->glock) != 0 ||
+        lock_init(&cells[j]->mlock) != 0 || lock_init(&cells[j]->slock) != 0)
       error("Failed to initialize cell spinlocks.");
   }
 }
@@ -2535,7 +2655,7 @@ void space_init(struct space *s, const struct swift_params *params,
   /* Init the space lock. */
   if (lock_init(&s->lock) != 0) error("Failed to create space spin-lock.");
 
-  /* Build the cells and the tasks. */
+  /* Build the cells recursively. */
   if (!dry_run) space_regrid(s, verbose);
 }
 
@@ -2707,6 +2827,7 @@ void space_clean(struct space *s) {
 
   for (int i = 0; i < s->nr_cells; ++i) cell_clean(&s->cells_top[i]);
   free(s->cells_top);
+  free(s->multipoles_top);
   free(s->parts);
   free(s->xparts);
   free(s->gparts);
diff --git a/src/space.h b/src/space.h
index 327f0225085663d1859bcdbd82a6ac2583fc2b43..cb16d7df56d4fb4c9f0206de7abb8216aacc1de9 100644
--- a/src/space.h
+++ b/src/space.h
@@ -102,6 +102,12 @@ struct space {
   /*! Buffer of unused cells for the sub-cells. */
   struct cell *cells_sub;
 
+  /*! The multipoles associated with the top-level (level 0) cells */
+  struct multipole *multipoles_top;
+
+  /*! Buffer of unused multipoles for the sub-cells. */
+  struct multipole *multipoles_sub;
+
   /*! The total number of parts in the space. */
   size_t nr_parts, size_parts;
 
@@ -186,8 +192,10 @@ void space_sparts_sort_mapper(void *map_data, int num_elements,
                               void *extra_data);
 void space_rebuild(struct space *s, int verbose);
 void space_recycle(struct space *s, struct cell *c);
-void space_recycle_list(struct space *s, struct cell *list_begin,
-                        struct cell *list_end);
+void space_recycle_list(struct space *s, struct cell *cell_list_begin,
+                        struct cell *cell_list_end,
+                        struct multipole *multipole_list_begin,
+                        struct multipole *multipole_list_end);
 void space_split(struct space *s, struct cell *cells, int nr_cells,
                  int verbose);
 void space_split_mapper(void *map_data, int num_elements, void *extra_data);