sim icon

Newtonian Gravity

Newton's Law of gravitation tells us the gravitational force on a mass, M, due to another mass, m, is

F=Gm1m2/r^2

where G is the gravitational constant (big "G"). The direction of the force on M is towards m. We can introduce the acceleration field due to the mass, m, to be

a=Gm/r^2

where r is the distance from the mass, m, and the unit r-vector is outwards from m. The direction of the acceleration at each point is inwards towards the mass (hence the appearance of the negative sign). The acceleration field gives the force per unit "test mass" placed in the field.

The following simulation shows such a gravitational field. Note that we use the acceleration as the field rather than the force, as the force on a test object placed in the field will depend on the mass of the test object.

The simulation initially shows a central mass which is fixed in place. The size of the central mass can be changed using the slider. The strength of the field can also be changed using a slider. The third slider sets the mass of a test object that can be inserted into the field. After setting the mass using the slider, clicking anywhere in the field to insert a mass (but not inside an existing mass). Initially start with interactions on (this means masses collide like hard spheres). When interaction is removed, the test sphere will pass through the central mass. The strength of the field, size of the central mass and number of test masses can be modified while the simulation is running. Modify these variables to observe how a changing gravitational field impacts the test masses.

Some questions to consider while running the simulation are

Centre mass size:
Field strength:
Test mass:

Code Samples

Matlab
bigG = 1.0;        % Gravitational constant
mass1 = 1.0;       % Mass of centre object
mass2 = 1.0;       % Mass of falling object
radius1 = 1.0;     % Radius of centre object
radius2 = 0.5;     % Radius of falling object
dphi0 = 0.2;       % Initial tangential velocity
dr0 = 0.0;         % Initial radial velocity
phi0 = 0.0;        % Initial tangential position
r0 = radius1+radius2+1.0;    % Initial radial position

dt = 1.0e-3;            % Simulation step size
outputRate = 100;       % Number of steps between output cycles
outputLength = 400;     % Length of simulation (# output files)

% Formulate our problem as a system of ODES: y' = odefun(t, x)
odefun = @(t, x) [x(2); -bigG*mass1/x(1).^2; dphi0];
xx = [r0; dr0; phi0];

% Functions to calculate the mass position
px = @(xx) xx(1)*sin(xx(3));
py = @(xx) xx(1)*cos(xx(3));
pxc = @(xx) px(xx) - radius2;
pyc = @(xx) py(xx) - radius2;

figure;
d2 = radius2*2;
x = [px(xx)]; y = [py(xx)];
h = plot(x, y);
ch = rectangle('Position',[pxc(xx) pyc(xx) d2 d2],'Curvature',[1,1]);
bh = rectangle('Position',[-radius1 -radius1 2*radius1 2*radius1],...
    'Curvature',[1,1]);
axis([-4 4 -4 4]);

while ishandle(h)
    
    % Solve the ODE using a first order method (might be inacurate)
    for n = 1:outputRate
        xx = xx + odefun(0.0, xx)*dt;
        if xx(1) < radius1 + radius2
            xx(1) = 2.0*(radius1+radius2) - xx(1);
            xx(2) = -xx(2);
        end
        xx(3) = mod(xx(3), 2.0*pi);
    end

    % Update the graph
    x = [x px(xx)]; set(h, 'xdata', x);
    y = [y py(xx)]; set(h, 'ydata', y);
    set(ch, 'Position',[pxc(xx) pyc(xx) d2 d2]);
    
    pause(0.05);
    drawnow;
end
Python
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

bigG = 1.0;        # Gravitational constant
mass1 = 1.0;       # Mass of centre object
mass2 = 1.0;       # Mass of falling object
radius1 = 1.0;     # Radius of centre object
radius2 = 0.5;     # Radius of falling object
dphi0 = 0.2;       # Initial tangential velocity
dr0 = 0.0;         # Initial radial velocity
phi0 = 0.0;        # Initial tangential position
r0 = radius1+radius2+1.0;    # Initial radial position

dt = 1.0e-3;            # Simulation step size
outputRate = 100;       # Number of steps between output cycles
outputLength = 400;     # Length of simulation (# output files)

odefun = lambda x: np.array([x[1], -bigG*mass1/x[0]**2, dphi0]);
xx = np.array([r0, dr0, phi0]);

px = lambda x: xx[0]*np.sin(xx[2])
py = lambda x: xx[0]*np.cos(xx[2])
pxc = lambda x: px(x) - radius2
pyc = lambda x: py(x) - radius2

fig, ax = plt.subplots()

x = [px(xx)]
y = [py(xx)]
line, = ax.plot(x, y, lw=1)
ax.set_xlim(-4, 4)
ax.set_ylim(-4, 4)

circle1 = plt.Circle((0.0, 0.0), radius1)
circle2 = plt.Circle((px(xx), py(xx)), radius2, fill=False)
ax.add_artist(circle1)
ax.add_artist(circle2)

def animate(step_number):

    global xx

    # Calculate the new position (1st order, might be inaccurate)
    for i in xrange(0, outputRate):
        xx = xx + odefun(xx)*dt
        if xx[0] < radius1+radius2:
            xx[0] = 2.0*(radius1+radius2) - xx[0]
            xx[1] = -xx[1]
        xx[2] = np.fmod(xx[2], 2.0*np.pi)

    # Update line/circle
    x.append(px(xx))
    y.append(py(xx))
    line.set_data(x, y)
    circle2.center = px(xx), py(xx)

    return line,circle2

ani = animation.FuncAnimation(fig, animate, xrange(1, outputLength),
    interval=25, blit=True, repeat=False)
#ani.save('output.mp4')
plt.show()
Cpp Gnuplot
#include <fstream>
#include <cmath>

const double bigG = 1.0;        // Gravitational constant
const double mass1 = 1.0;       // Mass of centre object
const double mass2 = 1.0;       // Mass of falling object
const double radius1 = 1.0;     // Radius of centre object
const double radius2 = 0.5;     // Radius of falling object
const double dphi0 = 0.2;       // Initial tangential velocity
const double dr0 = 0.0;         // Initial radial velocity
const double phi0 = 0.0;        // Initial tangential position
const double r0 = radius1+radius2+1.0;    // Initial radial position

const double dt = 1.0e-3;           // Simulation step size
const unsigned outputRate = 100;    // Number of steps between output cycles
const unsigned outputLength = 400;  // Length of simulation (# output files)

int main(int argc, char** argv) {

  double r = r0, dr = dr0;
  double phi = phi0, dphi = dphi0;
  double t = 0.0;

  std::ofstream fp("output.dat");
  fp << "# t\tr\tphi\n";
	fp << t << '\t' << r << '\t' << phi << '\n';

  for (unsigned i = 0; i < outputLength; ++i) {
    for (unsigned j = 0; j < outputRate; ++j) {

      // Use a first order approximation (might be inaccurate)
      double oldr = r;
      r += dt*dr;
      dr -= dt*bigG*mass1/oldr/oldr;

      // Test bounce condition
      if (r < radius1 + radius2) {
        r = 2.0*(radius1 + radius2) - r;
        dr = -dr;
      }

      phi = fmod(phi + dt*dphi, 2.0*M_PI);
    }

    // Write output
    fp << t << '\t' << r << '\t' << phi << '\n';
  }

  return 0;
}
set terminal gif animate 10
set output 'output.gif'
set xrange [-4:4]
set yrange [-4:4]
set size ratio -1

x(r, phi) = r*sin(phi)
y(r, phi) = r*cos(phi)
radius2 = 0.5

set object 1 circle at 0,0 size 1.0 fc rgb "navy"

stats "output.dat" nooutput
do for [i=1:int(STATS_records)] {
  plot "output.dat" u (x($2, $3)):(y($2, $3)) every ::::i w lines notitle, \
    "output.dat" u (x($2, $3)):(y($2, $3)):(radius2) \
        every ::i::i with circles notitle
}