--- /dev/null
+# Makefile to build all programs
+
+all : single-thread/nbody mthread/nbody openmp/nbody
+
+single-thread/nbody :
+ make -C single-thread
+
+mthread/nbody :
+ make -C mthread
+
+openmp/nbody :
+ make -C openmp
+
+clean :
+ make -C single-thread clean_full
+ make -C mthread clean_full
+ make -C openmp clean_full
+ rm *~
plot = Gnuplot.Gnuplot(persist=0)
programs = {
- "single-thread" : "./single-thread/nbody -g -v 5"
+ "single-thread" : "./single-thread/nbody -v 5"
}
-for n in range(1, 5):
- programs.update({"mthread"+str(n) : "./mthread/nbody -g -v 5 -n "+str(n)})
+for n in range(2, 6):
+ programs.update({"mthread"+str(n) : "./mthread/nbody -v 5 --pedantic-graphics -n "+str(n)})
+ programs.update({"mthread"+str(n) : "./mthread/nbody -v 5 -n "+str(n)})
+ #programs.update({"slow-mthread"+str(n) : "./mthread/nbody-slow --pedantic-graphics -v 5 -n "+str(n)})
+ #programs.update({"openmp"+str(n) : "./openmp/nbody -v 5 --pedantic-graphics -n " +str(n)})
def RunForSteps(program, field, steps):
results = []
- #print(str(program) + " -s "+str(steps) + " " + str(field))
- results.append(RunProgram(str(program) + " -s "+str(steps) + " " + str(field)))
+ print(str(program) + " -s "+str(steps) + " " + str(field))
+ results = RunProgram(str(program) + " -s "+str(steps) + " " + str(field))
return results
def VaryField(program, steps, fields):
fields = map(int, os.listdir("fields"))
fields.sort(key = lambda e : e)
+ ignore = [10, 100]
for n in fields:
+ if n in ignore:
+ continue
for p in data.keys():
- data[p] = [RunForSteps(programs[p], "fields/"+str(n), 200), n]
+ data[p] = RunForSteps(programs[p], "fields/"+str(n), 200)
#print(str(data[p]))
- if (len(data[p]) > 0):
- plot.replot(Gnuplot.Data(data[p][0], title=str(p)+":"+str(n), with_="lp linecolor "+str(fields.index(n))))
+
+ #print(str(data.items()));
+ for d in sorted(data.items(), key = lambda e : e[1][len(e[1])-1][1]):
+ if (len(d[1]) > 0):
+ plot.replot(Gnuplot.Data(d[1], title=str(d[0])+":"+str(n), with_="lp")) #linecolor "+str(fields.index(n))))
#print(str(data.items()))
# Score the programs
pthread_t * worker_thread = NULL; //Array of worker threads responsible for Force and Position updates
System * sub_system = NULL; //Array of Systems used to divide up the main "universe" System for worker threads
-pthread_mutex_t mutex_workers; // Mutex used for the barrier between Force and Position updates
-pthread_cond_t workers_done_cv; // Conditional used for the barrier between Force and Position updates
-unsigned workers_busy; // Number of workers currently doing something
pthread_mutex_t mutex_runstate; // Mutex around the runstate
+#ifdef PERSISTENT_THREADS
+Barrier force_barrier; // I laughed at this variable name. A bit sad really.
+Barrier position_barrier;
+#else
+Barrier worker_barrier;
+#endif //PERSISTENT_THREADS
+
+Barrier graphics_barrier;
/**
options.num_threads = s->N;
}
+ pthread_attr_t attr; //thread attribute for the workers.
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //Needs to be detached, so that memory can be reused.
+
if (options.num_threads > 1) // Allocate worker threads and sub systems, as long as there would be more than 1
{
worker_thread = (pthread_t*)(calloc(options.num_threads, sizeof(pthread_t)));
if (i == options.num_threads - 1)
sub_system[i].N += remainder; // The last thread gets the remainder
+ #ifdef PERSISTENT_THREADS
+ if (pthread_create(worker_thread+i, & attr, Worker_Thread, (void*)(sub_system+i)) != 0)
+ {
+ perror("In compute thread, couldn't create worker thread");
+ QuitProgram(true);
+ pthread_exit(NULL);
+ }
+ #endif //PERSISTENT_THREADS
+
}
- }
+
+ }
+ #ifdef PERSISTENT_THREADS
+ else
+ {
+ while (!ExitCondition())
+ {
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
+ {
+ DisplayStatistics();
+ }
- pthread_attr_t attr; //thread attribute for the workers.
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //Needs to be detached, so that memory can be reused.
+ // Just do everything in this thread
+ System_Forces(s, s);
+ System_Positions(s);
+ continue;
+ }
+ QuitProgram(false);
+ pthread_exit(NULL);
+ }
+ #else
// The main computation loop
while (true)
{
pthread_exit(NULL);
}
- if (options.draw_graphics == false && options.verbosity != 0
- && universe.steps % options.verbosity == 1)
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
{
DisplayStatistics();
}
-
- workers_busy = options.num_threads; //All threads working
-
//Compute forces
for (unsigned i = 0; i < options.num_threads; ++i)
{
QuitProgram(true);
pthread_exit(NULL);
}
+
}
-
- //Barrier - Wait for forces to be computed
- pthread_mutex_lock(&mutex_workers);
- while (workers_busy > 0)
- pthread_cond_wait(&workers_done_cv, &mutex_workers);
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Wait(&worker_barrier);
//All the forces are now computed
-
- workers_busy = options.num_threads; //All threads working
+
+ if (options.draw_graphics && options.pedantic_graphics)
+ {
+ Barrier_Wait(&graphics_barrier);
+ Barrier_Enter(&graphics_barrier);
+ }
//Compute positions
for (unsigned i = 0; i < options.num_threads; ++i)
}
//Wait for positions to be computed
- pthread_mutex_lock(&mutex_workers);
- while (workers_busy > 0)
- pthread_cond_wait(&workers_done_cv, &mutex_workers);
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Wait(&worker_barrier);
+
//Update number of steps computed
universe.steps += 1;
+ if (options.draw_graphics && options.pedantic_graphics)
+ Barrier_Leave(&graphics_barrier);
+
+
}
+ #endif //PERSISTENT_THREADS
+
+ return NULL;
}
+/**
+ * @function BeforeDraw
+ * @purpose Called in graphics thread before the draw loop
+ * When --pedantic-graphics enabled, will wait for position computations to finish before drawing
+ * Otherwise does nothing
+ */
+void BeforeDraw()
+{
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 0)
+ DisplayStatistics();
+ //printf("BEFORE DRAW\n");
+ if (!options.pedantic_graphics)
+ return;
+ #ifdef PERSISTENT_THREADS
+ Barrier_Wait(&position_barrier);
+
+ #else
+ Barrier_Wait(&graphics_barrier);
+ #endif //PERSISTENT_THREADS
+
+ Barrier_Enter(&graphics_barrier);
+}
+
+/**
+ * @function AfterDraw
+ * @purpose Called in graphics thread after the draw loop
+ * When --pedantic-graphics is supplied, will signal computation thread that drawing is finished
+ * So that positions can be safely altered
+ * Otherwise does nothing
+ */
+void AfterDraw()
+{
+ universe.steps += 1;
+ if (!options.pedantic_graphics)
+ return;
+ Barrier_Leave(&graphics_barrier);
+
+}
+
+#ifdef PERSISTENT_THREADS
+
+/**
+ * @function Worker_Thread
+ * @purpose Thread - Calculate stuff
+ */
+void * Worker_Thread(void * arg)
+{
+ System * s = (System*)(arg);
+ while (!ExitCondition())
+ {
+
+ Barrier_Enter(&force_barrier);
+
+ System_Forces(s, &universe);
+
+ Barrier_Leave(&force_barrier);
+
+ //printf("Computed forces for %p\n", arg);
+ Barrier_Wait(&force_barrier);
+ //printf("Computed ALL forces\n");
+
+ if (options.draw_graphics && options.pedantic_graphics)
+ Barrier_Wait(&graphics_barrier);
+
+ Barrier_Enter(&position_barrier);
+
+ System_Positions(s);
+
+ Barrier_Leave(&position_barrier);
+ //printf("Computed positions for %p\n", arg);
+ Barrier_Wait(&position_barrier);
+ //printf("Computed ALL positions\n");
+ }
+ QuitProgram(false);
+ pthread_exit(NULL);
+}
+
+
+#else
+
/**
* @function Force_Thread
* @purpose Thread - Calculates the forces on objects in a System
*/
void * Force_Thread(void * s)
{
-
+ Barrier_Enter(&worker_barrier);
+
System_Forces((System*)s, &universe); //Simple wrapper
- pthread_mutex_lock(&mutex_workers);
- workers_busy -= 1; // Count this thread as done
- if (workers_busy == 0)
- {
- pthread_cond_signal(&workers_done_cv); // All threads done; wake up the compute_thread
- }
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Leave(&worker_barrier);
+
return NULL;
}
*/
void * Position_Thread(void * s)
{
-
+ Barrier_Enter(&worker_barrier);
System_Positions((System*)s); // Simple wrapper
-
- pthread_mutex_lock(&mutex_workers);
- workers_busy -= 1; // Count this thread as done
- if (workers_busy == 0)
- {
- pthread_cond_signal(&workers_done_cv); //All threads done; wake up the compute_thread
- }
- pthread_mutex_unlock(&mutex_workers);
+ Barrier_Leave(&worker_barrier);
return NULL;
}
+#endif //PERSISTENT_THREADS
+
/**
* @function QuitProgram
* @purpose This function can either be called by the main thread in order to signal other threads
{
atexit(Thread_Cleanup);
+ #ifdef PERSISTENT_THREADS
+ Barrier_Init(&force_barrier);
+ Barrier_Init(&position_barrier);
+ #else
+ Barrier_Init(&worker_barrier);
+ #endif //PERSISTENT_THREADS
+ Barrier_Init(&graphics_barrier);
+
+
if (options.draw_graphics)
{
// The graphics are enabled, so create a thread to do computations
// Graphics are done in the main loop
+ //printf("Graphics are enabled\n");
+ #ifdef PERSISTENT_THREADS
+ Compute_Thread((void*)(&universe));
+ #else
if (pthread_create(&compute_thread, NULL, Compute_Thread, (void*)&universe) != 0)
{
perror("Error creating compute thread");
exit(EXIT_FAILURE);
}
+ #endif //PERSISTENT_THREADS
+ //printf("Run compute thread\n");
Graphics_Run(argc, argv);
}
else
+
Compute_Thread((void*)(&universe)); // Graphics are disabled, so do computations in the main thread
}
+
+
+
+void Barrier_Init(Barrier * b)
+{
+ b->threads_busy = 0;
+}
+
+void Barrier_Enter(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+ b->threads_busy += 1;
+ pthread_mutex_unlock(&(b->mutex));
+}
+
+void Barrier_Leave(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+ b->threads_busy -= 1;
+ if (b->threads_busy == 0)
+ {
+ pthread_cond_signal(&(b->threads_done_cv));
+ }
+ pthread_mutex_unlock(&(b->mutex));
+}
+
+void Barrier_Wait(Barrier * b)
+{
+ pthread_mutex_lock(&(b->mutex));
+ while (b->threads_busy > 0)
+ pthread_cond_wait(&(b->threads_done_cv), &(b->mutex));
+ pthread_mutex_unlock(&(b->mutex));
+}
#define DEFAULT_WORKING_THREADS 2
+//#define PERSISTENT_THREADS //If defined, threads will not be continually destroyed and then respawned
+
+
+
//Undefine default macros, replace with functions
#undef Simulation_Run
void Simulation_Run(int argc, char ** argv);
#undef QuitProgram
void QuitProgram(bool error);
+#undef BeforeDraw
+void BeforeDraw();
+#undef AfterDraw
+void AfterDraw();
+
void * Compute_Thread(void * system); //Thread - Continuously perform computations for a System of bodies. May spawn additional worker threads.
+#ifdef PERSISTENT_THREADS
+void * Worker_Thread(void * arg);
+#else
void * Force_Thread(void * system); //Thread - Compute forces for all objects in a system
void * Position_Thread(void * system); //Thread - Compute positions for all objects in a system
-
-
+#endif //PERSISTENT_THREADS
void Thread_Cleanup(void); //Called at program exit to safely join computation thread
-extern pthread_t compute_thread; // ID of the thread that runs Compute_Thread.
-extern pthread_mutex_t mutex_runstate; //Mutex around the "runstate" variable
-
-extern pthread_t * worker_thread; //Array of worker threads responsible for Force and Position updates
-extern System * sub_system; //Array of Systems used to divide up the main "universe" System for worker threads
-extern pthread_mutex_t mutex_workers;
-extern pthread_cond_t workers_done_cv;
-extern unsigned workers_busy;
+typedef struct
+{
+ pthread_mutex_t mutex;
+ unsigned threads_busy; // Counter of threads which are busy
+ pthread_cond_t threads_done_cv;
+} Barrier;
+
+void Barrier_Init(Barrier * b);
+void Barrier_Enter(Barrier * b);
+void Barrier_Leave(Barrier * b);
+void Barrier_Wait(Barrier * b);
#endif //_NBODY_MTHREAD_H
#include "../single-thread/nbody.c" // Include original code
#include "graphics.h" // For Graphics_Run function only
+omp_lock_t graphics_lock;
+bool graphics_busy = false;
+
/**
* @function Simulation_Run
* @purpose Start the simulation
omp_set_num_threads(1);
-
+ omp_init_lock(&graphics_lock);
#pragma omp parallel sections
{
break;
}
-
- if (options.draw_graphics == false && options.verbosity != 0
- && universe.steps % options.verbosity == 1)
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
{
DisplayStatistics();
}
}
}
+
+ omp_destroy_lock(&graphics_lock);
}
/**
System_Forces(&s, &universe);
}
+ // Wait for graphics to finish drawing
+
+ if (options.draw_graphics && options.pedantic_graphics)
+ {
+ while (graphics_busy);
+ }
+
#pragma omp parallel for
for (unsigned i = 0; i < options.num_threads; ++i)
{
universe.steps += 1;
+ // Tell graphics to continue
+ if (options.draw_graphics && options.pedantic_graphics)
+ {
+ omp_set_lock(&graphics_lock);
+ graphics_busy = true;
+ omp_unset_lock(&graphics_lock);
+
+ }
+
+}
+
+/**
+ * @function BeforeDraw
+ * @purpose Called before the graphics thread draws bodies.
+ * If pedantic graphics are used, will wait until graphics_busy is set to true before drawing
+ */
+void BeforeDraw()
+{
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 0)
+ DisplayStatistics();
+ if (!options.pedantic_graphics)
+ return;
+ while (graphics_busy == false);
+}
+
+/**
+ * @function AfterDraw
+ * @purpose Called after the graphics thread draws bodies
+ * If pedantic graphics used, will unset graphics_busy when done drawing
+ */
+void AfterDraw()
+{
+ if (!options.pedantic_graphics)
+ return;
+ omp_set_lock(&graphics_lock);
+ graphics_busy = false;
+ omp_unset_lock(&graphics_lock);
}
//#undef QuitProgram
//void QuitProgram(bool error);
+#undef BeforeDraw
+void BeforeDraw();
+#undef AfterDraw
+void AfterDraw();
+
void Compute(); //Compute a single step
#endif //_NBODY_OPENMP_H
{
if (options.draw_graphics == false)
{
- while (true)
- Graphics_Display();
-
- return;
+ // This message is left here for when I inevitably accidentally call the function
+ fprintf(stderr, "Graphics_Run should not be called if graphics are disabled!\n");
+ exit(EXIT_FAILURE);
}
glutInit(&argc, argv);
*/
void Graphics_Display()
{
- //Check whether the program should quit due to steps being computed, or a timeout
- if (ExitCondition())
+
+ if (options.draw_graphics == false)
{
- //printf("Leave graphics loop\n");
- glutLeaveMainLoop();
- return;
+ // This message is left here for when I inevitably accidentally call the function
+ fprintf(stderr, "Graphics_Display should not be called if graphics are disabled!\n");
+ exit(EXIT_FAILURE);
}
- #ifdef SINGLE_THREADED
- if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
- DisplayStatistics();
- System_Compute(&universe);
- #endif
-
- if (options.draw_graphics == false)
+ //Check whether the graphics thread should exit for any reason
+ if (ExitCondition())
+ {
+ glutLeaveMainLoop(); // original glut does not have this, which makes "nicely" exiting a bit annoying
return;
-
+ }
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
gluLookAt(eyeRho * sin(eyePhi) * sin(eyeTheta), eyeRho * cos(eyePhi),
eyeRho * sin(eyePhi) * cos(eyeTheta),look[0], look[1], look[2], 0, upY, 0);
+ BeforeDraw(); // Stuff to do before graphics is allowed to draw
+ // Single-thread - perform a computation step, obviously! It's not done anywhere else
+ // Multiple threads - ensure that all body positions are updated (ie: not halfway through step).
+ // (We don't care about the force and velocity variables though)
+
for (int i = 0; i < universe.N; ++i)
{
Body * b = universe.body+i;
glPopMatrix(); // restore the previous matrix
}
glFlush();
+
+ AfterDraw(); // Stuff to do after graphics is done drawing
+ // Single-thread - Nothing at all
+ // Multiple threads - signal threads it is safe to change position variables
+
}
/**
#include <stdio.h>
#include <time.h>
#include <signal.h>
+#include <string.h> // For parsing arguments
#include "nbody.h"
#include "graphics.h"
// --- Function forward declarations --- //
void HandleArguments(int argc, char ** argv); //Interprets program arguments and sets up the "options" variable
-void UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store); //Helper function to get switch value for unsigned
+unsigned UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store, unsigned * store2); //Helper function to get switch value for unsigned
void FloatArgument(unsigned * i, int argc, char ** argv, float * store); //Helper function to get switch value for float
void DisplayStatistics(); //Called on exit of program, displays information about computation time, steps computed, etc
void Interrupt(int dummy); // Interrupt handler function, called when SIGINT (Ctrl-C) is sent to program
options.input = NULL;
options.output = NULL;
options.num_threads = 0;
+ options.nested_threads = 0;
options.num_steps = 0;
options.timeout = 0;
options.draw_graphics = true;
+ options.pedantic_graphics = false;
options.print_positions = false;
options.verbosity = 0;
options.start_clock = clock(); //Get CPU cycles executed before simulation starts
Simulation_Run(argc, argv); // Start the simulation
- printf("Got here!\n");
+ //printf("Got here!\n");
exit(EXIT_FAILURE); //Should never get to this line
}
switch (argv[i][1]) //The argument is a switch if we get here
{
case 'n': //Number of threads switch
- UnsignedArgument(&i, argc, argv, &(options.num_threads));
+ UnsignedArgument(&i, argc, argv, &(options.num_threads), &(options.nested_threads));
#ifdef SINGLE_THREADED
fprintf(stderr, "Warning: -%c switch has no effect in the single-threaded program.\n", argv[i][1]);
- options.num_threads = 1;
+
#endif //SINGLE_THREADED
break;
case 's': //Number of steps switch
- UnsignedArgument(&i, argc, argv, &(options.num_steps));
+ UnsignedArgument(&i, argc, argv, &(options.num_steps), NULL);
break;
case 't': //Timeout switch (in seconds)
- UnsignedArgument(&i, argc, argv, &(options.timeout));
+ UnsignedArgument(&i, argc, argv, &(options.timeout), NULL);
break;
case 'g': //Graphics switch
options.draw_graphics = !options.draw_graphics;
break;
case 'v': //Verbosity switch
- UnsignedArgument(&i, argc, argv, &(options.verbosity));
+ UnsignedArgument(&i, argc, argv, &(options.verbosity), NULL);
break;
+ case '-':
+ if (strcmp(argv[i]+1, "pedantic-graphics"))
+ {
+ options.pedantic_graphics = true;
+ #ifdef SINGLE_THREADED
+ fprintf(stderr, "Warning: %s switch has no effect in the single threaded program.\n", argv[i]);
+ #endif //SINGLE_THREADED
+ }
+ else if (strcmp(argv[i]+1, "fast-graphics"))
+ {
+ options.pedantic_graphics = false;
+ #ifdef SINGLE_THREADED
+ fprintf(stderr, "Warning: %s switch has no effect in the single threaded program.\n", argv[i]);
+ #endif //SINGLE_THREADED
+
+ }
+ else
+ {
+ fprintf(stderr, "Unrecognised switch -%s\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ break;
+
+
default:
fprintf(stderr, "Unrecognised switch -%c\n", argv[i][1]);
exit(EXIT_FAILURE);
* @param argc - number of arguments
* @param argv - argument strings
* @param store - pointer to unsigned to store result in
+ * @param store2 - pointer to second unsigned
+ * @returns 1 if store was filled, 2 if both store1 and store2 were filled
*/
-void UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store)
+unsigned UnsignedArgument(unsigned * i, int argc, char ** argv, unsigned * store, unsigned * store2)
{
if (*i >= argc-1)
{
fprintf(stderr,"Supply a positive integer for the -%c switch.\n", argv[*i][1]);
exit(EXIT_FAILURE);
}
+
+ char * seperator = strstr(argv[*i+1], ":");
+ if (seperator != NULL)
+ {
+ if (store2 == NULL)
+ {
+ fprintf(stderr,"Supply a positive integer for the -%c switch.\n", argv[*i][1]);
+ exit(EXIT_FAILURE);
+ }
+ int val = atoi(seperator+1);
+ if (val <= 0)
+ {
+ fprintf(stderr,"Supply a positive integer for the -%c switch.\n", argv[*i][1]);
+ exit(EXIT_FAILURE);
+ }
+ *store2 = (unsigned)val;
+
+ *seperator = '\0';
+ }
+
int val = atoi(argv[*i+1]);
if (val <= 0)
{
fprintf(stderr,"Supply a positive integer for the -%c switch. %s is invalid.\n", argv[*i][1], argv[*i+1]);
exit(EXIT_FAILURE);
}
- *store = val;
+ *store = (unsigned)val;
*i += 1;
+
+ return (seperator == NULL) ? 1 : 2;
}
/**
* @function FloatArgument
* @param s1, s2 - The two systems
* NOTE: In single threaded version, called with s1 == s2 == &universe
* In multi threaded version, called with s2 == &universe, but s1 is constructed for each thread
+ * In nested multi-threaded version, s2 is constructed for the nested threads as well
*/
void System_Forces(System * s1, System * s2)
{
#define SINGLE_THREADED
-//These macros will be undefined by the multithreaded versions
-#define Simulation_Run(argc, argv) (Graphics_Run(argc, argv)) //Sets up the simulation; in multithreaded versions, will spawn threads
+// --- The below macros will be undefined by the multithreaded versions, and replaced with functions --- //
+
+//Sets up the simulation; in multithreaded versions, will spawn threads
+#define Simulation_Run(argc, argv) \
+if (options.draw_graphics) \
+ Graphics_Run(argc, argv); \
+else \
+{ \
+ while (ExitCondition() == false) \
+ { BeforeDraw(); AfterDraw(); } \
+}
+
#define QuitProgram(error) (runstate = (error == true) ? QUIT : QUIT_ERROR) //Prepares to exit program, is thread safe in multithreaded versions
+//Macro to be overwritten in multithreaded versions, called before the graphics is allowed to draw anything
+#define BeforeDraw() \
+if (options.verbosity != 0 && universe.steps % options.verbosity == 0) \
+ DisplayStatistics(); \
+System_Compute(&universe);
+
+
+
+//Macro to be overwritten in multithreaded versions, called after the graphics has finished drawing.
+#define AfterDraw()
+
+// --- Constants and other Macros --- //
+
#define M_PI 3.14159265358979323846264338327950288 /* pi */
#define G 6.67428E-11
#define DELTA_T 0.05
const char * output; // file to write final positions / velocities of bodies to
const char * program; // program name
unsigned num_threads; // number of worker threads to spawn (must be greater than 1 for any to be spawned)
+ unsigned nested_threads; // number of threads to nest computations with (must be greater than 1 for any to be spawned)
unsigned num_steps; // number of steps to run before stopping (run indefinately if equal to zero)
unsigned timeout; // number of seconds to run before stopping (run indefinately if equal to zero)
bool draw_graphics; // whether or not to actually draw graphics
+ bool pedantic_graphics; // whether the graphics thread will synchronise with the computation thread (true) or just draw as fast as possible (false)
bool print_positions; // print positions of bodies to stdout on every step
unsigned verbosity; // print statistics every number of steps indicated by this variable
clock_t start_clock; // clock cycles done when simulation starts