Mimas' Effect on Saturn's Cassini Ring Gap
- Vincent Andrade
- Adham Abdelwahab
- Tyler Kaminski
Professor Lukic
PEP 351
I pledge my honor that I have abided by the Stevens Honor System.
Introduction
Equations
Assumptions
- Saturn is at the origin (0,0).
- Particles are initially evenly distributed along the x-axis between a lower and upper limit.
- The lower and upper limits contain the Cassini Division.
- Mass of Mimas is boosted in order to accelerate results.
- Position of particles and Mimas are calculated for 60 and 1 second time steps.
- Particles are massless and not affected by each other, only by Saturn and Mimas.
Constants
Gravitational Constant,
Mass of Saturn,
Mass of Mimas,
Distance between Saturn and Mimas,
Cassini Division Inner Bound,
Cassini Division Outer Bound,
Python Implementation
import matplotlib.pyplot as plot
import numpy as np
# Constants for calculations
G = 6.67430e-11
# Mass of the central body (Saturn)
M = 5.6834e26
# Mass of Mimas
m = 3.7493e19
# Distance from Saturn to Mimas
d = 1.8552e8
# time step (1 minute)
t = 60
def orbit(interactive, num_particles, show_all, mimas_multiplier,
num_time_steps):
"""
Plot the orbit of Mimas and massless ring particles around Saturn.
Parameters:
interactive (bool): Plot the orbits in real time.
num_particles (int): The number of particles to simulate.
show_all (bool): Don't clear the plot between iterations.
mimas_multiplier (int): Multipler of Mimas' mass.
num_time_steps (int): The number of time steps to simulate.
"""
# Entire range of Saturn's rings
INNER_LIM = 74500 * 10**3
OUTER_LIM = 140220 * 10**3
# Range around the Cassini Division including B and A rings.
# INNER_LIM = 92000 * 10**3
# OUTER_LIM = 136780 * 10**3
# particles = np.random.randint(-OUTER_LIM, OUTER_LIM, (num_particles, 2))
p = np.array([(float(i), 0.0) for i in range(
INNER_LIM, OUTER_LIM, (OUTER_LIM - INNER_LIM) // num_particles)])
v = np.array([(0.0, np.sqrt(G * M / x)) for x, _ in p])
plot.gca().set_aspect('equal')
# omega used for calculating position of mimas
w = np.sqrt(G * M / d**3)
for n in range(0, num_time_steps * t, t):
if not show_all:
plot.cla()
xm = d * np.cos(n * w)
ym = d * np.sin(n * w)
# Plot Saturn and Mimas
if interactive or show_all or n == (num_time_steps - 1) * t:
plot.plot(xm, ym, 'ro')
plot.plot(0, 0, "yo")
for i in range(len(p)):
rs = np.sqrt(p[i][0]**2 + p[i][1]**2)
rm = np.sqrt((p[i][0] - xm)**2 + (p[i][1] - ym)**2)
ax = -G * M * p[i][0] / rs**3 - \
G * m * mimas_multiplier * (p[i][0] - xm) / rm**3
ay = -G * M * p[i][1] / rs**3 - \
G * m * mimas_multiplier * (p[i][1] - ym) / rm**3
p[i][0] += v[i][0] * t + ax/2 * t**2
p[i][1] += v[i][1] * t + ay/2 * t**2
v[i][0] += ax * t
v[i][1] += ay * t
if interactive or show_all or n == (num_time_steps - 1) * t:
plot.plot(p[i][0], p[i][1], "go", markersize=1)
if interactive:
plot.pause(0.00000001)
if not interactive:
plot.savefig('img/mimas_mult=' + str(mimas_multiplier) + '&p=' +
str(num_particles) + '&t=' + str(num_time_steps) + '.png')
print('Image Saved')
orbit(interactive=False, num_particles=1000, show_all=False,
mimas_multiplier=10000, num_time_steps=2000)
C Implementation With Python Driver
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
// Constants for calculations
float G = 6.67430e-11;
// Mass of the central body (Saturn)
float M = 5.6834e26;
// Mass of Mimas
float m = 3.7493e19;
// Distance from Saturn to Mimas
float d = 1.8552e8;
// time step (1 second)
int t = 1;
// Plot the orbit of Mimas and massless ring particles around Saturn.
// Parameters:
// num_particles: The number of particles to simulate.
// mimas_multiplier: Multiplier of Mimas' mass.
// num_time_steps: The number of time steps to simulate.
void orbit(int num_particles, int mimas_multiplier, int num_time_steps)
{
// Range of entirety of Saturn's rings.
// int INNER_LIM = 74500 * pow(10, 3);
// int OUTER_LIM = 140220 * pow(10, 3);
// Range around the Cassini Division including B and A rings.
int INNER_LIM = 92000 * pow(10, 3);
int OUTER_LIM = 136780 * pow(10, 3);
// Range of only the Cassini Division.
// int INNER_LIM = 117580 * pow(10, 3);
// int OUTER_LIM = 122170 * pow(10, 3);
float p[num_particles][2];
float v[num_particles][2];
for (int i = 0, j = INNER_LIM; j < OUTER_LIM;
j += (OUTER_LIM - INNER_LIM) / num_particles, ++i) {
p[i][0] = j;
p[i][1] = 0.0;
v[i][0] = 0.0;
v[i][1] = sqrt(G * M / p[i][0]);
}
// omega used for calculating position of mimas
float w = sqrt(G * M / pow(d, 3));
float xm = 0, ym = 0;
for (int n = 0; n < num_time_steps * t; n += t) {
xm = d * cos(n * w);
ym = d * sin(n * w);
for (int i = 0; i < num_particles; ++i) {
float rs = sqrt(pow(p[i][0], 2) + pow(p[i][1], 2));
float rm = sqrt(pow(p[i][0] - xm, 2) +
pow(p[i][1] - ym, 2));
float ax = -G * M * p[i][0] / pow(rs, 3) -
G * m * mimas_multiplier * (p[i][0] - xm) /
pow(rm, 3);
float ay = -G * M * p[i][1] / pow(rs, 3) -
G * m * mimas_multiplier * (p[i][1] - ym) /
pow(rm, 3);
p[i][0] += v[i][0] * t + ax / 2 * pow(t, 2);
p[i][1] += v[i][1] * t + ay / 2 * pow(t, 2);
v[i][0] += ax * t;
v[i][1] += ay * t;
}
}
printf("%f, %f\n", xm, ym);
for (int i = 0; i < num_particles; ++i)
printf("%f, %f\n", p[i][0], p[i][1]);
}
int main(int argc, char **argv)
{
orbit(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
return 0;
}
import matplotlib.pyplot as plot
import csv
import subprocess
import sys
import numpy as np
if len(sys.argv) != 4:
print("Usage:", sys.argv[0],
"<num_particles> <mimas_multiplier> <num_time_steps>")
exit(1)
subprocess.run(
"clang -lm -fsanitize=safe-stack -Ofast -march=native sim_ring_gaps.c -o sim_ring_gaps",
shell=True, check=True)
data_filename = 'data/mimas_mult=' + sys.argv[2] + '&p=' + sys.argv[1] + \
'&t=' + sys.argv[3] + '.csv'
subprocess.run(
"./sim_ring_gaps " + ' '.join(sys.argv[1:]) + " > '" + data_filename + "'",
check=True, shell=True)
data = csv.reader(open(data_filename))
xm, ym = next(data)
plot.gca().set_aspect('equal')
plot.title("p=" + sys.argv[1] + ", m=" + sys.argv[2] +
", t=" + sys.argv[3])
plot.xlabel("Distance from Saturn (m)")
plot.ylabel("Distance from Saturn (m)")
plot.plot(float(xm), float(ym), "ro", label="Mimas")
plot.plot(0, 0, "yo", markersize=20, label="Saturn")
x, y = next(data)
plot.plot(float(x), float(y), "go", markersize=1, label="Ring Particle")
plot.legend(loc='upper right')
particles_in_division = 0.0
for x, y in data:
if 1.1758e8 < np.sqrt(float(x)**2 + float(y)**2) < 1.2217e8:
particles_in_division += 1
plot.plot(float(x), float(y), "go", markersize=1)
print("Ratio of particles in division to total:",
particles_in_division / int(sys.argv[1]))
plot.savefig('img/mimas_mult=' + sys.argv[2] + '&p=' + sys.argv[1] + '&t=' +
sys.argv[3] + '.png')
Results
Conclusions
With the introduction of Mimas, the orbits of the particles become unstable, with gaps and perturbations occurring throughout the rings. As seen in project 1, the Euler method is not the most accurate method for simulating orbital motion. The particles end up further away from their initial orbits that intended, even without Mimas’ gravity. Can be partially accounted for by drastically lowering time step at the cost of greater computation time required for simulating longer orbital periods. Differences between the orbits with and without Mimas’ gravity are very clear. Given enough particles and enough time steps, Mimas’ gravitational pull results in the Cassini division.