summaryrefslogtreecommitdiff
path: root/docs/assets/n-body/script.js
diff options
context:
space:
mode:
Diffstat (limited to 'docs/assets/n-body/script.js')
-rw-r--r--docs/assets/n-body/script.js706
1 files changed, 0 insertions, 706 deletions
diff --git a/docs/assets/n-body/script.js b/docs/assets/n-body/script.js
deleted file mode 100644
index cd8b8b0..0000000
--- a/docs/assets/n-body/script.js
+++ /dev/null
@@ -1,706 +0,0 @@
-// Global variables
-var plotOrbit = true
-var plotClassic = false
-var plotRandom = false
-var plotRandom3Body = false
-var plotIndex = 0
-var delay = 10
-var tailLength = 1;
-var nBodies = 256;
-
-/*
- * Earth - Sun Orbit Plot
- * Taken from Numerics tutorial
- */
-
-const G = 6.67e-11;
-const Msun = 2e30;
-const AU = 1.5e11;
-const v0 = Math.sqrt(G * Msun / AU); // SI
-
-function dR(r, v) {
- const dv = [-G * Msun / Math.pow(r[0] ** 2 + r[1] ** 2, 3 / 2) * r[0], -G * Msun / Math.pow(r[0] ** 2 + r[1] ** 2, 3 / 2) * r[1]];
- const dr = [...v];
- return [dr, dv];
-}
-
-// initialize system
-let r = [-AU, 0];
-const theta = Math.atan2(r[1], r[0]);
-let v = [-v0 * Math.sin(theta), v0 * Math.cos(theta)];
-
-const t = Array.from({ length: 1001 }, (_, i) => i / 100 + 0.0); // years
-const yearSec = 365 * 24 * 3600;
-const dt = (t[1] - t[0]) * yearSec; // s
-const x4Plot = Array.from({ length: t.length }, () => 0);
-const y4Plot = Array.from({ length: t.length }, () => 0);
-
-// integrate using RK4!
-for (let i = 0; i < t.length; i++) {
- const k1 = dR(r, v).map(x => x.map(y => y * dt));
- const k2 = dR(r.map((ri, j) => ri + k1[0][j] / 2), v.map((vi, j) => vi + k1[1][j] / 2)).map(x => x.map(y => y * dt));
- const k3 = dR(r.map((ri, j) => ri + k2[0][j] / 2), v.map((vi, j) => vi + k2[1][j] / 2)).map(x => x.map(y => y * dt));
- const k4 = dR(r.map((ri, j) => ri + k3[0][j]), v.map((vi, j) => vi + k3[1][j])).map(x => x.map(y => y * dt));
- r = r.map((ri, j) => ri + (k1[0][j] + 2 * k2[0][j] + 2 * k3[0][j] + k4[0][j]) / 6);
- v = v.map((vi, j) => vi + (k1[1][j] + 2 * k2[1][j] + 2 * k3[1][j] + k4[1][j]) / 6);
- x4Plot[i] = r[0];
- y4Plot[i] = r[1];
-}
-
-// make data for plot
-var sun = { x: 0, y: 0 };
-const earth = { x: x4Plot.map(x => x / AU), y: y4Plot.map(y => y / AU) };
-const circle = { x: Array.from({ length: 1001 }, (_, i) => Math.cos(i / 100 * 2 * Math.PI)), y: Array.from({ length: 1001 }, (_, i) => Math.sin(i / 100 * 2 * Math.PI)) };
-
-/*
- * Generic Functions
- *
- *
- */
-
-function deltaR(coords, masses, nBodies, G) {
- let x = coords[0];
- let y = coords[1];
- let vx = coords[2];
- let vy = coords[3];
-
- let delta = math.clone(coords);
-
- for (let n = 0; n < nBodies; n++) {
- let xn = x[n];
- let yn = y[n];
- let deltaVx = 0.0;
- let deltaVy = 0.0;
-
- for (let i = 0; i < nBodies; i++) {
- if (i !== n) {
- let sep = Math.sqrt(Math.pow(xn - x[i], 2) + Math.pow(yn - y[i], 2)); // Euclidean distance
- deltaVx -= G * masses[i] * (xn - x[i]) / Math.pow(sep, 3);
- deltaVy -= G * masses[i] * (yn - y[i]) / Math.pow(sep, 3);
- }
- }
-
- delta[2][n] = deltaVx;
- delta[3][n] = deltaVy;
- }
-
- delta[0] = vx;
- delta[1] = vy;
-
- return delta;
-}
-
-function detectCollisionsEscape(coords, deltaT, maxSep) {
- const [x, y, vx, vy] = coords;
- const V = vx.map((v, i) => Math.sqrt(v ** 2 + vy[i] ** 2));
- const R = V.map(v => v * deltaT);
- let collision = false, collisionInds = null, escape = false, escapeInd = null;
-
- for (let n = 0; n < x.length; n++) {
- const rn = R[n], xn = x[n], yn = y[n];
- for (let i = 0; i < x.length; i++) {
- if (i !== n) {
- const minSep = rn + R[i];
- const sep = Math.sqrt((xn - x[i]) ** 2 + (yn - y[i]) ** 2);
- if (sep < minSep) {
- collision = true;
- collisionInds = [n, i];
- } else if (sep > maxSep) {
- escape = true;
- escapeInd = n;
- return [collision, collisionInds, escape, escapeInd];
- }
- }
- }
- }
- return [collision, collisionInds, escape, escapeInd];
-}
-
-
-
-function step(coords, masses, deltaT, nBodies = 3, G = 6.67408313131313e-11) {
- let k1 = math.multiply(deltaT, deltaR(coords, masses, nBodies, G));
- let k2 = math.multiply(deltaT, deltaR(math.add(coords, math.multiply(k1, 0.5)), masses, nBodies, G));
- let k3 = math.multiply(deltaT, deltaR(math.add(coords, math.multiply(k2, 0.5)), masses, nBodies, G));
- let k4 = math.multiply(deltaT, deltaR(math.add(coords, k3), masses, nBodies, G));
-
- coords = math.add(coords, math.multiply(math.add(k1, math.multiply(2.0, k2), math.multiply(2.0, k3), k4), 1/6));
-
- return coords;
-}
-
-function nBodyStep(coords, masses, deltaT, maxSep, nBodies, G = 6.67408313131313e-11) { // Similar to our step function before, but keeping track of collisions
- coords = step(coords, masses, deltaT, nBodies, G); // Update the positions as we did before
- //console.log(detectCollisionsEscape(coords, deltaT, maxSep));
- let [collision, collisionInds, escape, escapeInd] = detectCollisionsEscape(coords, deltaT, maxSep); // Detect collisions/escapes
-
-
- if (collision) { // Do inelastic collision and delete extra body (2 -> 1)
- const [i1, i2] = collisionInds;
- const [x1, x2] = [coords[0][i1], coords[0][i2]];
- const [y1, y2] = [coords[1][i1], coords[1][i2]];
- const [vx1, vx2] = [coords[2][i1], coords[2][i2]];
- const [vy1, vy2] = [coords[3][i1], coords[3][i2]];
- const [px1, px2] = [masses[i1] * vx1, masses[i2] * vx2];
- const [py1, py2] = [masses[i1] * vy1, masses[i2] * vy2];
- const px = px1 + px2;
- const py = py1 + py2;
- const newM = masses[i1] + masses[i2];
- const vfx = px / newM;
- const vfy = py / newM;
- coords[0][i1] = (x1 * masses[i1] + x2 * masses[i2]) / (masses[i1] + masses[i2]); // Center of mass
- coords[1][i1] = (y1 * masses[i1] + y2 * masses[i2]) / (masses[i1] + masses[i2]);
- coords[2][i1] = vfx;
- coords[3][i1] = vfy;
- coords[0].splice(i2, 1);
- coords[1].splice(i2, 1);
- coords[2].splice(i2, 1);
- coords[3].splice(i2, 1);
- masses[i1] = newM;
- masses.splice(i2, 1);
- nBodies--;
- }
- // Could also implement condition for escape where we stop calculating forces but I'm too lazy for now
- return [coords, masses, nBodies, collision, collisionInds, escape, escapeInd];
-}
-
-function uniform(min, max) {
- return Math.random() * (max - min) + min;
-}
-
-function deepCopyCoordsArray(arr) {
- return arr.map(innerArr => innerArr.slice());
-}
-
-function genNBodyResults(nBodies, tStop, nTPts, nBodiesStop = 10, G = 6.67408313131313e-11) {
-
- var btn = document.getElementById("startSim3");
- // Set button text to Solving
- var prevText = btn.innerHTML;
- btn.innerHTML = "Solving...";
-
- let coords = [Array(nBodies).fill(0), Array(nBodies).fill(0), Array(nBodies).fill(0), Array(nBodies).fill(0)];
- const Mstar = 2e30;
- const Mp = Mstar / 1e4;
-
- for (let i = 0; i < nBodies; i++) { // Initialize coordinates on ~Keplerian orbits
- let accept = false;
- let r = null;
- while (!accept) { // Prevent a particle from spawning within 0.2 AU too close to "star"
- r = Math.random() * 2 * 1.5e11; // Say radius of 2 AU
- if (r / 1.5e11 > 0.2) {
- accept = true;
- }
- }
- const theta = uniform(0, 2 * Math.PI);
- const x = r * Math.cos(theta);
- const y = r * Math.sin(theta);
- const v = Math.sqrt(G * Mstar / r);
- const perturbedV = v + v / 1000 * uniform(-1, 1); // Perturb the velocities ever so slightly
- const vTheta = Math.atan2(y, x);
- coords[0][i] = x;
- coords[1][i] = y;
- coords[2][i] = -perturbedV * Math.sin(vTheta);
- coords[3][i] = perturbedV * Math.cos(vTheta);
- }
-
- //console.log('Initial coords:', coords);
-
-
- let masses = Array(nBodies).fill(Mp); // Initialize masses
- masses[0] = Mstar; // Make index one special as the central star
- coords[0][0] = 0;
- coords[1][0] = 0;
- coords[2][0] = 0;
- coords[3][0] = 0; // Initialize central star at origin with no velocity
- const yearSec = 365 * 24 * 3600;
- const time = Array.from({ length: nTPts }, (_, i) => i * tStop / (nTPts - 1) * yearSec); // Years -> s
- let t = time[0];
- const deltaT = time[1] - time[0];
- let tInd = 0;
- const coordsRecord = [deepCopyCoordsArray(coords)];
- const massRecord = [masses.slice()]; // Initialize records with initial conditions
-
-
- while (tInd < nTPts && nBodies > nBodiesStop) {
- //console.log('Initial coords:', coords);
- [coords, masses, nBodies] = nBodyStep(coords, masses, deltaT, 10 * 1.5e11, nBodies, G); // Update
- coordsRecord.push(deepCopyCoordsArray(coords));
- massRecord.push(masses.slice()); // Add to records
- tInd++;
- t = time[tInd];
- //console.log(`currently at t = ${(t / yearSec).toFixed(2)} years\r`);
- }
- console.log(`final time = ${time[tInd] / yearSec} years with ${nBodies} bodies remaining`);
-
- // Set button text to Start Simulation
- btn.innerHTML = prevText;
-
- return [coordsRecord, massRecord, time.slice(0, tInd + 1)];
-}
-
-
-function initCondGen(nBodies, vRange = [-7e3, 7e3], posRange = [-35, 35]) {
- const m = Array.from({length: nBodies}, () => Math.random() * 1500 / 10);
-
- const rad = m.map(x => Math.pow(x, 0.8));
- const minV = vRange[0], maxV = vRange[1];
- const minPos = posRange[0], maxPos = posRange[1];
- const posList = [];
-
- function checkPos(randPos, n, posList, rad) {
- for (let i = 0; i < n - 1; i++) {
- const dist = Math.sqrt(Math.pow(posList[i][0] - randPos[0], 2) + Math.pow(posList[i][1] - randPos[1], 2));
- if (dist * 1.5e11 < (rad[n] + rad[i])) {
- return false;
- }
- }
- return true;
- }
-
- function genPos(nBodies, posList, rad, minPos, maxPos) {
- posList.push([Math.random() * (maxPos - minPos) + minPos, Math.random() * (maxPos - minPos) + minPos]);
- for (let n = 1; n < nBodies; n++) {
- let acceptPos = false;
- while (acceptPos === false) {
- const randPos = [Math.random() * (maxPos - minPos) + minPos, Math.random() * (maxPos - minPos) + minPos];
- acceptPos = checkPos(randPos, n, posList, rad);
- if (acceptPos === true) {
- posList.push(randPos);
- }
- }
- }
- return posList;
- }
-
- const pos = genPos(nBodies, posList, rad, minPos, maxPos).map(x => x.map(y => y * 1.5e11));
- const coords = [new Array(nBodies).fill(0), new Array(nBodies).fill(0), new Array(nBodies).fill(0), new Array(nBodies).fill(0)];
- const v = [];
-
- for (let i = 0; i < nBodies; i++) {
- coords[0][i] = pos[i][0];
- coords[1][i] = pos[i][1];
- const V = [Math.random() * (maxV - minV) + minV, Math.random() * (maxV - minV) + minV];
- v.push(V);
- coords[2][i] = V[0];
- coords[3][i] = V[1];
- }
-
- return {m: m.map(x => x * 2e30), rad: rad.map(x => x * 7e8), coords: coords};
-}
-
-function random3BodySimSolver(tStop, nTPts, nBodiesStop=2, G=6.674e-11) {
- let userInputPositionValue = parseInt(document.getElementById("distanceAU").value);
- let userInputVelocityValue = Number(document.getElementById("vRange").value);
- let initConditions = initCondGen(3,[-userInputVelocityValue, userInputVelocityValue],[-userInputPositionValue, userInputPositionValue]);
- let myMasses = initConditions.m;
- let myCoords = initConditions.coords;
-
- let nBodies3B = 3;
-
- const yearSec = 365 * 24 * 3600;
-
- const time = Array.from({ length: nTPts }, (_, i) => i * tStop / (nTPts - 1) * yearSec); // Years -> s
- let t = time[0];
- const deltaT = time[1] - time[0];
- let tInd = 0;
- const coordsRecord = [deepCopyCoordsArray(myCoords)];
- const massRecord = [myMasses.slice()]; // Initialize records with initial conditions
-
- while (tInd < nTPts && nBodies3B > nBodiesStop) {
- [myCoords, myMasses, nBodies3B] = nBodyStep(myCoords, myMasses, deltaT, 10 * 1.5e11, nBodies3B, G); // Update
- coordsRecord.push(deepCopyCoordsArray(myCoords));
- massRecord.push(myMasses.slice()); // Add to records
- tInd++;
- t = time[tInd];
- }
-
- console.log(nBodies3B)
-
- return [coordsRecord, massRecord, time.slice(0, tInd + 1)];
-
-}
-
-
-function calculateAndPlot() {
- try {
- Plotly.purge("plot");
- } catch (e) {
- console.log(e);
- }
-
- if (plotOrbit===true) {
- let traceSun = {
- x: [sun.x],
- y: [sun.y],
- mode: "markers",
- marker: {
- symbol: "star",
- size: 10,
- color: "gold",
- },
- name: "Sun",
- };
-
- const traceEarth = {
- x: earth.x,
- y: earth.y,
- mode: "lines",
- line: {
- color: "white"
- },
- name: "Earth",
- };
-
- const traceOrbit = {
- x: circle.x,
- y:circle.y,
- mode: "lines",
- line: {
- color: "crimson",
- dash: "dash"
- },
- name: "1 AU Circle",
- };
-
- const earthSunLayout = {
- title: "Earth-Sun Orbit",
- xaxis: {
- title: "x [AU]",
- range: [-1.1,1.1],
- showgrid: true,
- gridcolor: "rgba(255,255,255,0.5)",
- gridwidth: 1,
- zeroline: true,
- tickmode: "auto",
- nticks: 5,
- },
- yaxis: {
- title: "y [AU]",
- range: [-1.1,1.1],
- showgrid: true,
- gridcolor: "rgba(255,255,255,0.5)",
- gridwidth: 1,
- zeroline: false,
- tickmode: "auto",
- nticks: 5,
- },
- margin: {
- l: 50,
- r: 50,
- b: 50,
- t: 50,
- pad: 4,
- },
- paper_bgcolor: "black",
- plot_bgcolor: "black",
- };
- Plotly.newPlot("plot",[traceSun,traceEarth,traceOrbit], earthSunLayout);
- } else if (plotRandom==true) {
-
- var [coordsRecordR, _, tR] = genNBodyResults(nBodies,1,nBodies*2);
- //console.log(coordsRecordR);
- const yearSec = 365 * 24 * 3600;
-
- function createFrame(coordsR) {
- if (!coordsR || !coordsR[0] || !coordsR[1]) {
- return [];
- }
-
- const traceCentralStar = {
- x: [coordsR[0][0] / 1.5e11],
- y: [coordsR[1][0] / 1.5e11],
- mode: 'markers',
- type: 'scatter',
- name: 'Central star',
- marker: { color: 'gold', symbol: 'star', size: 10 },
- };
-
- const xCoords = coordsR[0].slice(1).map(x => x / 1.5e11);
- const yCoords = coordsR[1].slice(1).map(y => y / 1.5e11);
-
- const traceOtherBodies = {
- x: xCoords,
- y: yCoords,
- mode: 'markers',
- type: 'scatter',
- name: '',
- marker: { color: 'dodgerblue', symbol: 'circle', size: 2 },
- };
-
- return [traceCentralStar, traceOtherBodies];
- }
-
-
- function createLayout(i) {
- return {
- title: {
- text: `N-Body Problem`,//= ${Number(t[i] / yearSec).toFixed(3)} years`,
- x: 0.03,
- y: 0.97,
- xanchor: 'left',
- yanchor: 'top',
- font: { size: 14 },
- },
- xaxis: { title: 'x [AU]', range: [-2.1, 2.1] },
- yaxis: { title: 'y [AU]', range: [-2.1, 2.1], scaleanchor: 'x', scaleratio: 1 },
- showlegend: false,
- margin: { l: 60, r: 40, t: 40, b: 40 },
- //width: 800,
- //height: 800,
- plot_bgcolor: 'black',
- };
-}
-
- function animateNBodyProblem() {
- const nFrames = tR.length;
-
- for (let i = 0; i < nFrames; i++) {
- const frameData = createFrame(coordsRecordR[i]);
- const layout = createLayout(i);
- //Plotly.newPlot(plotDiv, frameData, layout);
- try {
- Plotly.animate("plot", {
- data: frameData, layout: layout
- }, {
- staticPlot: true,
- transition: {
- duration: 0,
- },
- frame: {
- duration: 0,
- redraw: false,
- }
- });
- } catch (err) {
- Plotly.newPlot('plot', frameData, layout);
- }
- }
-}
-
-animateNBodyProblem();
-
-
- } else if (plotRandom3Body==true) {
- let [coordsRecord3, _, t3] = random3BodySimSolver(1,1000);
-
- const yearSec = 365 * 24 * 3600;
-
- function createFrame(coords3) {
- if (!coords3 || !coords3[0] || !coords3[1]) {
- return [];
- }
-
- const xCoords = coords3[0].slice(0).map(x => x / 1.5e11);
- const yCoords = coords3[1].slice(0).map(y => y / 1.5e11);
-
- const traceOtherBodies = {
- x: xCoords,
- y: yCoords,
- mode: 'markers',
- type: 'scatter',
- name: '',
- marker: { color: 'dodgerblue', symbol: 'circle', size: 5 },
- };
-
- return [traceOtherBodies];
- }
-
- function createLayout(i) {
- return {
- title: {
- text: `3-Body Problem`,//= ${Number(t[i] / yearSec).toFixed(3)} years`,
- x: 0.03,
- y: 0.97,
- xanchor: 'left',
- yanchor: 'top',
- font: { size: 14 },
- },
- xaxis: { title: 'x [AU]' },
- yaxis: { title: 'y [AU]', scaleanchor: 'x', scaleratio: 1 },
- showlegend: false,
- margin: { l: 60, r: 40, t: 40, b: 40 },
- //width: 800,
- //height: 800,
- plot_bgcolor: 'black',
- };
- }
-
- function animate3BodyProblem() {
- const nFrames = t3.length;
-
- for (let i = 0; i < nFrames; i++) {
- const frameData = createFrame(coordsRecord3[i]);
- const layout = createLayout(i);
- //Plotly.newPlot(plotDiv, frameData, layout);
- try {
- Plotly.animate("plot", {
- data: frameData, layout: layout
- }, {
- staticPlot: true,
- transition: {
- duration: 0,
- },
- frame: {
- duration: 0,
- redraw: false,
- }
- });
- } catch (err) {
- Plotly.newPlot('plot', frameData, layout);
- }
- }
- }
-
- animate3BodyProblem();
-
- } else if (plotClassic==true) {
- // Initial conditions setup
- let M = [1, 1, 1];
- let x = [-0.97000436, 0.0, 0.97000436];
- let y = [0.24208753, 0.0, -0.24208753];
- let vx = [0.4662036850, -0.933240737, 0.4662036850];
- let vy = [0.4323657300, -0.86473146, 0.4323657300];
- let Ei = -1 / Math.sqrt(Math.pow(2 * 0.97000436, 2) + Math.pow(2 * 0.24208753, 2)) - 2 / Math.sqrt(Math.pow(0.97000436, 2) + Math.pow(0.24208753, 2)) + 0.5 * (math.sum(math.add(math.dotPow(vx, 2), math.dotPow(vy, 2))));
-
- function linspace(start, stop, num) {
- const step = (stop - start) / (num - 1);
- return Array.from({length: num}, (_, i) => start + (step * i));
- }
-
- let coords = [x, y, vx, vy];
- const time = linspace(0, 6.3259, 1001);
- let deltaT = time[1] - time[0];
-
- let X = math.zeros(3, time.length).toArray();
- let Y = math.zeros(3, time.length).toArray();
- let VX = math.zeros(3, time.length).toArray();
- let VY = math.zeros(3, time.length).toArray();
-
- for (let i = 0; i < time.length; i++) {
- coords = step(coords, M, deltaT, 3, 1);
- X.forEach((_, idx) => X[idx][i] = coords[0][idx]);
- Y.forEach((_, idx) => Y[idx][i] = coords[1][idx]);
- VX.forEach((_, idx) => VX[idx][i] = coords[2][idx]);
- VY.forEach((_, idx) => VY[idx][i] = coords[3][idx]);
- }
-
- function plotClassicFunc() {
- var tailLength = 1;
- if (plotIndex < tailLength) {
- tailLength = 0;
- } if (plotIndex > time.length) {
- plotIndex = 0;
- } else {
- tailLength = plotIndex - tailLength;
- }
-
- var currentIndex = plotIndex;
-
- try {
- time[currentIndex].toFixed(3);
- } catch (e) {
- console.log(e)
- currentIndex = 0;
- }
-
- const data = [
- {
- x: X[0].slice(tailLength, currentIndex),
- y: Y[0].slice(tailLength, currentIndex),
- mode: 'lines+markers',
- marker: {
- symbol: 'star',
- size: 8,
- line: { width: 0 },
- },
- line: {
- width: 2,
- },
- name: '',
- },
- {
- x: X[1].slice(tailLength, currentIndex),
- y: Y[1].slice(tailLength, currentIndex),
- mode: 'lines+markers',
- marker: {
- symbol: 'star',
- size: 8,
- line: { width: 0 },
- },
- line: {
- width: 2,
- },
- name: '',
- },
- {
- x: X[2].slice(tailLength, currentIndex),
- y: Y[2].slice(tailLength, currentIndex),
- mode: 'lines+markers',
- marker: {
- symbol: 'star',
- size: 8,
- line: { width: 0 },
- },
- line: {
- width: 2,
- },
- name: '',
- },
- ];
-
- // width: 1000, height: 400
- const layout = {
- title: '∞ Three-Body Problem: t = ' + time[currentIndex].toFixed(3),
- xaxis: {
- title: 'x',
- range: [-1.1,1.1]
- },
- yaxis: {
- title: 'y',
- scaleanchor: 'x',
- scaleratio: 1,
- range: [-0.5,0.5]
- },
- plot_bgcolor: 'black',
- paper_bgcolor: 'black',
- font: {
- color: 'white',
- },
- };
-
- try {
- Plotly.animate("plot", {
- data: data, layout: layout
- }, {
- staticPlot: true,
- transition: {
- duration: 0,
- },
- frame: {
- duration: 0,
- redraw: false,
- }
- });
- } catch (err) {
- Plotly.newPlot('plot', data, layout);
- }
-
-
- plotIndex += delay;
- if (plotClassic===true) {
- try {
- requestAnimationFrame(plotClassicFunc);
- }
- catch (err) {
- console.log(err)
- }
- }
-
- }
-
- plotClassicFunc();
-
- }
-}
-