/** * @file main.c * @author Sam Moore (20503628) - 2012 * @purpose Contains main function, argument handling * NOTE: This file is identical for both the single-threaded and multi-threaded versions of the program */ #include #include #include #include #include // For parsing arguments #include "nbody.h" #include "graphics.h" // --- Variable definitions --- // System universe; // global variable declared in "nbody.h" - The universe of bodies Options options; // global variable declared in "nbody.h" - Options passed to program through arguments // --- Function forward declarations --- // void HandleArguments(int argc, char ** argv); //Interprets program arguments and sets up the "options" variable unsigned IntegerArgument(unsigned * i, int argc, char ** argv, int * store, int * store2); //Helper function to get integer switch 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 // --- Function implementations --- // /** * @function main * @purpose The main function. Calls HandleArguments to set up options, * sets up functions to call at exit, starts simulation and graphics loops * @param argc - Number of arguments * @param argv - The argument strings */ int main(int argc, char** argv) { //Set default options values here options.program = argv[0]; options.input = NULL; options.output = NULL; options.num_threads = 0; options.nested_threads = 0; options.num_steps = -1; // Negative values => simulation runs forever unless otherwise specified options.timeout = -1; options.draw_graphics = true; options.pedantic_graphics = false; options.print_positions = false; options.verbosity = 0; options.theta = 0.50; //If there are no arguments, print information about usage if (argc == 1) { printf("Usage: %s [OPTIONS] field\n", argv[0]); printf("Controls:\n X - exit\n I, J, K, M - rotate\n W, Z, A, S - move to view" " point\n ./, - zoom in/out\n +/- - scaled zoom in/out\n"); exit(EXIT_SUCCESS); } //Parse the arguments HandleArguments(argc, argv); //Check there is an initial field. if (options.input == NULL) { fprintf(stderr, "Usage: %s [OPTIONS] field\n", argv[0]); fprintf(stderr, " (You did not provide a file for the initial field of bodies)\n"); exit(EXIT_FAILURE); } signal(SIGINT, Interrupt); //Handle SIGINT signals atexit(Universe_Cleanup); //On exit, cleanup universe (and write positions of bodies to file if supplied). atexit(DisplayStatistics); //On exit, print information about the computations done System_Init(&universe,options.input); //Initialise the universe from the initial field //Setup the time of day at which the simulation starts if (gettimeofday(&(options.start_time), NULL) != 0) { perror("Couldn't get time of day"); exit(EXIT_FAILURE); } options.start_clock = clock(); //Get CPU cycles executed before simulation starts Simulation_Run(argc, argv); // Start the simulation //printf("Main thread done!\n"); //pthread_exit(NULL); exit(EXIT_SUCCESS); //Should never get to this line } /** * @function HandleArguments * @purpose Fill the "options" variable by parsing command line arguments * @param argc - Number of arguments * @param argv - Argument strings */ void HandleArguments(int argc, char ** argv) { for (unsigned i = 1; i < argc; ++i) //For all arguments { if (argv[i][0] != '-') //If the argument is not a switch value... { //Interpret first non switch as input file, and second as output file. //If there are too many non switch arguments, give an error. if (options.input == NULL) options.input = argv[i]; else if (options.output == NULL) options.output = argv[i]; else { fprintf(stderr,"Usage: %s [OPTIONS] field [output]\n", argv[0]); fprintf(stderr," (You provided too many file names)\n"); exit(EXIT_FAILURE); } continue; } switch (argv[i][1]) //The argument is a switch if we get here { case 'n': //Number of threads switch IntegerArgument(&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", 'n'); #else if (options.num_threads <= 0) { fprintf(stderr, "Require at least one thread (-%c %s is invalid).\n",'n', argv[i]); exit(EXIT_FAILURE); } #endif //SINGLE_THREADED break; case 's': //Number of steps switch IntegerArgument(&i, argc, argv, &(options.num_steps), NULL); if (options.num_steps < 0) { fprintf(stderr, "Require zero or more steps to run (-%c %s is invalid).\n", 's', argv[i]); exit(EXIT_FAILURE); } break; case 't': //Timeout switch (in seconds) IntegerArgument(&i, argc, argv, &(options.timeout), NULL); if (options.timeout < 0) { fprintf(stderr, "Require a timeout greater or equal to zero (-%c %s is invalid).", 't', argv[i]); exit(EXIT_FAILURE); } break; case 'g': //Graphics switch options.draw_graphics = !options.draw_graphics; break; case 'v': //Verbosity switch IntegerArgument(&i, argc, argv, &(options.verbosity), NULL); break; case '-': if (strcmp(argv[i]+2, "pedantic-graphics") == 0) { 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]+2, "fast-graphics") == 0) { 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 if (strcmp(argv[i]+2, "theta") == 0) { FloatArgument(&i, argc, argv, &(options.theta)); #ifndef BARNES_HUT fprintf(stderr, "Warning: %s switch only works in Barnes Hut version.\n", argv[i-1]); #else if (options.theta < 0) { fprintf(stderr, "Require a theta value greater or equal to zero (%s %s is invalid).\n", argv[i-1], argv[i]); exit(EXIT_FAILURE); } #endif //BARNS_HUT } 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); break; } } } /** * @function IntegerArgument * @purpose Helper function to get up to two integers following a argument switch, seperated by ':' * @param i - position in the argument array, will be updated after this function * @param argc - number of arguments * @param argv - argument strings * @param store - pointer to unsigned to store result in * @param store2 - pointer to second integer (set to NULL if there is only one integer) * @returns 1 if store was filled, 2 if both store1 and store2 were filled */ unsigned IntegerArgument(unsigned * i, int argc, char ** argv, int * store, int * 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 && strcmp("0", argv[*i+1]) != 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; *i += 1; return (seperator == NULL) ? 1 : 2; } /** * @function FloatArgument * @purpose Helper function to get a float following a argument switch * @param i - position in the argument array, will be updated after this function * @param argc - number of arguments * @param argv - argument strings * @param store - pointer to float to store result in */ void FloatArgument(unsigned * i, int argc, char ** argv, float * store) { if (*i >= argc-1) { fprintf(stderr,"Supply a float for the -%c switch.\n", argv[*i][1]); exit(EXIT_FAILURE); } *store = atof(argv[*i+1]); if (*store == 0.0 && argv[*i+1][0] != '0') { fprintf(stderr,"Supply a float for the -%c switch.\n", argv[*i][1]); exit(EXIT_FAILURE); } *i += 1; } /** * @function Interrupt * @purpose Handle SIGINT signal; quit the program gracefully, so that statistics are still displayed * @param dummy - a dummy */ void Interrupt(int dummy) { QuitProgram(false); }