diff --git a/.gitmodules b/.gitmodules index a72158a48..259a9da7b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,9 @@ [submodule "external/mod2c"] path = external/mod2c url = https://github.com/BlueBrain/mod2c +[submodule "external/CLI11"] + path = external/CLI11 + url = https://github.com/CLIUtils/CLI11.git [submodule "external/nmodl"] path = external/nmodl url = https://github.com/BlueBrain/nmodl diff --git a/CMake/AddCLI11Submodule.cmake b/CMake/AddCLI11Submodule.cmake new file mode 100644 index 000000000..5b2185486 --- /dev/null +++ b/CMake/AddCLI11Submodule.cmake @@ -0,0 +1,23 @@ +# ============================================================================= +# Copyright (C) 2020 Blue Brain Project +# +# See top-level LICENSE file for details. +# ============================================================================= + + +include(FindPackageHandleStandardArgs) +find_package(FindPkgConfig QUIET) +find_path(CLI11_PROJ NAMES CMakeLists.txt PATHS "${CORENEURON_PROJECT_SOURCE_DIR}/external/CLI11") +find_package_handle_standard_args(CLI11 REQUIRED_VARS CLI11_PROJ) + +if(NOT CLI11_FOUND) + find_package(Git 1.8.3 QUIET) + if(NOT ${GIT_FOUND}) + message(FATAL_ERROR "git not found, clone repository with --recursive") + endif() + message(STATUS "Sub-module CLI11 missing: running git submodule update --init --recursive") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive -- + ${CORENEURON_PROJECT_SOURCE_DIR}/external/CLI11 + WORKING_DIRECTORY ${CORENEURON_PROJECT_SOURCE_DIR}) +endif() + diff --git a/CMakeLists.txt b/CMakeLists.txt index 058259d08..65ca2e018 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,18 +5,15 @@ # ============================================================================= cmake_minimum_required(VERSION 3.7 FATAL_ERROR) -project(coreneuron) +project(coreneuron VERSION 0.16.0) # ============================================================================= # CMake common project settings # ============================================================================= -set(VERSION_MAJOR "0") -set(VERSION_MINOR "16") -set(VERSION_PATCH "0") set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") +add_definitions(-DCORENRN_VERSION="${PROJECT_VERSION}") set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Empty or one of Debug, Release, RelWithDebInfo") @@ -46,6 +43,8 @@ include(ReleaseDebugAutoFlags) include(CrayPortability) include(SetRpath) include(CTest) +include(AddCLI11Submodule) +add_subdirectory(${CORENEURON_PROJECT_SOURCE_DIR}/external/CLI11) # ============================================================================= # Build options diff --git a/README.md b/README.md index 9c3e5f5b5..45a8ac078 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ Note that the CUDA Toolkit version should be compatible with PGI compiler instal You have to run GPU executable with the `--gpu` or `-gpu`. Make sure to enable cell re-ordering mechanism to improve GPU performance using `--cell_permute` option (permutation types : 2 or 1): ```bash -mpirun -n 1 ./bin/nrniv-core -d ../tests/integration/ring -mpi -e 100 --gpu --cell_permute 2 +mpirun -n 1 ./bin/nrniv-core --mpi --gpu --tstop 100 --datpath ../tests/integration/ring --cell-permute 2 ``` Note that if your model is using Random123 random number generator, you can't use same executable for CPU and GPU runs. We suggest to build separate executable for CPU and GPU simulations. This will be fixed in future releases. @@ -132,65 +132,26 @@ cmake .. -DCMAKE_CXX_FLAGS="-O3 -g" \ ## RUNNING SIMULATION: -Note that the CoreNEURON simulator dependends on NEURON to build the network model: see [NEURON](https://www.neuron.yale.edu/neuron/) documentation for more information. Once you build the model using NEURON, you can launch CoreNEURON on the same or different machine by: +Note that the CoreNEURON simulator depends on NEURON to build the network model: see [NEURON](https://www.neuron.yale.edu/neuron/) documentation for more information. Once you build the model using NEURON, you can launch CoreNEURON on the same or different machine by: ```bash export OMP_NUM_THREADS=2 #set appropriate value -mpiexec -np 2 build/bin/nrniv-core -e 10 -d /path/to/model/built/by/neuron -mpi +mpiexec -np 2 build/bin/nrniv-core --tstop 10 --datpath /path/to/model/built/by/neuron --mpi ``` [This tutorial](https://github.com/nrnhines/ringtest) provide more information for parallel runs and performance comparison. -In order to see the command line options, you can use: +### Command Line Interface -```bash -/path/to/isntall/directory/nrniv-core --help --b, --spikebuf ARG Spike buffer size. (100000) --c, --threading Parallel threads. The default is serial threads. --d, --datpath ARG Path containing CoreNeuron data files. (.) --dt, --dt ARG Fixed time step. The default value is set by - defaults.dat or is 0.025. --e, --tstop ARG Stop time (ms). (100) --f, --filesdat ARG Name for the distribution file. (files.dat) --g, --prcellgid ARG Output prcellstate information for the gid NUMBER. --gpu, --gpu Enable use of GPUs. The default implies cpu only - run. --h, --help Print a usage message briefly summarizing these - command-line options, then exit. --k, --forwardskip ARG Forwardskip to TIME --l, --celsius ARG Temperature in degC. The default value is set in - defaults.dat or else is 34.0. --mpi Enable MPI. In order to initialize MPI environment - this argument must be specified. --o, --outpath ARG Path to place output data files. (.) --p, --pattern ARG Apply patternstim using the specified spike file. --R, --cell-permute ARG Cell permutation, 0 No; 1 optimise node adjacency; 2 - optimize parent adjacency. (1) --s, --seed ARG Initialization seed for random number generator - (int). --v, --voltage ARG Initial voltage used for nrn_finitialize(1, v_init). - If 1000, then nrn_finitialize(0,...). (-65.) --W, --nwarp ARG Number of warps to balance. (0) --x, --extracon ARG Number of extra random connections in each thread to - other duplicate models (int). ---binqueue Use bin queue. ---checkpoint ARG Enable checkpoint and specify directory to store - related files. ---mindelay ARG Maximum integration interval (likely reduced by - minimum NetCon delay). (10) ---ms-phases ARG Number of multisend phases, 1 or 2. (2) ---ms-subintervals ARG Number of multisend subintervals, 1 or 2. (2) ---multisend Use Multisend spike exchange instead of Allgather. ---read-config ARG Read configuration file filename. ---restore ARG Restore simulation from provided checkpoint - directory. ---show Print args. ---skip-mpi-finalize Do not call mpi finalize. ---spkcompress ARG Spike compression. Up to ARG are exchanged during - MPI_Allgather. (0) ---write-config ARG Write configuration file filename. -``` +:warning: :warning: :warning: **In a recent update the command line interface was updated, so please update your scripts accordingly!** + +Some details on the new interface: + +The new command line interface is based on CLI11. You can find more details by running `coreneuron_exec --help`. + +Multiple characters options with single dash (`-gpu`, `-mpi`, `-dt`) are **not** supported anymore. All those options now require a double dash (`--gpu`, `--mpi`, `--dt`), but single characters options still support a single dash (e.g. `-g`). +The format of the configuration options file has changed, regenerate them if there is any problem. ## Results diff --git a/coreneuron/CMakeLists.txt b/coreneuron/CMakeLists.txt index 484f10b3b..bcbfc2441 100644 --- a/coreneuron/CMakeLists.txt +++ b/coreneuron/CMakeLists.txt @@ -20,6 +20,7 @@ file(GLOB_RECURSE CORENEURON_HEADER_FILES "*.h*") file(GLOB_RECURSE CORENEURON_TEMPLATE_FILES "*.ipp") file(GLOB CORENEURON_CODE_FILES "apps/main1.cpp" + "apps/corenrn_parameters.cpp" "gpu/*.cpp" "io/*.cpp" "mechanism/*.cpp" @@ -173,6 +174,7 @@ target_link_libraries(coreneuron ${CALIPER_LIB} ${CALIPER_MPI_LIB} ${likwid_LIBRARIES}) +target_include_directories(coreneuron SYSTEM PRIVATE ${CORENEURON_PROJECT_SOURCE_DIR}/external/CLI11/include) target_link_libraries(corenrnmech scopmath coreneuron) diff --git a/coreneuron/apps/corenrn_parameters.cpp b/coreneuron/apps/corenrn_parameters.cpp new file mode 100644 index 000000000..15dd3f325 --- /dev/null +++ b/coreneuron/apps/corenrn_parameters.cpp @@ -0,0 +1,178 @@ +/* +Copyright (c) 2016, Blue Brain Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "coreneuron/apps/corenrn_parameters.hpp" + +namespace coreneuron { +corenrn_parameters::corenrn_parameters(){ + + app.set_config("--read-config", "", "Read parameters from ini file", false) + ->check(CLI::ExistingFile); + app.add_option("--write-config", this->writeParametersFilepath, "Write parameters to this file", false); + + app.add_flag("--mpi", this->mpi_enable, "Enable MPI. In order to initialize MPI environment this argument must be specified."); + app.add_flag("--gpu", this->gpu, "Activate GPU computation."); + app.add_option("--dt", this->dt, "Fixed time step. The default value is set by defaults.dat or is 0.025.", true) + ->check(CLI::Range(-1'000.,1e9)); + app.add_option("-e, --tstop", this->tstop, "Stop Time in ms.") + ->check(CLI::Range(0., 1e9)); + app.add_flag("--show", this->print_arg, "Print arguments."); + + auto sub_gpu = app.add_option_group("GPU", "Commands relative to GPU."); + sub_gpu -> add_option("-W, --nwarp", this->nwarp, "Number of warps to balance.", true) + ->check(CLI::Range(0, 1'000'000)); + sub_gpu -> add_option("-R, --cell-permute", this->cell_interleave_permute, "Cell permutation: 0 No permutation; 1 optimise node adjacency; 2 optimize parent adjacency.", true) + ->check(CLI::Range(0, 3)); + + auto sub_input = app.add_option_group("input", "Input dataset options."); + sub_input -> add_option("-d, --datpath", this->datpath, "Path containing CoreNeuron data files.") + ->required(true)->check(CLI::ExistingPath); + sub_input -> add_option("-f, --filesdat", this->filesdat, "Name for the distribution file.", true) + ->check(CLI::ExistingFile); + sub_input -> add_option("-p, --pattern", this->patternstim, "Apply patternstim using the specified spike file.") + ->check(CLI::ExistingFile); + sub_input -> add_option("-s, --seed", this->seed, "Initialization seed for random number generator.") + ->check(CLI::Range(0, 100'000'000)); + sub_input -> add_option("-v, --voltage", this->voltage, "Initial voltage used for nrn_finitialize(1, v_init). If 1000, then nrn_finitialize(0,...).") + ->check(CLI::Range(-1e9, 1e9)); + sub_input -> add_option("--report-conf", this->reportfilepath, "Reports configuration file.") + ->check(CLI::ExistingPath); + sub_input -> add_option("--restore", this->restorepath, "Restore simulation from provided checkpoint directory.") + ->check(CLI::ExistingPath); + + auto sub_parallel = app.add_option_group("parallel", "Parallel processing options."); + sub_parallel -> add_flag("-c, --threading", this->threading, "Parallel threads. The default is serial threads."); + sub_parallel -> add_flag("--skip-mpi-finalize", this->skip_mpi_finalize, "Do not call mpi finalize."); + + auto sub_spike = app.add_option_group("spike", "Spike exchange options."); + sub_spike -> add_option("--ms-phases", this->ms_phases, "Number of multisend phases, 1 or 2.", true) + ->check(CLI::Range(1, 2)); + sub_spike -> add_option("--ms-subintervals", this->ms_subint, "Number of multisend subintervals, 1 or 2.", true) + ->check(CLI::Range(1, 2)); + sub_spike -> add_flag("--multisend", this->multisend, "Use Multisend spike exchange instead of Allgather."); + sub_spike -> add_option("--spkcompress", this->spkcompress, "Spike compression. Up to ARG are exchanged during MPI_Allgather.", true) + ->check(CLI::Range(0, 100'000)); + sub_spike->add_flag("--binqueue", this->binqueue, "Use bin queue." ); + + auto sub_config = app.add_option_group("config", "Config options."); + sub_config -> add_option("-b, --spikebuf", this->spikebuf, "Spike buffer size.", true) + ->check(CLI::Range(0, 2'000'000'000)); + sub_config -> add_option("-g, --prcellgid", this->prcellgid, "Output prcellstate information for the gid NUMBER.") + ->check(CLI::Range(-1, 2'000'000'000)); + sub_config -> add_option("-k, --forwardskip", this->forwardskip, "Forwardskip to TIME") + ->check(CLI::Range(0., 1e9)); + sub_config -> add_option("-l, --celsius", this->celsius, "Temperature in degC. The default value is set in defaults.dat or else is 34.0.", true) + ->check(CLI::Range(-1000., 1000.)); + sub_config -> add_option("-x, --extracon", this->extracon, "Number of extra random connections in each thread to other duplicate models.") + ->check(CLI::Range(0, 10'000'000)); + sub_config -> add_option("-z, --multiple", this->multiple, "Model duplication factor. Model size is normal size * multiple") + ->check(CLI::Range(1, 10'000'000)); + sub_config -> add_option("--mindelay", this->mindelay, "Maximum integration interval (likely reduced by minimum NetCon delay).", true) + ->check(CLI::Range(0., 1e9)); + sub_config -> add_option("--report-buffer-size", this->report_buff_size, "Size in MB of the report buffer.") + ->check(CLI::Range(1, 128)); + + auto sub_output = app.add_option_group("output", "Output configuration."); + sub_output -> add_option("-i, --dt_io", this->dt_io, "Dt of I/O.", true) + ->check(CLI::Range(-1000., 1e9)); + sub_output -> add_option("-o, --outpath", this->outpath, "Path to place output data files.", true) + ->check(CLI::ExistingPath); + sub_output -> add_option("--checkpoint", this->checkpointpath, "Enable checkpoint and specify directory to store related files.") + ->check(CLI::ExistingDirectory); +}; + +void corenrn_parameters::parse (int argc, char** argv) { + + try { + app.parse(argc, argv); + } catch (const CLI::ExtrasError &e) { + + std::cerr << "Single-dash arguments such as -mpi are deprecated, please check ./coreneuron_exec --help for more information. \n" << std::endl; + app.exit(e); + throw e; + + } catch (const CLI::ParseError &e) { + app.exit(e); + throw e; + } +}; + +std::ostream& operator<<(std::ostream& os, const corenrn_parameters& corenrn_param){ + + os << "GENERAL PARAMETERS" << std::endl + << std::left << std::setw(15) << "MPI" << std::right << std::setw(7) << corenrn_param.mpi_enable << " " + << std::left << std::setw(15) << "dt" << std::right << std::setw(7) << corenrn_param.dt << " " + << std::left << std::setw(15) << "Tstop" << std::right << std::setw(7) << corenrn_param.tstop << std::endl + << std::left << std::setw(15) << "Print_arg" << std::right << std::setw(7) << corenrn_param.print_arg << std::endl + << std::endl + << "GPU PARAMETERS" << std::endl + << std::left << std::setw(15) << "Nwarp" << std::right << std::setw(7) << corenrn_param.nwarp << " " + << std::left << std::setw(15) << "Cell_perm" << std::right << std::setw(7) << corenrn_param.cell_interleave_permute << std::endl + << std::endl + << "INPUT PARAMETERS" << std::endl + << std::left << std::setw(15) << "Voltage" << std::right << std::setw(7) << corenrn_param.voltage << " " + << std::left << std::setw(15) << "Seed" << std::right << std::setw(7) << corenrn_param.seed << std::endl + << std::left << std::setw(15) << "Datpath" << std::right << std::setw(7) << corenrn_param.datpath << std::endl + << std::left << std::setw(15) << "Filesdat" << std::right << std::setw(7) << corenrn_param.filesdat << std::endl; + if (!corenrn_param.patternstim.empty()) os << std::left << std::setw(15) << "Patternstim" << std::right << std::setw(7) << corenrn_param.patternstim << std::endl; + if (!corenrn_param.reportfilepath.empty()) os << std::left << std::setw(15) << "Reportpath" << std::right << std::setw(7) << corenrn_param.reportfilepath << std::endl; + if (!corenrn_param.restorepath.empty()) os << std::left << std::setw(15) << "Restorepath" << std::right << std::setw(7) << corenrn_param.restorepath << std::endl; + os << std::endl + << "PARALLEL COMPUTATION PARAMETERS" << std::endl + << std::left << std::setw(15) << "Threading" << std::right << std::setw(7) << corenrn_param.threading << " " + << std::left << std::setw(15) << "Skip_mpi_fin" << std::right << std::setw(7) << corenrn_param.skip_mpi_finalize << std::endl + << std::endl + << "SPIKE EXCHANGE" << std::endl + << std::left << std::setw(15) << "Ms_phases" << std::right << std::setw(7) << corenrn_param.ms_phases << " " + << std::left << std::setw(15) << "Ms_Subint" << std::right << std::setw(7) << corenrn_param.ms_subint << " " + << std::left << std::setw(15) << "Multisend" << std::right << std::setw(7) << corenrn_param.multisend << std::endl + << std::left << std::setw(15) << "Spk_compress" << std::right << std::setw(7) << corenrn_param.spkcompress << " " + << std::left << std::setw(15) << "Binqueue" << std::right << std::setw(7) << corenrn_param.binqueue << std::endl + << std::endl + << "CONFIGURATION" << std::endl + << std::left << std::setw(15) << "Spike Buffer" << std::right << std::setw(7) << corenrn_param.spikebuf << " " + << std::left << std::setw(15) << "Pr Cell Gid" << std::right << std::setw(7) << corenrn_param.prcellgid << " " + << std::left << std::setw(15) << "Forwardskip" << std::right << std::setw(7) << corenrn_param.forwardskip << std::endl + << std::left << std::setw(15) << "Celsius" << std::right << std::setw(7) << corenrn_param.celsius << " " + << std::left << std::setw(15) << "Extracon" << std::right << std::setw(7) << corenrn_param.extracon << " " + << std::left << std::setw(15) << "Multiple" << std::right << std::setw(7) << corenrn_param.multiple << std::endl + << std::left << std::setw(15) << "Mindelay" << std::right << std::setw(7) << corenrn_param.mindelay << " " + << std::left << std::setw(15) << "Rep_buff" << std::right << std::setw(7) << corenrn_param.report_buff_size << std::endl + << std::endl + << "OUTPUT PARAMETERS" << std::endl + << std::left << std::setw(15) << "dt_io" << std::right << std::setw(7) << corenrn_param.dt_io << std::endl + << std::left << std::setw(15) << "Outpath" << std::right << std::setw(7) << corenrn_param.outpath << std::endl; + if (!corenrn_param.checkpointpath.empty()) os << std::left << std::setw(15) << "Checkpointpath" << std::right << std::setw(7) << corenrn_param.checkpointpath<< std::endl; + + return os; +} + + +corenrn_parameters corenrn_param; + +} // namespace coreneuron diff --git a/coreneuron/apps/corenrn_parameters.hpp b/coreneuron/apps/corenrn_parameters.hpp new file mode 100644 index 000000000..f62491c34 --- /dev/null +++ b/coreneuron/apps/corenrn_parameters.hpp @@ -0,0 +1,115 @@ +/* +Copyright (c) 2016, Blue Brain Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef CN_PARAMETERS_H +#define CN_PARAMETERS_H + +#include +#include +#include +#include +#include + +/** + * \class corenrn_parameters + * \brief Parses and contains Command Line parameters for Core Neuron + * + * This structure contains all the parameters that CoreNeuron fetches + * from the Command Line. It uses the CLI11 libraries to parse these parameters + * and saves them in an internal public structure. Each parameter can be + * accessed or written freely. By default the constructor instantiates a + * CLI11 object and initializes it for CoreNeuron use. + * This object is freely accessible from any point of the program. + * An ostream method is also provided to print out all the parameters that + * CLI11 parse. + * Please keep in mind that, due to the nature of the subcommands in CLI11, + * the command line parameters for subcategories NEED to be come before the relative + * parameter. e.g. --mpi --gpu gpu --nwarp + * Also single dash long options are not supported anymore (-mpi -> --mpi). + */ + +namespace coreneuron { + +struct corenrn_parameters { + + const int report_buff_size_default=4; + + unsigned spikebuf=100'000; /// Internal buffer used on every rank for spikes + int prcellgid=-1; /// Gid of cell for prcellstate + unsigned ms_phases=2; /// Number of multisend phases, 1 or 2 + unsigned ms_subint=2; /// Number of multisend interval. 1 or 2 + unsigned spkcompress=0; /// Spike Compression + unsigned cell_interleave_permute=0; /// Cell interleaving permutation + unsigned nwarp=0; /// Number of warps to balance for cell_interleave_permute == 2 + unsigned multiple=1; /// Model duplication factor + unsigned extracon=0; /// Number of extra random connections in each thread to other duplicate models. + unsigned report_buff_size=report_buff_size_default; ///Size in MB of the report buffer. + int seed=-1; /// Initialization seed for random number generator (int) + + bool mpi_enable=false; /// Enable MPI flag. + bool print_arg=false; /// Print arguments flag. + bool skip_mpi_finalize=false; /// Skip MPI finalization + bool multisend=false; /// Use Multisend spike exchange instead of Allgather. + bool threading=false; /// Enable pthread/openmp + bool gpu=false; /// Enable GPU computation. + bool binqueue=false; /// Use bin queue. + bool print_version=false; /// Print the version and quit. + + double tstop=100; /// Stop time of simulation in msec + double dt=-1000.0; /// Timestep to use in msec + double dt_io=0.1; /// I/O timestep to use in msec + double dt_report; /// I/O timestep to use in msec for reports + double celsius=-1000.0; /// Temperature in degC. + double voltage=-65.0; /// Initial voltage used for nrn_finitialize(1, v_init). + double forwardskip=0.; /// Forward skip to TIME. + double mindelay=10.; /// Maximum integration interval (likely reduced by minimum NetCon delay). + + std::string patternstim; /// Apply patternstim using the specified spike file. + std::string datpath="."; /// Directory path where .dat files + std::string outpath="."; /// Directory where spikes will be written + std::string filesdat="files.dat"; /// Name of file containing list of gids dat files read in + std::string restorepath; /// Restore simulation from provided checkpoint directory. + std::string reportfilepath; /// Reports configuration file. + std::string checkpointpath; /// Enable checkpoint and specify directory to store related files. + std::string writeParametersFilepath; /// Write parameters to this file + + CLI::App app{"CoreNeuron - Optimised Simulator Engine for NEURON."}; ///CLI app that performs CLI parsing + + corenrn_parameters(); ///Constructor that initializes the CLI11 app. + + void parse(int argc, char* argv[]); /// Runs the CLI11_PARSE macro. + +}; + +std::ostream& operator<<(std::ostream& os, const corenrn_parameters& corenrn_param); /// Printing method. + +extern corenrn_parameters corenrn_param; /// Declaring global corenrn_parameters object for this instance of CoreNeuron. + +} // namespace coreneuron + +#endif //CN_PARAMETERS_H diff --git a/coreneuron/apps/main1.cpp b/coreneuron/apps/main1.cpp index b9367fa80..2225132b8 100644 --- a/coreneuron/apps/main1.cpp +++ b/coreneuron/apps/main1.cpp @@ -47,10 +47,9 @@ THE POSSIBILITY OF SUCH DAMAGE. #include "coreneuron/io/output_spikes.hpp" #include "coreneuron/io/nrn_checkpoint.hpp" #include "coreneuron/utils/memory_utils.h" -#include "coreneuron/io/nrnoptarg.hpp" +#include "coreneuron/apps/corenrn_parameters.hpp" #include "coreneuron/io/prcellstate.hpp" #include "coreneuron/utils/nrnmutdec.h" -#include "coreneuron/utils/sdprintf.h" #include "coreneuron/utils/nrn_stats.h" #include "coreneuron/io/nrnreport.hpp" #include "coreneuron/gpu/nrn_acc_manager.hpp" @@ -98,7 +97,7 @@ char* prepare_args(int& argc, char**& argv, int use_mpi, const char* arg) { args.insert(0, " coreneuron "); args.append(" --skip-mpi-finalize "); if (use_mpi) { - args.append(" -mpi "); + args.append(" --mpi "); } // we can't modify string with strtok, make copy @@ -188,10 +187,10 @@ void nrn_init_and_load_data(int argc, // set global variables // precedence is: set by user, globals.dat, 34.0 - celsius = nrnopt_get_dbl("--celsius"); + celsius = corenrn_param.celsius; #if _OPENACC - if (!nrnopt_get_flag("--gpu") && nrnopt_get_int("--cell-permute") == 2) { + if (!corenrn_param.gpu && corenrn_param.cell_interleave_permute == 2) { fprintf( stderr, "compiled with _OPENACC does not allow the combination of --cell-permute=2 and missing --gpu\n"); @@ -201,37 +200,39 @@ void nrn_init_and_load_data(int argc, // if multi-threading enabled, make sure mpi library supports it #if NRNMPI - if (nrnopt_get_flag("--threading")) { + if (corenrn_param.threading) { nrnmpi_check_threading_support(); } #endif // full path of files.dat file - std::string filesdat(nrnopt_get_str("--datpath") + "/" + nrnopt_get_str("--filesdat")); + std::string filesdat(corenrn_param.datpath + "/" + corenrn_param.filesdat); // read the global variable names and set their values from globals.dat - set_globals(nrnopt_get_str("--datpath").c_str(), nrnopt_get_flag("--seed"), - nrnopt_get_int("--seed")); + set_globals(corenrn_param.datpath.c_str(), (corenrn_param.seed==0), + corenrn_param.seed); // set global variables for start time, timestep and temperature - std::string restore_path = nrnopt_get_str("--restore"); + std::string restore_path = corenrn_param.restorepath; t = restore_time(restore_path.c_str()); - if (nrnopt_get_dbl("--dt") != -1000.) { // command line arg highest precedence - dt = nrnopt_get_dbl("--dt"); + if (corenrn_param.dt != -1000.) { // command line arg highest precedence + dt = corenrn_param.dt; } else if (dt == -1000.) { // not on command line and no dt in globals.dat dt = 0.025; // lowest precedence } - nrnopt_modify_dbl("--dt", dt); + + corenrn_param.dt = dt; rev_dt = (int)(1. / dt); - if (nrnopt_get_dbl("--celsius") != -1000.) { // command line arg highest precedence - celsius = nrnopt_get_dbl("--celsius"); + if (corenrn_param.celsius != -1000.) { // command line arg highest precedence + celsius = corenrn_param.celsius; } else if (celsius == -1000.) { // not on command line and no celsius in globals.dat celsius = 34.0; // lowest precedence } - nrnopt_modify_dbl("--celsius", celsius); + + corenrn_param.celsius = celsius; #ifdef ISPC_INTEROP ispc_celsius = celsius; @@ -240,16 +241,17 @@ void nrn_init_and_load_data(int argc, mk_netcvode(); // One part done before call to nrn_setup. Other part after. - if (nrnopt_get_flag("--pattern")) { + + if (!corenrn_param.patternstim.empty()) { nrn_set_extra_thread0_vdata(); } report_mem_usage("Before nrn_setup"); // set if need to interleave cells - use_interleave_permute = nrnopt_get_int("--cell-permute"); - cellorder_nwarp = nrnopt_get_int("--nwarp"); - use_solve_interleave = nrnopt_get_int("--cell-permute"); + use_interleave_permute = corenrn_param.cell_interleave_permute; + cellorder_nwarp = corenrn_param.nwarp; + use_solve_interleave = corenrn_param.cell_interleave_permute; #if LAYOUT == 1 // permuting not allowed for AoS @@ -257,7 +259,7 @@ void nrn_init_and_load_data(int argc, use_solve_interleave = 0; #endif - if (nrnopt_get_flag("--gpu") && use_interleave_permute == 0) { + if (corenrn_param.gpu && use_interleave_permute == 0) { if (nrnmpi_myid == 0) { printf( " WARNING : GPU execution requires --cell-permute type 1 or 2. Setting it to 1.\n"); @@ -267,43 +269,43 @@ void nrn_init_and_load_data(int argc, } // pass by flag so existing tests do not need a changed nrn_setup prototype. - nrn_setup_multiple = nrnopt_get_int("--multiple"); - nrn_setup_extracon = nrnopt_get_int("--extracon"); + nrn_setup_multiple = corenrn_param.multiple; + nrn_setup_extracon = corenrn_param.extracon; // multisend options - use_multisend_ = nrnopt_get_flag("--multisend") ? 1 : 0; - n_multisend_interval = nrnopt_get_int("--ms-subintervals"); - use_phase2_ = (nrnopt_get_int("--ms-phases") == 2) ? 1 : 0; + use_multisend_ = corenrn_param.multisend ? 1 : 0; + n_multisend_interval = corenrn_param.ms_subint; + use_phase2_ = (corenrn_param.ms_phases == 2) ? 1 : 0; // reading *.dat files and setting up the data structures, setting mindelay - nrn_setup(filesdat.c_str(), is_mapping_needed, nrn_need_byteswap, run_setup_cleanup); + nrn_setup(filesdat.c_str(), is_mapping_needed, nrn_need_byteswap, run_setup_cleanup, + corenrn_param.datpath.c_str(), restore_path.c_str(), &corenrn_param.mindelay); // Allgather spike compression and bin queuing. - nrn_use_bin_queue_ = nrnopt_get_flag("--binqueue"); - int spkcompress = nrnopt_get_int("--spkcompress"); + nrn_use_bin_queue_ = corenrn_param.binqueue; + int spkcompress = corenrn_param.spkcompress; nrnmpi_spike_compress(spkcompress, (spkcompress ? true : false), use_multisend_); report_mem_usage("After nrn_setup "); // Invoke PatternStim - if (nrnopt_get_flag("--pattern")) { - nrn_mkPatternStim(nrnopt_get_str("--pattern").c_str()); + if (!corenrn_param.patternstim.empty()) { + nrn_mkPatternStim(corenrn_param.patternstim.c_str()); } /// Setting the timeout nrn_set_timeout(200.); // show all configuration parameters for current run - nrnopt_show(); if (nrnmpi_myid == 0) { std::cout << " Start time (t) = " << t << std::endl << std::endl; } // allocate buffer for mpi communication - mk_spikevec_buffer(nrnopt_get_int("--spikebuf")); + mk_spikevec_buffer(corenrn_param.spikebuf); report_mem_usage("After mk_spikevec_buffer"); - if (nrnopt_get_flag("-gpu")) { + if (corenrn_param.gpu) { setup_nrnthreads_on_device(nrn_threads, nrn_nthread); } @@ -312,7 +314,7 @@ void nrn_init_and_load_data(int argc, } // call prcellstate for prcellgid - call_prcellstate_for_prcellgid(nrnopt_get_int("--prcellgid"), nrnopt_get_flag("-gpu"), 1); + call_prcellstate_for_prcellgid(corenrn_param.prcellgid, corenrn_param.gpu, 1); } void call_prcellstate_for_prcellgid(int prcellgid, int compute_gpu, int is_init) { @@ -430,15 +432,44 @@ static void trajectory_return() { using namespace coreneuron; + extern "C" void mk_mech_init(int argc, char** argv) { #if NRNMPI nrnmpi_init(1, &argc, &argv); #endif // read command line parameters and parameter config files - nrnopt_parse(argc, (const char**)argv); + + try { + corenrn_param.parse(argc, argv); + } + catch (...) { + if (!corenrn_param.app["--version"]->empty()) { + // --version has been called + } else { + nrn_abort(1); + } + } + + if (corenrn_param.print_version) { + std::cout << "CoreNeuron version: " CORENRN_VERSION << std::endl; + } + + if (corenrn_param.print_arg) { + if (nrnmpi_myid == 0) { + std::cout << std::fixed << std::setprecision(2); + std::cout << corenrn_param << std::endl; + } + } + + if (!corenrn_param.writeParametersFilepath.empty()) { + std::ofstream out(corenrn_param.writeParametersFilepath, std::ios::trunc); + out << corenrn_param.app.config_to_str(false, false); + out.close(); + } // reads mechanism information from bbcore_mech.dat - mk_mech(nrnopt_get_str("--datpath").c_str()); + + mk_mech((corenrn_param.datpath).c_str()); } extern "C" int run_solve_core(int argc, char** argv) { @@ -449,13 +480,13 @@ extern "C" int run_solve_core(int argc, char** argv) { report_mem_usage("After mk_mech"); - if (nrnopt_get_str("--report-conf").size()) { - if (nrnopt_get_int("--multiple") > 1) { + if (!corenrn_param.reportfilepath.empty()) { + if (corenrn_param.multiple > 1) { if (nrnmpi_myid == 0) printf("\n WARNING! : Can't enable reports with model duplications feature! \n"); } else { - configs = create_report_configurations(nrnopt_get_str("--report-conf").c_str(), - nrnopt_get_str("--outpath").c_str()); + configs = create_report_configurations(corenrn_param.reportfilepath.c_str(), + corenrn_param.outpath.c_str()); reports_needs_finalize = configs.size(); } } @@ -466,11 +497,12 @@ extern "C" int run_solve_core(int argc, char** argv) { nrn_init_and_load_data(argc, argv, !configs.empty()); } - std::string checkpoint_path = nrnopt_get_str("--checkpoint"); + std::string checkpoint_path = corenrn_param.checkpointpath; + if (strlen(checkpoint_path.c_str())) { nrn_checkpoint_arg_exists = true; } - std::string output_dir = nrnopt_get_str("--outpath"); + std::string output_dir = corenrn_param.outpath; if (nrnmpi_myid == 0) { mkdir_p(output_dir.c_str()); @@ -478,17 +510,17 @@ extern "C" int run_solve_core(int argc, char** argv) { #if NRNMPI nrnmpi_barrier(); #endif - bool compute_gpu = nrnopt_get_flag("-gpu"); - bool skip_mpi_finalize = nrnopt_get_flag("--skip-mpi-finalize"); + bool compute_gpu = corenrn_param.gpu; + bool skip_mpi_finalize = corenrn_param.skip_mpi_finalize; // clang-format off #pragma acc data copyin(celsius, secondorder) if (compute_gpu) // clang-format on { - double v = nrnopt_get_dbl("--voltage"); - double dt = nrnopt_get_dbl("--dt"); - double delay = nrnopt_get_dbl("--mindelay"); - double tstop = nrnopt_get_dbl("--tstop"); + double v = corenrn_param.voltage; + double dt = corenrn_param.dt; + double delay = corenrn_param.mindelay; + double tstop = corenrn_param.tstop; if (tstop < t && nrnmpi_myid == 0) { printf("Error: Stop time (%lf) < Start time (%lf), restoring from checkpoint? \n", @@ -516,7 +548,7 @@ extern "C" int run_solve_core(int argc, char** argv) { // register all reports into reportinglib double min_report_dt = INT_MAX; - int report_buffer_size = nrnopt_get_int("--report-buffer-size"); + int report_buffer_size = corenrn_param.report_buff_size; for (size_t i = 0; i < configs.size(); i++) { register_report(dt, tstop, delay, configs[i]); if (configs[i].report_dt < min_report_dt) { @@ -525,35 +557,37 @@ extern "C" int run_solve_core(int argc, char** argv) { } // Set the buffer size if is not the default value. Otherwise use report.conf on // register_report - if (!nrnopt_is_default_value("--report-buffer-size")) { + if (corenrn_param.report_buff_size != corenrn_param.report_buff_size_default) { set_report_buffer_size(report_buffer_size); } setup_report_engine(min_report_dt, delay); configs.clear(); // call prcellstate for prcellgid - call_prcellstate_for_prcellgid(nrnopt_get_int("--prcellgid"), compute_gpu, 0); + call_prcellstate_for_prcellgid(corenrn_param.prcellgid, compute_gpu, 0); // handle forwardskip - if (nrnopt_get_dbl("--forwardskip") > 0.0) { - handle_forward_skip(nrnopt_get_dbl("--forwardskip"), nrnopt_get_int("--prcellgid")); + if (corenrn_param.forwardskip > 0.0) { + handle_forward_skip(corenrn_param.forwardskip, corenrn_param.prcellgid); } /// Solver execution Instrumentor::start_profile(); Instrumentor::phase_begin("simulation"); - BBS_netpar_solve(nrnopt_get_dbl("--tstop")); + BBS_netpar_solve(corenrn_param.tstop); Instrumentor::phase_end("simulation"); Instrumentor::stop_profile(); + // direct mode and full trajectory gathering on CoreNEURON, send back. if (corenrn_embedded) { trajectory_return(); } + // Report global cell statistics report_cell_stats(); // prcellstate after end of solver - call_prcellstate_for_prcellgid(nrnopt_get_int("--prcellgid"), compute_gpu, 0); + call_prcellstate_for_prcellgid(corenrn_param.prcellgid, compute_gpu, 0); } // write spike information to outpath diff --git a/coreneuron/io/mk_mech.cpp b/coreneuron/io/mk_mech.cpp index 54b3bdc43..20a1c3cbf 100644 --- a/coreneuron/io/mk_mech.cpp +++ b/coreneuron/io/mk_mech.cpp @@ -38,7 +38,6 @@ THE POSSIBILITY OF SUCH DAMAGE. #include "coreneuron/mechanism/register_mech.hpp" #include "coreneuron/nrniv/nrniv_decl.h" #include "coreneuron/utils/nrn_assert.h" -#include "coreneuron/utils/sdprintf.h" #include "coreneuron/mechanism/mech/cfile/cabvars.h" #include "coreneuron/io/nrn2core_direct.h" #include "coreneuron/coreneuron.hpp" @@ -85,39 +84,42 @@ void mk_mech(const char* datpath) { mk_mech(); return; } - char fnamebuf[1024]; - sd_ptr fname = sdprintf(fnamebuf, sizeof(fnamebuf), "%s/%s", datpath, "bbcore_mech.dat"); - std::ifstream fs(fname); - - if (!fs.good()) { - fprintf(stderr, "Error: couldn't find bbcore_mech.dat file in the dataset directory \n"); - fprintf( - stderr, - " Make sure to pass full directory path of dataset using -d DIR or --datpath=DIR \n"); - } - - nrn_assert(fs.good()); - mk_mech(fs); - fs.close(); + { + std::string fname = std::string(datpath) + "/bbcore_mech.dat"; + std::ifstream fs(fname); + + if (!fs.good()) { + fprintf(stderr, "Error: couldn't find bbcore_mech.dat file in the dataset directory \n"); + fprintf( + stderr, + " Make sure to pass full directory path of dataset using -d DIR or --datpath=DIR \n"); + } - fname = sdprintf(fnamebuf, sizeof(fnamebuf), "%s/%s", datpath, "byteswap1.dat"); - FILE* f = fopen(fname, "r"); - if (!f) { - fprintf(stderr, "Error: couldn't find byteswap1.dat file in the dataset directory \n"); + nrn_assert(fs.good()); + mk_mech(fs); + fs.close(); } - nrn_assert(f); - // file consists of int32_t binary 1 . After reading can decide if - // binary info in files needs to be byteswapped. - int32_t x; - nrn_assert(fread(&x, sizeof(int32_t), 1, f) == 1); - nrn_need_byteswap = 0; - if (x != 1) { - BYTEHEADER; - nrn_need_byteswap = 1; - BYTESWAP(x, int32_t); - nrn_assert(x == 1); + + { + std::string fname = std::string(datpath) + "/byteswap1.dat"; + FILE* f = fopen(fname.c_str(), "r"); + if (!f) { + fprintf(stderr, "Error: couldn't find byteswap1.dat file in the dataset directory \n"); + } + nrn_assert(f); + // file consists of int32_t binary 1 . After reading can decide if + // binary info in files needs to be byteswapped. + int32_t x; + nrn_assert(fread(&x, sizeof(int32_t), 1, f) == 1); + nrn_need_byteswap = 0; + if (x != 1) { + BYTEHEADER; + nrn_need_byteswap = 1; + BYTESWAP(x, int32_t); + nrn_assert(x == 1); + } + fclose(f); } - fclose(f); } // we are embedded in NEURON, get info as stringstream from nrnbbcore_write.cpp diff --git a/coreneuron/io/nrn_setup.cpp b/coreneuron/io/nrn_setup.cpp index dd950e00a..8455fd7cd 100644 --- a/coreneuron/io/nrn_setup.cpp +++ b/coreneuron/io/nrn_setup.cpp @@ -43,7 +43,6 @@ THE POSSIBILITY OF SUCH DAMAGE. #include "coreneuron/utils/memory.h" #include "coreneuron/io/nrn_setup.hpp" #include "coreneuron/network/partrans.hpp" -#include "coreneuron/io/nrnoptarg.hpp" #include "coreneuron/io/nrn_checkpoint.hpp" #include "coreneuron/permute/node_permute.h" #include "coreneuron/permute/cellorder.hpp" @@ -674,7 +673,10 @@ void nrn_setup_cleanup() { void nrn_setup(const char* filesdat, bool is_mapping_needed, int byte_swap, - bool run_setup_cleanup) { + bool run_setup_cleanup, + const char* datpath, + const char* restore_path, + double* mindelay) { /// Number of local cell groups int ngroup = 0; @@ -735,17 +737,9 @@ void nrn_setup(const char* filesdat, FileHandler* file_reader = new FileHandler[ngroup]; - std::string datapath = nrnopt_get_str("--datpath"); - std::string restore_path = nrnopt_get_str("--restore"); - - // if not restoring then phase2 files will be read from dataset directory - if (!restore_path.length()) { - restore_path = datapath; - } - /* nrn_multithread_job supports serial, pthread, and openmp. */ - store_phase_args(ngroup, gidgroups, imult, file_reader, datapath.c_str(), restore_path.c_str(), - byte_swap); + store_phase_args(ngroup, gidgroups, imult, file_reader, datpath, + restore_path ? datpath : restore_path, byte_swap); // gap junctions if (nrn_have_gaps) { @@ -785,8 +779,7 @@ void nrn_setup(const char* filesdat, if (is_mapping_needed) coreneuron::phase_wrapper<(coreneuron::phase)3>(); - double mindelay = set_mindelay(nrnopt_get_dbl("--mindelay")); - nrnopt_modify_dbl("--mindelay", mindelay); + *mindelay = set_mindelay(*mindelay); if (run_setup_cleanup) // if run_setup_cleanup==false, user must call nrn_setup_cleanup() later nrn_setup_cleanup(); @@ -1009,9 +1002,6 @@ void nrn_cleanup(bool clean_ion_global_map) { nrnthread_chkpnt = nullptr; } - // clean ezOpt parser allocated memory (if any) - nrnopt_delete(); - // clean ions global maps if (clean_ion_global_map) { for (int i = 0; i < nrn_ion_global_map_size; i++) diff --git a/coreneuron/io/nrn_setup.hpp b/coreneuron/io/nrn_setup.hpp index a1da685a2..762badf1c 100644 --- a/coreneuron/io/nrn_setup.hpp +++ b/coreneuron/io/nrn_setup.hpp @@ -32,7 +32,6 @@ THE POSSIBILITY OF SUCH DAMAGE. #include #include "coreneuron/sim/multicore.hpp" #include "coreneuron/io/nrn_filehandler.hpp" -#include "coreneuron/utils/sdprintf.h" namespace coreneuron { static bool do_not_open; @@ -129,17 +128,15 @@ inline void* phase_wrapper_w(NrnThread* nt) { data_dir = restore_path_w; } - sd_ptr fname = sdprintf(fnamebuf, sizeof(fnamebuf), - std::string("%s/%d_" + getPhaseName

() + ".dat").c_str(), - data_dir, gidgroups_w[i]); + std::string fname = std::string(data_dir) + "/" + std::to_string(gidgroups_w[i]) + ".dat"; // Avoid trying to open the gid_gap.dat file if it doesn't exist when there are no // gap junctions in this gid - if (P == gap && !file_reader_w[i].file_exist(fname)) { + if (P == gap && !file_reader_w[i].file_exist(fname.c_str())) { file_reader_w[i].close(); } else { // if no file failed to open or not opened at all - file_reader_w[i].open(fname, byte_swap_w); + file_reader_w[i].open(fname.c_str(), byte_swap_w); } } read_phase_aux

(file_reader_w[i], imult_w[i], *nt); diff --git a/coreneuron/io/nrnoptarg.cpp b/coreneuron/io/nrnoptarg.cpp deleted file mode 100644 index 4aaeab97c..000000000 --- a/coreneuron/io/nrnoptarg.cpp +++ /dev/null @@ -1,368 +0,0 @@ -/* -Copyright (c) 2016, Blue Brain Project -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include -#include -#include -#include "coreneuron/mpi/nrnmpi.h" -#include "coreneuron/io/nrnoptarg.hpp" -#include "coreneuron/utils/ezoption/ezOptionParser.hpp" - -namespace coreneuron { -struct param_int { - const char* names; /* space separated (includes - or --) */ - int dflt, low, high; - const char* usage; -}; - -struct param_dbl { - const char* names; /* space separated (includes - or --) */ - double dflt, low, high; - const char* usage; -}; - -struct param_flag { - const char* names; /* space separated (includes - or --) */ - const char* usage; -}; - -struct param_str { - const char* names; /* space separated (includes - or --) */ - const char* dflt; - const char* usage; -}; - -static param_int param_int_args[] = { - {"--spikebuf -b", 100000, 1, 2000000000, "Spike buffer size. (100000)"}, - {"--spkcompress", 0, 0, 100000, - "Spike compression. Up to ARG are exchanged during MPI_Allgather. (0)"}, - {"--prcellgid -g", -1, -1, 2000000000, "Output prcellstate information for the gid NUMBER."}, - {"--cell-permute -R", 0, 0, 3, - "Cell permutation, 0 No; 1 optimise node adjacency; 2 optimize parent adjacency. (1)"}, - {"--nwarp -W", 0, 0, 1000000, "Number of warps to balance. (0)"}, - {"--ms-subintervals", 2, 1, 2, "Number of multisend subintervals, 1 or 2. (2)"}, - {"--ms-phases", 2, 1, 2, "Number of multisend phases, 1 or 2. (2)"}, - {"--multiple -z", 1, 1, 10000000, - "Model duplication factor. Model size is normal size * (int)."}, - {"--extracon -x", 0, 0, 10000000, - "Number of extra random connections in each thread to other duplicate models (int)."}, - {"--seed -s", -1, 0, 100000000, "Initialization seed for random number generator (int)."}, - {"--report-buffer-size", 4, 1, 128, "Size in MB of the report buffer (int)."}, - {nullptr, 0, 0, 0, nullptr}}; - -static param_dbl param_dbl_args[] = { - {"--tstop -e", 100.0, 0.0, 1e9, "Stop time (ms). (100)"}, - {"--dt -dt", -1000., -1000., 1e9, - "Fixed time step. The default value is set by defaults.dat or is 0.025."}, - {"--dt_io -i", 0.1, 1e-9, 1e9, "Dt of I/O. (0.1)"}, - {"--voltage -v", -65.0, -1e9, 1e9, - "Initial voltage used for nrn_finitialize(1, v_init). If 1000, then nrn_finitialize(0,...). (-65.)"}, - {"--celsius -l", -1000., -1000., 1000., - "Temperature in degC. The default value is set in defaults.dat or else is 34.0."}, - {"--forwardskip -k", 0., 0., 1e9, "Forwardskip to TIME"}, - {"--mindelay", 10., 0., 1e9, - "Maximum integration interval (likely reduced by minimum NetCon delay). (10)"}, - {nullptr, 0., 0., 0., nullptr}}; - -static param_flag param_flag_args[] = { - {"--help -h", - "Print a usage message briefly summarizing these command-line options, then exit."}, - {"--threading -c", "Parallel threads. The default is serial threads."}, - {"--gpu -gpu", "Enable use of GPUs. The default implies cpu only run."}, - {"-mpi", "Enable MPI. In order to initialize MPI environment this argument must be specified."}, - {"--show", "Print args."}, - {"--multisend", "Use Multisend spike exchange instead of Allgather."}, - {"--binqueue", "Use bin queue."}, - {"--skip-mpi-finalize", "Do not call mpi finalize."}, - {nullptr, nullptr}}; - -static param_str param_str_args[] = { - {"--pattern -p", "", "Apply patternstim using the specified spike file."}, - {"--datpath -d", ".", "Path containing CoreNeuron data files. (.)"}, - {"--checkpoint", "", "Enable checkpoint and specify directory to store related files."}, - {"--restore", "", "Restore simulation from provided checkpoint directory."}, - {"--filesdat -f", "files.dat", "Name for the distribution file. (files.dat)"}, - {"--outpath -o", ".", "Path to place output data files. (.)"}, - {"--write-config", "", "Write configuration file filename."}, - {"--read-config", "", "Read configuration file filename."}, - {"--report-conf", "", "reports configuration file"}, - {nullptr, nullptr, nullptr}}; - -static void graceful_exit(int); - -static ez::ezOptionParser* opt; - -int nrnopt_parse(int argc, const char* argv[]) { - opt = new ez::ezOptionParser; - - for (int i = 0; param_int_args[i].names; ++i) { - struct param_int& p = param_int_args[i]; - nrnopt_add_int(p.names, p.usage, p.dflt, p.low, p.high); - } - - for (int i = 0; param_str_args[i].names; ++i) { - struct param_str& p = param_str_args[i]; - nrnopt_add_str(p.names, p.usage, p.dflt); - } - - for (int i = 0; param_dbl_args[i].names; ++i) { - struct param_dbl& p = param_dbl_args[i]; - nrnopt_add_dbl(p.names, p.usage, p.dflt, p.low, p.high); - } - - for (int i = 0; param_flag_args[i].names; ++i) { - struct param_flag& p = param_flag_args[i]; - nrnopt_add_flag(p.names, p.usage); - } - - // note earliest takes precedence. - - // first the command line arguments - opt->parse(argc, argv); - - // then any specific configuration files mentioned in the command line - if (opt->isSet("--read-config")) { - // Import one or more files that use # as comment char. - std::vector > files; - opt->get("--read-config")->getMultiStrings(files); - - for (size_t j = 0; j < files.size(); ++j) { - if (!opt->importFile(files[j][0].c_str(), '#')) { - if (nrnmpi_myid == 0) - std::cerr << "ERROR: Failed to read configuration file " << files[j][0] - << std::endl; - graceful_exit(1); - } - } - } - - std::string usage; - std::vector badOptions; - - if (!opt->gotExpected(badOptions)) { - for (size_t i = 0; i < badOptions.size(); ++i) { - if (nrnmpi_myid) - std::cerr << "ERROR: Got unexpected number of arguments for option " - << badOptions[i] << ".\n\n"; - } - - opt->getUsage(usage); - if (nrnmpi_myid == 0) - std::cout << usage; - graceful_exit(1); - } - - if (opt->isSet("-h")) { - opt->getUsage(usage); - if (nrnmpi_myid == 0) - std::cout << usage; - graceful_exit(0); - } - - if (opt->firstArgs.size() > 1 || opt->lastArgs.size() > 0 || opt->unknownArgs.size() > 0) { - std::cerr << "ERROR: Unknown arguments" - << "\n"; - for (size_t i = 1; i < opt->firstArgs.size(); ++i) { - if (nrnmpi_myid == 0) - std::cerr << " " << opt->firstArgs[i]->c_str() << "\n"; - } - for (size_t i = 0; i < opt->unknownArgs.size(); ++i) { - if (nrnmpi_myid == 0) - std::cerr << " " << opt->unknownArgs[i]->c_str() << "\n"; - } - for (size_t i = 0; i < opt->lastArgs.size(); ++i) { - if (nrnmpi_myid == 0) - std::cerr << " " << opt->lastArgs[i]->c_str() << "\n"; - } - graceful_exit(1); - } - - std::vector badArgs; - if (!opt->gotValid(badOptions, badArgs)) { - for (size_t i = 0; i < badOptions.size(); ++i) { - if (nrnmpi_myid == 0) - std::cerr << "ERROR: Got invalid argument \"" << badArgs[i] << "\" for option " - << badOptions[i] << ".\n"; - } - graceful_exit(1); - } - - if (opt->isSet("--write-config")) { - std::string file; - opt->get("--write-config")->getString(file); - // Exports all options if second param is true; unset options will just use their default - // values. - if (!opt->exportFile(file.c_str(), true)) { - if (nrnmpi_myid == 0) - std::cerr << "ERROR: Failed to write to configuration file " << file.c_str() - << std::endl; - graceful_exit(1); - } - } - - if (opt->isSet("--show")) { - nrnopt_show(); - } - - return 0; -} - -static void nrnopt_add(const char* names, - const char* usage, - const char* dflt, - ez::ezOptionValidator* validator) { - std::vector vnames; - ez::SplitDelim(std::string(names), ' ', vnames); - int expect = dflt ? 1 : 0; - dflt = dflt ? dflt : ""; - if (vnames.size() == 1) { - opt->add(dflt, - 0, // Required? - expect, // Number of args expected - 0, // Delimiter if expecting multiple args - usage, // Help description - vnames[0].c_str(), // flag name - validator); - } else if (vnames.size() == 2) { - opt->add(dflt, 0, expect, 0, usage, vnames[0].c_str(), vnames[1].c_str(), validator); - } -} - -void nrnopt_add_flag(const char* names, const char* usage) { - nrnopt_add(names, usage, nullptr, nullptr); -} - -void nrnopt_add_str(const char* names, const char* usage, const char* dflt) { - nrnopt_add(names, usage, dflt, nullptr); -} - -void nrnopt_add_int(const char* names, const char* usage, int dflt, int low, int high) { - char s1[50], s2[100]; - sprintf(s1, "%d", dflt); - sprintf(s2, "%d,%d", low, high); - ez::ezOptionValidator* v = new ez::ezOptionValidator("s4", "gele", s2); - nrnopt_add(names, usage, s1, v); -} - -void nrnopt_add_dbl(const char* names, const char* usage, double dflt, double low, double high) { - char s1[50], s2[100]; - sprintf(s1, "%.16g", dflt); - sprintf(s2, "%.17g,%.17g", low, high); - ez::ezOptionValidator* v = new ez::ezOptionValidator("f", "gele", s2); - nrnopt_add(names, usage, s1, v); -} - -bool nrnopt_get_flag(const char* name) { - return opt->isSet(name) ? true : false; -} - -std::string nrnopt_get_str(const char* name) { - std::string val; - opt->get(name)->getString(val); - return val; -} - -int nrnopt_get_int(const char* name) { - int val; - opt->get(name)->getInt(val); - return val; -} - -double nrnopt_get_dbl(const char* name) { - double val; - opt->get(name)->getDouble(val); - return val; -} - -void nrnopt_modify_dbl(const char* name, double val) { - ez::OptionGroup* og = opt->get(name); - og->isSet = true; - std::vector*>& args = og->args; - std::vector* v = new std::vector; - char c[50]; - sprintf(c, "%.16g", val); - std::string* s = new std::string(c); - v->push_back(s); - args.insert(args.begin(), v); -} - -bool nrnopt_is_default_value(const char* name) { - return !opt->isSet(name); -} - -// more compact display of parameters. -// ie only first name and first value (or default or isSet). -void nrnopt_show() { - if (nrnmpi_myid) { - return; - } - std::string out; - - int n = opt->groups.size(); - for (int i = 0; i < n; ++i) { - if (i % 3 == 0) { - std::cout << out << std::endl; - out.clear(); - } else { - int n = (i % 3) * 25; - for (int i = out.size(); i < n; ++i) { - out += " "; - } - } - ez::OptionGroup* g = opt->groups[i]; - out += g->flags[0]->c_str(); - out += " = "; - if (g->args.size()) { - std::string s; - g->getString(s); - out += s.c_str(); - } else if (g->defaults.size() > 0) { - out += g->defaults.c_str(); - } else { - out += (g->isSet ? "set" : "not set"); - } - } - if (out.size() > 0) { - std::cout << out << std::endl; - } - std::cout << std::endl; -} - -void nrnopt_delete() { - delete opt; - opt = nullptr; -} - -static void graceful_exit(int err) { -#if !defined(nrnoptargtest) && NRNMPI - // actually, only avoid mpi message when all ranks exit(0) - nrnmpi_finalize(); -#endif - exit(nrnmpi_myid == 0 ? err : 0); -} -} // namespace coreneuron diff --git a/coreneuron/io/nrnoptarg.hpp b/coreneuron/io/nrnoptarg.hpp deleted file mode 100644 index cdb699d3b..000000000 --- a/coreneuron/io/nrnoptarg.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/* -Copyright (c) 2016, Blue Brain Project -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/** - * @file nrnoptarg.h - * @date 26 Oct 2014 - * - * @brief structure storing all command line arguments for coreneuron - * - * Abandon getopt and wrap ezOptionParser to allow options from a config file - * as well on command line (command line takes precedence) - */ - -#ifndef nrnoptarg_h -#define nrnoptarg_h - -#include - -namespace coreneuron { -// before nrnopt_parse() -void nrnopt_add_flag(const char* names, const char* usage); -void nrnopt_add_int(const char* names, const char* usage, int dflt, int low, int high); -void nrnopt_add_dbl(const char* names, const char* usage, double dflt, double low, double high); -void nrnopt_add_str(const char* names, const char* usage, const char* dflt); - -int nrnopt_parse(int argc, const char* argv[]); -void nrnopt_delete(); // when finished getting options, free memory -void nrnopt_show(); - -// after nrnopt_parse() -bool nrnopt_get_flag(const char* name); -int nrnopt_get_int(const char* name); -double nrnopt_get_dbl(const char* name); -std::string nrnopt_get_str(const char* name); -void nrnopt_modify_dbl(const char* name, double value); - -bool nrnopt_is_default_value(const char* name); -} // namespace coreneuron -#endif diff --git a/coreneuron/io/prcellstate.cpp b/coreneuron/io/prcellstate.cpp index 526b6dd01..d507afe86 100644 --- a/coreneuron/io/prcellstate.cpp +++ b/coreneuron/io/prcellstate.cpp @@ -33,7 +33,6 @@ THE POSSIBILITY OF SUCH DAMAGE. #include "coreneuron/sim/multicore.hpp" #include "coreneuron/io/nrn_setup.hpp" #include "coreneuron/network/netcon.hpp" -#include "coreneuron/utils/sdprintf.h" #include "coreneuron/nrniv/nrniv_decl.h" #include "coreneuron/utils/nrn_assert.h" #include "coreneuron/coreneuron.hpp" @@ -281,8 +280,8 @@ int prcellstate(int gid, const char* suffix) { if (ps.output_index_ == gid) { // found it so create a _.corenrn file char buf[200]; - sd_ptr filename = sdprintf(buf, sizeof(buf), "%d_%s.corenrn", gid, suffix); - FILE* f = fopen(filename, "w"); + std::string filename = std::to_string(gid) + "_" + suffix + ".corenrn"; + FILE* f = fopen(filename.c_str(), "w"); assert(f); fprintf(f, "gid = %d\n", gid); fprintf(f, "t = %.*g\n", precision, nt._t); diff --git a/coreneuron/mpi/nrnmpi.cpp b/coreneuron/mpi/nrnmpi.cpp index f8583048f..2436e7909 100644 --- a/coreneuron/mpi/nrnmpi.cpp +++ b/coreneuron/mpi/nrnmpi.cpp @@ -78,7 +78,7 @@ void nrnmpi_init(int nrnmpi_under_nrncontrol, int* pargc, char*** pargv) { b = true; break; } - if (strcmp("-mpi", (*pargv)[i]) == 0) { + if (strcmp("--mpi", (*pargv)[i]) == 0) { b = true; break; } diff --git a/coreneuron/nrniv/nrniv_decl.h b/coreneuron/nrniv/nrniv_decl.h index bcce293f2..4490dfb67 100644 --- a/coreneuron/nrniv/nrniv_decl.h +++ b/coreneuron/nrniv/nrniv_decl.h @@ -33,7 +33,6 @@ THE POSSIBILITY OF SUCH DAMAGE. #include #include "coreneuron/network/netcon.hpp" #include "coreneuron/utils/endianness.hpp" -#include "coreneuron/io/nrnoptarg.hpp" namespace coreneuron { /// Mechanism type to be used from stdindex2ptr and nrn_dblpntr2nrncore (in Neuron) @@ -60,7 +59,10 @@ extern void nrn_p_construct(void); extern void nrn_setup(const char* filesdat, bool is_mapping_needed, int byte_swap, - bool run_setup_cleanup = true); + bool run_setup_cleanup = true, + const char* datapath = "", + const char* restore_path = "", + double* mindelay = nullptr); extern double* stdindex2ptr(int mtype, int index, NrnThread&); extern void delete_trajectory_requests(NrnThread&); extern int nrn_setup_multiple; diff --git a/coreneuron/sim/fadvance_core.cpp b/coreneuron/sim/fadvance_core.cpp index 59fd9ba44..3fdb28eb0 100644 --- a/coreneuron/sim/fadvance_core.cpp +++ b/coreneuron/sim/fadvance_core.cpp @@ -159,7 +159,6 @@ void update(NrnThread* _nt) { double* vec_v = &(VEC_V(0)); double* vec_rhs = &(VEC_RHS(0)); int i2 = _nt->end; - #if defined(_OPENACC) int stream_id = _nt->stream_id; #endif diff --git a/coreneuron/utils/ezoption/ezOptionParser.hpp b/coreneuron/utils/ezoption/ezOptionParser.hpp deleted file mode 100755 index 03697ed42..000000000 --- a/coreneuron/utils/ezoption/ezOptionParser.hpp +++ /dev/null @@ -1,2178 +0,0 @@ -/*A portion of http://sourceforge.net/projects/ezoptionparser */ -/* - -Copyright (C) 2011,2012 Remik Ziemlinski - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -/* -This file is part of ezOptionParser. See MIT-LICENSE. - -Copyright (C) 2011,2012,2014 Remik Ziemlinski - -CHANGELOG - -v0.0.0 20110505 rsz Created. -v0.1.0 20111006 rsz Added validator. -v0.1.1 20111012 rsz Fixed validation of ulonglong. -v0.1.2 20111126 rsz Allow flag names start with alphanumeric (previously, flag had to start with alpha). -v0.1.3 20120108 rsz Created work-around for unique id generation with IDGenerator that avoids retarded c++ translation unit linker errors with single-header static variables. Forced inline on all methods to please retard compiler and avoid multiple def errors. -v0.1.4 20120629 Enforced MIT license on all files. -v0.2.0 20121120 Added parseIndex to OptionGroup. -v0.2.1 20130506 Allow disabling doublespace of OPTIONS usage descriptions. -v0.2.2 20140504 Jose Santiago added compiler warning fixes. - Bruce Shankle added a crash fix in description printing. -*/ -#ifndef EZ_OPTION_PARSER_H -#define EZ_OPTION_PARSER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace ez { -#define DEBUGLINE() printf("%s:%d\n", __FILE__, __LINE__); - -/* ################################################################### */ -template -static T fromString(const std::string* s) { - std::istringstream stream (s->c_str()); - T t; - stream >> t; - return t; -}; -template -static T fromString(const char* s) { - std::istringstream stream (s); - T t; - stream >> t; - return t; -}; -/* ################################################################### */ -static inline bool isdigit(const std::string & s, int i=0) { - int n = s.length(); - for(; i < n; ++i) - switch(s[i]) { - case '0': case '1': case '2': - case '3': case '4': case '5': - case '6': case '7': case '8': case '9': break; - default: return false; - } - - return true; -}; -/* ################################################################### */ -static bool isdigit(const std::string * s, int i=0) { - int n = s->length(); - for(; i < n; ++i) - switch(s->at(i)) { - case '0': case '1': case '2': - case '3': case '4': case '5': - case '6': case '7': case '8': case '9': break; - default: return false; - } - - return true; -}; -/* ################################################################### */ -/* -Compare strings for opts, so short opt flags come before long format flags. -For example, -d < --dimension < --dmn, and also lower come before upper. The default STL std::string compare doesn't do that. -*/ -static bool CmpOptStringPtr(std::string * s1, std::string * s2) { - int c1,c2; - const char *s=s1->c_str(); - for(c1=0; c1 < (long int)s1->size(); ++c1) - if (isalnum(s[c1])) // locale sensitive. - break; - - s=s2->c_str(); - for(c2=0; c2 < (long int)s2->size(); ++c2) - if (isalnum(s[c2])) - break; - - // Test which has more symbols before its name. - if (c1 > c2) - return false; - else if (c1 < c2) - return true; - - // Both have same number of symbols, so compare first letter. - char char1 = s1->at(c1); - char char2 = s2->at(c2); - char lo1 = tolower(char1); - char lo2 = tolower(char2); - - if (lo1 != lo2) - return lo1 < lo2; - - // Their case doesn't match, so find which is lower. - char up1 = isupper(char1); - char up2 = isupper(char2); - - if (up1 && !up2) - return false; - else if (!up1 && up2) - return true; - - return (s1->compare(*s2)<0); -}; -/* ################################################################### */ -/* -Makes a vector of strings from one string, -splitting at (and excluding) delimiter "token". -*/ -static void SplitDelim( const std::string& s, const char token, std::vector * result) { - std::string::const_iterator i = s.begin(); - std::string::const_iterator j = s.begin(); - const std::string::const_iterator e = s.end(); - - while(i!=e) { - while(i!=e && *i++!=token); - std::string *newstr = new std::string(j, i); - if (newstr->at(newstr->size()-1) == token) newstr->erase(newstr->size()-1); - result->push_back(newstr); - j = i; - } -}; -/* ################################################################### */ -// Variant that uses deep copies and references instead of pointers (less efficient). -static void SplitDelim( const std::string& s, const char token, std::vector & result) { - std::string::const_iterator i = s.begin(); - std::string::const_iterator j = s.begin(); - const std::string::const_iterator e = s.end(); - - while(i!=e) { - while(i!=e && *i++!=token); - std::string newstr(j, i); - if (newstr.at(newstr.size()-1) == token) newstr.erase(newstr.size()-1); - result.push_back(newstr); - j = i; - } -}; -/* ################################################################### */ -// Variant that uses list instead of vector for efficient insertion, etc. -static void SplitDelim( const std::string& s, const char token, std::list & result) { - std::string::const_iterator i = s.begin(); - std::string::const_iterator j = s.begin(); - const std::string::const_iterator e = s.end(); - - while(i!=e) { - while(i!=e && *i++!=token); - std::string *newstr = new std::string(j, i); - if (newstr->at(newstr->size()-1) == token) newstr->erase(newstr->size()-1); - result.push_back(newstr); - j = i; - } -}; -/* ################################################################### */ -static void ToU1(std::string ** strings, unsigned char * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = (unsigned char)atoi(strings[i]->c_str()); - } -}; -/* ################################################################### */ -static void ToS1(std::string ** strings, char * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = (char)atoi(strings[i]->c_str()); - } -}; -/* ################################################################### */ -static void ToU2(std::string ** strings, unsigned short * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = (unsigned short)atoi(strings[i]->c_str()); - } -}; -/* ################################################################### */ -static void ToS2(std::string ** strings, short * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = (short)atoi(strings[i]->c_str()); - } -}; -/* ################################################################### */ -static void ToS4(std::string ** strings, int * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = atoi(strings[i]->c_str()); - } -}; -/* ################################################################### */ -static void ToU4(std::string ** strings, unsigned int * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = (unsigned int)strtoul(strings[i]->c_str(), nullptr, 0); - } -}; -/* ################################################################### */ -static void ToS8(std::string ** strings, long long * out, int n) { - for(int i=0; i < n; ++i) { - std::stringstream ss(strings[i]->c_str()); - ss >> out[i]; - } -}; -/* ################################################################### */ -static void ToU8(std::string ** strings, unsigned long long * out, int n) { - for(int i=0; i < n; ++i) { - std::stringstream ss(strings[i]->c_str()); - ss >> out[i]; - } -}; -/* ################################################################### */ -static void ToF(std::string ** strings, float * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = (float)atof(strings[i]->c_str()); - } -}; -/* ################################################################### */ -static void ToD(std::string ** strings, double * out, int n) { - for(int i=0; i < n; ++i) { - out[i] = (double)atof(strings[i]->c_str()); - } -}; -/* ################################################################### */ -static void StringsToInts(std::vector & strings, std::vector & out) { - for(int i=0; i < (long int)strings.size(); ++i) { - out.push_back(atoi(strings[i].c_str())); - } -}; -/* ################################################################### */ -static void StringsToInts(std::vector * strings, std::vector * out) { - for(int i=0; i < (long int)strings->size(); ++i) { - out->push_back(atoi(strings->at(i)->c_str())); - } -}; -/* ################################################################### */ -static void StringsToLongs(std::vector & strings, std::vector & out) { - for(int i=0; i < (long int)strings.size(); ++i) { - out.push_back(atol(strings[i].c_str())); - } -}; -/* ################################################################### */ -static void StringsToLongs(std::vector * strings, std::vector * out) { - for(int i=0; i < (long int)strings->size(); ++i) { - out->push_back(atol(strings->at(i)->c_str())); - } -}; -/* ################################################################### */ -static void StringsToULongs(std::vector & strings, std::vector & out) { - for(int i=0; i < (long int)strings.size(); ++i) { - out.push_back(strtoul(strings[i].c_str(),0,0)); - } -}; -/* ################################################################### */ -static void StringsToULongs(std::vector * strings, std::vector * out) { - for(int i=0; i < (long int)strings->size(); ++i) { - out->push_back(strtoul(strings->at(i)->c_str(),0,0)); - } -}; -/* ################################################################### */ -static void StringsToFloats(std::vector & strings, std::vector & out) { - for(int i=0; i < (long int)strings.size(); ++i) { - out.push_back(atof(strings[i].c_str())); - } -}; -/* ################################################################### */ -static void StringsToFloats(std::vector * strings, std::vector * out) { - for(int i=0; i < (long int)strings->size(); ++i) { - out->push_back(atof(strings->at(i)->c_str())); - } -}; -/* ################################################################### */ -static void StringsToDoubles(std::vector & strings, std::vector & out) { - for(int i=0; i < (long int)strings.size(); ++i) { - out.push_back(atof(strings[i].c_str())); - } -}; -/* ################################################################### */ -static void StringsToDoubles(std::vector * strings, std::vector * out) { - for(int i=0; i < (long int)strings->size(); ++i) { - out->push_back(atof(strings->at(i)->c_str())); - } -}; -/* ################################################################### */ -static void StringsToStrings(std::vector * strings, std::vector * out) { - for(int i=0; i < (long int)strings->size(); ++i) { - out->push_back( *strings->at(i) ); - } -}; -/* ################################################################### */ -static void ToLowerASCII(std::string & s) { - int n = s.size(); - int i=0; - char c; - for(; i < n; ++i) { - c = s[i]; - if(c<='Z' && c>='A') - s[i] = c+32; - } -} -/* ################################################################### */ -static char** CommandLineToArgvA(char* CmdLine, int* _argc) { - char** argv; - char* _argv; - unsigned long len; - unsigned long argc; - char a; - unsigned long i, j; - - bool in_QM; - bool in_TEXT; - bool in_SPACE; - - len = strlen(CmdLine); - i = ((len+2)/2)*sizeof(void*) + sizeof(void*); - - argv = (char**)malloc(i + (len+2)*sizeof(char)); - - _argv = (char*)(((unsigned char*)argv)+i); - - argc = 0; - argv[argc] = _argv; - in_QM = false; - in_TEXT = false; - in_SPACE = true; - i = 0; - j = 0; - - while( (a = CmdLine[i]) ) { - if(in_QM) { - if( (a == '\"') || - (a == '\'')) // rsz. Added single quote. - { - in_QM = false; - } else { - _argv[j] = a; - j++; - } - } else { - switch(a) { - case '\"': - case '\'': // rsz. Added single quote. - in_QM = true; - in_TEXT = true; - if(in_SPACE) { - argv[argc] = _argv+j; - argc++; - } - in_SPACE = false; - break; - case ' ': - case '\t': - case '\n': - case '\r': - if(in_TEXT) { - _argv[j] = '\0'; - j++; - } - in_TEXT = false; - in_SPACE = true; - break; - default: - in_TEXT = true; - if(in_SPACE) { - argv[argc] = _argv+j; - argc++; - } - _argv[j] = a; - j++; - in_SPACE = false; - break; - } - } - i++; - } - _argv[j] = '\0'; - argv[argc] = nullptr; - - (*_argc) = argc; - return argv; -}; -/* ################################################################### */ -// Create unique ids with static and still allow single header that avoids multiple definitions linker error. -class ezOptionParserIDGenerator { -public: - static ezOptionParserIDGenerator& instance () { static ezOptionParserIDGenerator Generator; return Generator; } - short next () { return ++_id; } -private: - ezOptionParserIDGenerator() : _id(-1) {} - short _id; -}; -/* ################################################################### */ -/* Validate a value by checking: -- if as string, see if converted value is within datatype's limits, -- and see if falls within a desired range, -- or see if within set of given list of values. - -If comparing with a range, the values list must contain one or two values. One value is required when comparing with <, <=, >, >=. Use two values when requiring a test such as list[0] */ - GE, /* value >= list[0] */ - GTLT, /* list[0] < value < list[1] */ - GELT, /* list[0] <= value < list[1] */ - GELE, /* list[0] <= value <= list[1] */ - GTLE, /* list[0] < value <= list[1] */ - IN /* if value is in list */ - }; - - enum TYPE { NOTYPE=0, S1, U1, S2, U2, S4, U4, S8, U8, F, D, T }; - enum TYPE2 { NOTYPE2=0, INT8, UINT8, INT16, UINT16, INT32, UINT32, INT64, UINT64, FLOAT, DOUBLE, TEXT }; - - union { - unsigned char *u1; - char *s1; - unsigned short *u2; - short *s2; - unsigned int *u4; - int *s4; - unsigned long long *u8; - long long *s8; - float *f; - double *d; - std::string** t; - }; - - char op; - bool quiet; - short id; - char type; - int size; - bool insensitive; -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::~ezOptionValidator() { - reset(); -}; -/* ------------------------------------------------------------------- */ -void ezOptionValidator::reset() { - #define CLEAR(TYPE,P) case TYPE: if (P) delete [] P; P = 0; break; - switch(type) { - CLEAR(S1,s1); - CLEAR(U1,u1); - CLEAR(S2,s2); - CLEAR(U2,u2); - CLEAR(S4,s4); - CLEAR(U4,u4); - CLEAR(S8,s8); - CLEAR(U8,u8); - CLEAR(F,f); - CLEAR(D,d); - case T: - for(int i=0; i < size; ++i) - delete t[i]; - - delete [] t; - t = 0; - break; - default: break; - } - - size = 0; - op = NOOP; - type = NOTYPE; -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type) : s1(0), op(0), quiet(0), type(_type), size(0), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const char* list, int _size) : s1(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - s1 = new char[size]; - memcpy(s1, list, size); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned char* list, int _size) : u1(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - u1 = new unsigned char[size]; - memcpy(u1, list, size); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const short* list, int _size) : s2(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - s2 = new short[size]; - memcpy(s2, list, size*sizeof(short)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned short* list, int _size) : u2(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - u2 = new unsigned short[size]; - memcpy(u2, list, size*sizeof(unsigned short)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const int* list, int _size) : s4(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - s4 = new int[size]; - memcpy(s4, list, size*sizeof(int)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned int* list, int _size) : u4(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - u4 = new unsigned int[size]; - memcpy(u4, list, size*sizeof(unsigned int)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const long long* list, int _size) : s8(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - s8 = new long long[size]; - memcpy(s8, list, size*sizeof(long long)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const unsigned long long* list, int _size) : u8(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - u8 = new unsigned long long[size]; - memcpy(u8, list, size*sizeof(unsigned long long)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const float* list, int _size) : f(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - f = new float[size]; - memcpy(f, list, size*sizeof(float)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const double* list, int _size) : d(0), op(_op), quiet(0), type(_type), size(_size), insensitive(0) { - id = ezOptionParserIDGenerator::instance().next(); - d = new double[size]; - memcpy(d, list, size*sizeof(double)); -}; -/* ------------------------------------------------------------------- */ -ezOptionValidator::ezOptionValidator(char _type, char _op, const char** list, int _size, bool _insensitive) : t(0), op(_op), quiet(0), type(_type), size(_size), insensitive(_insensitive) { - id = ezOptionParserIDGenerator::instance().next(); - t = new std::string*[size]; - int i=0; - - for(; i < size; ++i) { - t[i] = new std::string(list[i]); - } -}; -/* ------------------------------------------------------------------- */ -/* Less efficient but convenient ctor that parses strings to setup validator. -_type: s1, u1, s2, u2, ..., f, d, t -_op: lt, gt, ..., in -_list: comma-delimited string -*/ -ezOptionValidator::ezOptionValidator(const char* _type, const char* _op, const char* _list, bool _insensitive) : t(0), quiet(0), type(0), size(0), insensitive(_insensitive) { - id = ezOptionParserIDGenerator::instance().next(); - - switch(_type[0]) { - case 'u': - switch(_type[1]) { - case '1': type = U1; break; - case '2': type = U2; break; - case '4': type = U4; break; - case '8': type = U8; break; - default: break; - } - break; - case 's': - switch(_type[1]) { - case '1': type = S1; - break; - case '2': type = S2; break; - case '4': type = S4; break; - case '8': type = S8; break; - default: break; - } - break; - case 'f': type = F; break; - case 'd': type = D; break; - case 't': type = T; break; - default: - if (!quiet) - std::cerr << "ERROR: Unknown validator datatype \"" << _type << "\".\n"; - break; - } - - int nop = 0; - if (_op != 0) - nop = strlen(_op); - - switch(nop) { - case 0: op = NOOP; break; - case 2: - switch(_op[0]) { - case 'g': - switch(_op[1]) { - case 'e': op = GE; break; - default: op = GT; break; - } - break; - case 'i': op = IN; - break; - default: - switch(_op[1]) { - case 'e': op = LE; break; - default: op = LT; break; - } - break; - } - break; - case 4: - switch(_op[1]) { - case 'e': - switch(_op[3]) { - case 'e': op = GELE; break; - default: op = GELT; break; - } - break; - default: - switch(_op[3]) { - case 'e': op = GTLE; break; - default: op = GTLT; break; - } - break; - } - break; - default: - if (!quiet) - std::cerr << "ERROR: Unknown validator operation \"" << _op << "\".\n"; - break; - } - - if (_list == 0) return; - // Create list of strings and then cast to native datatypes. - std::string unsplit(_list); - std::list split; - std::list::iterator it; - SplitDelim(unsplit, ',', split); - size = split.size(); - std::string **strings = new std::string*[size]; - - int i = 0; - for(it = split.begin(); it != split.end(); ++it) - strings[i++] = *it; - - if (insensitive) - for(i=0; i < size; ++i) - ToLowerASCII(*strings[i]); - - #define FreeStrings() { \ - for(i=0; i < size; ++i)\ - delete strings[i];\ - delete [] strings;\ - } - - #define ToArray(T,P,Y) case T: P = new Y[size]; To##T(strings, P, size); FreeStrings(); break; - switch(type) { - ToArray(S1,s1,char); - ToArray(U1,u1,unsigned char); - ToArray(S2,s2,short); - ToArray(U2,u2,unsigned short); - ToArray(S4,s4,int); - ToArray(U4,u4,unsigned int); - ToArray(S8,s8,long long); - ToArray(U8,u8,unsigned long long); - ToArray(F,f,float); - ToArray(D,d,double); - case T: t = strings; break; /* Don't erase strings array. */ - default: break; - } -}; -/* ------------------------------------------------------------------- */ -void ezOptionValidator::print() { - printf("id=%d, op=%d, type=%d, size=%d, insensitive=%d\n", id, op, type, size, insensitive); -}; -/* ------------------------------------------------------------------- */ -bool ezOptionValidator::isValid(const std::string * valueAsString) { - if (valueAsString == 0) return false; - -#define CHECKRANGE(E,T) {\ - std::stringstream ss(valueAsString->c_str()); \ - long long E##value; \ - ss >> E##value; \ - long long E##min = static_cast(std::numeric_limits::min()); \ - if (E##value < E##min) { \ - if (!quiet) \ - std::cerr << "ERROR: Invalid value " << E##value << " is less than datatype min " << E##min << ".\n"; \ - return false; \ - } \ - \ - long long E##max = static_cast(std::numeric_limits::max()); \ - if (E##value > E##max) { \ - if (!quiet) \ - std::cerr << "ERROR: Invalid value " << E##value << " is greater than datatype max " << E##max << ".\n"; \ - return false; \ - } \ -} - // Check if within datatype limits. - if (type != T) { - switch(type) { - case S1: CHECKRANGE(S1,char); break; - case U1: CHECKRANGE(U1,unsigned char); break; - case S2: CHECKRANGE(S2,short); break; - case U2: CHECKRANGE(U2,unsigned short); break; - case S4: CHECKRANGE(S4,int); break; - case U4: CHECKRANGE(U4,unsigned int); break; - case S8: { - if ( (valueAsString->at(0) == '-') && - isdigit(valueAsString,1) && - (valueAsString->size() > 19) && - (valueAsString->compare(1, 19, "9223372036854775808") > 0) ) { - if (!quiet) - std::cerr << "ERROR: Invalid value " << *valueAsString << " is less than datatype min -9223372036854775808.\n"; - return false; - } - - if (isdigit(valueAsString) && - (valueAsString->size() > 18) && - valueAsString->compare("9223372036854775807") > 0) { - if (!quiet) - std::cerr << "ERROR: Invalid value " << *valueAsString << " is greater than datatype max 9223372036854775807.\n"; - return false; - } - } break; - case U8: { - if (valueAsString->compare("0") < 0) { - if (!quiet) - std::cerr << "ERROR: Invalid value " << *valueAsString << " is less than datatype min 0.\n"; - return false; - } - - if (isdigit(valueAsString) && - (valueAsString->size() > 19) && - valueAsString->compare("18446744073709551615") > 0) { - if (!quiet) - std::cerr << "ERROR: Invalid value " << *valueAsString << " is greater than datatype max 18446744073709551615.\n"; - return false; - } - } break; - case F: { - double dmax = static_cast(std::numeric_limits::max()); - double dvalue = atof(valueAsString->c_str()); - double dmin = -dmax; - if (dvalue < dmin) { - if (!quiet) { - fprintf(stderr, "ERROR: Invalid value %g is less than datatype min %g.\n", dvalue, dmin); - } - return false; - } - - if (dvalue > dmax) { - if (!quiet) - std::cerr << "ERROR: Invalid value " << dvalue << " is greater than datatype max " << dmax << ".\n"; - return false; - } - } break; - case D: { - long double ldmax = static_cast(std::numeric_limits::max()); - std::stringstream ss(valueAsString->c_str()); - long double ldvalue; - ss >> ldvalue; - long double ldmin = -ldmax; - - if (ldvalue < ldmin) { - if (!quiet) - std::cerr << "ERROR: Invalid value " << ldvalue << " is less than datatype min " << ldmin << ".\n"; - return false; - } - - if (ldvalue > ldmax) { - if (!quiet) - std::cerr << "ERROR: Invalid value " << ldvalue << " is greater than datatype max " << ldmax << ".\n"; - return false; - } - } break; - case NOTYPE: default: break; - } - } else { - if (op == IN) { - int i=0; - if (insensitive) { - std::string valueAsStringLower(*valueAsString); - ToLowerASCII(valueAsStringLower); - for(; i < size; ++i) { - if (valueAsStringLower.compare(t[i]->c_str()) == 0) - return true; - } - } else { - for(; i < size; ++i) { - if (valueAsString->compare(t[i]->c_str()) == 0) - return true; - } - } - return false; - } - } - - // Only check datatype limits, and return; - if (op == NOOP) return true; - -#define VALIDATE(T, U, LIST) { \ - /* Value string converted to true native type. */ \ - std::stringstream ss(valueAsString->c_str());\ - U v;\ - ss >> v;\ - /* Check if within list. */ \ - if (op == IN) { \ - T * last = LIST + size;\ - return (last != std::find(LIST, last, v)); \ - } \ - \ - /* Check if within user's custom range. */ \ - T v0, v1; \ - if (size > 0) { \ - v0 = LIST[0]; \ - } \ - \ - if (size > 1) { \ - v1 = LIST[1]; \ - } \ - \ - switch (op) {\ - case LT:\ - if (size > 0) {\ - return v < v0;\ - } else {\ - std::cerr << "ERROR: No value given to validate if " << v << " < X.\n";\ - return false;\ - }\ - break;\ - case LE:\ - if (size > 0) {\ - return v <= v0;\ - } else {\ - std::cerr << "ERROR: No value given to validate if " << v << " <= X.\n";\ - return false;\ - }\ - break;\ - case GT:\ - if (size > 0) {\ - return v > v0;\ - } else {\ - std::cerr << "ERROR: No value given to validate if " << v << " > X.\n";\ - return false;\ - }\ - break;\ - case GE:\ - if (size > 0) {\ - return v >= v0;\ - } else {\ - std::cerr << "ERROR: No value given to validate if " << v << " >= X.\n";\ - return false;\ - }\ - break;\ - case GTLT:\ - if (size > 1) {\ - return (v0 < v) && (v < v1);\ - } else {\ - std::cerr << "ERROR: Missing values to validate if X1 < " << v << " < X2.\n";\ - return false;\ - }\ - break;\ - case GELT:\ - if (size > 1) {\ - return (v0 <= v) && (v < v1);\ - } else {\ - std::cerr << "ERROR: Missing values to validate if X1 <= " << v << " < X2.\n";\ - return false;\ - }\ - break;\ - case GELE:\ - if (size > 1) {\ - return (v0 <= v) && (v <= v1);\ - } else {\ - std::cerr << "ERROR: Missing values to validate if X1 <= " << v << " <= X2.\n";\ - return false;\ - }\ - break;\ - case GTLE:\ - if (size > 1) {\ - return (v0 < v) && (v <= v1);\ - } else {\ - std::cerr << "ERROR: Missing values to validate if X1 < " << v << " <= X2.\n";\ - return false;\ - }\ - break;\ - case NOOP: case IN: default: break;\ - } \ - } - - switch(type) { - case U1: VALIDATE(unsigned char, int, u1); break; - case S1: VALIDATE(char, int, s1); break; - case U2: VALIDATE(unsigned short, int, u2); break; - case S2: VALIDATE(short, int, s2); break; - case U4: VALIDATE(unsigned int, unsigned int, u4); break; - case S4: VALIDATE(int, int, s4); break; - case U8: VALIDATE(unsigned long long, unsigned long long, u8); break; - case S8: VALIDATE(long long, long long, s8); break; - case F: VALIDATE(float, float, f); break; - case D: VALIDATE(double, double, d); break; - default: break; - } - - return true; -}; -/* ################################################################### */ -class OptionGroup { -public: - OptionGroup() : delim(0), expectArgs(0), isRequired(false), isSet(false) { } - - ~OptionGroup() { - int i; - for(i=0; i < (long int)flags.size(); ++i) - delete flags[i]; - - flags.clear(); - parseIndex.clear(); - clearArgs(); - }; - - inline void clearArgs(); - inline void getInt(int&); - inline void getLong(long&); - inline void getLongLong(long long&); - inline void getULong(unsigned long&); - inline void getULongLong(unsigned long long&); - inline void getFloat(float&); - inline void getDouble(double&); - inline void getString(std::string&); - inline void getInts(std::vector&); - inline void getLongs(std::vector&); - inline void getULongs(std::vector&); - inline void getFloats(std::vector&); - inline void getDoubles(std::vector&); - inline void getStrings(std::vector&); - inline void getMultiInts(std::vector< std::vector >&); - inline void getMultiLongs(std::vector< std::vector >&); - inline void getMultiULongs(std::vector< std::vector >&); - inline void getMultiFloats(std::vector< std::vector >&); - inline void getMultiDoubles(std::vector< std::vector >&); - inline void getMultiStrings(std::vector< std::vector >&); - - // defaults value regardless of being set by user. - std::string defaults; - // If expects arguments, this will delimit arg list. - char delim; - // If not 0, then number of delimited args. -1 for arbitrary number. - int expectArgs; - // Descriptive help message shown in usage instructions for option. - std::string help; - // 0 or 1. - bool isRequired; - // A list of flags that denote this option, i.e. -d, --dimension. - std::vector< std::string* > flags; - // If was set (or found). - bool isSet; - // Lists of arguments, per flag instance, after splitting by delimiter. - std::vector< std::vector< std::string* > * > args; - // Index where each group was parsed from input stream to track order. - std::vector parseIndex; -}; -/* ################################################################### */ -void OptionGroup::clearArgs() { - int i,j; - for(i=0; i < (long int)args.size(); ++i) { - for(j=0; j < (long int)args[i]->size(); ++j) - delete args[i]->at(j); - - delete args[i]; - } - - args.clear(); - isSet = false; -}; -/* ################################################################### */ -void OptionGroup::getInt(int & out) { - if (!isSet) { - if (defaults.empty()) - out = 0; - else - out = atoi(defaults.c_str()); - } else { - if (args.empty() || args[0]->empty()) - out = 0; - else { - out = atoi(args[0]->at(0)->c_str()); - } - } -}; -/* ################################################################### */ -void OptionGroup::getLong(long & out) { - if (!isSet) { - if (defaults.empty()) - out = 0; - else - out = atoi(defaults.c_str()); - } else { - if (args.empty() || args[0]->empty()) - out = 0; - else { - out = atol(args[0]->at(0)->c_str()); - } - } -}; -/* ################################################################### */ -void OptionGroup::getLongLong(long long & out) { - if (!isSet) { - if (defaults.empty()) - out = 0; - else { - std::stringstream ss(defaults.c_str()); - ss >> out; - } - } else { - if (args.empty() || args[0]->empty()) - out = 0; - else { - std::stringstream ss(args[0]->at(0)->c_str()); - ss >> out; - } - } -}; -/* ################################################################### */ -void OptionGroup::getULong(unsigned long & out) { - if (!isSet) { - if (defaults.empty()) - out = 0; - else - out = atoi(defaults.c_str()); - } else { - if (args.empty() || args[0]->empty()) - out = 0; - else { - out = strtoul(args[0]->at(0)->c_str(),0,0); - } - } -}; -/* ################################################################### */ -void OptionGroup::getULongLong(unsigned long long & out) { - if (!isSet) { - if (defaults.empty()) - out = 0; - else { - std::stringstream ss(defaults.c_str()); - ss >> out; - } - } else { - if (args.empty() || args[0]->empty()) - out = 0; - else { - std::stringstream ss(args[0]->at(0)->c_str()); - ss >> out; - } - } -}; -/* ################################################################### */ -void OptionGroup::getFloat(float & out) { - if (!isSet) { - if (defaults.empty()) - out = 0.0; - else - out = (float)atof(defaults.c_str()); - } else { - if (args.empty() || args[0]->empty()) - out = 0.0; - else { - out = (float)atof(args[0]->at(0)->c_str()); - } - } -}; -/* ################################################################### */ -void OptionGroup::getDouble(double & out) { - if (!isSet) { - if (defaults.empty()) - out = 0.0; - else - out = atof(defaults.c_str()); - } else { - if (args.empty() || args[0]->empty()) - out = 0.0; - else { - out = atof(args[0]->at(0)->c_str()); - } - } -}; -/* ################################################################### */ -void OptionGroup::getString(std::string & out) { - if (!isSet) { - out = defaults; - } else { - if (args.empty() || args[0]->empty()) - out = ""; - else { - out = *args[0]->at(0); - } - } -}; -/* ################################################################### */ -void OptionGroup::getInts(std::vector & out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - StringsToInts(strings, out); - } - } else { - if (!(args.empty() || args[0]->empty())) - StringsToInts(args[0], &out); - } -}; -/* ################################################################### */ -void OptionGroup::getLongs(std::vector & out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - StringsToLongs(strings, out); - } - } else { - if (!(args.empty() || args[0]->empty())) - StringsToLongs(args[0], &out); - } -}; -/* ################################################################### */ -void OptionGroup::getULongs(std::vector & out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - StringsToULongs(strings, out); - } - } else { - if (!(args.empty() || args[0]->empty())) - StringsToULongs(args[0], &out); - } -}; -/* ################################################################### */ -void OptionGroup::getFloats(std::vector & out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - StringsToFloats(strings, out); - } - } else { - if (!(args.empty() || args[0]->empty())) - StringsToFloats(args[0], &out); - } -}; -/* ################################################################### */ -void OptionGroup::getDoubles(std::vector & out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - StringsToDoubles(strings, out); - } - } else { - if (!(args.empty() || args[0]->empty())) - StringsToDoubles(args[0], &out); - } -}; -/* ################################################################### */ -void OptionGroup::getStrings(std::vector& out) { - if (!isSet) { - if (!defaults.empty()) { - SplitDelim(defaults, delim, out); - } - } else { - if (!(args.empty() || args[0]->empty())) - StringsToStrings(args[0], &out); - } -}; -/* ################################################################### */ -void OptionGroup::getMultiInts(std::vector< std::vector >& out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - if (out.size() < 1) out.resize(1); - StringsToInts(strings, out[0]); - } - } else { - if (!args.empty()) { - int n = args.size(); - if ((long int)out.size() < n) out.resize(n); - for(int i=0; i < n; ++i) { - StringsToInts(args[i], &out[i]); - } - } - } -}; -/* ################################################################### */ -void OptionGroup::getMultiLongs(std::vector< std::vector >& out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - if (out.size() < 1) out.resize(1); - StringsToLongs(strings, out[0]); - } - } else { - if (!args.empty()) { - int n = args.size(); - if ((long int)out.size() < n) out.resize(n); - for(int i=0; i < n; ++i) { - StringsToLongs(args[i], &out[i]); - } - } - } -}; -/* ################################################################### */ -void OptionGroup::getMultiULongs(std::vector< std::vector >& out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - if (out.size() < 1) out.resize(1); - StringsToULongs(strings, out[0]); - } - } else { - if (!args.empty()) { - int n = args.size(); - if ((long int)out.size() < n) out.resize(n); - for(int i=0; i < n; ++i) { - StringsToULongs(args[i], &out[i]); - } - } - } -}; -/* ################################################################### */ -void OptionGroup::getMultiFloats(std::vector< std::vector >& out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - if (out.size() < 1) out.resize(1); - StringsToFloats(strings, out[0]); - } - } else { - if (!args.empty()) { - int n = args.size(); - if ((long int)out.size() < n) out.resize(n); - for(int i=0; i < n; ++i) { - StringsToFloats(args[i], &out[i]); - } - } - } -}; -/* ################################################################### */ -void OptionGroup::getMultiDoubles(std::vector< std::vector >& out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - if (out.size() < 1) out.resize(1); - StringsToDoubles(strings, out[0]); - } - } else { - if (!args.empty()) { - int n = args.size(); - if ((long int)out.size() < n) out.resize(n); - for(int i=0; i < n; ++i) { - StringsToDoubles(args[i], &out[i]); - } - } - } -}; -/* ################################################################### */ -void OptionGroup::getMultiStrings(std::vector< std::vector >& out) { - if (!isSet) { - if (!defaults.empty()) { - std::vector< std::string > strings; - SplitDelim(defaults, delim, strings); - if (out.size() < 1) out.resize(1); - out[0] = strings; - } - } else { - if (!args.empty()) { - int n = args.size(); - if ((long int)out.size() < n) out.resize(n); - - for(int i=0; i < n; ++i) { - for(int j=0; j < (long int)args[i]->size(); ++j) - out[i].push_back( *args[i]->at(j) ); - } - } - } -}; -/* ################################################################### */ -typedef std::map< int, ezOptionValidator* > ValidatorMap; - -class ezOptionParser { -public: - // How to layout usage descriptions with the option flags. - enum Layout { ALIGN, INTERLEAVE, STAGGER }; - - inline ~ezOptionParser(); - - inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, ezOptionValidator* validator=0); - inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, ezOptionValidator* validator=0); - inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, ezOptionValidator* validator=0); - inline void add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, const char * flag4, ezOptionValidator* validator=0); - inline bool exportFile(const char * filename, bool all=false); - inline OptionGroup * get(const char * name); - inline void getUsage(std::string & usage, int width=80, Layout layout=ALIGN); - inline void getUsageDescriptions(std::string & usage, int width=80, Layout layout=STAGGER); - inline bool gotExpected(std::vector & badOptions); - inline bool gotRequired(std::vector & badOptions); - inline bool gotValid(std::vector & badOptions, std::vector & badArgs); - inline bool importFile(const char * filename, char comment='#'); - inline int isSet(const char * name); - inline int isSet(std::string & name); - inline void parse(int argc, const char * argv[]); - inline void prettyPrint(std::string & out); - inline void reset(); - inline void resetArgs(); - - // Insert extra empty line betwee each option's usage description. - char doublespace; - // General description in human language on what the user's tool does. - // It's the first section to get printed in the full usage message. - std::string overview; - // A synopsis of command and options usage to show expected order of input arguments. - // It's the second section to get printed in the full usage message. - std::string syntax; - // Example (third) section in usage message. - std::string example; - // Final section printed in usage message. For contact, copyrights, version info. - std::string footer; - // Map from an option to an Id of its parent group. - std::map< std::string, int > optionGroupIds; - // Unordered collection of the option groups. - std::vector< OptionGroup* > groups; - // Store unexpected args in input. - std::vector< std::string* > unknownArgs; - // List of args that occur left-most before first option flag. - std::vector< std::string* > firstArgs; - // List of args that occur after last right-most option flag and its args. - std::vector< std::string* > lastArgs; - // List of validators. - ValidatorMap validators; - // Maps group id to a validator index into vector of validators. Validator index is -1 if there is no validator for group. - std::map< int, int > groupValidators; -}; -/* ################################################################### */ -ezOptionParser::~ezOptionParser() { - reset(); -} -/* ################################################################### */ -void ezOptionParser::reset() { - this->doublespace = 1; - - int i; - for(i=0; i < (long int)groups.size(); ++i) - delete groups[i]; - groups.clear(); - - for(i=0; i < (long int)unknownArgs.size(); ++i) - delete unknownArgs[i]; - unknownArgs.clear(); - - for(i=0; i < (long int)firstArgs.size(); ++i) - delete firstArgs[i]; - firstArgs.clear(); - - for(i=0; i < (long int)lastArgs.size(); ++i) - delete lastArgs[i]; - lastArgs.clear(); - - ValidatorMap::iterator it; - for(it = validators.begin(); it != validators.end(); ++it) - delete it->second; - - validators.clear(); - optionGroupIds.clear(); - groupValidators.clear(); -}; -/* ################################################################### */ -void ezOptionParser::resetArgs() { - int i; - for(i=0; i < (long int)groups.size(); ++i) - groups[i]->clearArgs(); - - for(i=0; i < (long int)unknownArgs.size(); ++i) - delete unknownArgs[i]; - unknownArgs.clear(); - - for(i=0; i < (long int)firstArgs.size(); ++i) - delete firstArgs[i]; - firstArgs.clear(); - - for(i=0; i < (long int)lastArgs.size(); ++i) - delete lastArgs[i]; - lastArgs.clear(); -}; -/* ################################################################### */ -void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, ezOptionValidator* validator) { - int id = this->groups.size(); - OptionGroup * g = new OptionGroup; - g->defaults = defaults; - g->isRequired = required; - g->expectArgs = expectArgs; - g->delim = delim; - g->isSet = 0; - g->help = help; - std::string *f1 = new std::string(flag1); - g->flags.push_back( f1 ); - this->optionGroupIds[flag1] = id; - this->groups.push_back(g); - - if (validator) { - int vid = validator->id; - validators[vid] = validator; - groupValidators[id] = vid; - } else { - groupValidators[id] = -1; - } -}; -/* ################################################################### */ -void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, ezOptionValidator* validator) { - int id = this->groups.size(); - OptionGroup * g = new OptionGroup; - g->defaults = defaults; - g->isRequired = required; - g->expectArgs = expectArgs; - g->delim = delim; - g->isSet = 0; - g->help = help; - std::string *f1 = new std::string(flag1); - g->flags.push_back( f1 ); - std::string *f2 = new std::string(flag2); - g->flags.push_back( f2 ); - this->optionGroupIds[flag1] = id; - this->optionGroupIds[flag2] = id; - - this->groups.push_back(g); - - if (validator) { - int vid = validator->id; - validators[vid] = validator; - groupValidators[id] = vid; - } else { - groupValidators[id] = -1; - } -}; -/* ################################################################### */ -void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, ezOptionValidator* validator) { - int id = this->groups.size(); - OptionGroup * g = new OptionGroup; - g->defaults = defaults; - g->isRequired = required; - g->expectArgs = expectArgs; - g->delim = delim; - g->isSet = 0; - g->help = help; - std::string *f1 = new std::string(flag1); - g->flags.push_back( f1 ); - std::string *f2 = new std::string(flag2); - g->flags.push_back( f2 ); - std::string *f3 = new std::string(flag3); - g->flags.push_back( f3 ); - this->optionGroupIds[flag1] = id; - this->optionGroupIds[flag2] = id; - this->optionGroupIds[flag3] = id; - - this->groups.push_back(g); - - if (validator) { - int vid = validator->id; - validators[vid] = validator; - groupValidators[id] = vid; - } else { - groupValidators[id] = -1; - } -}; -/* ################################################################### */ -void ezOptionParser::add(const char * defaults, bool required, int expectArgs, char delim, const char * help, const char * flag1, const char * flag2, const char * flag3, const char * flag4, ezOptionValidator* validator) { - int id = this->groups.size(); - OptionGroup * g = new OptionGroup; - g->defaults = defaults; - g->isRequired = required; - g->expectArgs = expectArgs; - g->delim = delim; - g->isSet = 0; - g->help = help; - std::string *f1 = new std::string(flag1); - g->flags.push_back( f1 ); - std::string *f2 = new std::string(flag2); - g->flags.push_back( f2 ); - std::string *f3 = new std::string(flag3); - g->flags.push_back( f3 ); - std::string *f4 = new std::string(flag4); - g->flags.push_back( f4 ); - this->optionGroupIds[flag1] = id; - this->optionGroupIds[flag2] = id; - this->optionGroupIds[flag3] = id; - this->optionGroupIds[flag4] = id; - - this->groups.push_back(g); - - if (validator) { - int vid = validator->id; - validators[vid] = validator; - groupValidators[id] = vid; - } else { - groupValidators[id] = -1; - } -}; -/* ################################################################### */ -bool ezOptionParser::exportFile(const char * filename, bool all) { - int i; - std::string out; - bool quote; - - // Export the first args, except the program name, so start from 1. - for(i=1; i < (long int)firstArgs.size(); ++i) { - quote = ((firstArgs[i]->find_first_of(" \t") != std::string::npos) && (firstArgs[i]->find_first_of("\'\"") == std::string::npos)); - - if (quote) - out.append("\""); - - out.append(*firstArgs[i]); - if (quote) - out.append("\""); - - out.append(" "); - } - - if (firstArgs.size() > 1) - out.append("\n"); - - std::vector stringPtrs(groups.size()); - int m; - int n = groups.size(); - for(i=0; i < n; ++i) { - stringPtrs[i] = groups[i]->flags[0]; - } - - OptionGroup *g; - // Sort first flag of each group with other groups. - std::sort(stringPtrs.begin(), stringPtrs.end(), CmpOptStringPtr); - for(i=0; i < n; ++i) { - g = get(stringPtrs[i]->c_str()); - if (g->isSet || all) { - if (!g->isSet || g->args.empty()) { - if (!g->defaults.empty()) { - out.append(*stringPtrs[i]); - out.append(" "); - quote = ((g->defaults.find_first_of(" \t") != std::string::npos) && (g->defaults.find_first_of("\'\"") == std::string::npos)); - if (quote) - out.append("\""); - - out.append(g->defaults); - if (quote) - out.append("\""); - - out.append("\n"); - } - } else { - int n = g->args.size(); - for(int j=0; j < n; ++j) { - out.append(*stringPtrs[i]); - out.append(" "); - m = g->args[j]->size(); - - for(int k=0; k < m; ++k) { - quote = ( (*g->args[j]->at(k)).find_first_of(" \t") != std::string::npos ); - if (quote) - out.append("\""); - - out.append(*g->args[j]->at(k)); - if (quote) - out.append("\""); - - if ((g->delim) && ((k+1) != m)) - out.append(1,g->delim); - } - out.append("\n"); - } - } - } - } - - // Export the last args. - for(i=0; i < (long int)lastArgs.size(); ++i) { - quote = ( lastArgs[i]->find_first_of(" \t") != std::string::npos ); - if (quote) - out.append("\""); - - out.append(*lastArgs[i]); - if (quote) - out.append("\""); - - out.append(" "); - } - - std::ofstream file(filename); - if (!file.is_open()) - return false; - - file << out; - file.close(); - - return true; -}; -/* ################################################################### */ -// Does not overwrite current options. -// Returns true if file was read successfully. -// So if this is used before parsing CLI, then option values will reflect -// this file, but if used after parsing CLI, then values will contain -// both CLI values and file's values. -// -// Comment lines are allowed if prefixed with #. -// Strings should be quoted as usual. -bool ezOptionParser::importFile(const char * filename, char comment) { - std::ifstream file (filename, std::ios::in | std::ios::ate); - if (!file.is_open()) - return false; - - // Read entire file contents. - std::ifstream::pos_type size = file.tellg(); - char * memblock = new char[(int)size+1]; // Add one for end of string. - file.seekg (0, std::ios::beg); - file.read (memblock, size); - memblock[size] = '\0'; - file.close(); - - // Find comment lines. - std::list lines; - std::string memblockstring(memblock); - delete[] memblock; - SplitDelim(memblockstring, '\n', lines); - int i,j,n; - std::list::iterator iter; - std::vector sq, dq; // Single and double quote indices. - std::vector::iterator lo; // For searching quote indices. - size_t pos; - const char *str; - std::string *line; - // Find all single and double quotes to correctly handle comment tokens. - for(iter=lines.begin(); iter != lines.end(); ++iter) { - line = *iter; - str = line->c_str(); - n = line->size(); - sq.clear(); - dq.clear(); - if (n) { - // If first char is comment, then erase line and continue. - pos = line->find_first_not_of(" \t\r"); - if ((pos==std::string::npos) || (line->at(pos)==comment)) { - line->erase(); - continue; - } else { - // Erase whitespace prefix. - line->erase(0,pos); - n = line->size(); - } - - if (line->at(0)=='"') - dq.push_back(0); - - if (line->at(0)=='\'') - sq.push_back(0); - } else { // Empty line. - continue; - } - - for(i=1; i < n; ++i) { - if ( (str[i]=='"') && (str[i-1]!='\\') ) - dq.push_back(i); - else if ( (str[i]=='\'') && (str[i-1]!='\\') ) - sq.push_back(i); - } - // Scan for comments, and when found, check bounds of quotes. - // Start with second char because already checked first char. - for(i=1; i < n; ++i) { - if ( (line->at(i)==comment) && (line->at(i-1)!='\\') ) { - // If within open/close quote pair, then not real comment. - if (sq.size()) { - lo = std::lower_bound(sq.begin(), sq.end(), i); - // All start of strings will be even indices, closing quotes is odd indices. - j = (int)(lo-sq.begin()); - if ( (j % 2) == 0) { // Even implies comment char not in quote pair. - // Erase from comment char to end of line. - line->erase(i); - break; - } - } else if (dq.size()) { - // Repeat tests for double quotes. - lo = std::lower_bound(dq.begin(), dq.end(), i); - j = (int)(lo-dq.begin()); - if ( (j % 2) == 0) { - line->erase(i); - break; - } - } else { - // Not in quotes. - line->erase(i); - break; - } - } - } - } - - std::string cmd; - // Convert list to string without newlines to simulate commandline. - for(iter=lines.begin(); iter != lines.end(); ++iter) { - if (! (*iter)->empty()) { - cmd.append(**iter); - cmd.append(" "); - } - } - - // Now parse as if from command line. - int argc=0; - char** argv = CommandLineToArgvA((char*)cmd.c_str(), &argc); - - // Parse. - parse(argc, (const char**)argv); - if (argv) free(argv); - for(iter=lines.begin(); iter != lines.end(); ++iter) - delete *iter; - - return true; -}; -/* ################################################################### */ -int ezOptionParser::isSet(const char * name) { - std::string sname(name); - - if (this->optionGroupIds.count(sname)) { - return this->groups[this->optionGroupIds[sname]]->isSet; - } - - return 0; -}; -/* ################################################################### */ -int ezOptionParser::isSet(std::string & name) { - if (this->optionGroupIds.count(name)) { - return this->groups[this->optionGroupIds[name]]->isSet; - } - - return 0; -}; -/* ################################################################### */ -OptionGroup * ezOptionParser::get(const char * name) { - if (optionGroupIds.count(std::string(name))) { - return groups[optionGroupIds[name]]; - } - - return 0; -}; -/* ################################################################### */ -void ezOptionParser::getUsage(std::string & usage, int width, Layout layout) { - - usage.append(overview); - usage.append("\n\n"); - usage.append("USAGE: "); - usage.append(syntax); - usage.append("\n\nOPTIONS:\n\n"); - getUsageDescriptions(usage, width, layout); - - if (!example.empty()) { - usage.append("EXAMPLES:\n\n"); - usage.append(example); - } - - if (!footer.empty()) { - usage.append(footer); - } -}; -/* ################################################################### */ -// Creates 2 column formatted help descriptions for each option flag. -void ezOptionParser::getUsageDescriptions(std::string & usage, int width, Layout layout) { - // Sort each flag list amongst each group. - int i; - // Store index of flag groups before sort for easy lookup later. - std::map stringPtrToIndexMap; - std::vector stringPtrs(groups.size()); - - for(i=0; i < (long int)groups.size(); ++i) { - std::sort(groups[i]->flags.begin(), groups[i]->flags.end(), CmpOptStringPtr); - stringPtrToIndexMap[groups[i]->flags[0]] = i; - stringPtrs[i] = groups[i]->flags[0]; - } - - size_t j, k; - std::string opts; - std::vector sortedOpts; - // Sort first flag of each group with other groups. - std::sort(stringPtrs.begin(), stringPtrs.end(), CmpOptStringPtr); - for(i=0; i < (long int)groups.size(); ++i) { - //printf("DEBUG:%d: %d %d %s\n", __LINE__, i, stringPtrToIndexMap[stringPtrs[i]], stringPtrs[i]->c_str()); - k = stringPtrToIndexMap[stringPtrs[i]]; - opts.clear(); - for(j=0; j < groups[k]->flags.size()-1; ++j) { - opts.append(*groups[k]->flags[j]); - opts.append(", "); - - if ((long int)opts.size() > width) - opts.append("\n"); - } - // The last flag. No need to append comma anymore. - opts.append( *groups[k]->flags[j] ); - - if (groups[k]->expectArgs) { - opts.append(" ARG"); - - if (groups[k]->delim) { - opts.append("1["); - opts.append(1, groups[k]->delim); - opts.append("ARGn]"); - } - } - - sortedOpts.push_back(opts); - } - - // Each option group will use this to build multiline help description. - std::list desc; - // Number of whitespaces from start of line to description (interleave layout) or - // gap between flag names and description (align, stagger layouts). - int gutter = 3; - - // Find longest opt flag string to set column start for help usage descriptions. - int maxlen=0; - if (layout == ALIGN) { - for(i=0; i < (long int)groups.size(); ++i) { - if (maxlen < (long int)sortedOpts[i].size()) - maxlen = sortedOpts[i].size(); - } - } - - // The amount of space remaining on a line for help text after flags. - int helpwidth; - std::list::iterator cIter, insertionIter; - size_t pos; - for(i=0; i < (long int)groups.size(); ++i) { - k = stringPtrToIndexMap[stringPtrs[i]]; - - if (layout == STAGGER) - maxlen = sortedOpts[i].size(); - - int pad = gutter + maxlen; - helpwidth = width - pad; - - // All the following split-fu could be optimized by just using substring (offset, length) tuples, but just to get it done, we'll do some not-too expensive string copying. - SplitDelim(groups[k]->help, '\n', desc); - // Split lines longer than allowable help width. - for(insertionIter=desc.begin(), cIter=insertionIter++; - cIter != desc.end(); - cIter=insertionIter++) { - if ((long int)((*cIter)->size()) > helpwidth) { - // Get pointer to next string to insert new strings before it. - std::string *rem = *cIter; - // Remove this line and add back in pieces. - desc.erase(cIter); - // Loop until remaining string is short enough. - while ((long int)rem->size() > helpwidth) { - // Find whitespace to split before helpwidth. - if (rem->at(helpwidth) == ' ') { - // If word ends exactly at helpwidth, then split after it. - pos = helpwidth; - } else { - // Otherwise, split occurs midword, so find whitespace before this word. - pos = rem->rfind(" ", helpwidth); - } - // Insert split string. - desc.insert(insertionIter, new std::string(*rem, 0, pos)); - // Now skip any whitespace to start new line. - pos = rem->find_first_not_of(' ', pos); - rem->erase(0, pos); - } - - if (rem->size()) - desc.insert(insertionIter, rem); - else - delete rem; - } - } - - usage.append(sortedOpts[i]); - if (layout != INTERLEAVE) - // Add whitespace between option names and description. - usage.append(pad - sortedOpts[i].size(), ' '); - else { - usage.append("\n"); - usage.append(gutter, ' '); - } - - if (desc.size() > 0) { // Crash fix by Bruce Shankle. - // First line already padded above (before calling SplitDelim) after option flag names. - cIter = desc.begin(); - usage.append(**cIter); - usage.append("\n"); - // Now inject the pad for each line. - for(++cIter; cIter != desc.end(); ++cIter) { - usage.append(pad, ' '); - usage.append(**cIter); - usage.append("\n"); - } - - if (this->doublespace) usage.append("\n"); - - for(cIter=desc.begin(); cIter != desc.end(); ++cIter) - delete *cIter; - - desc.clear(); - } - - } -}; -/* ################################################################### */ -bool ezOptionParser::gotExpected(std::vector & badOptions) { - int i,j; - - for(i=0; i < (long int)groups.size(); ++i) { - OptionGroup *g = groups[i]; - // If was set, ensure number of args is correct. - if (g->isSet) { - if ((g->expectArgs != 0) && g->args.empty()) { - badOptions.push_back(*g->flags[0]); - continue; - } - - for(j=0; j < (long int)g->args.size(); ++j) { - if ((g->expectArgs != -1) && (g->expectArgs != (long int)g->args[j]->size())) - badOptions.push_back(*g->flags[0]); - } - } - } - - return badOptions.empty(); -}; -/* ################################################################### */ -bool ezOptionParser::gotRequired(std::vector & badOptions) { - int i; - - for(i=0; i < (long int)groups.size(); ++i) { - OptionGroup *g = groups[i]; - // Simple case when required but user never set it. - if (g->isRequired && (!g->isSet)) { - badOptions.push_back(*g->flags[0]); - continue; - } - } - - return badOptions.empty(); -}; -/* ################################################################### */ -bool ezOptionParser::gotValid(std::vector & badOptions, std::vector & badArgs) { - int groupid, validatorid; - std::map< int, int >::iterator it; - - for(it = groupValidators.begin(); it != groupValidators.end(); ++it) { - groupid = it->first; - validatorid = it->second; - if (validatorid < 0) continue; - - OptionGroup *g = groups[groupid]; - ezOptionValidator *v = validators[validatorid]; - bool nextgroup = false; - - for (int i = 0; i < (long int)g->args.size(); ++i) { - if (nextgroup) break; - std::vector< std::string* > * args = g->args[i]; - for (int j = 0; j < (long int)args->size(); ++j) { - if (!v->isValid(args->at(j))) { - badOptions.push_back(*g->flags[0]); - badArgs.push_back(*args->at(j)); - nextgroup = true; - break; - } - } - } - } - - return badOptions.empty(); -}; -/* ################################################################### */ -void ezOptionParser::parse(int argc, const char * argv[]) { - if (argc < 1) return; - - /* - std::map::iterator it; - for ( it=optionGroupIds.begin() ; it != optionGroupIds.end(); it++ ) - std::cout << (*it).first << " => " << (*it).second << std::endl; - */ - - int i, k, firstOptIndex=0, lastOptIndex=0; - std::string s; - OptionGroup *g; - - for(i=0; i < argc; ++i) { - s = argv[i]; - - if (optionGroupIds.count(s)) - break; - } - - firstOptIndex = i; - - if (firstOptIndex == argc) { - // No flags encountered, so set last args. - this->firstArgs.push_back(new std::string(argv[0])); - - for(k=1; k < argc; ++k) - this->lastArgs.push_back(new std::string(argv[k])); - - return; - } - - // Store initial args before opts appear. - for(k=0; k < i; ++k) { - this->firstArgs.push_back(new std::string(argv[k])); - } - - for(; i < argc; ++i) { - s = argv[i]; - - if (optionGroupIds.count(s)) { - k = optionGroupIds[s]; - g = groups[k]; - g->isSet = 1; - g->parseIndex.push_back(i); - - if (g->expectArgs) { - // Read ahead to get args. - ++i; - if (i >= argc) return; - g->args.push_back(new std::vector); - SplitDelim(argv[i], g->delim, g->args.back()); - } - lastOptIndex = i; - } - } - - // Scan for unknown opts/arguments. - for(i=firstOptIndex; i <= lastOptIndex; ++i) { - s = argv[i]; - - if (optionGroupIds.count(s)) { - k = optionGroupIds[s]; - g = groups[k]; - if (g->expectArgs) { - // Read ahead for args and skip them. - ++i; - } - } else { - unknownArgs.push_back(new std::string(argv[i])); - } - } - - if ( lastOptIndex >= (argc-1) ) return; - - // Store final args without flags. - for(k=lastOptIndex + 1; k < argc; ++k) { - this->lastArgs.push_back(new std::string(argv[k])); - } -}; -/* ################################################################### */ -void ezOptionParser::prettyPrint(std::string & out) { - char tmp[256]; - int i,j,k; - - out += "First Args:\n"; - for(i=0; i < (long int)firstArgs.size(); ++i) { - sprintf(tmp, "%d: %s\n", i+1, firstArgs[i]->c_str()); - out += tmp; - } - - // Sort the option flag names. - int n = groups.size(); - std::vector stringPtrs(n); - for(i=0; i < n; ++i) { - stringPtrs[i] = groups[i]->flags[0]; - } - - // Sort first flag of each group with other groups. - std::sort(stringPtrs.begin(), stringPtrs.end(), CmpOptStringPtr); - - out += "\nOptions:\n"; - OptionGroup *g; - for(i=0; i < n; ++i) { - g = get(stringPtrs[i]->c_str()); - out += "\n"; - // The flag names: - for(j=0; j < (long int)g->flags.size()-1; ++j) { - sprintf(tmp, "%s, ", g->flags[j]->c_str()); - out += tmp; - } - sprintf(tmp, "%s:\n", g->flags.back()->c_str()); - out += tmp; - - if (g->isSet) { - if (g->expectArgs) { - if (g->args.empty()) { - sprintf(tmp, "%s (default)\n", g->defaults.c_str()); - out += tmp; - } else { - for(k=0; k < (long int)g->args.size(); ++k) { - for(j=0; j < (long int)g->args[k]->size()-1; ++j) { - sprintf(tmp, "%s%c", g->args[k]->at(j)->c_str(), g->delim); - out += tmp; - } - sprintf(tmp, "%s\n", g->args[k]->back()->c_str()); - out += tmp; - } - } - } else { // Set but no args expected. - sprintf(tmp, "Set\n"); - out += tmp; - } - } else { - sprintf(tmp, "Not set\n"); - out += tmp; - } - } - - out += "\nLast Args:\n"; - for(i=0; i < (long int)lastArgs.size(); ++i) { - sprintf(tmp, "%d: %s\n", i+1, lastArgs[i]->c_str()); - out += tmp; - } - - out += "\nUnknown Args:\n"; - for(i=0; i < (long int)unknownArgs.size(); ++i) { - sprintf(tmp, "%d: %s\n", i+1, unknownArgs[i]->c_str()); - out += tmp; - } -}; -} -/* ################################################################### */ -#endif /* EZ_OPTION_PARSER_H */ diff --git a/coreneuron/utils/sdprintf.cpp b/coreneuron/utils/sdprintf.cpp deleted file mode 100644 index 356631598..000000000 --- a/coreneuron/utils/sdprintf.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* -Copyright (c) 2016, Blue Brain Project -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/** - * @file sdprintf.cpp - * @date 19th Jan 2015 - * @brief sdprintf() and vsdprintf() implementation. - */ - -#include "coreneuron/utils/sdprintf.h" - -sd_ptr sdprintf(char* buf, size_t sz, const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - - sd_ptr s = vsdprintf(buf, sz, fmt, ap); - - va_end(ap); - return s; -} - -#if !defined(va_copy) -/* check for __va_copy: work around for icpc 2015 */ -#if defined(__va_copy) -#define va_copy(dest, src) __va_copy(dest, src) -#else -/* non-portable, so specialise for those cases where - * * value assignment does not work and va_copy is nonetheless - * * not defined */ -#warning "no va_copy() or __va_copy defined, using value assignment" -#define va_copy(dest, src) ((dest) = (src)) -#endif -#endif - -sd_ptr vsdprintf(char* buf, size_t sz, const char* fmt, va_list ap) { - using namespace std; - - sd_ptr s; - va_list ap2; - va_copy(ap2, ap); - - int rv = 0; - if (buf != 0 && sz > 0) - rv = vsnprintf(buf, sz, fmt, ap); - else { - char p[1]; - sz = 0; - rv = vsnprintf(p, sz, fmt, ap); - } - - if (rv < 0) { - s = 0; - goto exit; - } - - if ((size_t)rv < sz) { - s = buf; - goto exit; - } else { - // buffer too small; allocate and try again - sz = (size_t)rv + 1; - char* p = (char*)malloc(sz); - if (!p) { - va_end(ap2); - return 0; - } - - rv = vsnprintf(p, sz, fmt, ap2); - if (rv < 0 || (size_t)rv >= sz) { - free(p); - s = 0; - goto exit; - } - - s = sd_ptr(p, true); - goto exit; - } - -exit: - va_end(ap2); - return s; -} diff --git a/coreneuron/utils/sdprintf.h b/coreneuron/utils/sdprintf.h deleted file mode 100644 index 747b64695..000000000 --- a/coreneuron/utils/sdprintf.h +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright (c) 2016, Blue Brain Project -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/** - * @file sdprintf.h - * @date 19th Jan 2015 - * @brief Header providing interface to dynamically allocating - * sprintf wrapper. - * - * Provides sprintf()-like function that uses the offered character - * array if it is sufficiently large, else allocates space on the heap. - * - * The return object is a 'smart' pointer wrapper, offering a subset of - * the C++11 std::unique_ptr functionality. (We need C++03 - * compatibility, and do not want to introduce a dependency on the - * Boost library.) - * - * Code assumes C99-compatible behaviour of (non-standard before C++11) - * vsnprintf(). Microsoft Visual C++ does not conform, for example. - */ - -#ifndef SDPRINTF_H_ -#define SDPRINTF_H_ - -#include -#include -#include - -/** @brief 'Smart' pointer wrapper for char * buffers - * - * Assignment and copies preserve the value - * of the contained pointer, but the last assignee - * holds responsibility for deallocation of the - * buffer. - */ - -template -struct sd_ptr_generic { - sd_ptr_generic() : ptr(0), dflag(false) { - } - - sd_ptr_generic(const char* p, bool dflag_ = false) : ptr(p), dflag(dflag_) { - } - - sd_ptr_generic(const sd_ptr_generic& them) : ptr(them.ptr), dflag(them.dflag) { - them.dflag = false; - } - - sd_ptr_generic& operator=(const char* p) { - release(); - ptr = p; - return *this; - } - - sd_ptr_generic& operator=(const sd_ptr_generic& them) { - if (&them != this) { - release(); - ptr = them.ptr; - dflag = them.dflag; - them.dflag = false; - } - return *this; - } - - void release() { - if (dflag) - dealloc((void*)ptr); - dflag = false; - } - - const char* get() const { - return ptr; - } - operator const char*() const { - return get(); - } - - operator bool() const { - return (bool)ptr; - } - - ~sd_ptr_generic() { - release(); - } - - private: - const char* ptr; - mutable bool dflag; -}; - -typedef sd_ptr_generic sd_ptr; - -/** @brief Dynamically allocating snprintf() - * - * If the provided buffer is too small, sdprintf() - * allocates one of the appropriate size. - * - * @param buf Pointer to buffer in which to write string. - * @param sz Size in bytes of buffer. - * @param fmt A printf format string. - * - * @return An sd_ptr encapsulating the provided or allocated buffer. - * - * If the buffer pointer is nullptr, a new buffer is allocated. - * On error, returns a null sd_ptr. - */ - -sd_ptr sdprintf(char* buf, size_t sz, const char* fmt, ...); - -/** @brief Varargs version of sdprintf() (q.v.) - * - * @param buf Pointer to buffer in which to write string. - * @param sz Size in bytes of buffer. - * @param fmt A printf format string. - * @param ap A varargs list of printf arguments. - * - * @return An sd_ptr encapsulating the provided or allocated buffer. - */ - -sd_ptr vsdprintf(char* buf, size_t sz, const char* fmt, va_list ap); - -#endif // ndef SDPRINTF_H_ diff --git a/external/CLI11 b/external/CLI11 new file mode 160000 index 000000000..dd0d8e4fe --- /dev/null +++ b/external/CLI11 @@ -0,0 +1 @@ +Subproject commit dd0d8e4fe729e5b1110232c7a5c9566dad884686 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b6add2c70..6f439c1b2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -23,7 +23,6 @@ if(Boost_FOUND) if(CORENRN_ENABLE_UNIT_TESTS) include_directories(${Boost_INCLUDE_DIRS}) add_subdirectory(unit/endian) - add_subdirectory(unit/sdprintf) add_subdirectory(unit/cmdline_interface) add_subdirectory(unit/interleave_info) add_subdirectory(unit/alignment) diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 52d3d5eec..038b1b24e 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -4,7 +4,7 @@ # See top-level LICENSE file for details. # ============================================================================= -set(COMMON_ARGS "--tstop 100. --celsius 6.3 -mpi") +set(COMMON_ARGS "--tstop 100. --celsius 6.3 --mpi") set(RING_DATASET_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ring") set(RING_COMMON_ARGS "--datpath ${RING_DATASET_DIR} ${COMMON_ARGS}") set(RING_GAP_COMMON_ARGS "--datpath ${CMAKE_CURRENT_SOURCE_DIR}/ring_gap ${COMMON_ARGS}") @@ -15,26 +15,22 @@ if(CORENRN_ENABLE_GPU) endif() # List of tests with arguments -set( - TEST_CASES_WITH_ARGS - "ring!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring" - "ring_binqueue!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_binqueue --binqueue" - "ring_multisend!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_multisend --multisend" - "ring_spike_buffer!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_spike_buffer -b 1" - "ring_duplicate!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_duplicate -z 3" - "ring_permute1!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_permute1 ${PERMUTE1_ARGS}" - "ring_permute2!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_permute2 ${PERMUTE2_ARGS}" - "ring_gap!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap" - "ring_gap_binqueue!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_binqueue --binqueue" - "ring_gap_multisend!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_multisend --multisend" - "ring_gap_permute1!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_permute1 ${PERMUTE1_ARGS}" - "ring_gap_permute2!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_permute2 ${PERMUTE2_ARGS}" - ) +set(TEST_CASE_ARGS + "ring!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring" + "ring_binqueue!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_binqueue --binqueue" + "ring_multisend!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_multisend --multisend" + "ring_spike_buffer!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_spike_buffer --spikebuf 1" + "ring_duplicate!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_duplicate --multiple 3" + "ring_permute1!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_permute1 ${PERMUTE1_ARGS}" + "ring_permute2!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_permute2 ${PERMUTE2_ARGS}" + "ring_gap!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap" + "ring_gap_binqueue!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_binqueue --binqueue" + "ring_gap_multisend!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_multisend --multisend" + "ring_gap_permute1!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_permute1 ${PERMUTE1_ARGS}" + "ring_gap_permute2!${RING_GAP_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_gap_permute2 ${PERMUTE2_ARGS}" +) -set( - NEGATIVE_TEST_CASES_WITH_ARGS - "ring_different_seed!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_different_seed --seed 123456" - ) +set(NEGATIVE_TEST_CASE_ARGS "ring_different_seed!${RING_COMMON_ARGS} ${GPU_ARGS} --outpath ${CMAKE_CURRENT_BINARY_DIR}/ring_different_seed --seed 123456") # There are no directories for permute and multisend related tests, create them and copy ref spikes foreach(data_dir "ring" "ring_gap") @@ -49,14 +45,14 @@ foreach(data_dir "ring" "ring_gap") file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/${data_dir}/out.dat.ref" DESTINATION - "${CMAKE_CURRENT_SOURCE_DIR}/${data_dir}_${test_suffix}/") + "${CMAKE_CURRENT_BINARY_DIR}/${data_dir}_${test_suffix}/") endforeach() endforeach() file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/ring/out.dat.ref" DESTINATION - "${CMAKE_CURRENT_SOURCE_DIR}/ring_spike_buffer/") + "${CMAKE_CURRENT_BINARY_DIR}/ring_spike_buffer/") file(GLOB INPUT_FILES "${CMAKE_CURRENT_SOURCE_DIR}/ring/*.dat") diff --git a/tests/integration/reportinglib/reporting_test.sh.in b/tests/integration/reportinglib/reporting_test.sh.in index bf5e04aa6..3d3a6f3e7 100644 --- a/tests/integration/reportinglib/reporting_test.sh.in +++ b/tests/integration/reportinglib/reporting_test.sh.in @@ -2,6 +2,6 @@ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@CMAKE_BINARY_DIR@/lib:@reportinglib_LIB_DIR@ export OMP_NUM_THREADS=1 -@SRUN_PREFIX@ @CMAKE_BINARY_DIR@/bin/@CMAKE_SYSTEM_PROCESSOR@/special-core --read-config @TEST_NAME@.conf -mpi +@SRUN_PREFIX@ @CMAKE_BINARY_DIR@/bin/@CMAKE_SYSTEM_PROCESSOR@/special-core --mpi --read-config @TEST_NAME@.conf chmod +x ./@TEST_NAME@.check exit `./@TEST_NAME@.check` diff --git a/tests/jenkins/run_corenrn.sh b/tests/jenkins/run_corenrn.sh index c0d5c134f..5eca68da5 100755 --- a/tests/jenkins/run_corenrn.sh +++ b/tests/jenkins/run_corenrn.sh @@ -18,11 +18,11 @@ else fi if [ "${TEST}" = "patstim" ]; then - mpirun -n ${MPI_RANKS} ./${CORENRN_TYPE}/special-core --pattern patstim.spk -mpi -d test${TEST}dat -e 100 -o ${TEST} + mpirun -n ${MPI_RANKS} ./${CORENRN_TYPE}/special-core --show --mpi -e 100 --pattern patstim.spk -d test${TEST}dat -o ${TEST} elif [ "${TEST}" = "ringtest" ]; then - mpirun -n ${MPI_RANKS} ./${CORENRN_TYPE}/special-core -mpi -d coredat -e 100 -o ${TEST} + mpirun -n ${MPI_RANKS} ./${CORENRN_TYPE}/special-core --show --mpi -e 100 -d coredat -o ${TEST} else - mpirun -n ${MPI_RANKS} ./${CORENRN_TYPE}/special-core -mpi -d test${TEST}dat -e 100 -o ${TEST} + mpirun -n ${MPI_RANKS} ./${CORENRN_TYPE}/special-core --show --mpi -e 100 -d test${TEST}dat -o ${TEST} fi cat ${TEST}/out.dat > ${TEST}/out_cn_${TEST}.spk diff --git a/tests/jenkins/run_neuron.sh b/tests/jenkins/run_neuron.sh index 029e2d5ae..4c0137522 100755 --- a/tests/jenkins/run_neuron.sh +++ b/tests/jenkins/run_neuron.sh @@ -13,14 +13,14 @@ cd $WORKSPACE/${TEST_DIR} if [ "${TEST_DIR}" = "testcorenrn" ]; then mkdir test${TEST}dat mkdir ${TEST} - mpirun -n ${MPI_RANKS} ./x86_64/special -mpi -c sim_time=100 test${TEST}.hoc + mpirun -n ${MPI_RANKS} ./x86_64/special --mpi -c sim_time=100 test${TEST}.hoc cat out${TEST}.dat | sort -k 1n,1n -k 2n,2n > ${TEST}/out_nrn_${TEST}.spk rm out${TEST}.dat elif [ "${TEST_DIR}" = "ringtest" ]; then mkdir ${TEST} - mpirun -n 6 ./x86_64/special ringtest.py -mpi + mpirun -n 6 ./x86_64/special ringtest.py --mpi cat coredat/spk6.std | sort -k 1n,1n -k 2n,2n > ${TEST}/out_nrn_${TEST}.spk else echo "Not a valid TEST" exit 1 -fi \ No newline at end of file +fi diff --git a/tests/unit/cmdline_interface/CMakeLists.txt b/tests/unit/cmdline_interface/CMakeLists.txt index cb18eb1fc..20cb12b74 100644 --- a/tests/unit/cmdline_interface/CMakeLists.txt +++ b/tests/unit/cmdline_interface/CMakeLists.txt @@ -13,6 +13,7 @@ target_link_libraries(cmd_interface_test_bin coreneuron ${reportinglib_LIBRARY} ${sonatareport_LIBRARY}) +target_include_directories(cmd_interface_test_bin SYSTEM PRIVATE ${CORENEURON_PROJECT_SOURCE_DIR}/external/CLI11/include) add_test(NAME cmd_interface_test COMMAND ${TEST_EXEC_PREFIX} ${CMAKE_CURRENT_BINARY_DIR}/cmd_interface_test_bin) diff --git a/tests/unit/cmdline_interface/test_cmdline_interface.cpp b/tests/unit/cmdline_interface/test_cmdline_interface.cpp index bf59aa60f..e32c61be0 100644 --- a/tests/unit/cmdline_interface/test_cmdline_interface.cpp +++ b/tests/unit/cmdline_interface/test_cmdline_interface.cpp @@ -30,25 +30,18 @@ THE POSSIBILITY OF SUCH DAMAGE. #define BOOST_TEST_MAIN #include -#include "coreneuron/io/nrnoptarg.hpp" -#include +#include +#include "coreneuron/apps/corenrn_parameters.hpp" + using namespace coreneuron; + BOOST_AUTO_TEST_CASE(cmdline_interface) { const char* argv[] = { "nrniv-core", - "--spikebuf", - "100", - - "--threading", - - "--datpath", - "/this/is/the/data/path", - - "--checkpoint", - "/this/is/the/chkp/path", + "--mpi", "--dt", "0.02", @@ -56,53 +49,21 @@ BOOST_AUTO_TEST_CASE(cmdline_interface) { "--tstop", "0.1", - "--filesdat", - "/this/is/the/file/path", - - "--prcellgid", - "12", - "--gpu", - "--dt_io", - "0.2", - - "--forwardskip", - "0.02", - - "--celsius", - "25.12", - - "-mpi", - - "--outpath", - "/this/is/the/output/path", - - "--pattern", - "filespike.dat", - - "--report-conf", - "report.conf", - "--cell-permute", "2", - "--voltage", - "-32", - "--nwarp", "8", - "--extracon", - "1000", - - "--multiple", - "3", + "-d", + "./", - "--binqueue", + "--voltage", + "-32", - "--mindelay", - "0.1", + "--threading", "--ms-phases", "1", @@ -110,90 +71,98 @@ BOOST_AUTO_TEST_CASE(cmdline_interface) { "--ms-subintervals", "2", + "--multisend", + "--spkcompress", "32", - "--multisend"}; + "--binqueue", - int argc = 0; + "--spikebuf", + "100", - for (; strcmp(argv[argc], "--multisend"); argc++) - ; + "--prcellgid", + "12", - argc++; + "--forwardskip", + "0.02", - nrnopt_parse(argc, argv); - - BOOST_CHECK(nrnopt_get_int("--seed") == -1); // testing default value - - BOOST_CHECK(nrnopt_get_int("--spikebuf") == 100); + "--celsius", + "25.12", + + "--extracon", + "1000", - BOOST_CHECK(nrnopt_get_flag("--threading") == true); + "--multiple", + "3", - BOOST_CHECK(!strcmp(nrnopt_get_str("--datpath").c_str(), "/this/is/the/data/path")); + "--mindelay", + "0.1", - BOOST_CHECK(!strcmp(nrnopt_get_str("--checkpoint").c_str(), "/this/is/the/chkp/path")); + "--dt_io", + "0.2" + }; - BOOST_CHECK(nrnopt_get_dbl("--dt") == 0.02); + int argc = 0; - BOOST_CHECK(nrnopt_get_dbl("--tstop") == 0.1); + for (; strcmp(argv[argc], "0.2"); argc++); - BOOST_CHECK(!strcmp(nrnopt_get_str("--filesdat").c_str(), "/this/is/the/file/path")); + argc++; + + corenrn_parameters corenrn_param_test; - BOOST_CHECK(nrnopt_get_int("--prcellgid") == 12); + corenrn_param_test.parse(argc, const_cast(argv)); //discarding const as CLI11 interface is not const + + BOOST_CHECK(corenrn_param_test.seed == -1); // testing default value - BOOST_CHECK(nrnopt_get_flag("--gpu") == true); + BOOST_CHECK(corenrn_param_test.spikebuf == 100); - BOOST_CHECK(nrnopt_get_dbl("--dt_io") == 0.2); + BOOST_CHECK(corenrn_param_test.threading == true); - BOOST_CHECK(nrnopt_get_dbl("--forwardskip") == 0.02); + BOOST_CHECK(corenrn_param_test.dt == 0.02); - BOOST_CHECK(nrnopt_get_dbl("--celsius") == 25.12); + BOOST_CHECK(corenrn_param_test.tstop == 0.1); - BOOST_CHECK(nrnopt_get_flag("-mpi") == true); + BOOST_CHECK(corenrn_param_test.prcellgid == 12); - BOOST_CHECK(!strcmp(nrnopt_get_str("--outpath").c_str(), "/this/is/the/output/path")); + BOOST_CHECK(corenrn_param_test.gpu == true); - BOOST_CHECK(!strcmp(nrnopt_get_str("--pattern").c_str(), "filespike.dat")); + BOOST_CHECK(corenrn_param_test.dt_io == 0.2); - BOOST_CHECK(!strcmp(nrnopt_get_str("--report-conf").c_str(), "report.conf")); + BOOST_CHECK(corenrn_param_test.forwardskip == 0.02); - BOOST_CHECK(nrnopt_get_int("--cell-permute") == 2); + BOOST_CHECK(corenrn_param_test.celsius == 25.12); - BOOST_CHECK(nrnopt_get_dbl("--voltage") == -32); + BOOST_CHECK(corenrn_param_test.mpi_enable == true); - BOOST_CHECK(nrnopt_get_int("--nwarp") == 8); + BOOST_CHECK(corenrn_param_test.cell_interleave_permute == 2); - BOOST_CHECK(nrnopt_get_int("--extracon") == 1000); + BOOST_CHECK(corenrn_param_test.voltage == -32); - BOOST_CHECK(nrnopt_get_int("--multiple") == 3); + BOOST_CHECK(corenrn_param_test.nwarp == 8); - BOOST_CHECK(nrnopt_get_flag("--binqueue") == true); + BOOST_CHECK(corenrn_param_test.extracon == 1000); - BOOST_CHECK(nrnopt_get_dbl("--mindelay") == 0.1); + BOOST_CHECK(corenrn_param_test.multiple == 3); - BOOST_CHECK(nrnopt_get_int("--ms-phases") == 1); + BOOST_CHECK(corenrn_param_test.multisend == true); - BOOST_CHECK(nrnopt_get_int("--ms-subintervals") == 2); + BOOST_CHECK(corenrn_param_test.mindelay == 0.1); - BOOST_CHECK(nrnopt_get_int("--spkcompress") == 32); + BOOST_CHECK(corenrn_param_test.ms_phases == 1); - BOOST_CHECK(nrnopt_get_flag("--multisend") == true); + BOOST_CHECK(corenrn_param_test.ms_subint == 2); + BOOST_CHECK(corenrn_param_test.spkcompress == 32); - // check if nrnopt_modify_dbl works properly - nrnopt_modify_dbl("--dt", 18.1); - BOOST_CHECK(nrnopt_get_dbl("--dt") == 18.1); + BOOST_CHECK(corenrn_param_test.multisend == true); // check if default flags are false const char* argv_empty[] = {"nrniv-core"}; argc = 1; - nrnopt_parse(argc, argv_empty); + corenrn_param_test.dt = 18.1; + + BOOST_CHECK(corenrn_param_test.dt == 18.1); - BOOST_CHECK(nrnopt_get_flag("--threading") == false); - BOOST_CHECK(nrnopt_get_flag("--gpu") == false); - BOOST_CHECK(nrnopt_get_flag("-mpi") == false); - BOOST_CHECK(nrnopt_get_flag("--binqueue") == false); - BOOST_CHECK(nrnopt_get_flag("--multisend") == false); } diff --git a/tests/unit/sdprintf/CMakeLists.txt b/tests/unit/sdprintf/CMakeLists.txt deleted file mode 100644 index 561369a89..000000000 --- a/tests/unit/sdprintf/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# ============================================================================= -# Copyright (C) 2016-2019 Blue Brain Project -# -# See top-level LICENSE file for details. -# ============================================================================= - -include_directories(${CMAKE_SOURCE_DIR}/coreneuron ${Boost_INCLUDE_DIRS}) -file(GLOB sdprintf_test_src "*.cpp") - -add_executable(sdprintf_test_bin ${sdprintf_test_src}) -target_link_libraries(sdprintf_test_bin - ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} - coreneuron - ${reportinglib_LIBRARY} - ${sonatareport_LIBRARY}) - -add_test(NAME sdprintf_test - COMMAND ${TEST_EXEC_PREFIX} ${CMAKE_CURRENT_BINARY_DIR}/sdprintf_test_bin) diff --git a/tests/unit/sdprintf/test_sdprintf.cpp b/tests/unit/sdprintf/test_sdprintf.cpp deleted file mode 100644 index 8ca41f754..000000000 --- a/tests/unit/sdprintf/test_sdprintf.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* -Copyright (c) 2016, Blue Brain Project -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. -3. Neither the name of the copyright holder nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. -*/ - - -#define BOOST_TEST_MODULE sdprintf -#define BOOST_TEST_MAIN - -#include -#include -#include -#include - -#include - -#include - -#include "coreneuron/utils/sdprintf.h" - -BOOST_AUTO_TEST_CASE(sdprintf_noalloc) { - char buf[10]=""; - sd_ptr s=sdprintf(buf,sizeof(buf),"abc%s","def"); - - BOOST_CHECK(s==buf); - BOOST_CHECK(!strcmp(s,"abcdef")); - - // check edge case: string to write (with NUL) is size of buffer - s=sdprintf(buf,sizeof(buf),"abcdefghi"); - BOOST_CHECK(s==buf); - BOOST_CHECK(!strcmp(s,"abcdefghi")); -} - -bool make_invalid_wchar(wchar_t &w) { - std::setlocale(LC_ALL,"C"); - w=(wchar_t)WEOF; - - std::mbstate_t ps=std::mbstate_t(); - char s[MB_LEN_MAX]; - size_t r=wcrtomb(s,w,&ps); - return r==(size_t)-1; -} - -BOOST_AUTO_TEST_CASE(sdprintf_error) { - char buf[10]=""; - wchar_t invalid; - if (make_invalid_wchar(invalid)) { - sd_ptr s=sdprintf(buf,sizeof(buf),"%lc",(wint_t)invalid); - BOOST_CHECK(!s); - } - else { - BOOST_WARN("unable to check error return with invalid conversion"); - } -} - -BOOST_AUTO_TEST_CASE(sdprintf_nullbuf) { - sd_ptr s=sdprintf(0,0,"%s","hello"); - BOOST_CHECK(!strcmp(s,"hello")); -} - -BOOST_AUTO_TEST_CASE(sdprintf_alloc) { - char buf[10]=""; - sd_ptr s=sdprintf(0,0,"abc%s","defghijkl"); - BOOST_CHECK(!strcmp(s,"abcdefghijkl")); - BOOST_CHECK(s!=buf); -} - -BOOST_AUTO_TEST_CASE(sd_ptr_ctor) { - char p[1]; - - sd_ptr a; - BOOST_CHECK(!a); - BOOST_CHECK(a==0); - - sd_ptr b(0); - BOOST_CHECK(!b); - BOOST_CHECK(b==0); - - sd_ptr c(p); - BOOST_CHECK(c); - BOOST_CHECK(c==p); - - sd_ptr d(p,false); - BOOST_CHECK(d); - BOOST_CHECK(d==p); - - sd_ptr e(d); - BOOST_CHECK(d==p); - BOOST_CHECK(e==p); -} - -BOOST_AUTO_TEST_CASE(sd_ptr_assign) { - char p[1],q[1]; - sd_ptr a,b,c; - - a=p; - b=q; - c=0; - - BOOST_CHECK(a==p); - BOOST_CHECK(b==q); - BOOST_CHECK(c==0); - - a=b; - c=b; - - BOOST_CHECK(a==q); - BOOST_CHECK(c==q); -} - - -BOOST_AUTO_TEST_CASE(sd_ptr_get) { - char p[1]; - - sd_ptr a(p); - BOOST_CHECK(a.get()==p); - BOOST_CHECK(a==p); - BOOST_CHECK(a); - - sd_ptr z(0); - BOOST_CHECK(z.get()==0); - BOOST_CHECK(z==0); - BOOST_CHECK(!z); -} - -std::map nfree; -int nfree_total=0; - -void reset_free_count() { - nfree.clear(); - nfree_total=0; -} - -void test_free(void *p) { - ++nfree[p]; - ++nfree_total; -} - -BOOST_AUTO_TEST_CASE(sd_ptr_dealloc) { - typedef sd_ptr_generic mock_sd_ptr; - char p[1],q[1],r[1]; - - reset_free_count(); - { - mock_sd_ptr a(p),b(p,false); - BOOST_CHECK(a==p); - BOOST_CHECK(b==p); - } - BOOST_CHECK(nfree[p]==0); - BOOST_CHECK(nfree_total==0); - - reset_free_count(); - mock_sd_ptr a(p,true); - BOOST_CHECK(a==p); - a.release(); - BOOST_CHECK(nfree[p]==1); - BOOST_CHECK(nfree_total==1); - - reset_free_count(); - { - mock_sd_ptr a(p,true); - BOOST_CHECK(a==p); - } - BOOST_CHECK(nfree[p]==1); - BOOST_CHECK(nfree_total==1); - - reset_free_count(); - { - mock_sd_ptr a(p,true); - { - mock_sd_ptr b(a); - BOOST_CHECK(nfree[p]==0); - } - BOOST_CHECK(nfree[p]==1); - } - BOOST_CHECK(nfree[p]==1); - BOOST_CHECK(nfree_total==1); - - reset_free_count(); - { - mock_sd_ptr a(p,true); - mock_sd_ptr b(q,true); - mock_sd_ptr c(r,false); - BOOST_CHECK(a==p); - BOOST_CHECK(b==q); - BOOST_CHECK(c==r); - b=a; - BOOST_CHECK(b==p); - BOOST_CHECK(nfree[p]==0); - BOOST_CHECK(nfree[q]==1); - c=a; - BOOST_CHECK(c==p); - BOOST_CHECK(nfree[p]==0); - BOOST_CHECK(nfree[r]==0); - a=a; - BOOST_CHECK(a==p); - BOOST_CHECK(nfree[p]==0); - } - BOOST_CHECK(nfree[p]==1); - BOOST_CHECK(nfree_total==2); -} - -