diff --git a/.github/workflows/cmake_build_and_test.yml b/.github/workflows/cmake_build_and_test.yml new file mode 100644 index 0000000..a71270f --- /dev/null +++ b/.github/workflows/cmake_build_and_test.yml @@ -0,0 +1,45 @@ +name: cmake_build_and_test + +on: + push: + branches: [ main, develop ] + pull_request: + branches: [ main, develop ] + +jobs: + build_and_test: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Install Dependencies + run: | + sudo apt-get install g++ cmake libhdf5-dev + - name: Install OpenMC + run: | + cd /opt + git clone https://github.com/openmc-dev/openmc.git + cd openmc + git checkout develop + mkdir build && cd build + cmake -DCMAKE_INSTALL_PREFIX=.. .. + make + make install + - name: Build plasma source + run: | + mkdir build && cd build + cmake .. + make + - name: Test plasma source + run: | + cd build + ./tests + - name: Check Source Generator + run: | + cd build + ./source_generator -l "$PWD/source_sampling.so" -i "major_radius=9.06, minor_radius=2.92258, elongation=1.557, \ + triangularity=0.27, shafranov_shift=0.44789, pedestal_radius=2.33806, ion_density_pedestal=1.09e+20, \ + ion_density_separatrix=3e+19, ion_density_origin=1.09e+20, ion_density_alpha=1, ion_temperature_pedestal=6.09, \ + ion_temperature_separatrix=0.1, ion_temperature_origin=45.9, ion_temperature_alpha=8.06, ion_temperature_beta=6, \ + plasma_type=plasma, plasma_id=1, number_of_bins=100, minimum_toroidal_angle=0, maximum_toroidal_angle=360" diff --git a/.github/workflows/python.yml b/.github/workflows/python_package.yml similarity index 60% rename from .github/workflows/python.yml rename to .github/workflows/python_package.yml index b363ab5..45b174e 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python_package.yml @@ -13,44 +13,22 @@ jobs: build: runs-on: ubuntu-latest - container: quay.io/pypa/manylinux2014_x86_64 - strategy: - matrix: - python-version: [3.6, 3.7, 3.8] steps: - uses: actions/checkout@v2 + - name: Set up Python 3.8 + uses: actions/setup-python@v2 with: - fetch-depth: 0 # Get the repo history so we can version by number of commits - - name: Install OpenMC - run: | - yum install -y gcc-c++ cmake3 hdf5-devel - alternatives --install /usr/local/bin/cmake cmake /usr/bin/cmake3 20 \ - --slave /usr/local/bin/ctest ctest /usr/bin/ctest3 \ - --slave /usr/local/bin/cpack cpack /usr/bin/cpack3 \ - --slave /usr/local/bin/ccmake ccmake /usr/bin/ccmake3 \ - --family cmake - git clone --recurse-submodules https://github.com/openmc-dev/openmc.git - cd openmc - git checkout - mkdir build && cd build - cmake .. - make - make install + python-version: 3.8 - name: Build plasma source run: | - export PYVER=${{ matrix.python-version }} - alias python=$(ls -d /opt/python/* | grep ${PYVER//.})/bin/python - python -m pip install -r requirements-develop.txt - python -m pip install auditwheel - python setup.py bdist_wheel - python -m auditwheel show dist/*.whl - python -m auditwheel repair dist/*.whl + pip install -r requirements-develop.txt + python -m build --sdist . - name: Upload wheel artifact uses: actions/upload-artifact@v2 with: name: dist - path: wheelhouse + path: dist test: runs-on: ubuntu-latest @@ -72,7 +50,47 @@ jobs: path: dist - name: Install plasma source run: | - python -m pip install --no-index --find-links=file:dist parametric-plasma-source + python -m pip install dist/parametric_plasma_source-*.tar.gz + - name: Run tests + run: | + python -m pip install -r requirements-develop.txt + cd tests + python -m pytest + + test_openmc: + runs-on: ubuntu-latest + needs: build + strategy: + matrix: + python-version: [3.6, 3.7, 3.8] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Download build + uses: actions/download-artifact@v2 + with: + name: dist + path: dist + - name: Install OpenMC + run: | + cd /opt + sudo apt install g++ cmake libhdf5-dev + git clone https://github.com/openmc-dev/openmc.git + cd openmc + git checkout develop + mkdir build && cd build + cmake -DCMAKE_INSTALL_PREFIX=.. .. + make + make install + cd .. + python -m pip install . + - name: Install plasma source + run: | + python -m pip install dist/parametric_plasma_source-*.tar.gz - name: Run tests run: | python -m pip install -r requirements-develop.txt @@ -80,8 +98,9 @@ jobs: python -m pytest publish: + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags')}} runs-on: ubuntu-latest - needs: test + needs: [test, test_openmc] steps: - name: Download build @@ -90,6 +109,7 @@ jobs: name: dist path: dist - name: Publish wheel artifact to TestPyPI + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }} env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} diff --git a/.gitignore b/.gitignore index 4e04ee6..4493508 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ *.out *.app source_generator +testprog # Build path build/ diff --git a/.gitmodules b/.gitmodules index 02ee8fa..2726187 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "pybind11"] path = pybind11 url = https://github.com/pybind/pybind11 -[submodule "pugixml"] - path = pugixml - url = https://github.com/zeux/pugixml.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 27626d3..add2676 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,92 +1,5 @@ cmake_minimum_required(VERSION 3.3 FATAL_ERROR) project(parametric_plasma_source) -set(CMAKE_VERBOSE_MAKEFILE OFF) - -set(SRC_DIR parametric_plasma_source) - -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif() - -set(CMAKE_CXX_FLAGS "-Wall") -set(CMAKE_CXX_FLAGS_DEBUG "-g") -set(CMAKE_CXX_FLAGS_RELEASE "-O3") - -# Ensure submodules are available and up to date -find_package(Git QUIET) -if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") - # Update submodules as needed - option(GIT_SUBMODULE "Check submodules during build" ON) - if(GIT_SUBMODULE) - message(STATUS "Submodule update") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) - if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") - endif() - endif() -endif() - -if(NOT EXISTS "${PROJECT_SOURCE_DIR}/pybind11/CMakeLists.txt") - message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") -endif() - -# Build source_sampling -list(APPEND source_sampling_SOURCES - ${SRC_DIR}/source_sampling.cpp - ${SRC_DIR}/plasma_source.cpp -) - -# Use output paths from OpenMC install -# If OpenMC isn't available then we won't be able to build the plugin -# However, the package can still be run by using the sampling methods directly -# So don't terminate the build -find_package(OpenMC QUIET) - -if(OpenMC_FOUND) - # Build the source_sampling OpenMC plugin if OpenMC is available - set(OPENMC_INC_DIR ${OpenMC_DIR}/../../../include/openmc) - set(OPENMC_LIB_DIR ${OpenMC_DIR}/../../../lib) - - add_library(source_sampling SHARED ${source_sampling_SOURCES}) - - find_library(OPENMC_LIB openmc HINTS ${OPENMC_LIB_DIR} OPTIONAL) - - if (OPENMC_LIB) - set_target_properties(source_sampling PROPERTIES PREFIX "") - set_target_properties(source_sampling PROPERTIES POSITION_INDEPENDENT_CODE ON) - target_include_directories(source_sampling PUBLIC ${OPENMC_INC_DIR}) - target_link_libraries(source_sampling ${OPENMC_LIB} gfortran) - endif() -else() - message(WARNING "Unable to find OpenMC installation - the source_sampling plugin will not be built.") -endif() - -# Build plasma_source Python bindings -list(APPEND plasma_source_pybind_SOURCES - ${SRC_DIR}/plasma_source.cpp - ${SRC_DIR}/plasma_source_pybind.cpp -) - -add_subdirectory(pybind11) - -pybind11_add_module(plasma_source ${plasma_source_pybind_SOURCES}) - -add_subdirectory(pugixml) - -# Build source_generator -list(APPEND source_generator_SOURCES - ${SRC_DIR}/source_generator.cpp -) - -add_executable(source_generator ${source_generator_SOURCES}) - -find_package(HDF5 REQUIRED) - -set_target_properties(source_generator PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(source_generator PUBLIC ${OPENMC_INC_DIR}) -target_include_directories(source_generator PUBLIC ${HDF5_INCLUDE_DIRS}) -target_include_directories(source_generator PUBLIC ${OPENMC_DIR}/vendor/pugixml) -target_link_libraries(source_generator ${OPENMC_LIB} ${HDF5_LIBRARIES} stdc++fs) +include(parametric_plasma_source/CMakeLists.txt) +include(tests/src/CMakeLists.txt) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..382d847 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ + +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..0c8ba44 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,34 @@ +# Contributing to parametric-plasma-source + +Welcome to parametric-plasma-source - we hope you find the code useful. This guide lays out +a few pointers for how to contribute your own issues or changes to the project. + +## Code of Conduct + +Participants in the development of the parametric-plasma-source project are governed by the +[Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. +Please report unacceptable behavior to the open-radiation-sources team. + +## Raising an Issue + +Please report any bugs in the parametric-plasma-source, or suggest any feature enhancements, +via the [Issues](https://github.com/open-radiation-sources/parametric-plasma-source) listed +in GitHub. Before submitting, we ask that you check the currently open issues in case someone +has beaten you to it! + +## Submitting Changes + +If you would like to actively develop a fix for a bug, or implementation of a feature, then +please indicate so when you raise your Issue. All changes are reviewed via pull requests, +so please create your own fork of the parametric-plasma-source project and create a PR when +your changes are ready. + +## Releasing New Versions + +New versions of parametric-plasma-source will be released from time to time. This is currently +a semi-manual process, to control the generation of new releases and tags. A release is +initiated by incrementing the `__version__` value in the module's `__init__.py` in the `develop` +branch. When this change is then merged into the `main` branch, a new pre-release build will be +uploaded to [Test PyPI](https://test.pypi.org/project/parametric-plasma-source). The release is +then created in GitHub, which tags the repository with the same version and uploads the release +build to [PyPI](https://pypi.org/project/parametric-plasma-source). diff --git a/parametric_plasma_source/CMakeLists.txt b/parametric_plasma_source/CMakeLists.txt new file mode 100644 index 0000000..a29e4c1 --- /dev/null +++ b/parametric_plasma_source/CMakeLists.txt @@ -0,0 +1,92 @@ +cmake_minimum_required(VERSION 3.3 FATAL_ERROR) +project(parametric_plasma_source) + +set(SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src) + +message(STATUS ${SRC_DIR}) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(CMAKE_CXX_FLAGS "-Wall") +set(CMAKE_CXX_FLAGS_DEBUG "-g") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +# Ensure submodules are available and up to date +find_package(Git QUIET) +if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/../.git") + # Update submodules as needed + option(GIT_SUBMODULE "Check submodules during build" ON) + if(GIT_SUBMODULE) + message(STATUS "Submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/.. + RESULT_VARIABLE GIT_SUBMOD_RESULT) + if(NOT GIT_SUBMOD_RESULT EQUAL "0") + message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + endif() + endif() + + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../pybind11 ${CMAKE_CURRENT_LIST_DIR}/../pybind11/build) +else() + message(STATUS "Unable to get git submodules, finding pybind11") + find_package(pybind11 REQUIRED HINTS ${PYBIND11_PATH}) + + if(NOT pybind11_FOUND) + message(FATAL_ERROR "The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.") + endif() +endif() + +# Build source_sampling +list(APPEND source_sampling_SOURCES + ${SRC_DIR}/source_sampling.cpp + ${SRC_DIR}/plasma_source.cpp +) + +# Use output paths from OpenMC install +# If OpenMC isn't available then we won't be able to build the plugin +# However, the package can still be run by using the sampling methods directly +# So don't terminate the build +find_package(OpenMC QUIET) + +if(OpenMC_FOUND) + # Build the source_sampling OpenMC plugin if OpenMC is available + set(OPENMC_INC_DIR ${OpenMC_DIR}/../../../include) + set(OPENMC_LIB_DIR ${OpenMC_DIR}/../../../lib) + + add_library(source_sampling SHARED ${source_sampling_SOURCES}) + + find_library(OPENMC_LIB openmc HINTS ${OPENMC_LIB_DIR} OPTIONAL) + + if (OPENMC_LIB) + set_target_properties(source_sampling PROPERTIES PREFIX "") + set_target_properties(source_sampling PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(source_sampling PUBLIC ${OPENMC_INC_DIR}) + target_link_libraries(source_sampling ${OPENMC_LIB} gfortran) + endif() + + # Build source_generator if OpenMC is available + list(APPEND source_generator_SOURCES + ${SRC_DIR}/source_generator.cpp + ) + + add_executable(source_generator ${source_generator_SOURCES}) + + find_package(HDF5 REQUIRED) + + set_target_properties(source_generator PROPERTIES POSITION_INDEPENDENT_CODE ON) + target_include_directories(source_generator PUBLIC ${OPENMC_INC_DIR}) + target_include_directories(source_generator PUBLIC ${HDF5_INCLUDE_DIRS}) + target_link_libraries(source_generator ${OPENMC_LIB} ${HDF5_LIBRARIES} stdc++fs) +else() + message(WARNING "Unable to find OpenMC installation - the source_sampling plugin and source_generator executable will not be built.") +endif() + +# Build plasma_source Python bindings +list(APPEND plasma_source_pybind_SOURCES + ${SRC_DIR}/plasma_source.cpp + ${SRC_DIR}/plasma_source_pybind.cpp +) + +pybind11_add_module(plasma_source ${plasma_source_pybind_SOURCES}) diff --git a/parametric_plasma_source/__init__.py b/parametric_plasma_source/__init__.py index 0d71a4b..5ddc44c 100644 --- a/parametric_plasma_source/__init__.py +++ b/parametric_plasma_source/__init__.py @@ -1,9 +1,65 @@ import os +import subprocess +import warnings + +__version__ = "0.0.9.dev4" PLASMA_SOURCE_PATH = os.path.dirname(__file__) SOURCE_SAMPLING_PATH = os.sep.join([PLASMA_SOURCE_PATH, "source_sampling.so"]) +SOURCE_GENERATOR_PATH = os.sep.join([PLASMA_SOURCE_PATH, "source_generator"]) +HAS_OPENMC = True try: from .plasma_source import * except ImportError: - print("The plasma_source module could not be found. Please compile before using.") + HAS_OPENMC = False + warnings.warn("The plasma_source module could not be found. Please compile before using.") + + +def sample_source_openmc( + source, + source_sampling_path=SOURCE_SAMPLING_PATH, + num_particles=1000, + output_dir=".", + verbosity=5 +): + """ + Sample a source using OpenMC + + Parameters + ---------- + source: PlasmaSource + The source to sample. + source_sampling_path: str + Optional path to the OpenMC source plugin to use for the sampling, by default the + packaged source sampling shared object. + num_particles: int + Optional number of particles to sample, by default 1000. + output_dir: str + Optional path to directory that the output h5 file will be written to, by default + the current directory. + verbosity: int + Optional verbosity level, by default 5. + + Returns + ------- + output: CompletedProcess + The output from the source sampling process. + """ + if HAS_OPENMC: + source_generator_args = [ + SOURCE_GENERATOR_PATH, + "-l", + SOURCE_SAMPLING_PATH, + "-i", + str(source), + "-n", + str(num_particles), + "-o", + output_dir, + "-v", + str(verbosity) + ] + return subprocess.run(source_generator_args, check=True, stdout=subprocess.PIPE) + else: + raise RuntimeError("Unable to sample using OpenMC as OpenMC is not installed.") diff --git a/parametric_plasma_source/fortran_api/build_lib.sh b/parametric_plasma_source/fortran_api/build_lib.sh index f5fe35f..5d96d3d 100755 --- a/parametric_plasma_source/fortran_api/build_lib.sh +++ b/parametric_plasma_source/fortran_api/build_lib.sh @@ -12,6 +12,6 @@ else fi echo Building fortran api plasma source library with $FC and $CXX -rm -rf *.o libplasmasource.a *.mod testprog; $CXX -c ../plasma_source.cpp ../plasma_source_api.cpp -std=c++11; ar cr libplasmasource.a *.o; $FC -o testprog plasma_source_module.F90 testprog.F90 -lplasmasource -L./ -lstdc++ +rm -rf *.o libplasmasource.a *.mod testprog; $CXX -c ../src/plasma_source.cpp ../src/plasma_source_api.cpp -std=c++11; ar cr libplasmasource.a *.o; $FC -o testprog plasma_source_module.F90 testprog.F90 -lplasmasource -L./ -lstdc++ diff --git a/parametric_plasma_source/Plasma_source.h b/parametric_plasma_source/src/Plasma_source.h similarity index 100% rename from parametric_plasma_source/Plasma_source.h rename to parametric_plasma_source/src/Plasma_source.h diff --git a/parametric_plasma_source/plasma_source.cpp b/parametric_plasma_source/src/plasma_source.cpp similarity index 97% rename from parametric_plasma_source/plasma_source.cpp rename to parametric_plasma_source/src/plasma_source.cpp index 4af4941..f5375c7 100644 --- a/parametric_plasma_source/plasma_source.cpp +++ b/parametric_plasma_source/src/plasma_source.cpp @@ -60,7 +60,7 @@ void PlasmaSource::sample(std::array random_numbers, double &u, double &v, double &w, - double &E) { + double &E) const { double radius = 0.; int bin = 0; sample_radial(random_numbers[0],random_numbers[1],radius,bin); @@ -77,7 +77,7 @@ void PlasmaSource::sample(std::array random_numbers, * sample the pdf src_profile, to generate the sampled minor radius */ void PlasmaSource::sample_radial(double rn_store1, double rn_store2, - double &sampled_radius, int &sampled_bin) { + double &sampled_radius, int &sampled_bin) const { for ( int i = 0 ; i < numberOfBins ; i++ ) { if ( rn_store1 <= source_profile[i] ) { @@ -103,7 +103,7 @@ void PlasmaSource::sample_radial(double rn_store1, double rn_store2, * sample the energy of the neutrons, updates energy neutron in mev */ void PlasmaSource::sample_energy(const int bin_number, double random_number1, double random_number2, - double &energy_neutron) { + double &energy_neutron) const { // generate the normally distributed number const double twopi = 6.28318530718; double sample1 = std::sqrt(-2.0*std::log(random_number1)); @@ -120,7 +120,7 @@ void PlasmaSource::sample_energy(const int bin_number, double random_number1, do */ void PlasmaSource::convert_rad_to_rz( const double minor_sampled, const double rn_store, - double &radius, double &height) + double &radius, double &height) const { const double twopi = 6.28318530718; @@ -140,7 +140,7 @@ void PlasmaSource::convert_rad_to_rz( const double minor_sampled, * convert rz_to_xyz */ void PlasmaSource::convert_r_to_xy(const double r, const double rn_store, - double &x, double &y) + double &x, double &y) const { double toroidal_extent = maxToroidalAngle - minToroidalAngle; @@ -270,7 +270,7 @@ double PlasmaSource::dt_xs(double ion_temp) void PlasmaSource::isotropic_direction(const double random1, const double random2, double &u, double &v, - double &w) { + double &w) const { double t = 2*M_PI*random1; double p = acos(1. - 2.*random2); diff --git a/parametric_plasma_source/plasma_source.hpp b/parametric_plasma_source/src/plasma_source.hpp similarity index 96% rename from parametric_plasma_source/plasma_source.hpp rename to parametric_plasma_source/src/plasma_source.hpp index 8f81148..86b18b4 100644 --- a/parametric_plasma_source/plasma_source.hpp +++ b/parametric_plasma_source/src/plasma_source.hpp @@ -45,7 +45,7 @@ class PlasmaSource { double &u, double &v, double &w, - double &E); + double &E) const; /* * Function to setup the plasma source in the first case. @@ -77,13 +77,13 @@ class PlasmaSource { void sample_radial(double rn_store1, double rn_store2, double &sampled_radius, - int &sampled_bin); + int &sampled_bin) const; /* * sample the neutron energy in MeV */ void sample_energy(const int bin_number, double random_number1, double random_number2, - double &energy_neutron); + double &energy_neutron) const; /* * take the sampled minor radius and convert to cylindrical coordinates @@ -91,12 +91,12 @@ class PlasmaSource { void convert_rad_to_rz(const double minor_sampled, const double rn_store, double &radius, - double &height); + double &height) const; /* * convert partial cylindrical coords to xyz */ - void convert_r_to_xy(const double r, const double rn_store, double &x, double &y); + void convert_r_to_xy(const double r, const double rn_store, double &x, double &y) const; /* * get an isotropically direction vector @@ -105,7 +105,7 @@ class PlasmaSource { const double random2, double &u, double &v, - double &w); + double &w) const; /* * get a key-value pair string representation of this instance of the source diff --git a/parametric_plasma_source/plasma_source_api.cpp b/parametric_plasma_source/src/plasma_source_api.cpp similarity index 95% rename from parametric_plasma_source/plasma_source_api.cpp rename to parametric_plasma_source/src/plasma_source_api.cpp index 98fc9e3..7bda75e 100644 --- a/parametric_plasma_source/plasma_source_api.cpp +++ b/parametric_plasma_source/src/plasma_source_api.cpp @@ -59,6 +59,6 @@ void Sample_Plasma_Source(PLASMASOURCE* source, std::copy_n(std::begin(random_numbers_c),8,std::begin(random_numbers)); - source->SampleSource(random_numbers,x,y,z,u,v,w,E); + source->sample(random_numbers,x,y,z,u,v,w,E); } diff --git a/parametric_plasma_source/plasma_source_pybind.cpp b/parametric_plasma_source/src/plasma_source_pybind.cpp similarity index 100% rename from parametric_plasma_source/plasma_source_pybind.cpp rename to parametric_plasma_source/src/plasma_source_pybind.cpp diff --git a/parametric_plasma_source/source_generator.cpp b/parametric_plasma_source/src/source_generator.cpp similarity index 76% rename from parametric_plasma_source/source_generator.cpp rename to parametric_plasma_source/src/source_generator.cpp index 99be053..d7eb50a 100644 --- a/parametric_plasma_source/source_generator.cpp +++ b/parametric_plasma_source/src/source_generator.cpp @@ -1,8 +1,6 @@ #include #include -#include "pugixml.hpp" - #include "openmc/bank.h" #include "openmc/constants.h" #include "openmc/message_passing.h" @@ -12,14 +10,14 @@ namespace source_generator { - void print_settings(pugi::xml_node &root) + void print_settings(std::string path_source_library, std::string source_parameters) { using namespace openmc; std::cout << "Settings:" << std::endl; std::cout << " Number of particles: " << settings::n_particles << std::endl; - std::cout << " Source library: " << root.child("source").attribute("library").value() << std::endl; - std::cout << " Source parameters: " << root.child("source").attribute("parameters").value() << std::endl; + std::cout << " Source library: " << path_source_library << std::endl; + std::cout << " Source parameters: " << source_parameters << std::endl; std::cout << " Output path: " << settings::path_output << std::endl; std::cout << " Verbosity: " << settings::verbosity << std::endl; std::cout << std::endl; @@ -55,12 +53,10 @@ namespace source_generator settings::verbosity = 5; } - int parse_command_line(int argc, char* argv[], pugi::xml_node &root) + int parse_command_line(int argc, char* argv[], std::string &path_source_library, std::string &source_parameters) { using namespace openmc; - root.append_child("source"); - for (int i=1; i < argc; ++i) { std::string arg {argv[i]}; @@ -74,13 +70,12 @@ namespace source_generator else if (arg == "-l" || arg == "--library") { i += 1; - root.child("source").append_attribute("library").set_value(argv[i]); - settings::path_source_library = argv[i]; + path_source_library = argv[i]; } else if (arg == "-i" || arg == "--input") { i += 1; - root.child("source").append_attribute("parameters").set_value(argv[i]); + source_parameters = argv[i]; } else if (arg == "-o" || arg == "--output") { @@ -102,13 +97,13 @@ namespace source_generator bool missing_arg = false; - if (!root.child("source").attribute("library")) + if (path_source_library.empty()) { std::cout << "The --library or -l argument is mandatory and must be set." << std::endl; missing_arg = true; } - if (!root.child("source").attribute("parameters")) + if (source_parameters.empty()) { std::cout << "The --input or -i argument is mandatory and must be set." << std::endl; missing_arg = true; @@ -128,12 +123,12 @@ namespace source_generator int main(int argc, char* argv[]) { - pugi::xml_document doc; - pugi::xml_node root = doc.append_child("settings"); + std::string path_source_library; + std::string source_parameters; source_generator::set_defaults(); - int run = source_generator::parse_command_line(argc, argv, root); + int run = source_generator::parse_command_line(argc, argv, path_source_library, source_parameters); if (run < 0) { @@ -142,14 +137,15 @@ int main(int argc, char* argv[]) if (openmc::settings::verbosity >= 5) { - source_generator::print_settings(root); + source_generator::print_settings(path_source_library, source_parameters); } std::cout << "Sampling source:" << std::endl; - openmc::SourceDistribution source = openmc::SourceDistribution(root.child("source")); + openmc::model::external_sources.push_back(std::make_unique(path_source_library, source_parameters)); openmc::calculate_work(); openmc::allocate_banks(); openmc::initialize_source(); + std::cout << "Source sampling completed." << std::endl; return 0; } diff --git a/parametric_plasma_source/source_sampling.cpp b/parametric_plasma_source/src/source_sampling.cpp similarity index 95% rename from parametric_plasma_source/source_sampling.cpp rename to parametric_plasma_source/src/source_sampling.cpp index 49b71a5..89a0ef7 100644 --- a/parametric_plasma_source/source_sampling.cpp +++ b/parametric_plasma_source/src/source_sampling.cpp @@ -6,7 +6,7 @@ #include "plasma_source.hpp" // defines a class that wraps our PlasmaSource and exposes it to OpenMC. -class SampledSource : public openmc::CustomSource { +class SampledSource : public openmc::Source { private: // the source that we will sample from plasma_source::PlasmaSource source; @@ -19,7 +19,7 @@ class SampledSource : public openmc::CustomSource { // so that the source can be sampled from. // essentially wraps the sample_source method on the source and populates the // relevant values in the openmc::Particle::Bank. - openmc::Particle::Bank sample(uint64_t* seed) { + openmc::Particle::Bank sample(uint64_t* seed) const { openmc::Particle::Bank particle; // random numbers sampled from openmc::prn diff --git a/pugixml b/pugixml deleted file mode 160000 index 22401ba..0000000 --- a/pugixml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 22401bafaff996baecfb694ddc754855e184d377 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..0ba118c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools", "wheel", "cmake", "pybind11"] +build-backend = "setuptools.build_meta" diff --git a/requirements-develop.txt b/requirements-develop.txt index 50344e4..d554342 100644 --- a/requirements-develop.txt +++ b/requirements-develop.txt @@ -1,5 +1,7 @@ -black -flake8 -pytest -requests -wheel +black==20.8b1 +build==0.1.0 +cmake==3.18.2 +flake8==3.8.3 +pybind11==2.6.0 +pytest==6.0.1 +wheel==0.34.2 diff --git a/setup.py b/setup.py index cec54a1..bfd83a5 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,18 @@ -import json import os -import requests +import pybind11 import subprocess import sys -from setuptools import setup, find_packages, Extension +from setuptools import setup, Extension from setuptools.command.build_ext import build_ext +with open("parametric_plasma_source/__init__.py", "r") as f: + for line in f.readlines(): + if "__version__" in line: + version = line.split()[-1].strip('"') + + class CMakeExtention(Extension): def __init__(self, name, sourcedir=""): Extension.__init__(self, name, sources=[]) @@ -18,7 +23,7 @@ class CMakeBuild(build_ext): def run(self): try: subprocess.check_output(["cmake", "--version"]) - except OSError: + except FileNotFoundError: raise RuntimeError( "CMake must be installed to build the " "following extentions: " @@ -36,7 +41,9 @@ def build_extension(self, ext): cmake_args = [ "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, "-DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=" + extdir, + "-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=" + extdir, "-DPYTHON_EXECUTABLE=" + sys.executable, + "-DPYBIND11_PATH=" + pybind11.commands.DIR, ] cfg = "Debug" if self.debug else "Release" @@ -53,77 +60,17 @@ def build_extension(self, ext): if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) subprocess.check_call( - ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp, env=env + ["cmake", extdir] + cmake_args, cwd=self.build_temp, env=env ) subprocess.check_call( - ["cmake", "--build", "."] + build_args, cwd=self.build_temp - ) - - -def get_version(release_override="0.0.1"): - def get_last_version_root(last_version): - if ".post" in last_version or ".dev" in last_version: - last_version_root = ".".join(last_version.split(".")[:-1]) - else: - last_version_root = last_version - return last_version_root - - cwd = os.path.dirname(os.path.realpath(__file__)) - git_version = subprocess.check_output( - ["git", "describe", "--always", "--tags"], stderr=None, cwd=cwd - ).strip().decode("utf-8") - - if "." not in git_version: - # Git doesn't know about a tag yet, so set the version root to release_override - version_root = release_override - else: - version_root = git_version.split("-")[0] - - if "." not in git_version or "-" in git_version: - # This commit doesn't correspond to a tag, so mark it as post or dev - response = requests.get( - "https://test.pypi.org/pypi/parametric-plasma-source/json" + [ + "cmake", + "--build", + ".", + ] + + build_args, + cwd=self.build_temp, ) - if response.status_code == 200: - # Response from TestPyPI was successful - get latest version and increment - last_version = json.loads(response.content)["info"]["version"] - last_version_root = get_last_version_root(last_version) - - if last_version_root == version_root: - # We're still on the same released version, so increment the 'post' - post_count = 1 - if "post" in last_version: - post_index = last_version.rfind("post") + 4 - post_count = int(last_version[post_index:]) - post_count += 1 - version = version_root + ".post" + str(post_count) - else: - response = requests.get( - "https://pypi.org/pypi/parametric-plasma-source/json" - ) - dev_count = 1 - if response.status_code == 200: - # Response from PyPI was successful - get dev version and increment - last_version = json.loads(response.content)["info"]["version"] - last_version_root = get_last_version_root(last_version) - - if last_version_root == version_root: - if "dev" in last_version: - dev_index = last_version.rfind("dev") + 3 - dev_count = int(last_version[dev_index:]) - dev_count += 1 - version = version_root + ".dev" + str(dev_count) - else: - # Bad response from TestPyPI, so use git commits (requires git history) - # NOTE: May cause version clashes on mutliple branches - use test.pypi - # to avoid this. - num_commits = subprocess.check_output( - ["git", "rev-list", "--count", "HEAD"], stderr=None, cwd=cwd - ).strip().decode("utf-8") - version = release_override + ".post" + num_commits - else: - version = version_root - return version with open("README.md", "r") as fh: @@ -131,16 +78,25 @@ def get_last_version_root(last_version): setup( name="parametric_plasma_source", - version=get_version("0.0.6"), + version=version, author="Andrew Davis", author_email="jonathan.shimwell@ukaea.uk", description="Parametric plasma source for fusion simulations in OpenMC", long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/makeclean/parametric-plasma-source/", - packages=find_packages(), + packages=["parametric_plasma_source"], ext_modules=[CMakeExtention("parametric_plasma_source/plasma_source")], - package_data={"parametric_plasma_source": ["source_sampling.so"]}, + package_data={ + "parametric_plasma_source": [ + "src/plasma_source.cpp", + "src/plasma_source.hpp", + "src/plasma_source_pybind.cpp", + "src/source_sampling.cpp", + "src/source_generator.cpp", + "CMakeLists.txt", + ] + }, cmdclass=dict(build_ext=CMakeBuild), classifiers=[ "Programming Language :: Python :: 3", diff --git a/tests/src/CMakeLists.txt b/tests/src/CMakeLists.txt new file mode 100644 index 0000000..67aec22 --- /dev/null +++ b/tests/src/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(tests ${PROJECT_SOURCE_DIR}/tests/src/test_plasma_source.cpp) + +target_include_directories(tests PUBLIC ${PROJECT_SOURCE_DIR}/parametric_plasma_source/src ${PROJECT_SOURCE_DIR}/tests/src) +target_link_libraries(tests source_sampling) diff --git a/tests/src/catch.hpp b/tests/src/catch.hpp new file mode 100644 index 0000000..2a2d77a --- /dev/null +++ b/tests/src/catch.hpp @@ -0,0 +1,17877 @@ +/* + * Catch v2.13.3 + * Generated: 2020-10-31 18:20:31.045274 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 3 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX == 1 +# define CATCH_PLATFORM_MAC +# elif TARGET_OS_IPHONE == 1 +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// We have to avoid both ICC and Clang, because they try to mask themselves +// as gcc, and we want only GCC in this block +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(__clang__) // Handle Clang masquerading for msvc +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if __cpp_lib_byte > 0 + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template