--- /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
+System * nested_sub_system = NULL; //Array of Systems for division of "universe" for the nested worker threads
pthread_mutex_t mutex_runstate; // Mutex around the runstate
+pthread_attr_t attr; //thread attribute for the workers.
+
+Barrier force_barrier; // I laughed at this variable name. A bit sad really.
+Barrier position_barrier;
+
+
+Barrier graphics_barrier;
+
+
/**
* @function Compute_Thread
* @purpose Thread - Continuously computes steps for a system of bodies. Seperate from graphics functions.
if (options.num_threads <= 0)
options.num_threads = (DEFAULT_WORKING_THREADS > 1) ? DEFAULT_WORKING_THREADS : 1;
+ if (options.nested_threads <= 0)
+ options.nested_threads = 1;
+
// Do a sanity check; there is no point spawning more threads than bodies.
if (options.num_threads > s->N)
{
options.num_threads = s->N;
}
+ 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 (worker_thread == NULL)
- {
- perror("Couldn't allocate array of worker threads");
- QuitProgram(true);
- pthread_exit(NULL);
- }
- sub_system = (System*)(calloc(options.num_threads, sizeof(System)));
- if (sub_system == NULL)
+ worker_thread = Allocate_Threads(options.num_threads);
+ sub_system = Split_System(&universe, options.num_threads);
+
+ if (options.nested_threads > 1)
+ nested_sub_system = Split_System(&universe, options.nested_threads);
+
+ #ifdef PERSISTENT_THREADS
+ for (unsigned i = 0; i < options.num_threads; ++i)
{
- perror("Couldn't allocate array of systems for worker threads to use");
- QuitProgram(true);
- pthread_exit(NULL);
+ 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
- // Divide up the Body array owned by s into options.num_threads arrays, one for each worker thread
- unsigned bodies_per_system = (s->N) / options.num_threads;
- unsigned remainder = (s->N) % options.num_threads;
- for (unsigned i = 0; i < options.num_threads; ++i)
+
+ }
+ #ifdef PERSISTENT_THREADS
+ else
+ {
+ while (!ExitCondition())
{
- sub_system[i].body = (s->body)+(i*bodies_per_system);
- sub_system[i].N = bodies_per_system;
- sub_system[i].steps = 0;
- if (i == options.num_threads - 1)
- sub_system[i].N += remainder; // The last thread gets the remainder
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 1)
+ DisplayStatistics();
+ // Just do everything in this thread
+ System_Forces(s, s);
+ System_Positions(s);
}
+ QuitProgram(false);
+ pthread_exit(NULL);
}
-
- 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.
-
+ #else
// The main computation loop
- while (true)
+ while (!ExitCondition())
{
- //Check whether the program should quit due to steps being computed, or a timeout
- if (ExitCondition())
- {
- QuitProgram(false);
- 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();
- }
-
if (options.num_threads <= 1)
{
-
- 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(&force_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);
+ }
//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(&position_barrier);
+
//Update number of steps computed
universe.steps += 1;
+
+
+
+
+
+ }
+ QuitProgram(false);
+ #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;
+
+ Barrier_Wait(&position_barrier);
+
+
+ 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);
+
+
+ pthread_t * nested_workers = NULL;
+ System_ForcePair * system_pairs = NULL;
+ System * nested_position = NULL;
+
+ Barrier nested_barrier;
+ Barrier_Init(&nested_barrier);
+
+ printf("options.nested_threads == %d\n", (int)(options.nested_threads));
+ if (options.nested_threads != 1)
+ {
+ system_pairs = (System_ForcePair*)(calloc(options.nested_threads, sizeof(System_ForcePair)));
+ if (system_pairs == NULL) // Handle tedious error cases
+ {
+ perror("Couldn't allocate array of system pairs");
+ QuitProgram(true);
+ pthread_exit(NULL);
+ }
+ nested_workers = Allocate_Threads(options.nested_threads);
+ nested_position =
+
+ for (unsigned i = 0; i < options.nested_threads; ++i)
+ {
+ system_pairs[i].A = s;
+ system_pairs[i].B = nested_sub_system+i;
+ }
}
+
+ while (!ExitCondition())
+ {
+
+ if (options.nested_threads == 1)
+ {
+ Barrier_Enter(&force_barrier);
+ System_Forces(s, &universe);
+ Barrier_Leave(&force_barrier);
+ }
+ else
+ {
+ for (unsigned i = 0; i < options.nested_threads; ++i)
+ {
+ if (pthread_create(nested_workers+i, &attr, Force_Thread, (void*)(system_pairs+i)) != 0)
+ {
+ perror("In worker thread, couldn't create nested worker thread (force)");
+ QuitProgram(true);
+ free(nested_workers);
+ pthread_exit(NULL);
+ }
+ }
+ }
+ //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");
+ }
+ printf("Worker thread exits\n");
+ QuitProgram(false);
+ pthread_exit(NULL);
}
+#endif //PERSISTENT_THREADS
+
/**
* @function Force_Thread
* @purpose Thread - Calculates the forces on objects in a System
*/
void * Force_Thread(void * s)
{
-
- System_Forces((System*)s, &universe); //Simple wrapper
+ System_ForcePair * pair = (System_ForcePair*)s;
+ Barrier_Enter(&force_barrier);
+
+ System_Forces(pair->A, pair->B); //Simple wrapper
+
+ Barrier_Leave(&force_barrier);
- 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);
return NULL;
}
+
/**
* @function Position_Thread
* @purpose Thread - Calculates the positions of objects in a System
*/
void * Position_Thread(void * s)
{
-
+ Barrier_Enter(&position_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(&position_barrier);
return NULL;
}
{
atexit(Thread_Cleanup);
+ Barrier_Init(&force_barrier);
+ Barrier_Init(&position_barrier);
+ 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));
+}
+
+/**
+ * @function Split_System
+ * @purpose Helper to divide one system into an array of systems
+ * Each sub system will have N = (s->N / n) bodies in it
+ * @param s - The original system (typically &universe)
+ * @param n - The number of sub systems in the array
+ *
+ * WARNING: It is the caller's responsibility to free() the returned array
+ */
+System * Split_System(System * s, unsigned n)
+{
+ System * result = (System*)(calloc(n, sizeof(System)));
+ if (result == NULL)
+ {
+ perror("Couldn't create array of sub systems");
+ QuitProgram(true);
+ pthread_exit(NULL);
+ }
+
+ unsigned n_per_system = (s->N) / n;
+ unsigned remainder = (s->N) % n;
+
+ for (unsigned i = 0; i < n; ++i)
+ {
+ result[i].N = n_per_system;
+ if (i == n-1)
+ result[i].N += remainder;
+ result[i].body = (s->body) + (n_per_system * i);
+ result[i].steps = 0;
+ }
+ return result;
+}
+
+/**
+ * @function Allocate_Threads
+ * @purpose Helper function to allocate an array of pthread_t objects
+ * Handles all the pointless, er, "important" error checking that should be done
+ * @param n - Number of threads in the array
+ *
+ * WARNING: Remember to free() the array!!!
+ */
+pthread_t * Allocate_Threads(unsigned n)
+{
+ pthread_t * result = (pthread_t*)(calloc(n, sizeof(pthread_t)));
+ if (result == NULL)
+ {
+ perror("Unable to allocate memory for threads");
+ QuitProgram(true);
+ pthread_exit(NULL);
+ }
+ return result;
+}
#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.
-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
+System * Split_System(System * s, unsigned n); // Splits one system into a number of other systems, returns an array of size n
+pthread_t * Allocate_Threads(unsigned n); // Allocates space for threads - handles errors
+#ifdef PERSISTENT_THREADS
+void * Worker_Thread(void * arg);
+#endif //PERSISTENT_THREADS
+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
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
+/**
+ * Structure to represent a barrier for multiple threads
+ * @param mutex - Mutex around the counter
+ * @param busy - Counter of threads within the barrier
+ * @param threads_done_cv - Condition to wake up threads waiting on barrier once all working threads have left it
+ */
+typedef struct
+{
+ pthread_mutex_t mutex;
+ unsigned threads_busy; // Counter of threads which are busy
+ pthread_cond_t threads_done_cv;
+} Barrier;
+
+/**
+ * Structure to represent a pair of Systems; passed to Force_Thread
+ * @param A - System to calculate forces for
+ * @param B - System causing forces on System A
+ */
+typedef struct
+{
+ System * A;
+ System * B;
+} System_ForcePair;
-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;
+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
int windowWidth, windowHeight, upY;
double scale = 1.0;
+#ifdef FLYING_CAMERA
+Camera eye;
+#endif //FLYING_CAMERA
/**
* @function Graphics_Run
{
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);
look[2] = 0;
gluPerspective(VIEW_ANGLE, (double)WIDTH/(double)HEIGHT, WORLD_NEAR, WORLD_FAR);
+ #ifdef FLYING_CAMERA
+ eye.x[0] = 1; eye.x[1] = 0; eye.x[2] = 0;
+ eye.y[0] = 0; eye.y[1] = 1; eye.y[2] = 0;
+ eye.z[0] = 0; eye.z[1] = 0; eye.z[2] = 1;
+ #endif //FLYING_CAMERA
+
glutMainLoop();
}
*/
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);
glLoadIdentity();
+
+ #ifdef FLYING_CAMERA
+ gluLookAt(eye.p[0], eye.p[1], eye.p[2],
+ eye.p[0] + eye.x[0], eye.p[1] + eye.x[1], eye.p[2] + eye.x[2],
+ eye.z[0], eye.z[1], eye.z[2]);
+ #else
gluLookAt(eyeRho * sin(eyePhi) * sin(eyeTheta), eyeRho * cos(eyePhi),
eyeRho * sin(eyePhi) * cos(eyeTheta),look[0], look[1], look[2], 0, upY, 0);
+ #endif //FLYING_CAMERA
+
+ 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;
glColor3f(0.0f, b->mass/1e11*100, 0.0f);
+ //glColor3f(1.0f, 0.0f, 0.0f);
glPushMatrix(); // to save the current matrix
glTranslated(scale*b->x[0], scale*b->x[1], scale*b->x[2]);
glutSolidSphere (BALL_SIZE, 10, 10);
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
+
}
/**
return;
}
+ #ifdef FLYING_CAMERA
+ switch (theKey)
+ {
+ case 'W':
+ case 'w':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] += eye.x[i];
+ break;
+ case 'S':
+ case 's':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] -= eye.x[i];
+ break;
+ case 'A':
+ case 'a':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] += eye.y[i];
+ break;
+ case 'D':
+ case 'd':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] -= eye.y[i];
+ break;
+ case 'Z':
+ case 'z':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] += eye.z[i];
+ break;
+ case 'C':
+ case 'c':
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ eye.p[i] -= eye.z[i];
+ break;
+
+ case 'I':
+ case 'i':
+ {
+ float theta = M_PI/80.0;
+ Camera old = eye;
+
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ eye.z[i] = old.z[i] * cos(theta) + old.x[i] * sin(theta);
+ eye.x[i] = old.x[i] * cos(theta) - old.z[i] * sin(theta);
+ }
+ break;
+ }
+ case 'K':
+ case 'k':
+ {
+ float theta = -M_PI/80.0;
+ Camera old = eye;
+
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ eye.z[i] = old.z[i] * cos(theta) + old.x[i] * sin(theta);
+ eye.x[i] = old.x[i] * cos(theta) - old.z[i] * sin(theta);
+ }
+ break;
+ }
+
+ case 'J':
+ case 'j':
+ {
+ float theta = -M_PI/80.0;
+ Camera old = eye;
+
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ eye.y[i] = old.y[i] * cos(theta) + old.x[i] * sin(theta);
+ eye.x[i] = old.x[i] * cos(theta) - old.y[i] * sin(theta);
+ }
+ break;
+ }
+
+ case 'L':
+ case 'l':
+ {
+ float theta = M_PI/80.0;
+ Camera old = eye;
+
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ eye.y[i] = old.y[i] * cos(theta) + old.x[i] * sin(theta);
+ eye.x[i] = old.x[i] * cos(theta) - old.y[i] * sin(theta);
+ }
+ break;
+ }
+
+ case 'Q':
+ case 'q':
+ {
+ float theta = M_PI/80.0;
+ Camera old = eye;
+
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ eye.z[i] = old.z[i] * cos(theta) + old.y[i] * sin(theta);
+ eye.y[i] = old.y[i] * cos(theta) - old.z[i] * sin(theta);
+ }
+ break;
+ }
+ case 'E':
+ case 'e':
+ {
+ float theta = -M_PI/80.0;
+ Camera old = eye;
+
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ eye.z[i] = old.z[i] * cos(theta) + old.y[i] * sin(theta);
+ eye.y[i] = old.y[i] * cos(theta) - old.z[i] * sin(theta);
+ }
+ break;
+ }
+ }
+
+ /*
+ float x = 0; float y = 0; float z = 0;
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ x += eye.x[i] * eye.x[i];
+ y += eye.y[i] * eye.y[i];
+ z += eye.z[i] * eye.z[i];
+ }
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ {
+ eye.x[i] /= sqrt(x);
+ eye.y[i] /= sqrt(y);
+ eye.z[i] /= sqrt(z);
+ }
+ */
+ system("clear");
+ printf("Camera status:\n");
+ printf("Position: %f %f %f\n", eye.p[0], eye.p[1], eye.p[2]);
+ printf("x: %f %f %f (%f)\n", eye.x[0], eye.x[1], eye.x[2], sqrt(eye.x[0]*eye.x[0] + eye.x[1]*eye.x[1] + eye.x[2]*eye.x[2]));
+ printf("y: %f %f %f (%f)\n", eye.y[0], eye.y[1], eye.y[2], sqrt(eye.y[0]*eye.y[0] + eye.y[1]*eye.y[1] + eye.y[2]*eye.y[2]));
+ printf("z: %f %f %f (%f)\n", eye.z[0], eye.z[1], eye.z[2], sqrt(eye.z[0]*eye.z[0] + eye.z[1]*eye.z[1] + eye.z[2]*eye.z[2]));
+
+ #else
+
+
+
if (theKey == 'i' || theKey == 'I') {
eyePhi -= M_PI / 20;
if (eyePhi == 0)
}
if (sin(eyePhi) > 0) upY = 1;
else upY = 1;
+ #endif //FLYING_CAMERA
}
/**
#include "nbody.h"
#define WIDTH 800
-#define HEIGHT 800
+#define HEIGHT 640
#define POINT_SIZE 1
#define POSITION_X 112
#define POSITION_Y 20
#define WORLD_TOP 10000
#define VIEW_ANGLE 45
#define RHO 100
-#define WORLD_NEAR 0.1
+#define WORLD_NEAR 0.0001
#define WORLD_FAR 1000000
#define BALL_SIZE 0.5
#define REFRESH_RATE 0.001
void Graphics_Reshape(int width, int height);
+#define FLYING_CAMERA
+
+#ifdef FLYING_CAMERA
+typedef struct
+{
+ float p[DIMENSIONS]; // Translation position of the camera
+
+ float x[DIMENSIONS];
+ float y[DIMENSIONS];
+ float z[DIMENSIONS];
+
+} Camera;
+#endif //FLYING_CAMERA
+
#endif //_GRAPHICS_H
//EOF
#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)
{
*/
bool ExitCondition(void)
{
- return (runstate != RUN || (options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout))
+ bool result = (runstate != RUN || (options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout))
|| (options.num_steps > 0 && universe.steps > options.num_steps));
+ //printf("runstate %d\n timeout %d\n steps %d\n", (int)(runstate != RUN), (int)(options.timeout > 0.00 && ((unsigned)(time(NULL) - options.start_time.tv_sec) >= options.timeout)), (int)(options.num_steps > 0 && universe.steps > options.num_steps));
+ return result;
}
#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