diff --git a/theory/Multipoles/fmm_summary.tex b/theory/Multipoles/fmm_summary.tex
index 6a24cef51110bbe2f3401207c85d04f52e4261f9..9fd540282fe4478fa8b8b147ecf2e8e724972d26 100644
--- a/theory/Multipoles/fmm_summary.tex
+++ b/theory/Multipoles/fmm_summary.tex
@@ -179,26 +179,35 @@ the other kernels:
   a_x(\mathbf{x}_a) &= G\sum_{\mathbf{m}}^p \mathsf{M}_{\mathbf{m}} \mathsf{D}_{\mathbf{m}+\left(1,0,0\right)}(\mathbf{R}).
   \label{eq:fmm:M2P}
 \end{align}
-A traditional tree-code uses solely that kernel to obtain the forces
-from the multipoles (or often just monopoles, i.e. setting $p=0$ throughout)
-to the particles.\\
-All the kernels (Eqs.~\ref{eq:fmm:P2M}-\ref{eq:fmm:M2P}) are rather
-straightforward to evaluate as they are only made of additions and
-multiplications (provided $\mathsf{D}$ can be evaluated quickly),
-which are extremely efficient instructions on modern architectures
-(see Appendix \ref{sec:pot_derivatives} for the full
-expressions). However, the fully expanded sums can lead to rather
-large and prone to typo expressions. To avoid any mishaps, we use a
-\texttt{python} script to generate C code in which all the sums are
-unrolled and correct by construction. In \swift, we implemented the
-kernels up to order $p=5$, as it proved to be accurate enough for our
-purpose, but this could be extended to higher order easily. This
-implies storing $56$ numbers per cell for each $\textsf{M}$ and
-$\textsf{F}$ plus three numbers for the location of the centre of
-mass. For leaf-cells with large numbers of particles, as in \swift,
-this is a small memory overhead. One further small improvement
-consists in choosing $\mathbf{z}_A$ to be the centre of mass of cell
-$A$ rather than its geometrical centre. The first order multipoles
+A traditional tree-code uses solely that kernel to obtain the forces from
+the multipoles (or often just monopoles, i.e. setting $p=0$ throughout) to
+the particles. Similarly, the field tensor of a cell can receive the
+contribution from a single particle at a distance $\mathbf{R} \equiv
+\mathbf{z}_A - \mathbf{x}_b$ via the P2L kernel:
+\begin{equation}
+  \mathsf{F}_{\mathbf{n}}(\mathbf{z}_A) = G m_b
+  \mathsf{D}_{\mathbf{n}}(\mathbf{R}).
+  \label{eq:fmm:P2L}
+\end{equation}
+The M2P and P2L kernels can be used to speed up the calculations
+involving only as single particle.  All the kernels
+(Eqs.~\ref{eq:fmm:P2M}-\ref{eq:fmm:P2L}) are rather straightforward to
+evaluate as they are only made of additions and multiplications
+(provided $\mathsf{D}$ can be evaluated quickly), which are extremely
+efficient instructions on modern architectures (see Appendix
+\ref{sec:pot_derivatives} for the full expressions). However, the
+fully expanded sums can lead to rather large and prone to typo
+expressions. To avoid any mishaps, we use a \texttt{python} script to
+generate C code in which all the sums are unrolled and correct by
+construction. In \swift, we implemented the kernels up to order $p=5$,
+as it proved to be accurate enough for our purpose, but this could be
+extended to higher order easily. This implies storing $56$ numbers per
+cell for each $\textsf{M}$ and $\textsf{F}$ plus three numbers for the
+location of the centre of mass. For leaf-cells with large numbers of
+particles, as in \swift, this is a small memory overhead. One further
+small improvement consists in choosing $\mathbf{z}_A$ to be the centre
+of mass of cell $A$ rather than its geometrical centre. The first
+order multipoles
 ($\mathsf{M}_{100},\mathsf{M}_{010},\mathsf{M}_{001}$) then vanish by
 construction. This allows us to simplify some of the expressions and
 helps reduce, albeit by a small fraction, the memory footprint of the
diff --git a/theory/Multipoles/generate_multipoles/multipoles.py b/theory/Multipoles/generate_multipoles/multipoles.py
index 9c9f5513a728ca4fad5ad8771cf6fde1603f59f9..9c3134bfdb19437ad7e536d7e8069c028f2a4667 100644
--- a/theory/Multipoles/generate_multipoles/multipoles.py
+++ b/theory/Multipoles/generate_multipoles/multipoles.py
@@ -240,7 +240,10 @@ print("-------------------------------------------------\n")
 if order > 0:
     print("#if SELF_GRAVITY_MULTIPOLE_ORDER > %d" % (order - 1))
 
-print("/* Shift %s order terms (1st order mpole (all 0) commented out) */" % ordinal(order))
+print(
+    "/* Shift %s order terms (1st order mpole (all 0) commented out) */"
+    % ordinal(order)
+)
 
 # Create all the terms relevent for this order
 for i in range(order + 1):
@@ -324,6 +327,26 @@ if order > 0:
     print("#endif")
 
 
+print("")
+print("-------------------------------------------------")
+
+print("gravity_P2L():")
+print("-------------------------------------------------\n")
+
+if order > 0:
+    print("#if SELF_GRAVITY_MULTIPOLE_ORDER > %d\n" % (order - 1))
+
+# Loop over LHS order
+for i in range(order + 1):
+    for j in range(order + 1):
+        for k in range(order + 1):
+            if i + j + k == order:
+                print("l_b->F_%d%d%d += m * D_%d%d%d;" % (i, j, k, i, j, k))
+
+if order > 0:
+    print("#endif")
+
+
 print("")
 print("-------------------------------------------------")