Aufgabenblatt 1
This commit is contained in:
253
src/Simulation.java
Normal file
253
src/Simulation.java
Normal file
@ -0,0 +1,253 @@
|
||||
import codedraw.CodeDraw;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Random;
|
||||
|
||||
// TODO: insert answers to questions (Zusatzfragen) in 'Aufgabenblatt1.md' as comment.
|
||||
|
||||
// Simulates the formation of a massive solar system.
|
||||
public class Simulation {
|
||||
|
||||
// gravitational constant
|
||||
public static final double G = 6.6743e-11;
|
||||
|
||||
// one astronomical unit (AU) is the average distance of earth to the sun.
|
||||
public static final double AU = 150e9; // meters
|
||||
|
||||
// one light year
|
||||
public static final double LY = 9.461e15; // meters
|
||||
|
||||
// some further constants needed in the simulation
|
||||
public static final double SUN_MASS = 1.989e30; // kilograms
|
||||
public static final double SUN_RADIUS = 696340e3; // meters
|
||||
public static final double EARTH_MASS = 5.972e24; // kilograms
|
||||
public static final double EARTH_RADIUS = 6371e3; // meters
|
||||
|
||||
// set some system parameters
|
||||
public static final double SECTION_SIZE = 2 * AU; // the size of the square region in space
|
||||
public static final int NUMBER_OF_BODIES = 22;
|
||||
public static final double OVERALL_SYSTEM_MASS = 20 * SUN_MASS; // kilograms
|
||||
|
||||
// all quantities are based on units of kilogram respectively second and meter.
|
||||
|
||||
// The main simulation method using instances of other classes.
|
||||
public static void main(String[] args) {
|
||||
|
||||
//TODO: change implementation of this method according to 'Aufgabenblatt1.md'.
|
||||
|
||||
// simulation
|
||||
CodeDraw cd = new CodeDraw();
|
||||
Body[] bodies = new Body[NUMBER_OF_BODIES];
|
||||
Vector3[] forceOnBody = new Vector3[bodies.length];
|
||||
|
||||
Random random = new Random(2022);
|
||||
|
||||
for (int i = 0; i < bodies.length; i++) {
|
||||
bodies[i] = new Body();
|
||||
bodies[i].mass = Math.abs(random.nextGaussian()) * OVERALL_SYSTEM_MASS / bodies.length; // kg
|
||||
bodies[i].massCenter = new Vector3();
|
||||
bodies[i].currentMovement = new Vector3();
|
||||
bodies[i].massCenter.x = 0.2 * random.nextGaussian() * AU;
|
||||
bodies[i].massCenter.y = 0.2 * random.nextGaussian() * AU;
|
||||
bodies[i].massCenter.z = 0.2 * random.nextGaussian() * AU;
|
||||
|
||||
bodies[i].currentMovement.x = 0 + random.nextGaussian() * 5e3;
|
||||
bodies[i].currentMovement.y = 0 + random.nextGaussian() * 5e3;
|
||||
bodies[i].currentMovement.z = 0 + random.nextGaussian() * 5e3;
|
||||
|
||||
}
|
||||
|
||||
double seconds = 0;
|
||||
|
||||
// simulation loop
|
||||
while (true) {
|
||||
seconds++; // each iteration computes the movement of the celestial bodies within one second.
|
||||
|
||||
// merge bodies that have collided
|
||||
for (int i = 0; i < bodies.length; i++) {
|
||||
for (int j = i + 1; j < bodies.length; j++) {
|
||||
if (distance(bodies[j].massCenter, bodies[i].massCenter) <
|
||||
SpaceDraw.massToRadius(bodies[j].mass) + SpaceDraw.massToRadius(bodies[i].mass)) {
|
||||
bodies[i] = merge(bodies[i], bodies[j]);
|
||||
Body[] bodiesOneRemoved = new Body[bodies.length - 1];
|
||||
for (int k = 0; k < bodiesOneRemoved.length; k++) {
|
||||
bodiesOneRemoved[k] = bodies[k < j ? k : k + 1];
|
||||
}
|
||||
bodies = bodiesOneRemoved;
|
||||
|
||||
// since the body index i changed size there might be new collisions
|
||||
// at all positions of bodies, so start all over again
|
||||
i = -1;
|
||||
j = bodies.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for each body (with index i): compute the total force exerted on it.
|
||||
for (int i = 0; i < bodies.length; i++) {
|
||||
forceOnBody[i] = new Vector3(); // begin with zero
|
||||
for (int j = 0; j < bodies.length; j++) {
|
||||
if (i != j) {
|
||||
Vector3 forceToAdd = gravitationalForce(bodies[i], bodies[j]);
|
||||
forceOnBody[i] = plus(forceOnBody[i], forceToAdd);
|
||||
}
|
||||
}
|
||||
}
|
||||
// now forceOnBody[i] holds the force vector exerted on body with index i.
|
||||
|
||||
// for each body (with index i): move it according to the total force exerted on it.
|
||||
for (int i = 0; i < bodies.length; i++) {
|
||||
move(bodies[i], forceOnBody[i]);
|
||||
}
|
||||
|
||||
// show all movements in the canvas only every hour (to speed up the simulation)
|
||||
if (seconds % (3600) == 0) {
|
||||
// clear old positions (exclude the following line if you want to draw orbits).
|
||||
cd.clear(Color.BLACK);
|
||||
|
||||
// draw new positions
|
||||
for (Body body : bodies) {
|
||||
draw(cd, body);
|
||||
}
|
||||
|
||||
// show new positions
|
||||
cd.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TODO: remove static methods below.
|
||||
|
||||
// Draws a body in the 'cd' canvas showing a projection onto the (x,y)-plane. The body's mass
|
||||
// center coordinates and its radius are transformed into canvas coordinates. The color of
|
||||
// the body corresponds to the temperature of the body, assuming the relation of mass and
|
||||
// temperature of a main sequence star.
|
||||
// The canvas is assumed to show a quadratic SECTION_SIZE x SECTION_SIZE
|
||||
// section of space centered arround (x, y) = (0, 0).
|
||||
public static void draw(CodeDraw cd, Body b) {
|
||||
|
||||
cd.setColor(SpaceDraw.massToColor(b.mass));
|
||||
drawAsFilledCircle(cd, b.massCenter, SpaceDraw.massToRadius(b.mass));
|
||||
|
||||
}
|
||||
|
||||
// Draws a filled circle in the 'cd' canvas unsing the (x,y)-coordinates of 'center'
|
||||
// Coordinates and 'radius' are transformed into canvas coordinates. The canvas is assumed
|
||||
// to show a quadratic SECTION_SIZE x SECTION_SIZE projection of space centered arround (x, y) =
|
||||
// (0, 0).
|
||||
public static void drawAsFilledCircle(CodeDraw cd, Vector3 center, double radius) {
|
||||
double x = cd.getWidth() * (center.x + Simulation.SECTION_SIZE / 2) / Simulation.SECTION_SIZE;
|
||||
double y = cd.getWidth() * (center.y + Simulation.SECTION_SIZE / 2) / Simulation.SECTION_SIZE;
|
||||
radius = cd.getWidth() * radius / Simulation.SECTION_SIZE;
|
||||
cd.fillCircle(x, y, Math.max(radius, 1.5));
|
||||
}
|
||||
|
||||
// Returns a vector representing the gravitational force exerted by body 'b2' on body 'b1'.
|
||||
// The gravitational Force F is calculated by F = G*(m1*m2)/(r*r), with m1 and m2 being the masses of the objects
|
||||
// interacting, r being the distance between the centers of the masses and G being the gravitational constant.
|
||||
// To calculate the force exerted on b1, simply multiply the normalized vector pointing from b1 to b2 with the
|
||||
// calculated force.
|
||||
public static Vector3 gravitationalForce(Body b1, Body b2) {
|
||||
|
||||
Vector3 direction = minus(b2.massCenter, b1.massCenter);
|
||||
double distance = length(direction);
|
||||
normalize(direction);
|
||||
double force = G * b1.mass * b2.mass / (distance * distance);
|
||||
return times(direction, force);
|
||||
}
|
||||
|
||||
// Returns a new body that is formed by the collision of 'b1' and 'b2'. The impulse
|
||||
// of the returned body is the sum of the impulses of 'b1' and 'b2'.
|
||||
public static Body merge(Body b1, Body b2) {
|
||||
|
||||
Body result = new Body();
|
||||
result.mass = b1.mass + b2.mass;
|
||||
result.massCenter = times(plus(times(b1.massCenter, b1.mass), times(b2.massCenter,
|
||||
b2.mass)),
|
||||
1 / result.mass);
|
||||
result.currentMovement =
|
||||
times(plus(times(b1.currentMovement, b1.mass), times(b2.currentMovement, b2.mass)),
|
||||
1.0 / result.mass);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Move the body 'b' according to the 'force' excerted on it.
|
||||
public static void move(Body b, Vector3 force) {
|
||||
Vector3 newPosition = plus(
|
||||
plus(b.massCenter,
|
||||
times(force, 1 / b.mass)
|
||||
// F = m*a -> a = F/m
|
||||
),
|
||||
b.currentMovement
|
||||
);
|
||||
|
||||
// new minus old position.
|
||||
Vector3 newMovement = minus(newPosition, b.massCenter);
|
||||
|
||||
// update body state
|
||||
b.massCenter = newPosition;
|
||||
b.currentMovement = newMovement;
|
||||
}
|
||||
|
||||
// Returns the norm of v1-v2.
|
||||
public static double distance(Vector3 v1, Vector3 v2) {
|
||||
|
||||
double dX = v1.x - v2.x;
|
||||
double dY = v1.y - v2.y;
|
||||
double dZ = v1.z - v2.z;
|
||||
|
||||
return Math.sqrt(dX * dX + dY * dY + dZ * dZ);
|
||||
}
|
||||
|
||||
// Returns v1+v2.
|
||||
public static Vector3 plus(Vector3 v1, Vector3 v2) {
|
||||
|
||||
Vector3 result = new Vector3();
|
||||
result.x = v1.x + v2.x;
|
||||
result.y = v1.y + v2.y;
|
||||
result.z = v1.z + v2.z;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns v1-v2.
|
||||
public static Vector3 minus(Vector3 v1, Vector3 v2) {
|
||||
|
||||
Vector3 result = new Vector3();
|
||||
result.x = v1.x - v2.x;
|
||||
result.y = v1.y - v2.y;
|
||||
result.z = v1.z - v2.z;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns v*d.
|
||||
public static Vector3 times(Vector3 v, double d) {
|
||||
|
||||
Vector3 result = new Vector3();
|
||||
result.x = v.x * d;
|
||||
result.y = v.y * d;
|
||||
result.z = v.z * d;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the norm of 'v'.
|
||||
public static double length(Vector3 v) {
|
||||
|
||||
return distance(v, new Vector3()); // distance to origin.
|
||||
}
|
||||
|
||||
// Normalizes the specified vector 'v': changes the length of the vector such that its length
|
||||
// becomes one. The direction and orientation of the vector is not affected.
|
||||
public static void normalize(Vector3 v) {
|
||||
|
||||
double length = length(v);
|
||||
v.x /= length;
|
||||
v.y /= length;
|
||||
v.z /= length;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user