--- /dev/null
+/**
+ * @file nbody.c
+ * @purpose Barnes Hut OpenMP version of N-Body simulator, implementation
+ * @author Sam Moore (20503628) - 2012
+ */
+
+#include "nbody.h"
+#include "../single-thread/nbody.c" // Include original code
+#include "graphics.h" // For Graphics_Run function only
+
+#include "tree.h"
+
+
+
+omp_lock_t runstate_lock;
+
+#ifdef USE_THREADS
+omp_lock_t nested_lock;
+#endif //USE_THREADS
+
+Node * parent = NULL;
+
+/**
+ * @function Simulation_Run
+ * @purpose Start the simulation
+ * @param argc, argv - Passed to Graphics_Run if needed, otherwise unused
+ */
+void Simulation_Run(int argc, char ** argv)
+{
+
+ omp_init_lock(&runstate_lock);
+ #ifdef USE_THREADS
+ omp_init_lock(&nested_lock);
+ #endif //USE_THREADS
+
+ #ifdef DUAL_UNIVERSE
+ if (!options.pedantic_graphics)
+ {
+ fprintf(stderr, "Warning: Using pedantic graphics to ensure alternate reality matches our own (as closely as possible).\n");
+ options.pedantic_graphics = true;
+ }
+ #endif //DUAL_UNIVERSE
+
+ omp_set_nested(1);
+ if (omp_get_nested() == 0)
+ {
+ fprintf(stderr, "Couldn't enable nested parallelism\n.");
+ exit(EXIT_FAILURE);
+ }
+
+ if (options.num_threads == 0)
+ {
+ options.num_threads = omp_get_max_threads();
+ if (options.draw_graphics)
+ options.num_threads -= 1;
+ }
+ if (options.nested_threads == 0)
+ {
+ options.nested_threads = omp_get_max_threads() - options.num_threads;
+ if (options.nested_threads == 0)
+ options.nested_threads = 1;
+ }
+
+ #ifdef DUAL_UNIVERSE
+ System_Init(&alternate_universe, options.input);
+ #endif //DUAL_UNIVERSE
+
+ if (options.draw_graphics)
+ {
+ Graphics_Run(argc, argv);
+ if (options.pedantic_graphics == false)
+ {
+
+
+
+ #pragma omp parallel num_threads(2)
+ {
+ // This can't be done with sections!
+ // Because glut is useless, and can only be dealt with in the main thread
+ // I just hope that thread "0" is always the main thread!
+ if (omp_get_thread_num() == 0) // #pragma omp section
+ {
+ //printf("Thread %d gets graphics\n", omp_get_thread_num());
+ glutMainLoop();
+ QuitProgram(false); // Program gets to here if the window is quit
+ }
+ else // #pragma omp section
+ {
+ //printf("Thread %d gets computations\n", omp_get_thread_num());
+ Compute();
+ }
+
+ }
+ return;
+ }
+ }
+ Compute();
+
+ omp_destroy_lock(&runstate_lock);
+ #ifdef USE_THREADS
+ omp_destroy_lock(&nested_lock);
+ #endif //USE_THREADS
+
+
+
+}
+
+/**
+ * @function Compute
+ * @purpose The main computation loop
+ * Uses threading, but only on the first level
+ * I tried to introduce threading to some of the recursive functions in the tree...
+ * But failed
+ */
+void Compute(void)
+{
+ omp_set_nested(1);
+ if (omp_get_nested() == 0)
+ {
+ fprintf(stderr, "Couldn't enable nested parallelism\n.");
+ exit(EXIT_FAILURE);
+ }
+
+ omp_set_num_threads(options.num_threads);
+ if (omp_get_max_threads() != options.num_threads)
+ {
+ fprintf(stderr, "Couldn't use %d threads!\n", options.num_threads);
+ exit(EXIT_FAILURE);
+ }
+
+ parent = Node_Create(NULL);
+
+ Generate_Tree(&universe, parent);
+
+
+
+
+#pragma omp parallel
+{
+ while (!ExitCondition())
+ {
+ #pragma omp single
+ {
+ Node_CalculateMass(parent);
+ }
+
+ #pragma omp for // The easiest way to parallelise it
+ for (unsigned a = 0; a < universe.N; ++a)
+ {
+ for (unsigned i = 0; i < DIMENSIONS; ++i)
+ universe.body[a].F[i] = 0;
+
+ Node_Forces(universe.body+a, parent, options.theta);
+ //printf("Forces on %p done\n", (void*)(universe.body+a));
+ Body_Velocity(universe.body+a);
+ }
+
+ // put a nowait here so that the first thread out can start updating the tree
+ #pragma omp for nowait
+ for (unsigned a = 0; a < universe.N; ++a)
+ {
+
+ Body_Position(universe.body+a);
+ }
+
+ #pragma omp single // Ideally the first thread out should do this
+ {
+ Update_Tree(&universe, parent);
+ }
+
+ // I haven't noticed any difference in performance.
+ // I'm just showing off.
+ #pragma omp barrier
+
+ // Only the master may do stuff with graphics
+ #pragma omp master
+ {
+ if (options.draw_graphics && options.pedantic_graphics)
+ {
+ glutMainLoopEvent();
+ Graphics_Display();
+ }
+ }
+
+ // Someone should do this stuff too
+ #pragma omp single
+ {
+ universe.steps += 1;
+ if (options.verbosity != 0 && universe.steps % options.verbosity == 0)
+ DisplayStatistics();
+ }
+
+ }
+}
+ Node_Destroy(parent);
+ free(parent);
+
+ QuitProgram(false);
+}
+
+
+void QuitProgram(bool error)
+{
+ if (runstate == QUIT || runstate == QUIT_ERROR)
+ return;
+ omp_set_lock(&runstate_lock);
+ runstate = (error) ? QUIT_ERROR : QUIT;
+ omp_unset_lock(&runstate_lock);
+}
+
+#ifdef DUAL_UNIVERSE
+void BeforeDraw()
+{
+ // Before drawing the main universe... compute and draw the alternate universe
+ // Not threaded, because I am too lazy
+ System_Compute(&alternate_universe);
+
+ for (int i = 0; i < alternate_universe.N; ++i)
+ {
+
+ Body * b = alternate_universe.body+i;
+ Body * original = universe.body+i;
+ //glColor3f(0.0f, b->mass/1e11*100, 0.0f);
+ glColor3f(1.0f, 0.0f, 1.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
+
+ // Draw a line from the "alternate" body to the real one
+ glBegin(GL_LINES);
+ glVertex3f(scale*b->x[0], scale*b->x[1], scale*b->x[2]);
+ glVertex3f(scale*original->x[0], scale*original->x[1], scale*original->x[2]);
+ glEnd();
+ }
+}
+#endif //DUAL_UNIVERSE
+
+#ifndef AfterDraw
+void AfterDraw()
+{
+ /* // Uncomment to draw cubes
+ glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
+ Draw_Node(parent);
+ glFlush();
+ glPolygonMode( GL_FRONT_AND_BACK, GL_FILL);
+ */
+}
+#endif //AfterDraw