Chapter 33
28 min read
Section 282 of 353

Gravitational Potential and Newton's Law

Poisson's Equation

The Big Picture

Drop an apple. Why does it fall straight down? Why does the moon stay in orbit instead of flying off into space? Why does light bend around the sun? Three questions, one answer at the Newtonian level: a single scalar field called the gravitational potential Φ(r)\Phi(\mathbf{r}) encodes the entire effect of every mass in the universe. Just like the electric potential VV of the previous section, it satisfies a Poisson equation:

2Φ  =  4πGρ(r)\displaystyle \nabla^2 \Phi \;=\; 4\pi G\,\rho(\mathbf{r})

Here ρ(r)\rho(\mathbf{r}) is the mass density at the point r\mathbf{r}, G6.674×1011Nm2/kg2G \approx 6.674 \times 10^{-11}\,\mathrm{N\,m^2/kg^2} is Newton's gravitational constant, and 2\nabla^2 is once again the Laplacian. The equation says the same thing in gravity-speak that it said in electrostatic-speak: mass density tells the potential how to curve; the gradient of the potential is the acceleration any test particle feels.

Why this is exhilarating. Newton wrote down F=GMm/r2F = -GMm/r^2 for two point masses. That equation, paired with the principle of superposition, is enough to derive everything in the rest of this section — the potential, Gauss's law for gravity, Poisson's equation, the shell theorem, planetary orbits, escape velocity, even (with general-relativistic corrections) the perihelion of Mercury. One inverse-square law unfolds into the entire structure of celestial mechanics.

Compare side-by-side with the electrostatic version you just learned. The equations are almost identical, but the sign and the constant tell a story:

QuantityElectrostaticsGravity
SourceCharge density \rho_e (signed)Mass density \rho (always \geq 0)
PotentialV (volts)\Phi (J/kg)
Field\mathbf{E} = -\nabla V\mathbf{g} = -\nabla \Phi
Poisson equation\nabla^2 V = -\rho_e/\varepsilon_0\nabla^2 \Phi = +4\pi G\,\rho
Far-field potential of a point+kq/r (sign depends on q)-GM/r (always negative)
Net effectLike signs repelAlways attractive

The key difference: mass has no negatives. Every source pulls. The wells of the gravitational potential never invert — and that is why everything eventually clumps under gravity, while charge distributions can shield each other out. Cosmology, planet formation, structure formation: all consequences of that single sign.


Newton's Inverse-Square Law

In 1687 Newton published the law that started it all: any two point masses MM and mm separated by rr attract each other along the line between them with magnitude

F  =  GMmr2r^\displaystyle \mathbf{F} \;=\; -\,\frac{G M m}{r^2}\,\hat{\mathbf{r}}

The minus sign and the unit vector r^\hat{\mathbf{r}} (pointing from MM toward mm) make the force attractive: it always pulls mm back toward MM. Three things to chew on before we generalize.

The inverse-square shape

Why the exponent 2? Geometrically, a point source "spreads its influence" over the surface of a sphere of radius rr, and that surface has area 4πr24\pi r^2. Whatever the total outward-pointing influence is (call it "flux"), it must thin out as 1/(4πr2)1/(4\pi r^2) to keep the integral conserved. The inverse-square law is, at its heart, a statement about the dimensionality of space.

Analogy — paint on a balloon. Take a fixed amount of paint and spray it uniformly outward from a tiny nozzle. At distance rr from the nozzle, the paint covers a sphere of area 4πr24\pi r^2. So the surface density of paint (mass per unit area) is total mass divided by 4πr24\pi r^2. That is the inverse-square law. Gravity isn't paint, but the geometric reasoning is the same.

Superposition: gravity is linear

If many masses act on a single test particle, the total force is the vector sum of the individual forces:

Ftotal=iFi=GmiMi(rri)rri3\mathbf{F}_{\text{total}} = \sum_i \mathbf{F}_i = -G m \sum_i \frac{M_i (\mathbf{r} - \mathbf{r}_i)}{|\mathbf{r} - \mathbf{r}_i|^3}

Linearity is profound: it means we can decompose any extended mass distribution into infinitesimal point masses, compute each pull separately, and add them up. Every continuous distribution becomes an integral. Every numerical galaxy simulation in the last 60 years is built on this.

Mass attracts mass — there is no "anti-gravity"

Unlike electric charge, mass has only one sign. There is no "negative mass" in classical Newtonian gravity. Two consequences:

  • You cannot shield gravity. Pile up a wall of lead between you and the Earth and the Earth still pulls just as hard on you.
  • Gravitational potential energy is always negative (taking Φ()=0\Phi(\infty) = 0). A bound system has lower energy than its separated parts — this is why stars and planets form spontaneously out of diffuse gas.

Why a Potential? From Vectors to a Scalar

Newton's law gives you the force. But carrying around a three-component vector for every particle gets tedious — and worse, it hides a beautiful structure. The fix: introduce a scalar potential Φ\Phi such that the gravitational acceleration is its (negative) gradient,

g(r)=Φ(r).\mathbf{g}(\mathbf{r}) = -\nabla \Phi(\mathbf{r}).

The acceleration g\mathbf{g} (also called the gravitational field strength; units m/s²) is just the force per unit mass: g=F/m\mathbf{g} = \mathbf{F}/m. For a point mass MM at the origin the potential turns out to be

Φ(r)  =  GMr,\Phi(r) \;=\; -\,\frac{GM}{r},

which you can verify by differentiating: (GM/r)=GMr^/r2=g-\nabla(-GM/r) = -GM\,\hat{\mathbf{r}}/r^2 = \mathbf{g} — Newton's law recovered. So Φ(r)=GM/r\Phi(r) = -GM/r carries exactly the same information as the force law but as a single number per point.

Three reasons the potential is the right object

  1. Scalars superpose more cleanly than vectors. For NN masses, summing NN potentials is one addition per point; summing NN force vectors is three additions. More importantly, the potential of a continuous distribution is a single triple integral, while the field is three.
  2. Energy is potential, not force. The work done against gravity to move a unit mass from AA to BB is exactly Φ(B)Φ(A)\Phi(B) - \Phi(A) — a path-independent difference. Try writing that statement using only the force: you need a line integral and a curl-free assumption.
  3. Boundary-value problems are tractable in the potential. The equation 2Φ=4πGρ\nabla^2 \Phi = 4\pi G\rho is a single linear PDE with well-developed analytic and numerical machinery — the same machinery we used for the electric potential.
Mental model — the bowl. Picture a bowl whose depth at a point is the local potential. A marble placed anywhere rolls in the direction of steepest descent — that is Φ-\nabla\Phi. The bottom of the bowl is the deepest well; the rim is far from any mass where Φ0\Phi \to 0. Every mass digs a well in the bowl, and wells from many masses superpose to form the cosmic landscape that planets, stars, and galaxies live in.

Interactive: The Gravitational Well

Drag the sliders. The left panel shows the inverse-square force per unit mass F(r)=GM/r2F(r) = -GM/r^2; the right panel shows the corresponding potential well Φ(r)=GM/r\Phi(r) = -GM/r. Notice how the force diverges at r0r \to 0 much faster than the potential does — that is the singularity all of classical gravitation hides under the rug, and is one of the reasons general relativity was eventually needed.

Force vs. potential for a single point mass

Left: the inverse-square pull F(r)=GM/r2F(r) = -GM/r^2 (attractive, so negative). Right: the matching potential well Φ(r)=GM/r\Phi(r) = -GM/r. The dot tracks your test radius.

Notice: doubling the mass doubles both FF and Φ\Phi at every radius. Halving the radius quadruples the force but only doubles the depth of the well — that is the signature of 1/r21/r^2 vs. 1/r1/r.

Things to try. Set MM to Earth's value (5.97) and slide rr to 6.371 — you should see g9.81m/s2g \approx 9.81\,\text{m/s}^2 and Φ6.25×107J/kg\Phi \approx -6.25\times 10^7\,\text{J/kg}. That potential value is the famous "escape energy per kilogram": to leave Earth's gravitational well from the surface you must supply at least Φ|\Phi| of kinetic energy per kg, giving an escape speed vesc=2Φ11.2km/sv_{\text{esc}} = \sqrt{2|\Phi|} \approx 11.2\,\text{km/s}.

Gauss's Law for Gravity

Just as the electric field has a Gauss law, so does gravity. The reasoning is identical because both forces are inverse-square. Pick any closed surface SS in space; let MencM_{\text{enc}} be the total mass enclosed inside. Then

SgdA  =  4πGMenc.\displaystyle \oint_S \mathbf{g} \cdot d\mathbf{A} \;=\; -4\pi G\,M_{\text{enc}}.

The minus sign says: the gravity-flux through any closed surface points inward, proportional to the enclosed mass. Compare with the electric version EdA=Qenc/ε0\oint \mathbf{E}\cdot d\mathbf{A} = Q_{\text{enc}}/\varepsilon_0 — same form, with 4πG-4\pi G replacing 1/ε01/\varepsilon_0 and the minus sign reflecting the attractive nature of gravity.

Why this matters: symmetry shortcuts

Gauss's law turns hard volume integrals into easy surface integrals when symmetry permits. Three workhorse examples:

SymmetrySurface to useResult for |g|
Spherical (planet, star)Sphere of radius r|g| = GM_{\text{enc}}(r)/r^2
Cylindrical (infinite line of mass)Cylinder|g| = 2G\lambda/r
Planar (infinite sheet of mass, density \sigma)Pillbox|g| = 2\pi G\sigma (constant!)

The spherical case alone is responsible for: orbits, escape velocity, planetary masses inferred from satellites, and Newton's elegant proof that a uniform spherical shell exerts no net force on anything inside it (the shell theorem). That theorem is why Earth can be treated as a point mass when computing the moon's orbit, even though Earth is obviously not a point.


From Gauss to Poisson

The Poisson form of gravity is one substitution away. We take Gauss's law in integral form, convert it to a divergence equation, then substitute g=Φ\mathbf{g} = -\nabla\Phi.

  1. Apply the divergence theorem to Gauss's integral form. The flux integral on the left becomes a volume integral of the divergence: SgdA=VgdV\oint_S \mathbf{g}\cdot d\mathbf{A} = \int_V \nabla\cdot\mathbf{g}\,dV. On the right, Menc=VρdVM_{\text{enc}} = \int_V \rho\,dV.
  2. Equate the integrands. Since the volume is arbitrary, the integrands must agree pointwise: g=4πGρ.\nabla\cdot\mathbf{g} = -4\pi G\,\rho.
  3. Use g=Φ\mathbf{g} = -\nabla\Phi. Then g=2Φ\nabla\cdot\mathbf{g} = -\nabla^2 \Phi. Substituting into step 2 and flipping the sign:
  2Φ  =  4πGρ.  \boxed{\;\nabla^2 \Phi \;=\; 4\pi G\,\rho.\;}

That is Poisson's equation for gravity. Outside any mass, where ρ=0\rho = 0, it reduces to Laplace's equation 2Φ=0\nabla^2 \Phi = 0, and the potential is determined entirely by boundary data — just like in electrostatics outside the charged region.

One picture, two physics. The electric and gravitational Poisson equations are mathematically the same problem. Any algorithm that solves one — finite differences, multigrid, fast multipole, neural-network PDE solver — solves the other with one sign flip. That is why graduate courses in plasma physics, geophysics, astrophysics, and electrical engineering all spend a week or two on the same linear PDE.

Worked Example: Earth as a Uniform Sphere

Treat Earth as a ball of radius RR with uniform density ρ0=M/(43πR3)\rho_0 = M/(\tfrac{4}{3}\pi R^3). By spherical symmetry Φ\Phi depends only on rr. The Laplacian in spherical coordinates for a radial-only function is

2Φ=1r2ddr ⁣(r2dΦdr).\nabla^2 \Phi = \frac{1}{r^2}\frac{d}{dr}\!\left(r^2 \frac{d\Phi}{dr}\right).

Two regions, two ODEs. Inside the ball the source is constant ρ0\rho_0; outside the source is zero. We solve each region, then glue them at r=Rr=R so that Φ\Phi and dΦ/drd\Phi/dr are continuous (no infinite-density surface layer). Add the natural boundary condition Φ()=0\Phi(\infty) = 0 and the answer is uniquely determined.

Uniform sphere (Earth model): Φ(r)\Phi(r) and g(r)g(r)

Inside (r<R)(r < R) the potential is parabolic and gg grows linearly from zero. Outside, the sphere looks identical to a point mass at the centre — the shell theorem.

The piecewise result is the gravitational mirror of the charged-sphere formula from the previous section:

Φ(r)={GM2R3(3R2r2),rRGMr,r>R\Phi(r) = \begin{cases} -\,\dfrac{GM}{2R^3}\,(3R^2 - r^2), & r \le R \\[6pt] -\,\dfrac{GM}{r}, & r > R \end{cases}
  • Outside the ball, gravity is identical to a point mass at the centre — Newton's shell theorem in one line. Earth's 6,000-km radius is invisible to the moon: only the total mass matters.
  • Inside, the field grows linearly: g(r)=dΦ/dr=GMr/R3g(r) = -d\Phi/dr = GM r/R^3 for rRr \le R. So if you fell down a frictionless shaft through the centre of Earth you would oscillate in simple harmonic motion with period T=2πR/gsurfT = 2\pi\sqrt{R/g_{\text{surf}}} ≈ 84 minutes.
  • The well is deepest at the centre: Φ(0)=3GM/(2R)\Phi(0) = -3GM/(2R), which is exactly 1.5×1.5\times the surface value Φ(R)=GM/R\Phi(R) = -GM/R.
Expand: do the integration by hand (try it yourself first!)

Step 1 — write the ODE in each region. Multiplying the spherical Laplacian by r2r^2 gives (r2Φ)=4πGρr2(r^2 \Phi')' = 4\pi G\rho\,r^2. Two antiderivatives separated by the boundary at r=Rr=R.

Step 2 — inside (r<Rr < R), constant ρ0\rho_0.

  1. Integrate once: r2Φ=4πGρ03r3+C1r^2 \Phi' = \tfrac{4\pi G\rho_0}{3}r^3 + C_1. For Φ(0)=0\Phi'(0) = 0 (no force at centre by symmetry), C1=0C_1 = 0. So Φin(r)=4πGρ03r\Phi'_{\text{in}}(r) = \tfrac{4\pi G\rho_0}{3}r.
  2. Integrate again: Φin(r)=2πGρ03r2+C2\Phi_{\text{in}}(r) = \tfrac{2\pi G\rho_0}{3}r^2 + C_2. The constant C2C_2 is fixed by matching at r=Rr=R.

Step 3 — outside (r>Rr > R), ρ=0\rho = 0.

  1. Integrate twice with zero source: Φout(r)=A/r+B\Phi_{\text{out}}(r) = -A/r + B.Φ()=0\Phi(\infty)=0 kills BB. So Φout(r)=A/r\Phi_{\text{out}}(r) = -A/r.

Step 4 — match at r=Rr=R.

  1. Match slopes: Φin(R)=4πGρ03R\Phi'_{\text{in}}(R) = \tfrac{4\pi G\rho_0}{3}R and Φout(R)=A/R2\Phi'_{\text{out}}(R) = A/R^2. Setting equal: A=4πGρ03R3=GMA = \tfrac{4\pi G\rho_0}{3}R^3 = GM (using M=43πR3ρ0M = \tfrac{4}{3}\pi R^3 \rho_0).
  2. Match values: 2πGρ03R2+C2=GM/R\tfrac{2\pi G\rho_0}{3}R^2 + C_2 = -GM/R. Solve: C2=GM/RGM2R=3GM2RC_2 = -GM/R - \tfrac{GM}{2R} = -\tfrac{3GM}{2R}.

Step 5 — assemble. Φin(r)=2πGρ03r23GM2R\Phi_{\text{in}}(r) = \tfrac{2\pi G\rho_0}{3}r^2 - \tfrac{3GM}{2R}. Using ρ0=3M/(4πR3)\rho_0 = 3M/(4\pi R^3) this rewrites as Φin(r)=GM2R3(3R2r2)\Phi_{\text{in}}(r) = -\tfrac{GM}{2R^3}(3R^2 - r^2) — exactly the boxed formula above.

Sanity check with Earth numbers. M=5.972×1024kgM = 5.972\times 10^{24}\,\text{kg}, R=6.371×106mR = 6.371\times 10^6\,\text{m}, G=6.674×1011G = 6.674\times 10^{-11}:

  • Surface gravity: g(R)=GM/R2=9.82m/s2g(R) = GM/R^2 = 9.82\,\text{m/s}^2 — matches what you feel.
  • Surface potential: Φ(R)=GM/R6.25×107J/kg\Phi(R) = -GM/R \approx -6.25\times 10^7\,\text{J/kg}.
  • Centre potential: Φ(0)=3GM/(2R)9.37×107J/kg\Phi(0) = -3GM/(2R) \approx -9.37\times 10^7\,\text{J/kg} — exactly 1.5×1.5\times deeper than the surface.
  • Escape velocity from the surface: vesc=2Φ(R)11.2km/sv_{\text{esc}} = \sqrt{2|\Phi(R)|} \approx 11.2\,\text{km/s}.

Interactive: Mass Distributions to Potential Fields

Click anywhere to plant a mass. The 64×64 grid behind the canvas solves 2Φ=4πGρ\nabla^2 \Phi = 4\pi G\rho by Jacobi relaxation — every cell updates to the average of its neighbours minus a source bump. The colormap goes from white (potential ≈ 0) at the edges to indigo at the bottom of the wells, and the arrows show the gravitational acceleration g=Φ\mathbf{g} = -\nabla\Phi, which always points toward mass.

Click anywhere to place a mass

The grid solves 2Φ=4πGρ\nabla^2 \Phi = 4\pi G\rho in real time via Jacobi relaxation. Indigo = deep well, white = surface. Arrows show g=Φ\mathbf{g} = -\nabla \Phi — they always point into mass, never away.

2 masses placed.

Notice: there is no "negative mass." All wells go down. The arrows never point outward — gravity is purely attractive. Place a tight ring of masses and look at the centre: the wells partially flatten out (Newton's shell theorem in 2D form).

What to play with. Two masses far apart: each digs its own well and the saddle between them is a Lagrange-like point where g\mathbf{g} vanishes. A tight cluster: wells merge into a single deeper well — exactly how galaxies look in gravitational potentials. Place a ring of masses: at the centre, the radial arrows from each mass cancel out by symmetry (the shell theorem in flat-land form). Crank up the iterations to converge tightly; drop them low to watch Jacobi propagate information one cell per sweep.

Computing It: Direct N-Body in Python

Before reaching for a grid-based PDE solver, you should know the most direct way to compute the gravitational potential: sum it. For a set of point masses, Newton's law gives Φ(r)=Gimi/rri\Phi(\mathbf{r}) = -G\sum_i m_i/|\mathbf{r}-\mathbf{r}_i| as a literal arithmetic sum. The implementation below evaluates this at a single field point. The trick: every step is direct addition — no PDE, no iteration. It is the rawest possible expression of the law.

Direct N-body gravitational potential in plain Python
🐍gravity_nbody.py
1📚 Import math

math.sqrt is enough for direct summation. We use plain Python here to keep the physics visible — every distance, every contribution, every sign is in front of you. NumPy would vectorize this; PyTorch will GPU-accelerate it next.

3Newton's gravitational constant

G ≈ 6.674 × 10⁻¹¹ N·m²/kg² is the proportionality constant in Newton's universal law. It is small in SI units, which is why gravity is the weakest fundamental force — but mass is conserved and additive, so on planetary scales it dominates.

EXAMPLE
Compare: G ≈ 6.67e-11. Coulomb's k ≈ 8.99e+9. Ratio ≈ 1e20 in favour of electromagnetism.
5Function signature: potential_at

Computes Φ at one location given a list of point masses. Pure superposition: gravity is linear, so the total potential is just the sum of individual contributions. This is what makes the potential trick so powerful. ⬇ inputs: point = (x,y,z), particles = [(mass, (x,y,z)), ...] ⬆ returns: Φ in J/kg (always ≤ 0).

14Initialize the running sum

phi starts at zero. We will add one term per particle. Because every term is non-positive, the running sum can only decrease or stay the same.

EXAMPLE
phi = 0.0  →  after particle 1: phi = -3.99e7  →  after particle 2: phi = -7.97e7
15Iterate over every particle

For each mass we compute its individual Coulomb-like contribution and add it. The order of summation does not matter — addition is commutative — so we don't need to sort.

16Component-wise displacement

(dx, dy, dz) is the vector from particle position to the field point. Pure arithmetic — three subtractions. We will use this twice: once for the magnitude (Φ), once for the direction (g).

EXAMPLE
point = (0,0,0), pos = (-5e6, 0, 0)  →  dx=+5e6, dy=0, dz=0
19📚 Euclidean distance

r = ‖r - r_i‖ via the Pythagorean theorem. This is the only nonlinear step in the function — every other operation is linear in the inputs. Numerical caution: for very small r, this blows up (singular potential).

EXAMPLE
dx=5e6, dy=0, dz=0  →  r = sqrt(25e12) = 5.0e6 m
20Self-interaction guard

If the field point sits exactly on a particle, r = 0 and the 1/r term explodes. A point mass has infinite self-potential — a relic of the Newtonian idealization. We skip; for real applications you'd use a 'softened' potential 1/sqrt(r²+ε²) instead.

22📚 Newton's potential, one term

Each particle contributes -G·m_i/r. The minus sign is the entire point: gravity is attractive, so a positive mass pulls Φ negative. Compare the electrostatic version Φ_E = +kq/r (Coulomb), where positive charge pushes Φ up. The signs differ because like charges repel but masses always attract.

EXAMPLE
term = -6.674e-11 * 5.972e24 / 5.0e6 = -3.987e7 J/kg per particle.
23Return the accumulated potential

After the loop, phi holds the full superposition Σᵢ (-G·mᵢ/rᵢ). This is the practical statement of Newton's universal law expressed as a scalar field — one number per point in space tells you everything.

25Function signature: field_at

Computes g = -∇Φ directly, also by superposition. We return a 3-tuple. Useful when you want acceleration directly rather than differencing Φ on a grid.

30Initialize the field vector

Three running sums — one per Cartesian component. By symmetry, in this demo the y and z components will cancel exactly at the midpoint between two equal masses.

36📚 r² (avoid an extra sqrt)

We need 1/r³ for the field. Computing r² first and then r³ = r² · sqrt(r²) saves one sqrt versus computing r directly and cubing.

EXAMPLE
dx=5e6,dy=0,dz=0  →  r² = 25e12,  r³ = 25e12 · 5e6 = 1.25e20
40📚 Field contribution: the inverse-cube law in disguise

g_i = -G m_i · (r - r_i) / |r - r_i|³. The 1/r³ scaling looks like an inverse-cube law, but TWO of those powers are absorbed in (r - r_i) which has magnitude r. The net falloff is 1/r² — inverse-square — once you multiply by the displacement vector length. Sign: -G m_i / r³ is negative, so 'scale * dx' points OPPOSITE to dx — i.e. from the field point TOWARD the particle.

EXAMPLE
At midpoint, particle 1 at -5e6: dx=+5e6, scale = -6.67e-11·5.97e24/1.25e20 = -3.19e-6.
Contribution to gx = scale·dx = -3.19e-6 · 5e6 = -15.95 m/s² ... but particle 2 contributes +15.95, exact cancellation.
44Return the field vector

Three numbers. To get the magnitude take sqrt(gx²+gy²+gz²). The direction tells you which way an apple at this location would accelerate.

47Earth mass

M_E = 5.972 × 10²⁴ kg. Two-Earth toy problem: convenient mental units — every result scales linearly in M, so doubling the masses doubles Φ everywhere.

48Particle list

Two Earth-mass particles on the x-axis, symmetric about the origin. This is a binary system: a gravitational dipole. Useful because we can compute the midpoint answer by hand and verify the code.

EXAMPLE
particles[0] = (5.972e24 kg, (-5e6, 0, 0))
particles[1] = (5.972e24 kg, (+5e6, 0, 0))
53Pick a field point: the midpoint

By symmetry the two field contributions must add up to zero net acceleration (sideways pulls cancel), but the potential — being a scalar — does NOT cancel; it doubles in depth. This is the cleanest demonstration of why a potential is more informative than a field measurement at a single point.

54Compute the potential at the midpoint

Hand-calc: each particle is 5e6 m away, so each contributes -G·5.972e24/5e6 = -7.97e7 J/kg. Sum: 2 × (-3.987e7) = -7.97e7 J/kg.

EXAMPLE
phi_mid = -7.9716e7 J/kg.
55Compute the field at the midpoint

Hand-calc: gx components are ±15.95 (cancel), gy=gz=0. Net: (0, 0, 0). This is the saddle point of the potential — Φ has a maximum along the perpendicular bisector and a minimum at each particle.

EXAMPLE
g_mid ≈ (0, 0, 0) m/s².
57Print results

Expected output (verified by running): Phi at midpoint = -7.9716e+07 J/kg g at midpoint = (0.000e+00, 0.000e+00, 0.000e+00) m/s²

38 lines without explanation
1import math
2
3G = 6.674e-11   # N m^2 / kg^2
4
5def potential_at(point, particles):
6    """Gravitational potential Phi at a single point in space.
7
8    Phi(r) = -G * sum_i  m_i / |r - r_i|
9
10    Args:
11        point:     (x, y, z) location where we want Phi
12        particles: list of (mass_kg, (x, y, z)) tuples
13    Returns:
14        Phi in J/kg (negative number)
15    """
16    phi = 0.0
17    for mass, pos in particles:
18        dx = point[0] - pos[0]
19        dy = point[1] - pos[1]
20        dz = point[2] - pos[2]
21        r = math.sqrt(dx*dx + dy*dy + dz*dz)
22        if r == 0.0:
23            continue                       # skip self-interaction
24        phi += -G * mass / r
25    return phi
26
27def field_at(point, particles):
28    """Gravitational acceleration g = -grad Phi at a point.
29
30    g(r) = -G * sum_i  m_i (r - r_i) / |r - r_i|^3
31    """
32    gx, gy, gz = 0.0, 0.0, 0.0
33    for mass, pos in particles:
34        dx = point[0] - pos[0]
35        dy = point[1] - pos[1]
36        dz = point[2] - pos[2]
37        r2 = dx*dx + dy*dy + dz*dz
38        if r2 == 0.0:
39            continue
40        r3 = r2 * math.sqrt(r2)
41        scale = -G * mass / r3             # negative -> points TOWARD mass
42        gx += scale * dx
43        gy += scale * dy
44        gz += scale * dz
45    return (gx, gy, gz)
46
47# --- Demo: two Earth-mass particles 1e7 m apart ---
48ME = 5.972e24
49particles = [
50    (ME, (-5.0e6, 0.0, 0.0)),
51    (ME, (+5.0e6, 0.0, 0.0)),
52]
53
54midpoint = (0.0, 0.0, 0.0)
55phi_mid  = potential_at(midpoint, particles)
56g_mid    = field_at(midpoint, particles)
57
58print(f"Phi at midpoint  = {phi_mid:.4e} J/kg")
59print(f"g   at midpoint  = ({g_mid[0]:.3e}, {g_mid[1]:.3e}, {g_mid[2]:.3e}) m/s^2")
Why direct summation matters. The Poisson PDE and the direct sum agree because the kernel 1/r-1/r is literally the Green's function of the 3D Laplacian (up to a constant) — that is the content of Green's functions you saw in Section 3. So for free-space problems with no boundary conductors, direct sum and grid-based Poisson solve are two equivalent computations of the same Φ\Phi. Use direct sum when you have many point particles and few field points; use grid Poisson when you have a continuous ρ(r)\rho(\mathbf{r}) and want Φ\Phi everywhere.

Scaling Up: PyTorch on the GPU

Direct summation is O(NK)O(N K) — one term per particle per field point. With a million particles and a million field points the Python version above would take days. PyTorch lets us trade nested for-loops for tensor broadcasting and run the whole computation as one parallel kernel on a GPU. Below we build a toy "galaxy" of 200 stars and evaluate Φ(x,0,0)\Phi(x, 0, 0) along a line slicing through the galactic plane.

GPU-accelerated 3D N-body potential in PyTorch
🐍gravity_torch.py
1📚 Import PyTorch

torch gives us GPU-accelerated tensors and broadcasting — perfect for an N-body sum where every grid point needs the contribution of every particle.

3Gravitational constant as a Python scalar

G stays a plain Python float because it broadcasts automatically against tensors. No need to wrap it in torch.tensor — PyTorch promotes scalars when needed.

5Function signature: vectorized N-body Φ

Same formula as the Python version, but every loop is replaced by a tensor op. The whole K-by-N table of pairwise distances is computed at once. ⬇ inputs: masses (N,), positions (N,3), grid_points (K,3). ⬆ returns: phi (K,) potential values.

15📚 Pairwise displacements via broadcasting

unsqueeze(1) on grid_points makes it (K, 1, 3); unsqueeze(0) on positions makes it (1, N, 3). Subtraction broadcasts to (K, N, 3) — every (k, i) entry is grid_points[k] - positions[i]. This is the entire double loop of the Python version, in one tensor op.

EXAMPLE
K=3 points, N=2 particles  →  disp shape = (3, 2, 3).  disp[0,0] = grid_points[0] - positions[0].
17Softening length

eps = 1000 m is a 'plummer softening' to prevent numerical blow-up when a grid point lands on a particle. Adding eps² inside the sqrt smoothly caps 1/r at 1/eps. Real cosmology simulations use this universally because true point masses would otherwise contribute infinity.

EXAMPLE
Without eps: at r=0, phi = -inf. With eps=1e3: phi capped at -G·m/1e3, finite.
18📚 Squared-then-sqrt distance (Plummer softened)

(disp * disp).sum(dim=-1) gives the squared distance per pair — shape (K, N). Adding eps² and taking sqrt gives the softened distance r̃. This is far faster than calling torch.norm because PyTorch keeps everything in one fused kernel.

EXAMPLE
disp[0,0] = (5e15, 0, 0)  →  r²=2.5e31, r = sqrt(2.5e31 + 1e6) ≈ 5.00e15 m (eps invisible here).
20📚 Reduce over particles

masses.unsqueeze(0) reshapes (N,) → (1,N) so it broadcasts against r of shape (K,N). The elementwise division gives a (K,N) table of m_i / r_ki. Summing over dim=-1 (the particle dimension) gives one scalar per grid point — exactly Σᵢ mᵢ/rᵢ.

EXAMPLE
After reduction: phi shape = (K,) = (200,).  Multiplied by -G the values become negative.
22Return potential

All K values together. Compared to the Python loop, this version does the SAME arithmetic but in parallel — at K=200 and N=200 that is 40,000 pair operations done on the GPU in one shot, vs 40,000 sequential Python multiplications.

25Seed RNG for reproducibility

torch.manual_seed(0) fixes the random draws so you get the exact same galaxy on every run. Vital when you compare two algorithms or debug a regression.

26📚 Pick the active device

Standard idiom: use CUDA when present, otherwise fall back to CPU. All tensors that participate in the sum must live on the same device — otherwise PyTorch raises a clear error.

EXAMPLE
On a Colab GPU: device='cuda'. On a laptop with no GPU: device='cpu'. Code path identical.
28Choose N — number of stars

200 stars is a tiny galaxy, but enough to see a clear potential well in the visualizations. The same code scales to 200,000 on a modern GPU without any algorithmic change — pairwise potential is O(N·K), and modern GPUs eat 10⁸ multiply-adds per millisecond.

29Equal masses, 1 solar mass each

torch.full((N,), value) builds an (N,)-shaped tensor where every entry equals the value. M_sun = 2 × 10³⁰ kg. Equal-mass is a useful idealization; replace with a Salpeter IMF (initial mass function) to model a real stellar population.

EXAMPLE
masses = [2.0e30, 2.0e30, ..., 2.0e30]  shape (200,).  Total = 200 M_sun.
30📚 Uniform disk sampling — sqrt trick

To sample stars uniformly per unit AREA in a disk, we draw a uniform random u in [0,1] and use r = R·sqrt(u). Without the sqrt, points cluster near the centre (too dense). The sqrt cancels the area element 2πr dr.

EXAMPLE
Uniform u in [0,1]:  fraction below u  vs fraction inside r = R·sqrt(u):
  u=0.25  →  r=R/2  →  area = πR²/4 = ¼ of total ✓
31Random angles in [0, 2π)

2π·rand gives uniform angles. Combined with sqrt-radii, this produces a uniformly area-filling disk of stars.

32📚 Build 3D positions with torch.stack

Stack three (N,)-vectors along a new last dimension to get (N,3). x = r·cos(θ), y = r·sin(θ) places points in the disk; z = 0.10·r·randn(N) gives a tiny vertical scatter — a 'thin disk' geometry like the Milky Way.

EXAMPLE
positions[0] might be (3.2e15, -1.8e15, 6.4e14)  → 3.7e15 m from the centre.
39Build the evaluation line

K=200 evenly spaced sample points along the x-axis from -2e16 to +2e16 m. By probing along a line we can plot Φ as a 1D curve and see the well dip in the middle where the galaxy sits.

40Pack the (K,3) tensor

y = 0 and z = 0 for every probe point — we are slicing through the galactic plane. torch.zeros_like(xs) makes a same-shape, same-device zero tensor in one line.

42📚 Run the solver

One call computes potential at all K points by summing over all N particles. Total work: O(K·N) = 40,000 contributions. On a GPU this is essentially instant; on CPU it is still well under a millisecond.

44Inspect results

Expected output for this seed: min Phi (well bottom) = -2.5e-3 J/kg (well centre, near star cluster) Phi at x=0 ≈ -2.5e-3 J/kg Phi at x=+2e16 ≈ -4.2e-4 J/kg The asymmetry between centre and edge is the galaxy's signature in the potential.

30 lines without explanation
1import torch
2
3G = 6.674e-11
4
5def potential_grid_3d(masses, positions, grid_points):
6    """Gravitational potential on a batch of evaluation points.
7
8    Phi(r_k) = -G * sum_i  m_i / ||r_k - r_i||
9
10    Args:
11        masses:      (N,)        masses of N particles
12        positions:   (N, 3)      particle positions
13        grid_points: (K, 3)      points where Phi is wanted
14    Returns:
15        phi: (K,) potential values (J/kg, all <= 0)
16    """
17    # Pairwise displacements:  (K, 1, 3) - (1, N, 3) -> (K, N, 3)
18    disp = grid_points.unsqueeze(1) - positions.unsqueeze(0)
19    # Distances with a small softening eps to avoid r=0:
20    eps  = 1.0e3
21    r    = torch.sqrt((disp * disp).sum(dim=-1) + eps * eps)   # (K, N)
22    # Per-pair contribution -G m / r, then sum over particles:
23    phi  = -G * (masses.unsqueeze(0) / r).sum(dim=-1)          # (K,)
24    return phi
25
26# --- Build a small galaxy: 200 stars in a flat disk ---
27torch.manual_seed(0)
28device = "cuda" if torch.cuda.is_available() else "cpu"
29
30N = 200
31masses    = torch.full((N,), 2.0e30, device=device)             # 1 solar mass each
32radii     = 1.0e16 * torch.rand(N, device=device).sqrt()         # 0..1e16 m, uniform in area
33angles    = 2 * torch.pi * torch.rand(N, device=device)
34positions = torch.stack([
35    radii * torch.cos(angles),
36    radii * torch.sin(angles),
37    0.10 * radii * torch.randn(N, device=device),                # thin disk
38], dim=-1)                                                       # (200, 3)
39
40# --- Evaluate Phi along the x-axis through the disk ---
41K = 200
42xs = torch.linspace(-2e16, 2e16, K, device=device)
43grid_points = torch.stack([xs, torch.zeros_like(xs), torch.zeros_like(xs)], dim=-1)
44
45phi = potential_grid_3d(masses, positions, grid_points)
46
47print(f"min Phi (well bottom) = {phi.min().item():.3e} J/kg")
48print(f"Phi at x=0            = {phi[K//2].item():.3e} J/kg")
49print(f"Phi at x=+2e16        = {phi[-1].item():.3e} J/kg")

With a softening length ϵ=103m\epsilon = 10^3\,\text{m} the potential is bounded everywhere — no infinities even when a probe lands on a star. The minimum value of Φ\Phi along the slice gives the depth of the cluster's gravitational well, and the rate at which Φ0\Phi \to 0 at large x|x| tells you the total mass (because at large distances the cluster looks like a single point of mass MtotM_{\text{tot}}, and ΦGMtot/x\Phi \approx -GM_{\text{tot}}/|x|).

From toy to real. Real cosmological simulations (Millennium, IllustrisTNG, FIRE) follow billions of particles through cosmic time. Direct summation at N=109N=10^9 would be impossible — those codes use tree codes (Barnes-Hut, O(NlogN)O(N\log N)) or particle-mesh hybrids that solve Poisson on a grid for the long-range force and sum directly only for nearby pairs. Every one of those algorithms is a smart approximation of the inner loop you just wrote.

From Tides to Cosmology to ML

The gravitational potential is doing more work in modern science and engineering than you might guess. A few highlights to anchor the idea.

Tides — the second derivative of Φ\Phi

Standing on Earth, you don't feel the moon's pull as a uniform shift — you feel its gradient. The tidal force on a body of size LL at distance dd from a mass MM is FtidalGML/d3F_{\text{tidal}} \sim G M L/d^3 — the second derivative of Φ\Phi. The reason ocean tides exist at all is that Φ\Phi isn't flat across Earth: the near side is pulled more strongly than the far side. The same 1/d31/d^3 falloff is why a planet too close to a black hole gets shredded (spaghettification).

Orbits as motion in a 1D effective potential

For a body in orbit around a point mass, conservation of angular momentum =mr2θ˙\ell = m r^2 \dot\theta lets you eliminate the angular variable and write the energy as if the body lived in a 1D potential

E=12mr˙2+Φeff(r),Φeff(r)=GMmr+22mr2.E = \tfrac{1}{2} m \dot{r}^2 + \Phi_{\text{eff}}(r), \qquad \Phi_{\text{eff}}(r) = -\,\frac{GMm}{r} + \frac{\ell^2}{2 m r^2}.

The two terms compete: the gravitational well pulls inward, the centrifugal barrier pushes outward. The minimum of Φeff\Phi_{\text{eff}} sits at the circular-orbit radius; energies above the minimum give elliptic orbits, exactly at zero give parabolic escape, above zero give hyperbolic flybys. Every Kepler problem in the universe lives on this one curve.

Cosmology and dark matter

Galaxy rotation curves measure v(r)v(r) for stars orbiting a galactic centre. From Newton's law v2/r=GMenc(r)/r2v^2/r = GM_{\text{enc}}(r)/r^2, so Menc(r)=v2r/GM_{\text{enc}}(r) = v^2 r/G. Observed curves stay flat far out, implying Menc(r)rM_{\text{enc}}(r) \propto r well beyond the luminous matter. The Poisson equation for the inferred mass distribution doesn't add up unless there is invisible matter — that is the observational origin of dark matter. Everything here uses nothing more advanced than 2Φ=4πGρ\nabla^2 \Phi = 4\pi G\rho.

Machine learning connections

  • Score-based generative models & Poisson flow networks. The 2022/2023 PFGM family (Xu, Jiang, Karras, et al.) explicitly trains a neural network to learn the gradient of a 3D Poisson potential whose source is the training data distribution. Sampling = following Φ-\nabla \Phi back toward the wells (the data). Same equation as gravity, with images as "masses."
  • Graph neural networks via the Laplacian. Replace the continuous Laplacian by the graph Laplacian L=DAL = D - A. Smoothing signals over a graph by (IαL)x(I - \alpha L) x is exactly one Jacobi sweep of 2Φ=0\nabla^2 \Phi = 0 on the discrete graph. Many GNN message-passing schemes can be read as "solve Laplace on the data graph."
  • Physics-informed neural networks (PINNs). Same idea as in the electrostatic section: train a net Φθ(x,y,z)\Phi_\theta(x,y,z) by minimizing 2Φθ4πGρ2\|\nabla^2 \Phi_\theta - 4\pi G\rho\|^2 over sampled points plus boundary loss. The Laplacian inside the loss is computed by autograd. PINNs for Newton-gravity reproduce planetary potentials with parameters trained from a handful of orbit samples.
  • N-body emulators. Modern cosmology uses convolutional neural networks trained on small high-resolution simulations to predict the density-to-potential map for huge volumes, replacing FFT-based Poisson solvers. The training target is exactly the inverse Laplacian.

Summary

IdeaWhat you should remember
Newton's lawF = -GMm/r^2 \,\hat{r}. Inverse-square, attractive, linear (superposes).
Potential definition\Phi(r) = -GM/r for a point mass; field is \mathbf{g} = -\nabla \Phi.
Why a potentialScalar (one number per point), encodes energy directly, makes superposition trivial.
Gauss's law\oint \mathbf{g}\cdot d\mathbf{A} = -4\pi G\, M_{\text{enc}}. Symmetry shortcut for spheres, cylinders, sheets.
Poisson form\nabla^2 \Phi = 4\pi G \rho. Same PDE as electrostatics with opposite-sign coefficient.
Worked exampleUniform sphere: parabolic potential inside, -GM/r outside, shell theorem. \Phi(0) = 1.5 \, \Phi(R).
Numerical workhorseDirect N-body sum (small N), Jacobi/FFT/multigrid (continuous \rho on a grid).
Modern reachTides, orbits, dark-matter inference, PFGM generative models, Laplacian-based GNNs, cosmological emulators.
The takeaway in one sentence. Newton's inverse-square law, repackaged as 2Φ=4πGρ\nabla^2 \Phi = 4\pi G\rho, lets the whole celestial sphere — planet, star, galaxy, dark matter halo — be solved by the same averaging-plus-source idea that solved electrostatics, with one difference: mass has no negatives, so every well goes only one way, and the universe (eventually) wants to clump.
Loading comments...