-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSquared.java
391 lines (326 loc) · 15.7 KB
/
Squared.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
// Barnes.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.concurrent.*;
import javax.swing.*;
import java.util.Random;
public class Squared {
public static void main(String[] args) {
/*
Reads planets from a .csv file.
First line should be the amount of planets to read as an integer.
Following lines should be planets of the format:
mass,radius,positionX,positionY,velocityX,velocityY
*/
/** Command Line arguments
* 1 gNumBodies -> How many planets
* 2 numSteps -> Amount of steps in the program
* 3 numWorkers -> how many parallel workers
* 4 graphics -> false or true : show graphics or not
* 5 file bool -> false or true : read from file
* 6 file -> file name. If empty, default would be used
*/
int gNumBodies;
int numSteps;
int numWorkers;
Boolean graphics;
Boolean fileb;
if(args.length > 0){
gNumBodies = Integer.valueOf(args[0]) > 0 ? Integer.valueOf(args[0]) : 1;
numSteps = Integer.valueOf(args[1]) > 0 ? Integer.valueOf(args[1]) : 300;
numWorkers = Integer.valueOf(args[3]) > 0 ? Integer.valueOf(args[3]) : 1;
graphics = args.length > 4 ? Boolean.valueOf(args[4]) : false;
fileb = args.length > 5 ? Boolean.valueOf(args[5]) : false;
}
else{
gNumBodies = 120;
numSteps = 40000;
numWorkers = 4;
graphics = false;
fileb = false;
}
System.out.println("gNumBodies: " + gNumBodies);
System.out.println("numSteps: " + numSteps);
System.out.println("numWorkers: " + numWorkers);
// Random number generator. Used for planets
Random rand = new Random();
// Planets
Planet[] planets = new Planet[gNumBodies];
// Space size
int height = 32;
int width = 32;
// Frame size multiplicator
int wm = 10;
int hm = 10;
for(int k = 0; k < 5; k++){
// Create planets
if(fileb) {
String file = args.length > 6 ? args[6] : "testPlanets.csv";
try (BufferedReader buff = new BufferedReader(new FileReader(file))) {
// read the amount of planets
String line;
// read and create planets
int id = 0;
String[] row;
while ((line = buff.readLine()) != null) {
row = line.split(",");
planets[id++] = new Planet(id, Double.parseDouble(row[0]), Double.parseDouble(row[1]), Double.parseDouble(row[2]), Double.parseDouble(row[3]), Double.parseDouble(row[4]), Double.parseDouble(row[5]));
}
} catch (Exception e) {
//System.out.println("File " + args[0] + " could not be opened.");
}
}
else {
// Randomize planets
for(int i = 0; i < gNumBodies; i++){
planets[i] = new Planet(
i,
(rand.nextDouble()*Math.pow(10, 13)),
rand.nextDouble()+1,
rand.nextDouble()*width,
rand.nextDouble()*height,
rand.nextDouble()*0,
rand.nextDouble()*0);
}
}
// Print out all the planets
/*
System.out.println("Planets in the order they were added");
for (Planet planet : planets) {
System.out.println(planet.toString());
}
*/
// Graphics
Draw draw = new Draw(width*wm+50, height*hm+50, gNumBodies);
if(graphics){
JFrame frame = new JFrame();
draw = new Draw(width*wm+50, height*hm+50, gNumBodies);
for(int i = 0; i < gNumBodies; i++){
draw.addCircle(i, planets[i].getX()*wm, planets[i].getY()*hm, 30);
}
frame.setSize(width*wm+50, height*hm+50);
frame.setTitle("N-Body Problem");
frame.add(draw);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
//System.out.println("Planets in the order they were added");
/*
System.out.println("\nBefore");
for (Planet planet : planets) {
System.out.println(planet.toString());
}
*/
CyclicBarrier barrier = new CyclicBarrier(numWorkers);
int stripSize = (gNumBodies % numWorkers == 0) ? (gNumBodies / numWorkers) : ((gNumBodies / numWorkers) + 1);
int start;
int end;
long startTime = System.nanoTime();
// Create workers
Worker[] workers = new Worker[numWorkers];
for (int i = 0; i < numWorkers; i++) {
start = i * stripSize;
end = (i == numWorkers - 1) ? (gNumBodies - 1) : (start + stripSize - 1); // edge case. Giving the last worker extra work if the division is uneven
workers[i] = new Worker(i, width, height, barrier, planets, start, end, numSteps, draw, graphics);
workers[i].start();
}
// Wait for workers to complete their
for (int i = 0; i < numWorkers; i++) {
try{
workers[i].join();
} catch (InterruptedException ex){
System.out.println("JOIN INTERRUPTED");
return;
}
}
long runTime = System.nanoTime() - startTime;
double secs = Math.round(runTime * Math.pow(10, -6));
System.out.println("Runtime: " + secs + " ms");
}
/*System.out.println("\nAfter");
for (Planet planet : planets) {
System.out.println(planet.toString());
}*/
//tree.prettyPrint();
}
private static class Worker extends Thread {
int id;
CyclicBarrier barrier;
Planet[] planets;
int startPlanetIndex;
int endPlanetIndex;
int steps;
private final double gforce = 6.67 * Math.pow(10, -11);
private final double secondsPerFrame = 0.01;
private final double elasticity = 1.0;
Draw draw;
int width;
int height;
Boolean graphics;
public Worker(int id, int width, int height, CyclicBarrier barrier, Planet[] planets, int startPlanetIndex, int endPlanetIndex, int steps, Draw draw, Boolean graphics) {
this.id = id;
this.barrier = barrier;
this.planets = planets;
this.startPlanetIndex = startPlanetIndex;
this.endPlanetIndex = endPlanetIndex;
this.steps = steps;
this.draw = draw;
this.width = width;
this.height = height;
this.graphics = graphics;
}
private void calculateForce(int index, Planet[] planets){
// calculateForce takes a planet and a node,
// calculates the force that the planet will feel from the node,
// calculates the acceleration created by that force,
// and adds this acceleration to a sum of accelerations that the planet feels
/* Formulas
Newton's second law
F = mass * acceleration
Gravitational force
F = G * (mass1 * mass2) / distance^2
*/
// Calculate the distances between the planets
for(int i = 0; i < planets.length; i++){
if(planets[i].id == planets[index].id) continue;
double distanceX = planets[i].getX() - planets[index].getX();
double distanceY = planets[i].getY() - planets[index].getY();
double distance = Math.sqrt(distanceX*distanceX + distanceY*distanceY);
double force;
// Calculate the gravitational force
if (distance == 0 ) {
force = 0;
}
else {
force = gforce * planets[index].mass * planets[i].mass / (distance*distance);
}
// Calculate the directional components of the gravitational force
double angle = Math.atan2(distanceY, distanceX);
double forceX = force * Math.cos(angle);
double forceY = force * Math.sin(angle);
// Calculate and add the accelerations in x and y directions
double accelerationX = forceX / planets[index].mass;
double accelerationY = forceY / planets[index].mass;
// Handle an eventual collision
if (distance <= (planets[index].size + planets[i].size)) {
// If a collision is detected we ignore the gravitational force of this planet for this time step. This should produce a bouncing effect.
// Find directional vector between planets
double directionX = planets[i].getX() - planets[index].getX();
double directionY = planets[i].getY() - planets[index].getY();
// Normalize the directional vector
double directionalLength = Math.sqrt(directionX*directionX + directionY*directionY);
directionX = directionX / directionalLength;
directionY = directionY / directionalLength;
// Project velocities onto directinal vector, producing two scalars
double v1 = planets[index].xVel * directionX + planets[index].yVel * directionY;
double v2 = planets[i].xVel * directionX + planets[i].yVel * directionY;
// Calculate scalar velocity after collision
double collisionVelocity = (planets[index].mass*v1 + planets[i].mass*v2 - planets[i].mass * elasticity * (v1 - v2)) / (planets[index].mass + planets[i].mass);
// Calculate the directional acceleration
double collisionAcceleration = (collisionVelocity) - v1; // + (collisionVelocity < 0 ? -0.2 : 0.2)
// Multiply acceleration with directional vector to produce directional acceleration vector
double collisionX = directionX * collisionAcceleration;
double collisionY = directionY * collisionAcceleration;
// Add collisions acceleration to the planet's accelerations
planets[index].ax += collisionX + -(accelerationX * ((planets[index].size + planets[i].size) / distance));
planets[index].ay += collisionY + -(accelerationY * ((planets[index].size + planets[i].size) / distance));
}
else {
planets[index].ax += accelerationX;
planets[index].ay += accelerationY;
}
}
}
public void run() {
for(int j = 0; j < steps; j++){
// Calculate force
// for each of the worker's planets
// calculate next position of the planet
for(int i = startPlanetIndex; i <= endPlanetIndex; i++){
calculateForce(i, planets);
// Calculate how far the planet will move based on current velocity and acceleration
// distance = (current_velocity * time) + total_acceleration * (time^2) / 2
double distanceX = (planets[i].xVel * secondsPerFrame) + planets[i].ax * secondsPerFrame*secondsPerFrame / 2;
double distanceY = (planets[i].yVel * secondsPerFrame) + planets[i].ay * secondsPerFrame*secondsPerFrame / 2;
// Calculate new velocity
// v = v0 + a * t
planets[i].xVel += planets[i].ax * secondsPerFrame;
planets[i].yVel += planets[i].ay * secondsPerFrame;
// System.out.println("What is distX: " + distanceX);
// System.out.println("What is distY: " + distanceY);
double newX = planets[i].getX() + distanceX; //planets[i].getX() + 1;
double newY = planets[i].getY() + distanceY; //planets[i].getY() + 1;
//System.out.println("New X for planets[i] " + planets[i].id + " is: " + distanceX);
if (newX < 0) {
newX = 0;
}
else if (width - 1 <= newX) {
newX = width - 1;
}
if (newY < 0) {
newY = 0;
}
else if (height - 1 <= newY) {
newY = height - 1;
}
planets[i].setX(newX);
planets[i].setY(newY);
}
// await other workers to finish their calculations
try{
barrier.await();
} catch(InterruptedException ex) {
System.out.println("Interrupted exception barrier.");
return;
} catch(BrokenBarrierException ex) {
System.out.println("Broken barrier exception.");
return;
}
// move planets
// for each of the worker's planets
// call planet.update()
// (also draw on screen)
for(int i = startPlanetIndex; i <= endPlanetIndex; i++){
planets[i].updateCoordinates();
//System.out.println(planets[i].toString());
if(graphics)
draw.addCircle(i, planets[i].getX()*10, planets[i].getY()*10, 20*planets[i].size);
}
// wait for all planets to update
try{
barrier.await();
} catch(InterruptedException ex) {
System.out.println("Interrupted exception barrier.");
return;
} catch(BrokenBarrierException ex) {
System.out.println("Broken barrier exception.");
return;
}
// First worker will rebuid the tree and other will wait for it to finish
if(id == 0 && graphics){
draw.redo();
/*
System.out.printf("\nstep %d\n", j);
for (Planet planet : planets) {
System.out.println(planet.toString());
}
*/
}
try{
barrier.await();
} catch(InterruptedException ex) {
System.out.println("Interrupted exception barrier.");
return;
} catch(BrokenBarrierException ex) {
System.out.println("Broken barrier exception.");
return;
}
if(graphics){
try{
TimeUnit.MILLISECONDS.sleep(10);
} catch(InterruptedException ex) {}
}
}
}
}
}