diff --git a/argparse/argparse.c b/argparse/argparse.c index a9e00dbf1f6ad5273d3ff5b65805eb3b880f3666..f53184bab1d9278bf38d0445b7136c8424e8a573 100644 --- a/argparse/argparse.c +++ b/argparse/argparse.c @@ -83,7 +83,7 @@ argparse_getvalue(struct argparse *self, const struct argparse_option *opt, } break; case ARGPARSE_OPT_INTEGER: - errno = 0; + errno = 0; if (self->optvalue) { *(int *)opt->value = strtol(self->optvalue, (char **)&s, 0); self->optvalue = NULL; @@ -93,13 +93,13 @@ argparse_getvalue(struct argparse *self, const struct argparse_option *opt, } else { argparse_error(self, opt, "requires a value", flags); } - if (errno) + if (errno) argparse_error(self, opt, strerror(errno), flags); if (s[0] != '\0') argparse_error(self, opt, "expects an integer value", flags); break; case ARGPARSE_OPT_FLOAT: - errno = 0; + errno = 0; if (self->optvalue) { *(float *)opt->value = strtof(self->optvalue, (char **)&s); self->optvalue = NULL; @@ -109,7 +109,7 @@ argparse_getvalue(struct argparse *self, const struct argparse_option *opt, } else { argparse_error(self, opt, "requires a value", flags); } - if (errno) + if (errno) argparse_error(self, opt, strerror(errno), flags); if (s[0] != '\0') argparse_error(self, opt, "expects a numerical value", flags); @@ -321,7 +321,7 @@ argparse_usage(struct argparse *self) } if (options->type == ARGPARSE_OPT_INTEGER) { len += strlen("=<int>"); - } + } if (options->type == ARGPARSE_OPT_FLOAT) { len += strlen("=<flt>"); } else if (options->type == ARGPARSE_OPT_STRING) { diff --git a/argparse/test_argparse.c b/argparse/test_argparse.c index 4b8e3dd0b978a6e6bcc6cf6e3a20abb91c66012b..bb1827ff3e58cbe4337cfcc098d8419bf40baaaf 100644 --- a/argparse/test_argparse.c +++ b/argparse/test_argparse.c @@ -13,9 +13,17 @@ static const char *const usages[] = { #define PERM_WRITE (1<<1) #define PERM_EXEC (1<<2) +struct stuff { + const char *path[10]; + int npath; +}; + static int callback(struct argparse *self, const struct argparse_option *opt) { printf("Called back... %s\n", *(char **)opt->value); + struct stuff *data = (struct stuff *)opt->data; + data->path[data->npath] = *(char **)opt->value; + data->npath++; return 1; } @@ -26,15 +34,21 @@ main(int argc, const char **argv) int self_gravity = 0; int int_num = 0; float flt_num = 0.f; - const char *path = NULL; + struct stuff data; + data.npath = 0; + data.path[0] = NULL; + const char *buffer; int perms = 0; + int npath; + struct argparse_option options[] = { OPT_HELP(), OPT_GROUP("Basic options"), OPT_BOOLEAN('f', "force", &force, "force to do", NULL, 0, 0), OPT_BOOLEAN(0, "self-gravity", &self_gravity, "use self gravity", NULL, 0, 0), - OPT_STRING('P', "path", &path, "path to read", &callback, 0, 0), + OPT_STRING('P', "path", &buffer, "path to read", &callback, + (intptr_t)&data , 0), OPT_INTEGER('i', "int", &int_num, "selected integer", NULL, 0, 0), OPT_FLOAT('s', "float", &flt_num, "selected float", NULL, 0, 0), OPT_END(), @@ -48,8 +62,10 @@ main(int argc, const char **argv) printf("force: %d\n", force); if (self_gravity != 0) printf("self_gravity: %d\n", self_gravity); - if (path != NULL) - printf("path: %s\n", path); + if (data.npath > 0) { + for (int i = 0 ; i < data.npath; i++) + printf("path: %s\n", data.path[i]); + } if (int_num != 0) printf("int_num: %d\n", int_num); if (flt_num != 0) diff --git a/examples/main.c b/examples/main.c index f603c44e4fb5a7c08a3980218430678061708fbc..1147794d8b9ce15a57ba68f2e1cb62094cb97bd0 100644 --- a/examples/main.c +++ b/examples/main.c @@ -52,73 +52,8 @@ /* Global profiler. */ struct profiler prof; -/** - * @brief Help messages for the command line parameters. - */ -void print_help_message(void) { - - printf("\nUsage: swift [OPTION]... PARAMFILE\n"); - printf(" swift_mpi [OPTION]... PARAMFILE\n\n"); - - printf("Valid options are:\n"); - printf(" %2s %14s %s\n", "-a", "", "Pin runners using processor affinity."); - printf(" %2s %14s %s\n", "-c", "", - "Run with cosmological time integration."); - printf(" %2s %14s %s\n", "-C", "", "Run with cooling."); - printf( - " %2s %14s %s\n", "-d", "", - "Dry run. Read the parameter file, allocate memory but does not read "); - printf( - " %2s %14s %s\n", "", "", - "the particles from ICs and exit before the start of time integration."); - printf(" %2s %14s %s\n", "", "", - "Allows user to check validy of parameter and IC files as well as " - "memory limits."); - printf(" %2s %14s %s\n", "-D", "", - "Always drift all particles even the ones far from active particles. " - "This emulates"); - printf(" %2s %14s %s\n", "", "", - "Gadget-[23] and GIZMO's default behaviours."); - printf(" %2s %14s %s\n", "-e", "", - "Enable floating-point exceptions (debugging mode)."); - printf(" %2s %14s %s\n", "-f", "{int}", - "Overwrite the CPU frequency (Hz) to be used for time measurements."); - printf(" %2s %14s %s\n", "-g", "", - "Run with an external gravitational potential."); - printf(" %2s %14s %s\n", "-G", "", "Run with self-gravity."); - printf(" %2s %14s %s\n", "-M", "", - "Reconstruct the multipoles every time-step."); - printf(" %2s %14s %s\n", "-n", "{int}", - "Execute a fixed number of time steps. When unset use the time_end " - "parameter to stop."); - printf(" %2s %14s %s\n", "-o", "{str}", - "Generate a default output parameter file."); - printf(" %2s %14s %s\n", "-P", "{sec:par:val}", - "Set parameter value and overwrites values read from the parameters " - "file. Can be used more than once."); - printf(" %2s %14s %s\n", "-r", "", "Continue using restart files."); - printf(" %2s %14s %s\n", "-s", "", "Run with hydrodynamics."); - printf(" %2s %14s %s\n", "-S", "", "Run with stars."); - printf(" %2s %14s %s\n", "-b", "", "Run with stars feedback."); - printf(" %2s %14s %s\n", "-t", "{int}", - "The number of threads to use on each MPI rank. Defaults to 1 if not " - "specified."); - printf(" %2s %14s %s\n", "-T", "", "Print timers every time-step."); - printf(" %2s %14s %s\n", "-v", "[12]", "Increase the level of verbosity:"); - printf(" %2s %14s %s\n", "", "", "1: MPI-rank 0 writes,"); - printf(" %2s %14s %s\n", "", "", "2: All MPI-ranks write."); - printf(" %2s %14s %s\n", "-x", "", "Run with structure finding."); - printf(" %2s %14s %s\n", "-y", "{int}", - "Time-step frequency at which task graphs are dumped."); - printf(" %2s %14s %s\n", "-Y", "{int}", - "Time-step frequency at which threadpool tasks are dumped."); - printf(" %2s %14s %s\n", "-h", "", "Print this help message and exit."); - printf( - "\nSee the file parameter_example.yml for an example of " - "parameter file.\n"); -} - -static const char *const usage[] = { +// Usage string. +static const char *const swift_usage[] = { "swift [options] [[--] param-file]", "swift [options] param-file", "swift_mpi [options] [[--] param-file]", @@ -126,6 +61,19 @@ static const char *const usage[] = { NULL, }; +// Function to handle multiple -P arguments. +struct cmdparams { + const char *param[PARSER_MAX_NO_OF_PARAMS]; + int nparam; +}; + +static int handle_cmdparam(struct argparse *self, + const struct argparse_option *opt) { + struct cmdparams *cmdps = (struct cmdparams *)opt->data; + cmdps->param[cmdps->nparam] = *(char **)opt->value; + cmdps->nparam++; + return 1; +} /** * @brief Main routine that loads a few particles and generates some output. @@ -212,120 +160,171 @@ int main(int argc, char *argv[]) { int verbose = 0; int nr_threads = 1; int with_verbose_timers = 0; - int nparams = 0; char *output_parameters_filename = NULL; - char *cmdparams[PARSER_MAX_NO_OF_PARAMS]; + char *cpufreqarg = NULL; char *param_filename = NULL; char restart_file[200] = ""; unsigned long long cpufreq = 0; + struct cmdparams cmdps; + cmdps.nparam = 0; + cmdps.param[0] = NULL; + char *buffer = NULL; /* Parse the command-line parameters. */ struct argparse_option options[] = { OPT_HELP(), + OPT_GROUP("Simulation options"), - OPT_BOOLEAN('b', "feedback", &with_feedback, "", NULL, 0, 0), - OPT_BOOLEAN('c', "cosmology", &with_cosmology, "", NULL, 0, 0), - OPT_BOOLEAN('C', "cooling", &with_cooling, "", NULL, 0, 0), + OPT_BOOLEAN('b', "feedback", &with_feedback, "Run with stars feedback", + NULL, 0, 0), + OPT_BOOLEAN('c', "cosmology", &with_cosmology, + "Run with cosmological time integration.", NULL, 0, 0), + OPT_BOOLEAN('C', "cooling", &with_cooling, "Run with cooling", NULL, 0, 0), OPT_BOOLEAN('F', "sourceterms", &with_sourceterms, "", NULL, 0, 0), - OPT_BOOLEAN('g', "external-gravity", &with_external_gravity, "", NULL, 0, 0), - OPT_BOOLEAN('G', "self-gravity", &with_self_gravity, "", NULL, 0, 0), - OPT_BOOLEAN('M', "multipole-reconstructionx", &with_mpole_reconstruction, "", NULL, 0, 0), - OPT_BOOLEAN('s', "hydrodynamics", &with_hydro, "", NULL, 0, 0), - OPT_BOOLEAN('S', "stars", &with_stars, "", NULL, 0, 0), - OPT_BOOLEAN('x', "velociraptor", &with_structure_finding, "", NULL, 0, 0), + OPT_BOOLEAN('g', "external-gravity", &with_external_gravity, + "Run with an external gravitational potential.", NULL, 0, 0), + OPT_BOOLEAN('G', "self-gravity", &with_self_gravity, + "Run with self-gravity.", NULL, 0, 0), + OPT_BOOLEAN('M', "multipole-reconstruction", &with_mpole_reconstruction, + "Reconstruct the multipoles every time-step.", NULL, 0, 0), + OPT_BOOLEAN('s', "hydrodynamics", &with_hydro, "Run with hydrodynamics.", + NULL, 0, 0), + OPT_BOOLEAN('S', "stars", &with_stars, "Run with stars", NULL, 0, 0), + OPT_BOOLEAN('x', "velociraptor", &with_structure_finding, + "Run with structure finding", NULL, 0, 0), + OPT_GROUP("Control options"), - OPT_BOOLEAN('a', "affinity", &with_aff, "", NULL, 0, 0), - OPT_BOOLEAN('d', "dry-run", &dry_run, "", NULL, 0, 0), - OPT_BOOLEAN('D', "drift-all", &with_drift_all, "", NULL, 0, 0), - OPT_BOOLEAN('e', "fpe", &with_fp_exceptions, "", NULL, 0, 0), - OPT_INTEGER('f', "cpu-frequency", &cpufreq, "", NULL, 0, 0), - OPT_INTEGER('n', "steps", &nsteps, "", NULL, 0, 0), - OPT_STRING('o', "output-params", &output_parameters_filename, "", NULL, 0, 0), - OPT_STRING('P', "param", &cmdparams, "", NULL, 0, 0), /* Need callback * handler */ - OPT_BOOLEAN('r', "restart", &restart, "", NULL, 0, 0), - OPT_INTEGER('t', "threads", &nr_threads, "", NULL, 0, 0), - OPT_INTEGER('T', "timers", &with_verbose_timers, "", NULL, 0, 0), - OPT_INTEGER('v', "verbose", &verbose, "", NULL, 0, 0), - OPT_INTEGER('y', "task-dumps", &dump_tasks, "", NULL, 0, 0), - OPT_INTEGER('Y', "threadpool-dumps", &dump_threadpool, "", NULL, 0, 0), + OPT_BOOLEAN('a', "affinity", &with_aff, + "Pin runners using processor affinity.", NULL, 0, 0), + OPT_BOOLEAN('d', "dry-run", &dry_run, + "Dry run. Read the parameter file, allocates memory but " + "does not read the particles from ICs.\t\nExits before the " + "start of time integration. Checks the validity of " + "parameters and IC files as well as memory limits.", + NULL, 0, 0), + OPT_BOOLEAN('D', "drift-all", &with_drift_all, "Always drift all " + "particles even the ones far from active particles. " + "This emulates Gadget-[23] and GIZMO's default behaviours.", + NULL, 0, 0), + OPT_BOOLEAN('e', "fpe", &with_fp_exceptions, "Enable floating-point" + "exceptions (debugging mode).", NULL, 0, 0), + OPT_STRING('f', "cpu-frequency", &cpufreqarg, "Overwrite the CPU " + "frequency (Hz) to be used for time measurements.", NULL, 0, 0), + OPT_INTEGER('n', "steps", &nsteps, "Execute a fixed number of time " + "steps. When unset use the time_end parameter to stop.", + NULL, 0, 0), + OPT_STRING('o', "output-params", &output_parameters_filename, + "Generate a default output parameter file.", NULL, 0, 0), + OPT_STRING('P', "param", &buffer, "Set parameter value, " + "overwriting a value read from the parameter " + "file. Can be used more than once {sec:par:value}.", + handle_cmdparam, (intptr_t)&cmdps, 0), + OPT_BOOLEAN('r', "restart", &restart, "Continue using restart files.", + NULL, 0, 0), + OPT_INTEGER('t', "threads", &nr_threads, "The number of threads to " + "use on each MPI rank. Defaults to 1 if not specified.", + NULL, 0, 0), + OPT_INTEGER('T', "timers", &with_verbose_timers, + "Print timers every time-step.", NULL, 0, 0), + OPT_INTEGER('v', "verbose", &verbose, "Run in verbose mode, in MPI " + "mode 2 outputs from all ranks.", NULL, 0, 0), + OPT_INTEGER('y', "task-dumps", &dump_tasks, + "Time-step frequency at which task graphs are dumped.", + NULL, 0, 0), + OPT_INTEGER('Y', "threadpool-dumps", &dump_threadpool, + "Time-step frequency at which threadpool tasks are dumped.", + NULL, 0, 0), }; struct argparse argparse; - argparse_init(&argparse, options, usage, 0); - argparse_describe(&argparse, "\nSWIFT usage", ""); + argparse_init(&argparse, options, swift_usage, 0); + argparse_describe(&argparse, "\nParameters:", NULL /* no epilog */); int nargs = argparse_parse(&argparse, argc, (const char **)argv); /* Need a parameter file. */ if (nargs != 1) { - error("No parameter file was supplied"); /* Also usage? */ + if (myrank == 0) argparse_usage(&argparse); + printf("\nError: no parameter file was supplied.\n"); + return 1; } param_filename = argv[0]; /* Checks of options. */ #if ! defined(HAVE_SETAFFINITY) || !defined(HAVE_LIBNUMA) - if (with_aff) error("No NUMA support for thread affinity"); + if (with_aff) { + printf("Error: no NUMA support for thread affinity\n"); + return 1; + } #endif #ifndef HAVE_FE_ENABLE_EXCEPT - if (with_fp_exceptions) error("No support for floating point exceptions"); + if (with_fp_exceptions) { + printf("Error: no support for floating point exceptions\n"); + return 1; + } #endif #ifndef HAVE_VELOCIRAPTOR - if (with_structure_finding) error("VELOCIraptor is not available"); + if (with_structure_finding) { + printf("Error: VELOCIraptor is not available\n"); + return 1; + } #endif #ifndef SWIFT_DEBUG_TASKS - if (dump_tasks) error("Task dumping is only possible if SWIFT was configured" - " with the --enable-task-debugging option."); + if (dump_tasks) { + printf("Error: task dumping is only possible if SWIFT was configured" + " with the --enable-task-debugging option.\n"); + return 1; + } #endif #ifndef SWIFT_DEBUG_THREADPOOL - if (dump_threadpool) error("Threadpool dumping is only possible if SWIFT " - "was configured with the " - "--enable-threadpool-debugging option."); + if (dump_threadpool) { + printf("Error: threadpool dumping is only possible if SWIFT was " + "configured with the --enable-threadpool-debugging option.\n"); + return 1; + } #endif + /* The CPU frequency is a long long, so we need to parse that ourselves. */ + if (cpufreqarg != NULL) { + if (sscanf(cpufreqarg, "%llu", &cpufreq) != 1) { + if (myrank == 0) printf("Error parsing CPU frequency (%s).\n", + cpufreqarg); + return 1; + } + } + /* Write output parameter file */ - if (myrank == 0 && strcmp(output_parameters_filename, "") != 0) { + if (myrank == 0 && output_parameters_filename != NULL) { io_write_output_field_parameter(output_parameters_filename); printf("End of run.\n"); return 0; } - /* check inputs */ - if (optind == argc - 1) { - if (!strcpy(param_filename, argv[optind++])) - error("Error reading parameter file name."); - } else if (optind > argc - 1) { - if (myrank == 0) printf("Error: A parameter file name must be provided\n"); - if (myrank == 0) print_help_message(); - return 1; - } else { - if (myrank == 0) printf("Error: Too many parameters given\n"); - if (myrank == 0) print_help_message(); - return 1; - } if (!with_self_gravity && !with_hydro && !with_external_gravity) { - if (myrank == 0) - printf("Error: At least one of -s, -g or -G must be chosen.\n"); - if (myrank == 0) print_help_message(); - return 1; + if (myrank == 0) { + argparse_usage(&argparse); + printf("\nError: At least one of -s, -g or -G must be chosen.\n"); + } + return 1; } if (with_stars && !with_external_gravity && !with_self_gravity) { - if (myrank == 0) - printf( - "Error: Cannot process stars without gravity, -g or -G must be " - "chosen.\n"); - if (myrank == 0) print_help_message(); - return 1; + if (myrank == 0) { + argparse_usage(&argparse); + printf("\nError: Cannot process stars without gravity, -g or -G " + "must be chosen.\n"); + } + return 1; } if (!with_stars && with_feedback) { - if (myrank == 0) - printf( - "Error: Cannot process feedback without stars, -S must be " - "chosen.\n"); - if (myrank == 0) print_help_message(); - return 1; + if (myrank == 0) { + argparse_usage(&argparse); + printf("\nError: Cannot process feedback without stars, -S must be " + "chosen.\n"); + } + return 1; } /* Let's pin the main thread, now we know if affinity will be used. */ @@ -418,11 +417,11 @@ int main(int argc, char *argv[]) { parser_read_file(param_filename, params); /* Handle any command-line overrides. */ - if (nparams > 0) { + if (cmdps.nparam > 0) { message( "Overwriting values read from the YAML file with command-line " "values."); - for (int k = 0; k < nparams; k++) parser_set_param(params, cmdparams[k]); + for (int k = 0; k < cmdps.nparam; k++) parser_set_param(params, cmdps.param[k]); } } #ifdef WITH_MPI