diff --git a/.clang-format b/.clang-format index 6f7fef667..5839c7261 100644 --- a/.clang-format +++ b/.clang-format @@ -100,6 +100,8 @@ IncludeCategories: Priority: 2 - Regex: '^"utils\/.*.h"' Priority: 2 + - Regex: '^"traits\/.*.h"' + Priority: 2 - Regex: '^"metrics\/.*\.h"' Priority: 3 - Regex: '^"framework\/.*\.h"' diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 000000000..788e361f6 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,36 @@ +Checks: > + -*, + bugprone-*, + performance-*, + clang-analyzer-*, + clang-diagnostic-*, + misc-include-cleaner, + misc-const-correctness, + misc-unused-parameters, + misc-unused-using-decls, + cppcoreguidelines-pro-type-member-init, + cppcoreguidelines-slicing, + cppcoreguidelines-virtual-class-destructor, + cppcoreguidelines-prefer-member-initializer, + modernize-use-nullptr, + modernize-use-override, + modernize-use-default-member-init, + modernize-use-emplace, + modernize-loop-convert, + modernize-make-unique, + modernize-make-shared, + modernize-use-using, + readability-container-size-empty, + readability-redundant-*, + readability-simplify-boolean-expr, + readability-misleading-indentation, + readability-inconsistent-declaration-parameter-name, + -misc-const-correctness, + -bugprone-easily-swappable-parameters, + -bugprone-unchecked-optional-access +WarningsAsErrors: "*" +HeaderFilterRegex: "src/.*" +FormatStyle: file +SystemHeaders: false +CheckOptions: + misc-include-cleaner.IgnoreHeaders: 'adios2\.h;adios2/.*;Kokkos_.*\.hpp' diff --git a/.github/workflows/pgens-tests.yml b/.github/workflows/pgens-tests.yml new file mode 100644 index 000000000..c9e07fb0b --- /dev/null +++ b/.github/workflows/pgens-tests.yml @@ -0,0 +1,70 @@ +name: Premerge compilation on CPUs for all pgens + +on: + push: + pull_request: + types: [ready_for_review] + +jobs: + check-commit: + runs-on: ubuntu-24.04 + outputs: + run_tests: ${{ steps.check_message.outputs.run_tests }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Check commit message + id: check_message + run: | + if [[ "${{ github.event_name }}" == "pull_request" && "${{ github.event.action }}" == "ready_for_review" ]]; then + echo "run_tests=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + if git log -1 --pretty=%B | grep -q "RUNPGENS"; then + echo "run_tests=true" >> "$GITHUB_OUTPUT" + else + echo "run_tests=false" >> "$GITHUB_OUTPUT" + fi + pgens: + needs: check-commit + if: needs.check-commit.outputs.run_tests == 'true' + name: PGENS (mpi=${{ matrix.mpi }}) + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + mpi: [ON, OFF] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + - name: Install GCC 14 and build tools + run: | + sudo apt-get update + sudo apt-get install -y gcc-14 g++-14 gfortran-14 build-essential wget libevent-dev libhwloc-dev + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 50 + sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 50 + sudo update-alternatives --install /usr/bin/gfortran gfortran /usr/bin/gfortran-14 50 + gcc --version + g++ --version + - name: Install CMake (3.x) + run: | + sudo apt-get install -y cmake + cmake --version + - name: Install OpenMPI 5 + run: | + if [ "${{ matrix.mpi }}" = "ON" ]; then + sudo apt-get install -y libopenmpi-dev openmpi-bin + mpirun --version + fi + - name: Compile all pgens + run: | + if [ "${{ matrix.mpi }}" = "ON" ]; then + export CC=mpicc + export CXX=mpicxx + else + export CC=gcc-14 + export CXX=g++-14 + fi + ./dev/scripts/tests.sh --build build --with_pgens --flags "-D mpi=${{ matrix.mpi }} -D output=OFF" diff --git a/.github/workflows/cpuarch.yml b/.github/workflows/unit-tests.yml similarity index 51% rename from .github/workflows/cpuarch.yml rename to .github/workflows/unit-tests.yml index 2c68e984f..1a12b589a 100644 --- a/.github/workflows/cpuarch.yml +++ b/.github/workflows/unit-tests.yml @@ -1,4 +1,4 @@ -name: CPU Compilation/Unit Tests +name: Premerge tests on CPUs with different configurations on: push: @@ -20,7 +20,7 @@ jobs: echo "run_tests=true" >> "$GITHUB_OUTPUT" exit 0 fi - if git log -1 --pretty=%B | grep -q "CPUTEST"; then + if git log -1 --pretty=%B | grep -q "RUNTESTS"; then echo "run_tests=true" >> "$GITHUB_OUTPUT" else echo "run_tests=false" >> "$GITHUB_OUTPUT" @@ -28,7 +28,7 @@ jobs: tests: needs: check-commit if: needs.check-commit.outputs.run_tests == 'true' - name: UNIT_TESTS (precision=${{ matrix.precision }}, mpi=${{ matrix.mpi }}, output=${{ matrix.output }}) + name: UNIT_TESTS with (precision=${{ matrix.precision }}, mpi=${{ matrix.mpi }}, output=${{ matrix.output }}) runs-on: ubuntu-24.04 strategy: fail-fast: false @@ -67,7 +67,7 @@ jobs: sudo apt-get install -y libopenmpi-dev openmpi-bin mpirun --version fi - - name: Configure + - name: Compile and run all tests run: | if [ "${{ matrix.mpi }}" = "ON" ]; then export CC=mpicc @@ -76,56 +76,4 @@ jobs: export CC=gcc-14 export CXX=g++-14 fi - cmake -B build -D TESTS=ON -D precision=${{ matrix.precision }} -D mpi=${{ matrix.mpi }} -D output=${{ matrix.output }} - - name: Compile - run: cmake --build build -j "$(nproc)" - - name: Run tests - run: ctest --test-dir build --output-on-failure - pgens: - needs: check-commit - if: needs.check-commit.outputs.run_tests == 'true' - name: PGENS (pgen=${{ matrix.pgen }}) - runs-on: ubuntu-24.04 - strategy: - fail-fast: false - matrix: - pgen: - [ - streaming, - turbulence, - reconnection, - shock, - magnetosphere, - accretion, - wald, - examples/custom_emission, - examples/external_fields, - examples/match_fix_field_boundaries, - examples/custom_energy_distribution, - examples/custom_spatial_distribution, - examples/atmosphere, - examples/replenish_injector, - ] - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Install GCC 14 and build tools - run: | - sudo apt-get update - sudo apt-get install -y gcc-14 g++-14 gfortran-14 build-essential wget libevent-dev libhwloc-dev - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 50 - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-14 50 - sudo update-alternatives --install /usr/bin/gfortran gfortran /usr/bin/gfortran-14 50 - gcc --version - g++ --version - - name: Install CMake (3.x) - run: | - sudo apt-get install -y cmake - cmake --version - - name: Configure - run: | - cmake -B build -D pgen=${{ matrix.pgen }} -D output=OFF - - name: Compile - run: cmake --build build -j "$(nproc)" + ./dev/scripts/tests.sh --build build --with_tests --flags "-D precision=${{ matrix.precision }} -D mpi=${{ matrix.mpi }} -D output=${{ matrix.output }}" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 62248c605..e683ed8c5 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ temp*/ logs/ *.log *.bak +temp* +tmp* # Trash files .trash/ @@ -61,3 +63,5 @@ action-token *.vim ignore-* tombi/ +tidy/ +.claude \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..b96326906 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,123 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is an astrophysical particle-in-cell plasma simulation code which works in arbitrary curvilinear coordinates and supports multiple simulation engines. It is built using the Kokkos performance portability library with C++20. It is parallelized with MPI, and uses the ADIOS2 library for outputting and checkpointing the simulation data. + +## Repository Structure + +``` +entity +├── cmake # additional cmake files +│ ├── adios2Config.cmake # default configurations for in-tree build of adios2 +│ ├── config.cmake # compile-time configuration options +│ ├── defaults.cmake # default values for configurations +│ ├── dependencies.cmake # functions to fetch and build the dependencies +│ ├── kokkosConfig.cmake # default configurations for in-tree build of Kokkos +│ ├── report.cmake # configuration repoting +│ ├── styling.cmake # styling functions +│ └── tests.cmake # root cmake for tests +├── dev # developer-specific tools +│ ├── nix # nix-shells +│ ├── runners # dockerfiles for github runners on different architectures +│ ├── scripts # developer-specific scripts +│ ├── Dockerfile.common # parent docker environment for development +│ ├── Dockerfile.cuda # cuda docker environment +│ ├── Dockerfile.rocm # rocm docker environment +│ ├── welcome.cuda +│ └── welcome.rocm +├── extern # git submodules +│ ├── Kokkos +│ ├── adios2 +│ └── entity-pgens +├── include # included header-only third-party libraries +│ ├── plog +│ └── toml11 +├── minimal # set of minimalist programs for testing MPI/Kokkos/adios2 +├── pgens # problem generators +├── src # main code containing all separate submodules +│ ├── archetypes # archetypes which can be used by the user in problem generators +│ ├── engines # simulation engines +│ ├── framework # main structures, classes and containers +│ ├── global # global definitions and utilities +│ ├── kernels # core kernels (defined as functors) +│ ├── metrics # various metric classes +│ ├── output # functions related to output +│ ├── CMakeLists.txt +│ └── entity.cpp # main entry-point +├── tests # unit tests for all submodules +├── .clang-format # code formatting guidelines for clang-format +├── .gitattributes +├── .gitignore +├── .gitmodules +├── .taplo.toml # formatting guidelines for toml files +├── CITATION +├── CLAUDE.md +├── CMakeLists.txt # root cmake file +├── CODE_OF_CONDUCT.md +├── LICENSE +├── README.md +├── conda-entity-nompi.sh +├── dependencies.py # deployment scripts on various machines +├── docker-compose.yml +└── input.example.toml # most complete toml file with all possible input options +``` + +## Testing + +The code is tested using the `./dev/scripts/tests.sh` script which compiles and runs all the unit tests using `ctest`: + +```sh +./dev/scripts/tests.sh --build build_dir --flags "-D mpi=ON" --with_tests +``` + +All the unit tests are inside the `tests/` directory each within the respective subdirectory; e.g., tests for `src/kernels` are in `tests/kernels`. When testing, build the tests both with and without MPI and, ideally, with and without GPU (when available). + +You can also compile all the problem generators: + +```sh +./dev/scripts/tests.sh --build build_dir --flags "-D mpi=ON" --with_pgens +``` + +## Code guidelines + +* Format of the code is enforced using `clang-format` and `cmake-format`. You can run the formatting on all files with `./dev/scripts/format.sh`. + +* Best practices are also enforced using `clang-tidy`; to generate recommendations for all the files, run `./dev/scripts/tidy.sh --build build_dir` where `build_dir` is the directory where the code was built, or for specific files: `./dev/scripts/tidy.sh --build build_dir --files "(file1|file2).cpp"` or only for the changed files: `./dev/scripts/tidy.sh --build build_dir --changed`. The recommendations will be in the `tidy/` directory. + +* Use `const` and `auto` declarations where possible. + +* For real-valued literals, use `ONE`, `ZERO`, `HALF` etc. instead of `1.0`, `0.0`, `0.5` to ensure the compiler will not need to cast. If the value is not defined as a macro, use `static_cast(123.4)`. + +* Use {} in declarations to signify a null (placeholder) value for the given variable: + ```cpp + auto a { -1 }; // <- value of `a` *will* be changed later (-1 is a placeholder) + auto b = -1; // <- value of `b` is known at the time of declaration (but *may* change later) + const auto b = -1; // <- value of `b` is not expected to change later + ``` + +* Each header file has to have a description at the top, consisting of the following fields: + + * `@file` [required] the name of the file (as it should be included in other files) + * `@brief` [required] brief description of what the file contains + * `@implements` list of class/function/macros implementations + * structs/classes in this section have no prefix (templates are marked with <>) + * functions are marked with their return type, e.g. -> void + * type aliases have a prefix type + * enums or enum-like objects are marked with enum + * macros have a prefix macro + * all of the above are also marked with their respective namespaces (if any): namespace:: + * `@cpp`: list of cpp files that implement the header + * `@namespaces`: list of namespaces defined in the file + * `@macros`: list of macros that the file depends on + * `@note` any additional notes (stack as many as necessary) + +* `#ifdef`/`#define` macros should be avoided. Use C++20 concept and `if constexpr ()` expressions to specialize functions and classes instead (ideally, specialize them explicitly). `#ifdef`-s are only acceptable in platform/library-specific parts of the code (e.g., `MPI_ENABLED`, `GPU_ENABLED`, `DEBUG`, etc.), or for major shortcuts. + +* Header files should start with `#ifndef` ... `#define` ... and end with `#endif`; do not use `#pragma` guards. The name of the macro should be the same as the name of the file in uppercase, with underscores instead of dots and slashes. For example, for `global/utils/formatting.h`, the macro should be `GLOBAL_UTILS_FORMATTING_H`. + +* There is no difference between `.h` and `.hpp` files as both indicate C++ header files. As a consistency convention, we use `.h` for common headers which may be included from multiple `.cpp` files (e.g., metrics), while `.hpp` are very specific headers for only a single (or a couple of) .cpp file (e.g. kernels). + +* Do assertions on parameters and quantities whenever possible. Outside the kernels, use `raise::Error(message, HERE)` and `raise::ErrorIf(condition, message, HERE)` to throw exceptions. Inside the kernels, use `raise::KernelError(HERE, message, **args)`. To enable compile-time errors, use `static_assert(condition, message)`. The `HERE` keyword is macro that includes the filename and line number in the error message. \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 985eebc53..1c4204843 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,35 +28,35 @@ include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/defaults.cmake) # defaults set(DEBUG - ${default_debug} - CACHE BOOL "Debug mode") + ${default_debug} + CACHE BOOL "Debug mode") set(precision - ${default_precision} - CACHE STRING "Precision") + ${default_precision} + CACHE STRING "Precision") set(deposit - ${default_deposit} - CACHE STRING "Deposit") + ${default_deposit} + CACHE STRING "Deposit") set(shape_order - ${default_shape_order} - CACHE STRING "Shape function") + ${default_shape_order} + CACHE STRING "Shape function") set(pgen - ${default_pgen} - CACHE STRING "Problem generator") + ${default_pgen} + CACHE STRING "Problem generator") set(output - ${default_output} - CACHE BOOL "Enable output") + ${default_output} + CACHE BOOL "Enable output") set(mpi - ${default_mpi} - CACHE BOOL "Use MPI") + ${default_mpi} + CACHE BOOL "Use MPI") set(gpu_aware_mpi - ${default_gpu_aware_mpi} - CACHE BOOL "Enable GPU-aware MPI") + ${default_gpu_aware_mpi} + CACHE BOOL "Enable GPU-aware MPI") # -------------------------- Compilation settings -------------------------- # set(CMAKE_CXX_STANDARD 20) @@ -65,42 +65,42 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) if(${DEBUG} STREQUAL "OFF") set(CMAKE_BUILD_TYPE - Release - CACHE STRING "CMake build type") + Release + CACHE STRING "CMake build type") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNDEBUG -O3") else() set(CMAKE_BUILD_TYPE - Debug - CACHE STRING "CMake build type") + Debug + CACHE STRING "CMake build type") set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -DDEBUG -Wall -Wextra -Wno-unknown-pragmas") + "${CMAKE_CXX_FLAGS} -DDEBUG -Wall -Wextra -Wno-unknown-pragmas") endif() # options set(precisions - "single" "double" - CACHE STRING "Precisions") + "single" "double" + CACHE STRING "Precisions") set(deposits - "zigzag" "esirkepov" - CACHE STRING "Deposits") + "zigzag" "esirkepov" + CACHE STRING "Deposits") if(${deposit} STREQUAL "zigzag") set(shape_order - ${default_shape_order} - CACHE STRING "Shape functions") + ${default_shape_order} + CACHE STRING "Shape functions") endif() set(shape_orders - "1;2;3;4;5;6;7;8;9;10;11" - CACHE STRING "Shape orders") + "1;2;3;4;5;6;7;8;9;10;11" + CACHE STRING "Shape orders") include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/config.cmake) # ------------------------- Third-Party Tests ------------------------------ # set(BUILD_TESTING - OFF - CACHE BOOL "Build tests") + OFF + CACHE BOOL "Build tests") # ------------------------ Third-party dependencies ------------------------ # include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/dependencies.cmake) @@ -129,8 +129,8 @@ else() endif() if(("${Kokkos_DEVICES}" MATCHES "CUDA") - OR ("${Kokkos_DEVICES}" MATCHES "HIP") - OR ("${Kokkos_DEVICES}" MATCHES "SYCL")) + OR ("${Kokkos_DEVICES}" MATCHES "HIP") + OR ("${Kokkos_DEVICES}" MATCHES "SYCL")) set(DEVICE_ENABLED ON) else() set(DEVICE_ENABLED OFF) @@ -148,8 +148,8 @@ if(${mpi}) endif() else() set(gpu_aware_mpi - OFF - CACHE BOOL "Use explicit copy when using MPI + GPU") + OFF + CACHE BOOL "Use explicit copy when using MPI + GPU") endif() endif() @@ -175,10 +175,24 @@ add_subdirectory(${SRC_DIR}/framework ${CMAKE_CURRENT_BINARY_DIR}/framework) add_subdirectory(${SRC_DIR}/output ${CMAKE_CURRENT_BINARY_DIR}/output) # ------------------------------- Main source ------------------------------ # -if(NOT ${pgen} STREQUAL ${default_pgen}) - set_problem_generator(${pgen}) - add_subdirectory(${SRC_DIR}/engines ${CMAKE_CURRENT_BINARY_DIR}/engines) - add_subdirectory(${SRC_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src) + +if(NOT DEFINED pgens) + set(single_pgen_mode ON) + if(NOT ${pgen} STREQUAL ${default_pgen}) + set(pgen_suffix "") + set_problem_generator(${pgen}) + add_subdirectory(${SRC_DIR}/engines ${CMAKE_CURRENT_BINARY_DIR}/engines) + add_subdirectory(${SRC_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src) + endif() +else() + set(single_pgen_mode OFF) + foreach(pg ${pgens}) + string(REPLACE "/" "_" pg_nodir ${pg}) + set(pgen_suffix "_${pg_nodir}") + set_problem_generator(${pg}) + add_subdirectory(${SRC_DIR}/engines ${CMAKE_CURRENT_BINARY_DIR}/${pg_nodir}/engines) + add_subdirectory(${SRC_DIR} ${CMAKE_CURRENT_BINARY_DIR}/${pg_nodir}/src) + endforeach() endif() if(TESTS) @@ -186,9 +200,4 @@ if(TESTS) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/tests.cmake) endif() -if(BENCHMARK) - # ------------------------------ Benchmark --------------------------------- # - include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/benchmark.cmake) -endif() - include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/report.cmake) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index cfd678063..0ff56dab1 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -7,7 +7,7 @@ This code of conduct outlines shared principles and expectations for all partici - If you contribute something to the repository, it becomes part of the project and thus will also be regarded as open-source. # Contributions and Credit -- The only attribution we strongly encourage is a citation of either the code repository, or the corresponding method papers (coming soon). +- The only attribution we strongly encourage is a citation of either the code repository, or the corresponding method papers (see `CITATION`). - All contributions are made voluntarily, and there is no expectation of recognition of isolated individuals. - There's no built-in expectation of credit or authorship for modules or changes pushed to the repository. Anyone is free to use any part of the code with no attribution to the author of any specific module or algorithm. - The code is there for everyone to use, and its only goal is to enable the community to produce exciting science! @@ -24,6 +24,6 @@ This code of conduct outlines shared principles and expectations for all partici # Community and Participation - Everyone is welcome in the community. -- Joining meetings on Zoom, using the Slack workspace, giving feedback, taking part in decision making and planning, or following development doesn't require any special status -- it's open to all. +- Joining online meetings on Zoom/Slack, using the Slack workspace, giving feedback, taking part in decision making and planning, or following development doesn't require any special status -- it's open to all. Finally, while we cannot enforce it, we strongly encourage any projects that build on this code to be open source too. If you build upon this project, we welcome transparency and openness in spirit. You may contribute to the code as much or as little as you like; all effort is appreciated, none is required. diff --git a/benchmark/benchmark.cpp b/benchmark/benchmark.cpp deleted file mode 100644 index 98306c92b..000000000 --- a/benchmark/benchmark.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "global.h" - -#include -#include - -auto main(int argc, char* argv[]) -> int { - ntt::GlobalInitialize(argc, argv); - try { - // ... - } catch (const std::exception& e) { - std::cerr << "Error: " << e.what() << std::endl; - GlobalFinalize(); - return 1; - } - GlobalFinalize(); - return 0; -} diff --git a/cmake/benchmark.cmake b/cmake/benchmark.cmake deleted file mode 100644 index af0d334ea..000000000 --- a/cmake/benchmark.cmake +++ /dev/null @@ -1,18 +0,0 @@ -# cmake-lint: disable=C0103 - -set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) - -set(exec benchmark.xc) -set(src ${CMAKE_CURRENT_SOURCE_DIR}/benchmark/benchmark.cpp) - -add_executable(${exec} ${src}) - -set(libs ntt_global ntt_metrics ntt_kernels ntt_archetypes ntt_framework) -if(${output}) - list(APPEND libs ntt_output) -endif() -add_dependencies(${exec} ${libs}) -if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") - list(APPEND libs stdc++fs) -endif() -target_link_libraries(${exec} PRIVATE ${libs}) diff --git a/cmake/config.cmake b/cmake/config.cmake index e9b0de390..a22d7c082 100644 --- a/cmake/config.cmake +++ b/cmake/config.cmake @@ -73,11 +73,12 @@ function(set_problem_generator pgen_name) message(FATAL_ERROR "pgen.hpp file not found in ${pgen_path}") endif() - add_library(ntt_pgen INTERFACE) - target_link_libraries(ntt_pgen INTERFACE ntt_global ntt_framework - ntt_archetypes ntt_kernels) + set(PGEN_TARGET ntt_pgen${pgen_suffix}) + add_library(${PGEN_TARGET} INTERFACE) + target_link_libraries(${PGEN_TARGET} INTERFACE ntt_global ntt_framework + ntt_archetypes ntt_kernels) - target_include_directories(ntt_pgen INTERFACE ${pgen_path}) + target_include_directories(${PGEN_TARGET} INTERFACE ${pgen_path}) set(PGEN ${pgen_name} diff --git a/cmake/report.cmake b/cmake/report.cmake index 0fbd072cf..7e4779468 100644 --- a/cmake/report.cmake +++ b/cmake/report.cmake @@ -1,13 +1,25 @@ if(${PGEN_FOUND}) - printchoices( - "Problem generator" - "pgen" - "${problem_generators}" - ${PGEN} - "" - "${Blue}" - PGEN_REPORT - 0) + if(${single_pgen_mode}) + printchoices( + "Problem generator" + "pgen" + "${problem_generators}" + "${PGEN}" + "" + "${Blue}" + PGEN_REPORT + 0) + else() + printchoices( + "Problem generators" + "pgens" + "${problem_generators}" + "${pgens}" + "" + "${Blue}" + PGEN_REPORT + 0) + endif() endif() if(${TESTS}) @@ -15,7 +27,7 @@ if(${TESTS}) foreach(test_dir IN LISTS TEST_DIRECTORIES) get_property( LOCAL_TEST_NAMES - DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}/tests + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/tests/${test_dir} PROPERTY TESTS) list(APPEND TEST_NAMES ${LOCAL_TEST_NAMES}) endforeach() diff --git a/cmake/styling.cmake b/cmake/styling.cmake index daae19c65..949f87271 100644 --- a/cmake/styling.cmake +++ b/cmake/styling.cmake @@ -84,12 +84,9 @@ function( string(APPEND rstring ":") if(${Padding} EQUAL 0) - list(LENGTH "${Choices}" nchoices) - math(EXPR lastchoice "${nchoices} - 1") - set(longest 0) foreach(ch IN LISTS Choices) - string(LENGTH ${ch} clen) + string(LENGTH "${ch}" clen) if(clen GREATER longest) set(longest ${clen}) endif() @@ -102,70 +99,64 @@ function( endif() math(EXPR lastcol "${ncols} - 1") - set(counter 0) + set(col_pos 0) foreach(ch IN LISTS Choices) - if(NOT ${Value} STREQUAL "") - if(${ch} STREQUAL ${Value}) - set(col ${Color}) - else() - set(col ${Dim}) - endif() - else() - set(col ${Dim}) + if(col_pos EQUAL 0) + string(APPEND rstring "\n ") endif() - if(NOT ${Default} STREQUAL "") - if(${ch} STREQUAL ${Default}) - set(col ${Underline}${col}) + set(col "${Dim}") + if(NOT "${Value}" STREQUAL "") + list(FIND Value "${ch}" _idx) + if(_idx GREATER -1) + set(col "${Color}") endif() endif() + if(NOT "${Default}" STREQUAL "" AND "${ch}" STREQUAL "${Default}") + set(col "${Underline}${col}") + endif() - string(LENGTH "${ch}" clen) - math(EXPR PaddingNeeded "${longest} - ${clen} + 4") + string(APPEND rstring "${col}~ ${ch}${ColorReset}") - if(counter EQUAL ${lastcol} AND NOT ${counter} EQUAL ${lastchoice}) - string(APPEND rstring "${col}~ ${ch}${ColorReset}") - else() - if(counter EQUAL 0) - string(APPEND rstring "\n ") - endif() - string(APPEND rstring "${col}~ ${ch}${ColorReset}") - foreach(i RANGE 0 ${PaddingNeeded}) + if(NOT col_pos EQUAL lastcol) + string(LENGTH "${ch}" clen) + math(EXPR pad "${longest} - ${clen} + 4") + foreach(i RANGE 0 ${pad}) string(APPEND rstring " ") endforeach() endif() - math(EXPR counter "(${counter} + 1) % ${ncols}") + math(EXPR col_pos "(${col_pos} + 1) % ${ncols}") endforeach() else() padto("${rstring}" " " ${Padding} rstring) - if(${Value} STREQUAL "ON") - set(col ${Green}) - elseif(${Value} STREQUAL "OFF") - set(col ${Red}) - else() - set(col ${Color}) - endif() - set(new_choices "") + set(parts "") foreach(ch IN LISTS Choices) - set(elem "${ch}") - if((NOT "${Value}" STREQUAL "") AND (${ch} STREQUAL ${Value})) - set(elem "${col}${ch}${ColorReset}") + if("${ch}" STREQUAL "ON") + set(ch_col "${Green}") + elseif("${ch}" STREQUAL "OFF") + set(ch_col "${Red}") else() - set(elem "${Dim}${ch}${ColorReset}") + set(ch_col "${Color}") endif() - if((NOT "${Default}" STREQUAL "") AND (${ch} STREQUAL ${Default})) + + set(elem "${Dim}${ch}${ColorReset}") + if(NOT "${Value}" STREQUAL "") + list(FIND Value "${ch}" _idx) + if(_idx GREATER -1) + set(elem "${ch_col}${ch}${ColorReset}") + endif() + endif() + if(NOT "${Default}" STREQUAL "" AND "${ch}" STREQUAL "${Default}") set(elem "${Underline}${elem}${ColorReset}") endif() - string(APPEND new_choices "${elem};") + + list(APPEND parts "${elem}") endforeach() - string(LENGTH "${new_choices}" nlen) - math(EXPR nlen "${nlen} - 1") - string(SUBSTRING "${new_choices}" 0 ${nlen} new_choices) - set(Choices ${new_choices}) - string(REPLACE ";" "/" Choices "${Choices}") - string(APPEND rstring "${Choices}") + + string(JOIN "/" joined ${parts}) + string(APPEND rstring "${joined}") endif() set(${OutputString} diff --git a/cmake/tests.cmake b/cmake/tests.cmake index bc397ea53..f226ff45e 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -1,7 +1,7 @@ include(CTest) enable_testing() -set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests) set(TEST_DIRECTORIES "") @@ -13,6 +13,6 @@ list(APPEND TEST_DIRECTORIES framework) list(APPEND TEST_DIRECTORIES output) foreach(test_dir IN LISTS TEST_DIRECTORIES) - add_subdirectory(${SRC_DIR}/${test_dir}/tests - ${CMAKE_CURRENT_BINARY_DIR}/${test_dir}/tests) + add_subdirectory(${SRC_DIR}/${test_dir} + ${CMAKE_CURRENT_BINARY_DIR}/tests/${test_dir}) endforeach() diff --git a/dev/scripts/format.sh b/dev/scripts/format.sh new file mode 100755 index 000000000..e12bef2ec --- /dev/null +++ b/dev/scripts/format.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash + +verify=false + +for arg in "$@"; do + case $arg in + --verify) verify=true ;; + esac +done + +if $verify; then + diff_output="" + + if command -v cmake-format &>/dev/null; then + while IFS= read -r -d '' f; do + if ! diff -q <(cmake-format "$f") "$f" &>/dev/null; then + diff_output+=" $f\n" + fi + done < <(find cmake/ src/ minimal/ tests/ -type f \( -name "*.cmake" -o -name "*.txt" \) -print0) + fi + + if command -v clang-format &>/dev/null; then + while IFS= read -r -d '' f; do + if ! clang-format --style=file --dry-run --Werror "$f" &>/dev/null; then + diff_output+=" $f\n" + fi + done < <(find pgens/ src/ minimal/ tests/ -type f \( -name "*.cpp" -o -name "*.hpp" -o -name "*.h" \) -print0) + fi + + if [ -n "$diff_output" ]; then + echo "Formatting check failed. The following files need formatting:" + printf "$diff_output" + exit 1 + else + echo "All files are properly formatted." + fi +else + if command -v cmake-format &>/dev/null; then + find cmake/ src/ minimal/ tests/ -type f -name "*.cmake" -o -name "*.txt" | xargs cmake-format -i + fi + + if command -v clang-format &>/dev/null; then + find pgens/ src/ minimal/ tests/ -type f -name "*.cpp" -o -name "*.hpp" -o -name "*.h" | xargs clang-format --style=file -i + fi +fi diff --git a/dev/scripts/tests.sh b/dev/scripts/tests.sh new file mode 100755 index 000000000..1b26686b4 --- /dev/null +++ b/dev/scripts/tests.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + +build_dir="" +extra_flags="" +nproc=$(nproc) +with_pgens=false +make_plots=false +with_tests=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --build) + if [[ -z "${2:-}" || "$2" == --* ]]; then + echo "Error: --build requires a value" + exit 1 + fi + build_dir="$2" + shift 2 + ;; + --with_pgens) + with_pgens=true + shift + ;; + --with_tests) + with_tests=true + shift + ;; + --make_plots) + make_plots=true + shift + ;; + --flags) + extra_flags="$2" + shift 2 + ;; + --nproc) + if [[ -z "${2:-}" || "$2" == --* ]]; then + echo "Error: --nproc requires a value" + exit 1 + fi + nproc="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 --build [--flags ] [--with_pgens] [--with_tests] [--make_plots] [--nproc ]" + exit 1 + ;; + esac +done + +if [ "${with_pgens}" = false ] && [ "${with_tests}" = false ]; then + + echo "Error: At least one of --with_pgens or --with_tests must be specified" + exit 1 + +fi + +if [ "${make_plots}" = true ] && [ "${with_pgens}" = false ]; then + + echo "Error: --make_plots requires --with_pgens" + exit 1 + +fi + +if [ "${build_dir}" != "" ]; then + + pgens=$(find pgens/ -mindepth 2 -name "pgen.hpp" -exec dirname {} \; | sed 's|^pgens/||' | paste -sd ";" -) + + if [ "${with_pgens}" = true ]; then + extra_flags="${extra_flags} -D pgens=${pgens}" + fi + + if [ "${with_tests}" = true ]; then + extra_flags="${extra_flags} -D TESTS=ON" + fi + + ( + cmake -B ${build_dir} ${extra_flags} && + cmake --build ${build_dir} -j ${nproc} + ) || exit 1 + + if [ "${with_tests}" = true ]; then + ctest --test-dir ${build_dir} --output-on-failure + fi + + if [ "${with_pgens}" = true ]; then + temp_dir="$(realpath "${build_dir}")/runs" + + if [ "${make_plots}" = true ]; then + if ! python3 -c "import nt2" &>/dev/null; then + echo "Error: nt2py python package is not installed. Please install it with 'pip install nt2py' and try again." + exit 1 + fi + fi + + for pgen in $(echo ${pgens} | tr ";" "\n"); do + if [[ "${pgen}" == *"examples/"* ]]; then + if [[ "${pgen}" == *"tutorial"* ]]; then + continue + fi + if [[ "${pgen}" == *"particle_update"* ]]; then + continue + fi + pgen_alt=$(echo "${pgen}" | sed 's|examples/|examples_|') + mkdir -p "${temp_dir}/${pgen_alt}" + cp "${build_dir}/${pgen_alt}/src/entity_${pgen_alt}.xc" "${temp_dir}/${pgen_alt}/" + find "pgens/${pgen}" -type f -name "*.py" -exec cp {} "${temp_dir}/${pgen_alt}/" \; + find "pgens/${pgen}" -type f -name "*.toml" -exec cp {} "${temp_dir}/${pgen_alt}/" \; + fi + done + + for pgen in $(echo ${pgens} | tr ";" "\n"); do + if [[ "${pgen}" == *"examples/"* ]]; then + if [[ "${pgen}" == *"tutorial"* ]]; then + continue + fi + if [[ "${pgen}" == *"particle_update"* ]]; then + continue + fi + pgen_alt=$(echo "${pgen}" | sed 's|examples/|examples_|') + inputs=$(find "${temp_dir}/${pgen_alt}" -type f -name "*.toml") + for toml_file in ${inputs}; do + toml_basename=$(basename "${toml_file}") + echo "Running pgen: ${pgen} with input ${toml_basename}" + if [[ "${extra_flags}" == *"mpi=ON"* ]]; then + (cd "${temp_dir}/${pgen_alt}" && mpiexec -n 2 ./entity_${pgen_alt}.xc -input "${toml_basename}") + else + (cd "${temp_dir}/${pgen_alt}" && ./entity_${pgen_alt}.xc -input "${toml_basename}") + fi + done + if [ "${make_plots}" = true ]; then + python_scripts=$(find "${temp_dir}/${pgen_alt}" -type f -name "*.py") + for py_file in ${python_scripts}; do + py_basename=$(basename "${py_file}") + echo "Running python script: ${py_basename} for pgen ${pgen}" + (cd "${temp_dir}/${pgen_alt}" && python3 "${py_basename}") + done + fi + fi + done + + if [ "${make_plots}" = true ]; then + mkdir -p "${temp_dir}/results" + find "${temp_dir}" -maxdepth 2 -type f \( -name "*.png" -o -name "*.mp4" \) -exec mv {} "${temp_dir}/results/" \; + fi + fi + +fi diff --git a/dev/scripts/tidy.sh b/dev/scripts/tidy.sh new file mode 100755 index 000000000..785401b48 --- /dev/null +++ b/dev/scripts/tidy.sh @@ -0,0 +1,291 @@ +#!/usr/bin/env bash +set -u + +build_dir="" +file_filter="" +fast_mode=false +use_changed=false +changed_ref="" +verify=false + +while [[ $# -gt 0 ]]; do + case "$1" in + --build) + if [[ -z "${2:-}" || "$2" == --* ]]; then + echo "Error: --build requires a value" + exit 1 + fi + build_dir="$2" + shift 2 + ;; + --files) + if [[ -z "${2:-}" || "$2" == --* ]]; then + echo "Error: --files requires a value" + exit 1 + fi + file_filter="$2" + shift 2 + ;; + --changed) + use_changed=true + if [[ -n "${2:-}" && "$2" != --* ]]; then + changed_ref="$2" + shift 2 + else + shift + fi + ;; + --fast) + # Skip clang-analyzer-* (inter-procedural analysis; ~5-10x slower than other checks) + fast_mode=true + shift + ;; + --verify) + verify=true + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: $0 --build [--files ] [--changed []] [--fast] [--verify]" + exit 1 + ;; + esac +done + +if [[ -z "$build_dir" ]]; then + echo "Error: --build is required" + exit 1 +fi + +if [[ "$use_changed" == true && -n "$file_filter" ]]; then + echo "Error: --changed and --files are mutually exclusive" + exit 1 +fi + +out_dir="tidy" +project_root="$(pwd)" +allowed_re="^${project_root}/(src|pgens)/" +jobs="$(nproc 2>/dev/null || sysctl -n hw.ncpu)" + +# Use cltcache if available for incremental runs (pip install cltcache) +# cltcache requires explicit compiler flags via '--'; it does not support -p . +# We preprocess compile_commands.json once at startup to extract per-file flags. +tidy_bin="clang-tidy" +tidy_prefix="" +_cltcache="" +if command -v cltcache &>/dev/null; then + _cltcache="cltcache" +elif [[ -x "${project_root}/.venv/bin/cltcache" ]]; then + _cltcache="${project_root}/.venv/bin/cltcache" +fi +if [[ -n "$_cltcache" ]]; then + tidy_prefix="$_cltcache" + echo "Using cltcache" +fi + +extra_checks="" +if [[ "$fast_mode" == true ]]; then + extra_checks="--checks=-clang-analyzer-*" + echo "Fast mode: skipping clang-analyzer-*" +fi + +# Build file_filter from git diff when --changed is given. +# .cpp files match directly; changed headers match all .cpp files in the same directory +# (since clang-tidy has no include graph, this is the best available heuristic). +if [[ "$use_changed" == true ]]; then + if [[ -n "$changed_ref" ]]; then + diff_files=$(git diff --name-only "${changed_ref}...HEAD" 2>/dev/null || true) + else + diff_files=$(git diff --name-only HEAD 2>/dev/null || true) + fi + + diff_files=$(echo "$diff_files" | grep -E '\.(cpp|h|hpp)$' || true) + + if [[ -z "$diff_files" ]]; then + if [[ -z "$changed_ref" ]]; then + echo "No changed .cpp/.h files in working tree. Try --changed (e.g. --changed master)." + else + echo "No changed .cpp/.h files vs ${changed_ref}." + fi + exit 0 + fi + + file_filter=$( + { + echo "$diff_files" | grep '\.cpp$' | while IFS= read -r f; do + printf '%s\n' "${project_root}/${f}" | sed 's/[.]/\\./g' + done + echo "$diff_files" | grep -E '\.(h|hpp)$' | while IFS= read -r f; do + dir="${project_root}/$(dirname "$f")" + printf '%s/[^/]+\\.cpp\n' "$(printf '%s' "$dir" | sed 's/[.]/\\./g')" + done + } | sort -u | paste -sd'|' - + ) +fi + +rm -rf "$out_dir" +mkdir -p "$out_dir" + +if [[ -n "$file_filter" ]]; then + file_list=$(jq -r '.[].file' "$build_dir/compile_commands.json" | sort -u | grep -E "$file_filter" || true) + if [[ -z "$file_list" ]]; then + echo "No files matched: $file_filter" + exit 1 + fi + echo "Matched files:" + echo "$file_list" | sed 's/^/ /' +else + file_list=$(jq -r '.[].file' "$build_dir/compile_commands.json" | sort -u) +fi + +total=$(echo "$file_list" | wc -l | tr -d ' ') + +# Precompute per-file compiler flags so cltcache can receive them via '--'. +# Uses shlex to handle shell-quoted flags (e.g. "-D FOO=\"bar\"") correctly. +# Strips flags irrelevant to compilation: -o, -c, -MF/-MT/-MQ/-MD/-MMD/-MP, +# and the source file path itself. +flags_db="" +if [[ -n "$tidy_prefix" ]]; then + flags_db=$(mktemp /tmp/tidy_flags_XXXXXX.json) + python3 - "$build_dir/compile_commands.json" >"$flags_db" <<'PYEOF' +import json, shlex, sys + +SKIP_NEXT = {"-o", "-MF", "-MT", "-MQ"} +SKIP_SELF = {"-MD", "-MMD", "-MP"} + +data = json.load(open(sys.argv[1])) +result = {} +for e in data: + args = shlex.split(e.get("command", "")) + src = e["file"] + filtered, skip = [], False + for a in args[1:]: # drop compiler binary + if skip: + skip = False + continue + if a in SKIP_NEXT: + skip = True + continue + if a in SKIP_SELF: + continue + filtered.append(a) + result[src] = filtered + +print(json.dumps(result)) +PYEOF +fi + +# Temp dir: each job touches a file here when done — race-condition-free counter +progress_dir=$(mktemp -d /tmp/tidy_progress_XXXXXX) + +# Write a helper script so each parallel job writes its own per-diagnostic-file logs +# without routing through a single serial process +tmpscript=$(mktemp /tmp/tidy_run_XXXXXX.sh) + +cleanup() { + rm -f "$tmpscript" "$flags_db" + rm -rf "$progress_dir" +} +trap cleanup EXIT + +cat >"$tmpscript" <<'ENDSCRIPT' +#!/usr/bin/env bash +file="$1" +build_dir="$2" +out_dir="$3" +project_root="$4" +tidy_bin="$5" +tidy_prefix="${6:-}" +extra_checks="${7:-}" +progress_dir="${8:-}" +flags_db="${9:-}" +allowed_re="^${project_root}/(src|pgens)/" + +tmpout=$(mktemp /tmp/tidy_out_XXXXXX) +trap 'rm -f "$tmpout"' EXIT + +if [[ -n "$tidy_prefix" && -n "$flags_db" ]]; then + # cltcache requires explicit compiler flags via '--' + mapfile -t compile_args < <(jq -r --arg f "$file" '(.[$f] // [])[]' "$flags_db") + if [[ ${#compile_args[@]} -gt 0 ]]; then + # shellcheck disable=SC2086 + $tidy_prefix "$tidy_bin" --quiet $extra_checks "$file" -- "${compile_args[@]}" > "$tmpout" 2>&1 || true + else + # File not in flags db; fall back to -p (no caching for this file) + "$tidy_bin" --quiet -p "$build_dir" $extra_checks "$file" > "$tmpout" 2>&1 || true + fi +else + # shellcheck disable=SC2086 + $tidy_prefix "$tidy_bin" --quiet -p "$build_dir" $extra_checks "$file" > "$tmpout" 2>&1 || true +fi + +# Route each diagnostic to the log for the file it occurs in (not the analyzed file) +awk -v out="$out_dir" -v root="$project_root/" -v allowed="$allowed_re" ' + /^\/?[^:]+:[0-9]+:[0-9]+:/ { + match($0, /^[^:]+/) + current_file = substr($0, 1, RLENGTH) + if (current_file !~ allowed) { + current_file = "" + next + } + if (index(current_file, root) == 1) { + current_file = substr(current_file, length(root) + 1) + } + logfile = out "/" current_file ".log" + cmd = "mkdir -p \"$(dirname \"" logfile "\")\"" + system(cmd) + print $0 >> logfile + next + } + current_file != "" { + logfile = out "/" current_file ".log" + print $0 >> logfile + } +' "$tmpout" + +[[ -n "$progress_dir" ]] && touch "$progress_dir/$$.$RANDOM" +ENDSCRIPT +chmod +x "$tmpscript" + +# Progress bar — runs in background, polls the counter dir every 0.2s +bar_full="########################################" +bar_empty="----------------------------------------" +( + while true; do + done_n=$(find "$progress_dir" -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' ') + filled=$((done_n * 40 / total)) + printf "\r[%s%s] %d/%d" \ + "${bar_full:0:$filled}" "${bar_empty:0:$((40 - filled))}" "$done_n" "$total" + [[ "$done_n" -ge "$total" ]] && break + sleep 0.2 + done + printf "\n" +) & +progress_pid=$! + +echo "$file_list" | xargs -P "$jobs" -I{} \ + "$tmpscript" {} "$build_dir" "$out_dir" "$project_root" "$tidy_bin" "$tidy_prefix" "$extra_checks" "$progress_dir" "$flags_db" + +kill "$progress_pid" 2>/dev/null || true +wait "$progress_pid" 2>/dev/null || true +printf "\r[%s] %d/%d\n" "$bar_full" "$total" "$total" + +# Dedup (parallel jobs may write overlapping header diagnostics from different TUs) +find "$out_dir" -name '*.log' | while read -r log; do + awk '!seen[$0]++' "$log" >"$log.tmp" && mv "$log.tmp" "$log" + [[ ! -s "$log" ]] && rm -f "$log" +done + +if [[ "$verify" == true ]]; then + logs=$(find "$out_dir" -name '*.log' | sort) + if [[ -z "$logs" ]]; then + echo "OK: no warnings or errors." + exit 0 + else + echo "FAILED: clang-tidy reported issues in:" + echo "$logs" | sed 's/^/ /' + exit 1 + fi +else + echo "Done. Logs written to $out_dir/" +fi diff --git a/conda-entity-nompi.sh b/legacy/conda-entity-nompi.sh similarity index 100% rename from conda-entity-nompi.sh rename to legacy/conda-entity-nompi.sh diff --git a/docker-compose.yml b/legacy/docker-compose.yml similarity index 100% rename from docker-compose.yml rename to legacy/docker-compose.yml diff --git a/minimal/CMakeLists.txt b/minimal/CMakeLists.txt index c77775223..6a43c0dbd 100644 --- a/minimal/CMakeLists.txt +++ b/minimal/CMakeLists.txt @@ -84,7 +84,7 @@ endif() if("ADIOS2_NOMPI" IN_LIST MODES) set(libs "") - if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") list(APPEND libs stdc++fs) endif() set(exec adios2-nompi.xc) @@ -101,7 +101,7 @@ endif() if("ADIOS2_MPI" IN_LIST MODES) set(libs "") - if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") + if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") list(APPEND libs stdc++fs) endif() set(exec adios2-mpi.xc) diff --git a/minimal/adios2.cpp b/minimal/adios2.cpp index c89bf383b..e664ef87e 100644 --- a/minimal/adios2.cpp +++ b/minimal/adios2.cpp @@ -366,7 +366,8 @@ template auto define_constdim_array(adios2::IO& io, const std::vector& glob_shape, const std::vector& loc_corner, - const std::vector& loc_shape) -> std::string { + const std::vector& loc_shape) + -> std::string { const std::string arrname = "ConstantDimArr" + std::to_string(glob_shape.size()) + "D::" + std::string(typeid(T).name()); diff --git a/pgens/CMakeLists.txt b/pgens/CMakeLists.txt deleted file mode 100644 index e3d047a98..000000000 --- a/pgens/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -# ------------------------------ -# @defines: ntt_pgen [INTERFACE] -# -# @includes: -# -# * ../src/ -# ------------------------------ - -add_library(ntt_pgen INTERFACE) -target_link_libraries(ntt_pgen INTERFACE ntt_global ntt_framework - ntt_archetypes ntt_kernels) - -target_include_directories(ntt_pgen - INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/${PGEN}) diff --git a/pgens/accretion/pgen.hpp b/pgens/accretion/pgen.hpp index d79408588..a9dbd65bc 100644 --- a/pgens/accretion/pgen.hpp +++ b/pgens/accretion/pgen.hpp @@ -5,15 +5,14 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/metric.h" +#include "traits/pgen.h" #include "utils/numeric.h" #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" -#include "archetypes/spatial_dist.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" - +#include "framework/parameters/parameters.h" #include "kernels/particle_moments.hpp" namespace user { @@ -80,19 +79,19 @@ namespace user { } } - Inline auto bx3(const coord_t& x_Ph) const -> real_t { + Inline auto bx3(const coord_t& /*x_Ph*/) const -> real_t { return ZERO; } - Inline auto dx1(const coord_t& x_Ph) const -> real_t { + Inline auto dx1(const coord_t& /*x_Ph*/) const -> real_t { return ZERO; } - Inline auto dx2(const coord_t& x_Ph) const -> real_t { + Inline auto dx2(const coord_t& /*x_Ph*/) const -> real_t { return ZERO; } - Inline auto dx3(const coord_t& x_Ph) const -> real_t { + Inline auto dx3(const coord_t& /*x_Ph*/) const -> real_t { return ZERO; } @@ -101,21 +100,20 @@ namespace user { const real_t m_eps; }; - template - struct PointDistribution : public arch::SpatialDistribution { - PointDistribution(const std::vector& xi_min, - const std::vector& xi_max, - const real_t sigma_thr, - const real_t dens_thr, - const SimulationParams& params, - Domain* domain_ptr) - : arch::SpatialDistribution { domain_ptr->mesh.metric } - , metric { domain_ptr->mesh.metric } + template + struct PointDistribution { + PointDistribution(const std::vector& xi_min, + const std::vector& xi_max, + const real_t sigma_thr, + const real_t dens_thr, + const SimulationParams& params, + Domain* domain_ptr) + : metric { domain_ptr->mesh.metric } , EM { domain_ptr->fields.em } , density { domain_ptr->fields.buff } , sigma_thr { sigma_thr } - , inv_n0 { ONE / params.template get("scales.n0") } - , dens_thr { dens_thr } { + , dens_thr { dens_thr } + , inv_n0 { ONE / params.template get("scales.n0") } { std::copy(xi_min.begin(), xi_min.end(), x_min); std::copy(xi_max.begin(), xi_max.end(), x_max); @@ -140,7 +138,7 @@ namespace user { Kokkos::parallel_for( "ComputeMoments", prtl_spec.rangeActiveParticles(), - kernel::ParticleMoments_kernel({}, scatter_buff, 0u, + kernel::ParticleMoments_kernel({}, scatter_buff, 0u, prtl_spec.i1, prtl_spec.i2, prtl_spec.i3, prtl_spec.dx1, prtl_spec.dx2, prtl_spec.dx3, prtl_spec.ux1, prtl_spec.ux2, prtl_spec.ux3, @@ -185,36 +183,29 @@ namespace user { } private: - tuple_t x_min; - tuple_t x_max; + const M metric; + ndfield_t EM; + ndfield_t density; + tuple_t x_min { ZERO }; + tuple_t x_max { ZERO }; const real_t sigma_thr; const real_t dens_thr; const real_t inv_n0; - Domain* domain_ptr; - ndfield_t density; - ndfield_t EM; - const M metric; }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value - }; - static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; + static constexpr auto dimensions { ::traits::pgen::compatible_with {} }; - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + const SimulationParams& params; const std::vector xi_min; const std::vector xi_max; @@ -223,30 +214,30 @@ namespace user { InitFields init_flds; const Metadomain* metadomain; - inline PGen(SimulationParams& p, const Metadomain& m) - : arch::ProblemGenerator(p) - , xi_min { p.template get>("setup.xi_min") } - , xi_max { p.template get>("setup.xi_max") } - , sigma_max { p.template get("setup.sigma_max") } - , sigma0 { p.template get("scales.sigma0") } - , multiplicity { p.template get("setup.multiplicity") } - , nGJ { p.template get("scales.B0") * - SQR(p.template get("scales.skindepth0")) } - , temperature { p.template get("setup.temperature") } - , m_eps { p.template get("setup.m_eps") } + PGen(SimulationParams& p, const Metadomain& m) + : params { p } + , xi_min { params.template get>("setup.xi_min") } + , xi_max { params.template get>("setup.xi_max") } + , sigma_max { params.template get("setup.sigma_max") } + , sigma0 { params.template get("scales.sigma0") } + , multiplicity { params.template get("setup.multiplicity") } + , nGJ { params.template get("scales.B0") * + SQR(params.template get("scales.skindepth0")) } + , temperature { params.template get("setup.temperature") } + , m_eps { params.template get("setup.m_eps") } , init_flds { m.mesh().metric, m_eps } , metadomain { &m } {} - inline void InitPrtls(Domain& local_domain) { - const auto energy_dist = arch::Maxwellian(local_domain.mesh.metric, - local_domain.random_pool(), - temperature); - const auto spatial_dist = PointDistribution(xi_min, - xi_max, - sigma_max / sigma0, - multiplicity * nGJ, - params, - &local_domain); + void InitPrtls(Domain& local_domain) { + const auto energy_dist = arch::energy_dist::Maxwellian( + local_domain.random_pool(), + temperature); + const auto spatial_dist = PointDistribution(xi_min, + xi_max, + sigma_max / sigma0, + multiplicity * nGJ, + params, + &local_domain); arch::InjectNonUniform( params, @@ -258,16 +249,18 @@ namespace user { true); } - void CustomPostStep(std::size_t, long double time, Domain& local_domain) { - const auto energy_dist = arch::Maxwellian(local_domain.mesh.metric, - local_domain.random_pool(), - temperature); - const auto spatial_dist = PointDistribution(xi_min, - xi_max, - sigma_max / sigma0, - multiplicity * nGJ, - params, - &local_domain); + void CustomPostStep(timestep_t /*step*/, + simtime_t /*time*/, + Domain& local_domain) { + const auto energy_dist = arch::energy_dist::Maxwellian( + local_domain.random_pool(), + temperature); + const auto spatial_dist = PointDistribution(xi_min, + xi_max, + sigma_max / sigma0, + multiplicity * nGJ, + params, + &local_domain); arch::InjectNonUniform( params, local_domain, diff --git a/pgens/examples/atmosphere/atmosphere.py b/pgens/examples/atmosphere/atmosphere.py index d64bc0cff..45b167386 100644 --- a/pgens/examples/atmosphere/atmosphere.py +++ b/pgens/examples/atmosphere/atmosphere.py @@ -7,27 +7,20 @@ plt.rcParams["figure.figsize"] = (6, 2) plt.rcParams["font.family"] = "serif" -data_normal = nt2.Data("atmosphere") -data_no_g = nt2.Data("atmosphere_no_g") -data_no_inject = nt2.Data("atmosphere_no_reinject") +data = nt2.Data("atmosphere") -for d in [data_normal, data_no_g, data_no_inject]: - d.fields.coords["xh"] = ( - d.fields.coords["x"] / d.attrs["grid.boundaries.atmosphere.height"] - ) +data.fields.coords["xh"] = ( + data.fields.coords["x"] / data.attrs["grid.boundaries.atmosphere.height"] +) t = 1 -data_normal.fields.N_1_2.sel(t=t, method="nearest").plot(label="normal", x="xh", lw=1) -data_no_g.fields.N_1_2.sel(t=t, method="nearest").plot(label="no gravity", x="xh", lw=1) -data_no_inject.fields.N_1_2.sel(t=t, method="nearest").plot( - label="no reinjection", x="xh", lw=1 -) +data.fields.N_1_2.sel(t=t, method="nearest").plot(x="xh", lw=1) xs = np.linspace(0, 20, 100) plt.plot( xs, - data_normal.attrs["grid.boundaries.atmosphere.density"] * np.exp(-xs), + data.attrs["grid.boundaries.atmosphere.density"] * np.exp(-xs), label=r"$n_{\rm max} \exp{\{-x/h\}}$", lw=1, ls="--", @@ -38,7 +31,6 @@ plt.xlabel(r"$x/h$") plt.xlim(0, 20) plt.yscale("log") -plt.legend() plt.ylim(1e-2, 20) plt.savefig("atmosphere.png", bbox_inches="tight") diff --git a/pgens/examples/atmosphere/atmosphere.toml b/pgens/examples/atmosphere/atmosphere.toml index a3d8d828d..1de21ee8b 100644 --- a/pgens/examples/atmosphere/atmosphere.toml +++ b/pgens/examples/atmosphere/atmosphere.toml @@ -1,5 +1,5 @@ [simulation] - name = "atmosphere_no_g" + name = "atmosphere" engine = "srpic" runtime = 1.0 diff --git a/pgens/examples/atmosphere/pgen.hpp b/pgens/examples/atmosphere/pgen.hpp index 65cb7b68e..b20dae18e 100644 --- a/pgens/examples/atmosphere/pgen.hpp +++ b/pgens/examples/atmosphere/pgen.hpp @@ -4,32 +4,27 @@ #include "enums.h" #include "global.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" +#include "traits/pgen.h" + #include "framework/domain/metadomain.h" namespace user { using namespace ntt; template - struct PGen : public arch::ProblemGenerator { - + struct PGen { static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - - inline PGen(const SimulationParams& p, const Metadomain& metadomain) - : arch::ProblemGenerator { p } {} + PGen(const SimulationParams& /*params*/, + const Metadomain& /*metadomain*/) {} }; } // namespace user diff --git a/pgens/examples/custom_emission/custom_emission.toml b/pgens/examples/custom_emission/custom_emission.toml index fd4cd135e..850fb9ccb 100644 --- a/pgens/examples/custom_emission/custom_emission.toml +++ b/pgens/examples/custom_emission/custom_emission.toml @@ -52,7 +52,7 @@ emission_probability = 0.05 [output] - interval = 1 + interval = 10 [output.fields] quantities = ["N_1", "N_2", "B"] diff --git a/pgens/examples/custom_emission/pgen.hpp b/pgens/examples/custom_emission/pgen.hpp index e1d200bab..2ae082148 100644 --- a/pgens/examples/custom_emission/pgen.hpp +++ b/pgens/examples/custom_emission/pgen.hpp @@ -5,12 +5,11 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/pgen.h" +#include "traits/policies.h" #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" -#include "kernels/emission/traits.h" #include "kernels/injectors.hpp" #include @@ -93,9 +92,9 @@ namespace user { const auto rnd = Random(generator); random_pool.free_state(generator); if (rnd < probability) { - delta_u_Ph[0] = -0.1 * u_Ph[0]; - delta_u_Ph[1] = -0.1 * u_Ph[1]; - delta_u_Ph[2] = -0.1 * u_Ph[2]; + delta_u_Ph[0] = -static_cast(0.1) * u_Ph[0]; + delta_u_Ph[1] = -static_cast(0.1) * u_Ph[1]; + delta_u_Ph[2] = -static_cast(0.1) * u_Ph[2]; const auto uSqr = NORM_SQR(u_Ph[0], u_Ph[1], u_Ph[2]); const auto gammaSqr = ONE + uSqr; @@ -158,38 +157,35 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - + const SimulationParams& params; const Metadomain& metadomain; const real_t emission_probability; InitFields init_flds {}; - inline PGen(const SimulationParams& p, const Metadomain& metadomain) - : arch::ProblemGenerator { p } + PGen(const SimulationParams& p, const Metadomain& metadomain) + : params { p } , metadomain { metadomain } , emission_probability { params.template get( "setup.emission_probability") } { - static_assert(kernel::traits::emission::IsValid, M>, "RandomEmission does not satisfy the requirements of an emission policy"); + static_assert(EmissionPolicyClass, M>, "RandomEmission does not satisfy the requirements of an emission policy"); } - inline auto EmissionPolicy(simtime_t, - spidx_t, - Domain& domain) const -> RandomEmission { + auto EmissionPolicy(simtime_t, spidx_t, Domain& domain) const + -> RandomEmission { return RandomEmission { domain.random_pool(), emission_probability, domain.species[1].npart(), domain.species[1].i1, @@ -202,7 +198,7 @@ namespace user { }; } - inline void InitPrtls(Domain& domain) { + void InitPrtls(Domain& domain) { const auto empty = std::vector {}; const auto x1_arr = params.template get>( "setup.x1_arr", diff --git a/pgens/examples/custom_energy_distribution/custom_energy_distribution.py b/pgens/examples/custom_energy_distribution/custom_energy_distribution.py index fdbb082cb..b561531b7 100644 --- a/pgens/examples/custom_energy_distribution/custom_energy_distribution.py +++ b/pgens/examples/custom_energy_distribution/custom_energy_distribution.py @@ -19,4 +19,4 @@ ) ax2.set(xlabel="$x$", ylabel="$u_y$", ylim=(-1.5, 1.5)) -plt.show() +plt.savefig("custom_energy_distribution.png", bbox_inches="tight") diff --git a/pgens/examples/custom_energy_distribution/pgen.hpp b/pgens/examples/custom_energy_distribution/pgen.hpp index e4fc1232b..623ec0d54 100644 --- a/pgens/examples/custom_energy_distribution/pgen.hpp +++ b/pgens/examples/custom_energy_distribution/pgen.hpp @@ -4,10 +4,10 @@ #include "enums.h" #include "global.h" +#include "traits/pgen.h" + #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" namespace user { @@ -32,7 +32,7 @@ namespace user { Inline void operator()(const coord_t& x_Ph, vec_t& v) const { // sample a static 3D maxwellian + drift in x1 direction with sinusoidal spatial dependence // @NOTE: for relativistic drift, use the built-in drifting Maxwellian - arch::JuttnerSinge(v, temperature, random_pool); + arch::energy_dist::JuttnerSinge(v, temperature, random_pool); v[0] += drift_amplitude * math::sin(x_Ph[0] * kx); } @@ -56,29 +56,26 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - + const SimulationParams& params; const Metadomain& metadomain; - inline PGen(const SimulationParams& p, const Metadomain& metadomain) - : arch::ProblemGenerator { p } - , metadomain { metadomain } {} + PGen(const SimulationParams& p, const Metadomain& m) + : params { p } + , metadomain { m } {} - inline void InitPrtls(Domain& domain) { + void InitPrtls(Domain& domain) { const auto temperature = params.template get("setup.temperature"); const auto drift_amplitude = params.template get( "setup.drift_amplitude"); diff --git a/pgens/examples/custom_particle_update/pgen.hpp b/pgens/examples/custom_particle_update/pgen.hpp index 9d7113d42..3d95eaeca 100644 --- a/pgens/examples/custom_particle_update/pgen.hpp +++ b/pgens/examples/custom_particle_update/pgen.hpp @@ -4,74 +4,67 @@ #include "enums.h" #include "global.h" -#include "arch/traits.h" +#include "traits/pgen.h" #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" #include -/* -------------------------------------------------------------------------- */ -/* Local macros (same as in particle_pusher_sr.hpp) */ -/* -------------------------------------------------------------------------- */ #define from_Xi_to_i(XI, I) \ { \ - I = static_cast((XI + 1)) - 1; \ + (I) = static_cast(((XI) + 1)) - 1; \ } #define from_Xi_to_i_di(XI, I, DI) \ { \ from_Xi_to_i((XI), (I)); \ - DI = static_cast((XI)) - static_cast(I); \ + (DI) = static_cast((XI)) - static_cast(I); \ } -#define i_di_to_Xi(I, DI) static_cast((I)) + static_cast((DI)) +#define i_di_to_Xi(I, DI) (static_cast((I)) + static_cast((DI))) namespace user { using namespace ntt; template - struct PGen : public arch::ProblemGenerator { - + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + const SimulationParams& params; - const Metadomain& global_domain; + const Metadomain& metadomain; const real_t temperature, temperature_gradient; - inline PGen(const SimulationParams& p, const Metadomain& global_domain) - : arch::ProblemGenerator { p } - , global_domain { global_domain } + PGen(const SimulationParams& p, const Metadomain& m) + : params { p } + , metadomain { m } , temperature { params.template get("setup.temperature", 0.0) } , temperature_gradient { params.template get("setup.temperature_gradient", 0.0) } {} - inline void InitPrtls(Domain& local_domain) { + void InitPrtls(Domain& local_domain) { const auto empty = std::vector {}; const auto x1_e = params.template get>("setup.x1_e", - empty); + empty); const auto x2_e = params.template get>("setup.x2_e", - empty); + empty); const auto x3_e = params.template get>("setup.x3_e", - empty); + empty); const auto phi_e = params.template get>("setup.phi_e", empty); const auto ux1_e = params.template get>("setup.ux1_e", @@ -82,11 +75,11 @@ namespace user { empty); const auto x1_i = params.template get>("setup.x1_i", - empty); + empty); const auto x2_i = params.template get>("setup.x2_i", - empty); + empty); const auto x3_i = params.template get>("setup.x3_i", - empty); + empty); const auto phi_i = params.template get>("setup.phi_i", empty); const auto ux1_i = params.template get>("setup.ux1_i", @@ -109,7 +102,7 @@ namespace user { { "ux2", ux2_i }, { "ux3", ux3_i } }; - if constexpr (M::CoordType == Coord::Cart or D == Dim::_3D) { + if constexpr (M::CoordType == Coord::Cartesian or D == Dim::_3D) { data_e["x3"] = x3_e; data_i["x3"] = x3_i; } else if constexpr (D == Dim::_2D) { @@ -117,8 +110,8 @@ namespace user { data_i["phi"] = phi_i; } - arch::InjectGlobally(global_domain, local_domain, (spidx_t)1, data_e); - arch::InjectGlobally(global_domain, local_domain, (spidx_t)2, data_i); + arch::InjectGlobally(metadomain, local_domain, (spidx_t)1, data_e); + arch::InjectGlobally(metadomain, local_domain, (spidx_t)2, data_i); } auto FixFieldsConst(const bc_in&, const em&) const -> std::pair { @@ -130,106 +123,115 @@ namespace user { real_t temp_cold, temp_hot; real_t xmin, xmax; - template - Inline void operator()(index_t p, Coord& xp, PusherKernel& pusher) const { - - const auto x_Cd = static_cast(pusher.i1(p)) + - static_cast(pusher.dx1(p)); - const auto x_Ph = pusher.metric.template convert<1, Crd::Cd, Crd::XYZ>(x_Cd); + CustomPrtlUpdate(random_number_pool_t& pool, + real_t temp_cold, + real_t temp_hot, + real_t xmin, + real_t xmax) + : pool { pool } + , temp_cold { temp_cold } + , temp_hot { temp_hot } + , xmin { xmin } + , xmax { xmax } {} + + Inline void operator()(index_t p, + const kernel::sr::PusherContext& ctx, + const kernel::sr::PusherBoundaries&, + const kernel::PusherArrays& particles, + const M& metric) const { + + const auto x_Cd = static_cast(particles.i1(p)) + + static_cast(particles.dx1(p)); + const auto x_Ph = metric.template convert<1, Crd::Cd, Crd::XYZ>(x_Cd); vec_t v { ZERO }; const coord_t x_dummy { ZERO }; // step 1: calculate the particle 3 velocity - const real_t gamma_p = math::sqrt( - ONE + SQR(pusher.ux1(p)) + SQR(pusher.ux2(p)) + SQR(pusher.ux3(p))); + const real_t gamma_p = math::sqrt(ONE + SQR(particles.ux1(p)) + + SQR(particles.ux2(p)) + + SQR(particles.ux3(p))); - const real_t beta_x_p = math::abs(pusher.ux1(p)) / gamma_p; - const real_t xp_prev = i_di_to_Xi(pusher.i1_prev(p), pusher.dx1_prev(p)); + const real_t beta_x_p = math::abs(particles.ux1(p)) / gamma_p; + const real_t xp_prev = i_di_to_Xi(particles.i1_prev(p), + particles.dx1_prev(p)); // Reflecting boundary that resamples velocity if (x_Ph < xmin) { - arch::JuttnerSinge(v, temp_cold, pool); + arch::energy_dist::JuttnerSinge(v, temp_cold, pool); // calculate the time for the particle to reach the wall - const int delta_i1_to_wall = pusher.i1_prev(p); - const prtldx_t delta_dx1_to_wall = pusher.dx1_prev(p); + const int delta_i1_to_wall = particles.i1_prev(p); + const prtldx_t delta_dx1_to_wall = particles.dx1_prev(p); const real_t dx_to_wall = i_di_to_Xi(delta_i1_to_wall, delta_dx1_to_wall); const real_t dt_to_wall = dx_to_wall / - pusher.metric.template transform<1, Idx::XYZ, Idx::U>(x_dummy, - beta_x_p); + metric.template transform<1, Idx::XYZ, Idx::U>( + x_dummy, + beta_x_p); // update the velocity to the post-collision value (reflection with new speed sampled from Juttner distribution) - pusher.ux1(p) = math::abs(v[0]); - pusher.ux2(p) = v[1]; - pusher.ux3(p) = v[2]; + particles.ux1(p) = math::abs(v[0]); + particles.ux2(p) = v[1]; + particles.ux3(p) = v[2]; // calculate remaining time after the collision - const real_t remaining_dt = pusher.dt - dt_to_wall; + const real_t remaining_dt = ctx.dt - dt_to_wall; const real_t remaining_dt_inv_energy = remaining_dt / - math::sqrt(ONE + - SQR(pusher.ux1(p)) + - SQR(pusher.ux2(p)) + - SQR(pusher.ux3(p))); + math::sqrt( + ONE + SQR(particles.ux1(p)) + + SQR(particles.ux2(p)) + + SQR(particles.ux3(p))); // update the position to the post-collision value (reflection with new speed sampled from Juttner distribution) - pusher.i1(p) = 0; - pusher.dx1(p) = pusher.metric.template transform<1, Idx::XYZ, Idx::U>( - x_dummy, - pusher.ux1(p)) * - remaining_dt_inv_energy; - - // Re-sync coordinate variable xp for subsequent boundary procedures - xp[0] = static_cast(pusher.i1(p)) + - static_cast(pusher.dx1(p)); + particles.i1(p) = 0; + particles.dx1(p) = metric.template transform<1, Idx::XYZ, Idx::U>( + x_dummy, + particles.ux1(p)) * + remaining_dt_inv_energy; } else if (x_Ph > xmax) { - arch::JuttnerSinge(v, temp_hot, pool); + arch::energy_dist::JuttnerSinge(v, temp_hot, pool); // step 2: calculate the time for the particle to reach the piston - const int delta_i1_to_wall = pusher.ni1 - 1 - pusher.i1_prev(p); - const prtldx_t delta_dx1_to_wall = ONE - pusher.dx1_prev(p); + const int delta_i1_to_wall = ctx.ni1 - 1 - particles.i1_prev(p); + const prtldx_t delta_dx1_to_wall = ONE - particles.dx1_prev(p); const real_t dx_to_wall = i_di_to_Xi(delta_i1_to_wall, delta_dx1_to_wall); const real_t dt_to_wall = dx_to_wall / - pusher.metric.template transform<1, Idx::XYZ, Idx::U>(x_dummy, - beta_x_p); + metric.template transform<1, Idx::XYZ, Idx::U>( + x_dummy, + beta_x_p); // update the velocity to the post-collision value (reflection with new speed sampled from Juttner distribution) - pusher.ux1(p) = -math::abs(v[0]); - pusher.ux2(p) = v[1]; - pusher.ux3(p) = v[2]; + particles.ux1(p) = -math::abs(v[0]); + particles.ux2(p) = v[1]; + particles.ux3(p) = v[2]; // step 3: calculate remaining time after the collision - const real_t remaining_dt = pusher.dt - dt_to_wall; + const real_t remaining_dt = ctx.dt - dt_to_wall; const real_t remaining_dt_inv_energy = remaining_dt / - math::sqrt(ONE + - SQR(pusher.ux1(p)) + - SQR(pusher.ux2(p)) + - SQR(pusher.ux3(p))); + math::sqrt( + ONE + SQR(particles.ux1(p)) + + SQR(particles.ux2(p)) + + SQR(particles.ux3(p))); // update the position to the post-collision value (reflection with new speed sampled from Juttner distribution) - pusher.i1(p) = pusher.ni1 - 2; - pusher.dx1(p) = ONE - - pusher.metric.template transform<1, Idx::XYZ, Idx::U>( - x_dummy, - math::abs(pusher.ux1(p))) * - remaining_dt_inv_energy; - - // Re-sync coordinate variable xp for subsequent boundary procedures - xp[0] = static_cast(pusher.i1(p)) + - static_cast(pusher.dx1(p)); + particles.i1(p) = ctx.ni1 - 2; + particles.dx1(p) = ONE - metric.template transform<1, Idx::XYZ, Idx::U>( + x_dummy, + math::abs(particles.ux1(p))) * + remaining_dt_inv_energy; } } }; template - auto CustomParticleUpdate(simtime_t time, spidx_t sp, D& domain) const { - + auto CustomParticleUpdate(simtime_t /*time*/, spidx_t sp, D& domain) const + -> CustomPrtlUpdate { return CustomPrtlUpdate { domain.random_pool(), temperature / domain.species[sp - 1].mass(), // sp is 1-indexed temperature_gradient * temperature / domain.species[sp - 1].mass(), - global_domain.mesh().extent(in::x1).first, // xmin - global_domain.mesh().extent(in::x1).second // xmax + metadomain.mesh().extent(in::x1).first, // xmin + metadomain.mesh().extent(in::x1).second // xmax }; } }; diff --git a/pgens/examples/custom_spatial_distribution/pgen.hpp b/pgens/examples/custom_spatial_distribution/pgen.hpp index 0ba434e54..486db0168 100644 --- a/pgens/examples/custom_spatial_distribution/pgen.hpp +++ b/pgens/examples/custom_spatial_distribution/pgen.hpp @@ -4,10 +4,10 @@ #include "enums.h" #include "global.h" +#include "traits/pgen.h" + #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" namespace user { @@ -33,30 +33,26 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { - + struct PGen { static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; + const SimulationParams& params; - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - - inline PGen(const SimulationParams& p, const Metadomain& metadomain) - : arch::ProblemGenerator { p } {} + PGen(const SimulationParams& p, const Metadomain& /*metadomain*/) + : params { p } {} - inline void InitPrtls(Domain& domain) { + void InitPrtls(Domain& domain) { const auto sdist = CustomSpatialDistribution {}; - const auto edist = arch::Maxwellian { domain.mesh.metric, - domain.random_pool(), - static_cast(0.1) }; + const auto edist = arch::energy_dist::Maxwellian( + domain.random_pool(), + static_cast(0.1)); // distributions are then passed to the nonuniform particle injector // function (same energy distribution is used for both species) diff --git a/pgens/examples/external_fields/external_fields.py b/pgens/examples/external_fields/external_fields.py index 8122d8aa6..85cf72fbe 100644 --- a/pgens/examples/external_fields/external_fields.py +++ b/pgens/examples/external_fields/external_fields.py @@ -20,4 +20,4 @@ ) ax.set(xlim=(-1, 1), ylim=(-1, 1), xlabel=r"$x$", ylabel=r"$y$", aspect=1) -plt.show() +plt.savefig("external_fields.png", bbox_inches="tight") diff --git a/pgens/examples/external_fields/pgen.hpp b/pgens/examples/external_fields/pgen.hpp index 9f7eca314..ac4f6faed 100644 --- a/pgens/examples/external_fields/pgen.hpp +++ b/pgens/examples/external_fields/pgen.hpp @@ -4,10 +4,11 @@ #include "enums.h" #include "global.h" +#include "traits/pgen.h" + #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" +#include "framework/parameters/parameters.h" #include @@ -53,35 +54,31 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - + const SimulationParams& params; const Metadomain& metadomain; - inline PGen(const SimulationParams& p, const Metadomain& metadomain) - : arch::ProblemGenerator { p } - , metadomain { metadomain } {} + PGen(const SimulationParams& p, const Metadomain& m) + : params { p } + , metadomain { m } {} /* * @returns a pair of (apply_external_fields, external_fields) * * @note apply_external_fields is true for species other than 1 (i.e., 2 and 3 in this case) */ - inline auto ExternalFields(simtime_t time, - spidx_t sp, - const Domain& domain) const + auto ExternalFields(simtime_t time, spidx_t sp, const Domain& /*domain*/) const -> std::pair> { // apply only to species 2 and 3 return { @@ -90,7 +87,7 @@ namespace user { }; } - inline void InitPrtls(Domain& domain) { + void InitPrtls(Domain& domain) { const auto prtls = params.template get>>( "setup.prtls"); const auto prtl_species = params.template get>( @@ -104,8 +101,8 @@ namespace user { "setup.prtls should be a vector of vectors of size 3+D", HERE); for (auto p = 0u; p < prtls.size(); ++p) { - const auto prtl = prtls[p]; - const auto prtl_spec = prtl_species[p]; + const auto& prtl = prtls[p]; + const auto prtl_spec = prtl_species[p]; std::map> data_arr; data_arr["x1"] = { prtl[0] }; if constexpr (D == Dim::_2D or D == Dim::_3D) { diff --git a/pgens/examples/match_fix_field_boundaries/pgen.hpp b/pgens/examples/match_fix_field_boundaries/pgen.hpp index 473d17bb3..5740ff5e4 100644 --- a/pgens/examples/match_fix_field_boundaries/pgen.hpp +++ b/pgens/examples/match_fix_field_boundaries/pgen.hpp @@ -4,8 +4,8 @@ #include "enums.h" #include "global.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" +#include "traits/pgen.h" + #include "framework/domain/metadomain.h" namespace user { @@ -27,27 +27,21 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value - }; - static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + static constexpr auto dimensions { ::traits::pgen::compatible_with {} }; const real_t amplitude, omega; const real_t t_transition, t_duration; - inline PGen(const SimulationParams& p, const Metadomain&) - : arch::ProblemGenerator { p } - , amplitude { p.template get("setup.amplitude", ONE) } + PGen(const SimulationParams& p, const Metadomain&) + : amplitude { p.template get("setup.amplitude", ONE) } , omega { p.template get("setup.omega", ONE) } , t_transition { p.template get("setup.t_transition") } , t_duration { p.template get("setup.t_duration") } {} @@ -66,9 +60,8 @@ namespace user { * * @return Pair of (value to set, whether to set it or not) */ - auto FixFieldsConst(simtime_t time, - const bc_in& bc, - em comp) const -> std::pair { + auto FixFieldsConst(simtime_t time, const bc_in& bc, em comp) const + -> std::pair { if (bc == bc_in::Mx1) { const auto phase { time * omega }; real_t ampl { ZERO }; diff --git a/pgens/examples/replenish_injector/pgen.hpp b/pgens/examples/replenish_injector/pgen.hpp index 8c196140f..fb46eaca8 100644 --- a/pgens/examples/replenish_injector/pgen.hpp +++ b/pgens/examples/replenish_injector/pgen.hpp @@ -4,11 +4,11 @@ #include "enums.h" #include "global.h" +#include "traits/pgen.h" + #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" #include "archetypes/spatial_dist.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" #include "kernels/particle_moments.hpp" @@ -32,40 +32,39 @@ namespace user { // example of a non-uniform target density that peaks at the center of the domain real_t r2 { ZERO }; for (auto d = 0u; d < D; ++d) { - r2 += SQR(x_Ph[d] - 0.5); + r2 += SQR(x_Ph[d] - HALF); } - return std::exp( - -r2 / SQR(0.2)); // <-- characteristic width of the density profile + return std::exp(-r2 / SQR(static_cast(0.2))); + // ^ + // characteristic width of the density profile } }; template - struct PGen : public arch::ProblemGenerator { - + struct PGen { + static constexpr auto D { M::Dim }; static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + const SimulationParams& params; EMFields init_flds; const std::string target_density; - inline PGen(const SimulationParams& p, const Metadomain& metadomain) - : arch::ProblemGenerator { p } + PGen(const SimulationParams& p, const Metadomain& /*metadomain*/) + : params { p } , target_density { params.template get( "setup.target_density") } {} - void CustomPostStep(timestep_t step, simtime_t time, Domain& domain) { + void CustomPostStep(timestep_t step, simtime_t /*time*/, Domain& domain) { if (step % 100u != 0u) { return; } @@ -112,13 +111,12 @@ namespace user { Kokkos::Experimental::contribute(domain.fields.buff, scatter_buff); } - const auto energy_dist = arch::Maxwellian( - domain.mesh.metric, + const auto energy_dist = arch::energy_dist::Maxwellian( domain.random_pool(), 0.2); // <-- target temperature for injection if (target_density == "uniform") { // pass the computed density to the replenisher - const auto replenish_sdist = arch::ReplenishUniform( + const auto replenish_sdist = arch::spatial_dist::ReplenishUniform( domain.mesh.metric, domain.fields.buff, 0u, // <-- index in buff where the density is stored @@ -133,7 +131,7 @@ namespace user { } else { const auto target_density_profile = NonUniformTargetDensity {}; const auto replenish_sdist = - arch::Replenish( + arch::spatial_dist::Replenish( domain.mesh.metric, domain.fields.buff, 0u, // <-- index in buff where the density is stored diff --git a/pgens/examples/tutorial_cartesian_sr/pgen.hpp b/pgens/examples/tutorial_cartesian_sr/pgen.hpp index f312fbd5d..daa488909 100644 --- a/pgens/examples/tutorial_cartesian_sr/pgen.hpp +++ b/pgens/examples/tutorial_cartesian_sr/pgen.hpp @@ -4,10 +4,10 @@ #include "enums.h" #include "global.h" +#include "traits/pgen.h" + #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" #include "archetypes/spatial_dist.h" -#include "archetypes/traits.h" #include "archetypes/utils.h" #include "framework/domain/metadomain.h" #include "framework/parameters/parameters.h" @@ -106,56 +106,54 @@ namespace user { } template - struct PGen : public arch::ProblemGenerator { + struct PGen { static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; // DipoleField init_flds; - inline auto ExternalFields(simtime_t, spidx_t, const Domain&) const + auto ExternalFields(simtime_t, spidx_t, const Domain&) const -> std::pair> { return { true, DipoleField {} }; } + const SimulationParams& params; const Metadomain& metadomain; - inline PGen(const SimulationParams& p, const Metadomain& metadomain) - : arch::ProblemGenerator { p } - , metadomain { metadomain } {} + PGen(const SimulationParams& p, const Metadomain& m) + : params { p } + , metadomain { m } {} - void CustomPostStep(timestep_t step, simtime_t time, Domain& domain) { - const auto temperature = this->params.template get( - "setup.temperature"); - const auto drift_vel = this->params.template get( - "setup.drift_vel"); - const auto inject_xrange = this->params.template get>( + void CustomPostStep(timestep_t /*step*/, simtime_t /*time*/, Domain& domain) { + const auto temperature = params.template get("setup.temperature"); + const auto drift_vel = params.template get("setup.drift_vel"); + const auto inject_xrange = params.template get>( "setup.inject_xrange"); // compute the density of species #1 and #2 // and save in the field buffer (index 0) - arch::ComputeMomentWithSpecies(this->params, + arch::ComputeMomentWithSpecies(params, domain, { 1u, 2u }, domain.fields.buff); - const auto energy_dist = arch::Maxwellian( - domain.mesh.metric, + const auto energy_dist = arch::energy_dist::Maxwellian( domain.random_pool(), temperature, // <-- target temperature for injection { drift_vel, ZERO, ZERO }); // <-- drift 4-velocity // pass the computed density to the replenisher - const auto replenish_sdist = arch::ReplenishUniform( + const auto replenish_sdist = arch::spatial_dist::ReplenishUniform( domain.mesh.metric, domain.fields.buff, 0u, // <-- index in buff where the density is stored ONE); // <-- target density for replenishment arch::InjectNonUniform( - this->params, + params, domain, { 1u, @@ -167,10 +165,9 @@ namespace user { false, { { inject_xrange[0], inject_xrange[1] }, Range::All, Range::All }); // <-- injection region - const auto r_purge = this->params.template get("setup.r_purge"); - const auto r_plummet = this->params.template get( - "setup.r_plummet"); - const auto plummet_speed = this->params.template get( + const auto r_purge = params.template get("setup.r_purge"); + const auto r_plummet = params.template get("setup.r_plummet"); + const auto plummet_speed = params.template get( "setup.plummet_speed"); // particles below r_plummet, gain a constant speed towards the origin, // and are removed when they reach r_purge diff --git a/pgens/magnetosphere/pgen.hpp b/pgens/magnetosphere/pgen.hpp index 706c35018..4ef798844 100644 --- a/pgens/magnetosphere/pgen.hpp +++ b/pgens/magnetosphere/pgen.hpp @@ -5,18 +5,18 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/pgen.h" #include "utils/numeric.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" +#include "framework/parameters/parameters.h" #include namespace user { using namespace ntt; - enum class FieldGeometry { + enum class FieldGeometry : uint8_t { dipole, monopole }; @@ -85,38 +85,29 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value - }; - - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + static constexpr auto dimensions { ::traits::pgen::compatible_with {} }; const real_t Bsurf, Rstar, Omega; const std::string field_geom; InitFields init_flds; - inline PGen(const SimulationParams& p, const Metadomain& m) - : arch::ProblemGenerator(p) - , Bsurf { p.template get("setup.Bsurf", ONE) } + PGen(const SimulationParams& p, const Metadomain& m) + : Bsurf { p.template get("setup.Bsurf", ONE) } , Rstar { m.mesh().extent(in::x1).first } , Omega { static_cast(constant::TWO_PI) / p.template get("setup.period", ONE) } , field_geom { p.template get("setup.field_geometry", "dipole") } , init_flds { Bsurf, Rstar, field_geom } {} - inline PGen() {} - auto AtmFields(simtime_t time) const -> DriveFields { return DriveFields { time, Bsurf, Rstar, Omega, field_geom }; } diff --git a/pgens/pgen.hpp b/pgens/pgen.hpp index d2d091ebf..3daf262be 100644 --- a/pgens/pgen.hpp +++ b/pgens/pgen.hpp @@ -4,10 +4,9 @@ #include "enums.h" #include "global.h" +#include "traits/pgen.h" #include "utils/formatting.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" #include @@ -16,34 +15,28 @@ namespace user { using namespace ntt; template - struct PGen : public arch::ProblemGenerator { + struct PGen { // compatibility traits for the problem generator static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - - inline PGen(const SimulationParams& p, const Metadomain&) - : arch::ProblemGenerator { p } { + PGen(const SimulationParams&, const Metadomain&) { const auto message = fmt::format( "Problem generator initialized with `%s` engine and `%dD %s` metric", SimEngine(S).to_string(), - D, + M::Dim, Metric(M::MetricType).to_string()); PLOGI << message; } diff --git a/pgens/piston_shock/pgen.hpp b/pgens/piston_shock/pgen.hpp deleted file mode 100644 index 745f751d2..000000000 --- a/pgens/piston_shock/pgen.hpp +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef PROBLEM_GENERATOR_H -#define PROBLEM_GENERATOR_H - -#include "enums.h" -#include "global.h" - -#include "arch/traits.h" - -#include "archetypes/energy_dist.h" -#include "archetypes/particle_injector.h" -#include "archetypes/piston.h" -#include "archetypes/problem_generator.h" -#include "archetypes/spatial_dist.h" -#include "archetypes/utils.h" -#include "framework/domain/domain.h" -#include "framework/domain/metadomain.h" - -#include - -namespace user { - using namespace ntt; - - template - struct PGen : public arch::ProblemGenerator { - - // compatibility traits for the problem generator - static constexpr auto engines { - arch::traits::pgen::compatible_with::value - }; - static constexpr auto metrics { - arch::traits::pgen::compatible_with::value - }; - static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value - }; - - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - - const Metadomain& global_domain; - - // domain properties - const real_t global_xmin, global_xmax; - - // plasma - const real_t temperature, temperature_ratio; - // piston properties - const real_t piston_velocity, piston_initial_position; - - inline PGen(const SimulationParams& p, const Metadomain& global_domain) - : arch::ProblemGenerator { p } - , global_domain { global_domain } - , global_xmin { global_domain.mesh().extent(in::x1).first } - , global_xmax { global_domain.mesh().extent(in::x1).second } - , piston_velocity { p.template get("setup.piston_velocity", 0.0) } - , piston_initial_position { p.template get( - "setup.piston_initial_position", - 0.0) } - , temperature { p.template get("setup.temperature", 0.0) } - , temperature_ratio { p.template get( - "setup.temperature_ratio") } {} - - inline void InitPrtls(Domain& local_domain) { - real_t xg_min = global_xmin + piston_initial_position; - real_t xg_max = global_xmax; - - // define box to inject into - boundaries_t box; - // loop over all dimensions - for (auto d { 0u }; d < (unsigned int)M::Dim; ++d) { - // compute the range for the x-direction - if (d == static_cast(in::x1)) { - box.push_back({ xg_min, xg_max }); - } else { - // inject into full range in other directions - box.push_back(Range::All); - } - } - - // define temperatures of species - const auto temperatures = std::make_pair(temperature, - temperature_ratio * temperature); - // define drift speed of species - const auto drifts = std::make_pair(std::vector { ZERO, ZERO, ZERO }, - std::vector { ZERO, ZERO, ZERO }); - - // inject particles - arch::InjectUniformMaxwellians(params, - local_domain, - ONE, - temperatures, - { 1, 2 }, - drifts, - false, - box); - } - - struct CustomPrtlUpdate { - real_t piston_position_current; // current position of piston - real_t piston_velocity_current; - real_t xg_max; - - bool is_left; - - bool massive; - - template - Inline void operator()(index_t p, Coord& xp, PusherKernel& pusher) const { - - real_t piston_position_use; - - // make sure piston has not reached the right wall - if (piston_position_current < xg_max) { - piston_position_use = piston_position_current; - } else { - piston_position_use = xg_max; - } - - if (arch::CrossesPiston(p, - pusher, - piston_position_use, - piston_velocity_current, - is_left)) { - arch::PistonUpdate(p, - pusher, - piston_position_use, - piston_velocity_current, - massive); - } - } - }; - - template - auto CustomParticleUpdate(simtime_t time, spidx_t sp, D& domain) const { - return CustomPrtlUpdate { piston_initial_position + - static_cast(time) * piston_velocity, - piston_velocity, - global_domain.mesh().extent(in::x1).second, - true, - true }; - }; - }; - -} // namespace user - -#endif // PROBLEM_GENERATOR_H \ No newline at end of file diff --git a/pgens/piston_shock/piston_shock.toml b/pgens/piston_shock/piston_shock.toml deleted file mode 100644 index 6a81d1ab5..000000000 --- a/pgens/piston_shock/piston_shock.toml +++ /dev/null @@ -1,75 +0,0 @@ -[simulation] - name = "piston_shock" - engine = "srpic" - runtime = 100.0 - -[grid] - resolution = [300, 24] - extent = [[0.0, 100.0], [0.0, 8.0]] - - [grid.metric] - metric = "minkowski" - - [grid.boundaries] - fields = [["CONDUCTOR", "CONDUCTOR"], ["PERIODIC"]] - particles = [["REFLECT", "REFLECT"], ["PERIODIC"]] - -[scales] - larmor0 = 5.7735 - skindepth0 = 1.0 - -[algorithms] - current_filters = 8 - - [algorithms.timestep] - CFL = 0.7 - -[particles] - ppc0 = 8.0 - - [[particles.species]] - label = "e-" - mass = 1.0 - charge = -1.0 - maxnpart = 2e7 - - [[particles.species]] - label = "e-" - mass = 1.0 - charge = 1.0 - maxnpart = 2e7 - -[setup] - piston_velocity = 0.99 # speed of leftmoving piston - piston_initial_position = 0.0 # speed towards the wall [c] - temperature = 0.168 # temperature of maxwell distribution [kB T / (m_i c^2)] - temperature_ratio = 1.0 # temperature ratio of electrons to protons - #Bmag = 1.0 # magnetic field strength as fraction of magnetisation - #Btheta = 63.0 # magnetic field angle in the plane - #Bphi = 0.0 # magnetic field angle out of plane - #filling_fraction = 0.99 # fraction of the shock piston filled with plasma - #injector_velocity = 0.0 # speed of injector [c] - #injection_start = 0.0 # start time of moving injector - #injection_frequency = 100 - -[output] - interval_time = 1000.0 - format = "BPFile" - - [output.fields] - quantities = ["N", "B", "E"] - - [output.particles] - enable = true - stride = 10 - - [output.spectra] - enable = false - -[diagnostics] - log_level = "WARNING" - blocking_timers = true - -[checkpoint] - interval = 10000 - keep = 1 \ No newline at end of file diff --git a/pgens/piston_shock/shock.toml b/pgens/piston_shock/shock.toml deleted file mode 100644 index 8cde86990..000000000 --- a/pgens/piston_shock/shock.toml +++ /dev/null @@ -1,74 +0,0 @@ -[simulation] - name = "shock" - engine = "srpic" - runtime = 15200.0 - -[grid] - resolution = [32768, 256] - extent = [[0.0, 8192.0], [-32.0, 32.0]] - - [grid.metric] - metric = "minkowski" - - [grid.boundaries] - fields = [["CONDUCTOR", "MATCH"], ["PERIODIC"]] - particles = [["REFLECT", "ABSORB"], ["PERIODIC"]] - -[scales] - larmor0 = 5.7735 - skindepth0 = 1.0 - -[algorithms] - current_filters = 8 - - [algorithms.timestep] - CFL = 0.7 - -[particles] - ppc0 = 8.0 - - [[particles.species]] - label = "e-" - mass = 1.0 - charge = -1.0 - maxnpart = 2e7 - - [[particles.species]] - label = "p+" - mass = 100.0 - charge = 1.0 - maxnpart = 2e7 - -[setup] - drift_ux = 0.15 # speed towards the wall [c] - temperature = 0.168 # temperature of maxwell distribution [kB T / (m_i c^2)] - temperature_ratio = 1.0 # temperature ratio of electrons to protons - Bmag = 1.0 # magnetic field strength as fraction of magnetisation - Btheta = 63.0 # magnetic field angle in the plane - Bphi = 0.0 # magnetic field angle out of plane - filling_fraction = 0.99 # fraction of the shock piston filled with plasma - injector_velocity = 0.0 # speed of injector [c] - injection_start = 0.0 # start time of moving injector - injection_frequency = 100 - -[output] - interval_time = 1000.0 - format = "BPFile" - - [output.fields] - quantities = ["N", "B", "E"] - - [output.particles] - enable = true - stride = 10 - - [output.spectra] - enable = false - -[diagnostics] - log_level = "WARNING" - blocking_timers = true - -[checkpoint] - interval = 10000 - keep = 1 \ No newline at end of file diff --git a/pgens/reconnection/pgen.hpp b/pgens/reconnection/pgen.hpp index 4711aec22..b0450b600 100644 --- a/pgens/reconnection/pgen.hpp +++ b/pgens/reconnection/pgen.hpp @@ -5,13 +5,12 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/pgen.h" #include "utils/numeric.h" #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" -#include "archetypes/problem_generator.h" #include "archetypes/spatial_dist.h" -#include "archetypes/traits.h" #include "archetypes/utils.h" #include "framework/domain/metadomain.h" #include "kernels/particle_moments.hpp" @@ -19,15 +18,14 @@ namespace user { using namespace ntt; - template - struct CurrentLayer : public arch::SpatialDistribution { - CurrentLayer(const M& metric, real_t cs_width, real_t center_x, real_t cs_y) - : arch::SpatialDistribution { metric } - , cs_width { cs_width } + template + struct CurrentLayer { + CurrentLayer(real_t cs_width, real_t center_x, real_t cs_y) + : cs_width { cs_width } , center_x { center_x } , cs_y { cs_y } {} - Inline auto operator()(const coord_t& x_Ph) const -> real_t { + Inline auto operator()(const coord_t& x_Ph) const -> real_t { return ONE / SQR(math::cosh((x_Ph[1] - cs_y) / cs_width)) * (ONE - math::exp(-SQR((x_Ph[0] - center_x) / cs_width))); } @@ -138,22 +136,21 @@ namespace user { // constant particle density for particle boundaries template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + const SimulationParams& params; + Metadomain& metadomain; const real_t bg_B, bg_Bguide, bg_temperature, inj_ypad; const real_t cs_width, cs_overdensity, cs_x, cs_y; @@ -161,33 +158,29 @@ namespace user { const simtime_t t_open; bool bc_opened { false }; - Metadomain& metadomain; - InitFields init_flds; - inline PGen(const SimulationParams& p, Metadomain& m) - : arch::ProblemGenerator(p) - , bg_B { p.template get("setup.bg_B", 1.0) } - , bg_Bguide { p.template get("setup.bg_Bguide", 0.0) } - , bg_temperature { p.template get("setup.bg_temperature", 0.001) } - , inj_ypad { p.template get("setup.inj_ypad", (real_t)0.05) } - , cs_width { p.template get("setup.cs_width") } - , cs_overdensity { p.template get("setup.cs_overdensity") } + PGen(const SimulationParams& p, Metadomain& m) + : params { p } + , metadomain { m } + , bg_B { params.template get("setup.bg_B", 1.0) } + , bg_Bguide { params.template get("setup.bg_Bguide", 0.0) } + , bg_temperature { params.template get("setup.bg_temperature", 0.001) } + , inj_ypad { params.template get("setup.inj_ypad", (real_t)0.05) } + , cs_width { params.template get("setup.cs_width") } + , cs_overdensity { params.template get("setup.cs_overdensity") } , cs_x { INV_2 * (m.mesh().extent(in::x1).second + m.mesh().extent(in::x1).first) } , cs_y { INV_2 * (m.mesh().extent(in::x2).second + m.mesh().extent(in::x2).first) } , ymin { m.mesh().extent(in::x2).first } , ymax { m.mesh().extent(in::x2).second } - , t_open { p.template get( + , t_open { params.template get( "setup.t_open", 1.5 * HALF * (m.mesh().extent(in::x1).second - m.mesh().extent(in::x1).first)) } - , metadomain { m } , init_flds { bg_B, bg_Bguide, cs_width, cs_y } {} - inline PGen() {} - auto MatchFieldsInX1(simtime_t) const -> BoundaryFieldsInX1 { return BoundaryFieldsInX1 { bg_B, bg_Bguide, (real_t)0.1, cs_width, cs_x, cs_y }; @@ -197,7 +190,7 @@ namespace user { return BoundaryFieldsInX2 { bg_B, bg_Bguide, cs_width, cs_y }; } - inline void InitPrtls(Domain& local_domain) { + void InitPrtls(Domain& local_domain) { // background arch::InjectUniformMaxwellian(params, local_domain, @@ -214,14 +207,10 @@ namespace user { const auto cs_temperature = HALF * sigma / cs_overdensity; // current layer - auto edist_cs = arch::Maxwellian(local_domain.mesh.metric, - local_domain.random_pool(), - cs_temperature, - { ZERO, ZERO, cs_drift_u }); - const auto sdist_cs = CurrentLayer(local_domain.mesh.metric, - cs_width, - cs_x, - cs_y); + auto edist_cs = arch::energy_dist::Maxwellian( + local_domain.random_pool(), + cs_temperature); + const auto sdist_cs = CurrentLayer(cs_width, cs_x, cs_y); arch::InjectNonUniform( params, local_domain, @@ -241,9 +230,9 @@ namespace user { metadomain.setPrtlBC(bc_in::Px1, PrtlBC::ABSORB); } - const auto energy_dist = arch::Maxwellian(domain.mesh.metric, - domain.random_pool(), - bg_temperature); + const auto energy_dist = arch::energy_dist::Maxwellian( + domain.random_pool(), + bg_temperature); const auto dx = domain.mesh.metric.template sqrt_h_<1, 1>({}); @@ -288,7 +277,7 @@ namespace user { Kokkos::Experimental::contribute(domain.fields.buff, scatter_buff); } - const auto replenish_sdist = arch::ReplenishUniform( + const auto replenish_sdist = arch::spatial_dist::ReplenishUniform( domain.mesh.metric, domain.fields.buff, 0u, diff --git a/pgens/shock/pgen.hpp b/pgens/shock/pgen.hpp index b8289c9a3..39f6baf99 100644 --- a/pgens/shock/pgen.hpp +++ b/pgens/shock/pgen.hpp @@ -4,12 +4,11 @@ #include "enums.h" #include "global.h" +#include "traits/pgen.h" #include "utils/error.h" #include "utils/numeric.h" #include "archetypes/field_setter.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "archetypes/utils.h" #include "framework/domain/metadomain.h" @@ -67,24 +66,20 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; - - Metadomain& global_domain; + const SimulationParams& params; + Metadomain& metadomain; // domain properties const real_t global_xmin, global_xmax; @@ -97,25 +92,29 @@ namespace user { real_t Btheta, Bphi, Bmag; InitFields init_flds; - inline PGen(const SimulationParams& p, Metadomain& global_domain) - : arch::ProblemGenerator { p } - , global_domain { global_domain } - , global_xmin { global_domain.mesh().extent(in::x1).first } - , global_xmax { global_domain.mesh().extent(in::x1).second } - , drift_ux { p.template get("setup.drift_ux") } - , temperature { p.template get("setup.temperature") } - , temperature_ratio { p.template get("setup.temperature_ratio") } - , Bmag { p.template get("setup.Bmag", ZERO) } - , Btheta { p.template get("setup.Btheta", ZERO) } - , Bphi { p.template get("setup.Bphi", ZERO) } + PGen(const SimulationParams& p, Metadomain& m) + : params { p } + , metadomain { m } + , global_xmin { metadomain.mesh().extent(in::x1).first } + , global_xmax { metadomain.mesh().extent(in::x1).second } + , drift_ux { params.template get("setup.drift_ux") } + , temperature { params.template get("setup.temperature") } + , temperature_ratio { params.template get( + "setup.temperature_ratio") } + , Bmag { params.template get("setup.Bmag", ZERO) } + , Btheta { params.template get("setup.Btheta", ZERO) } + , Bphi { params.template get("setup.Bphi", ZERO) } , init_flds { Bmag, Btheta, Bphi, drift_ux } - , filling_fraction { p.template get("setup.filling_fraction", 1.0) } - , injector_velocity { p.template get("setup.injector_velocity", 1.0) } - , injection_start { p.template get("setup.injection_start", 0.0) } - , injection_frequency { p.template get("setup.injection_frequency", 100) } - , dt { p.template get("algorithms.timestep.dt") } {} - - inline PGen() {} + , filling_fraction { params.template get("setup.filling_fraction", + 1.0) } + , injector_velocity { params.template get( + "setup.injector_velocity", + 1.0) } + , injection_start { params.template get("setup.injection_start", 0.0) } + , injection_frequency { params.template get( + "setup.injection_frequency", + 100) } + , dt { params.template get("algorithms.timestep.dt") } {} auto MatchFields(simtime_t) const -> InitFields { return init_flds; @@ -125,9 +124,7 @@ namespace user { -> std::pair { if (comp == em::ex1) { return { init_flds.ex1({ ZERO }), true }; - } else if (comp == em::ex2) { - return { ZERO, true }; - } else if (comp == em::ex3) { + } else if ((comp == em::ex2) or (comp == em::ex3)) { return { ZERO, true }; } else if (comp == em::bx1) { return { init_flds.bx1({ ZERO }), true }; @@ -141,7 +138,7 @@ namespace user { } } - inline void InitPrtls(Domain& domain) { + void InitPrtls(Domain& domain) { /* * Plasma setup as partially filled box @@ -167,7 +164,7 @@ namespace user { for (auto d { 0u }; d < (unsigned int)M::Dim; ++d) { // compute the range for the x-direction if (d == static_cast(in::x1)) { - box.push_back({ xg_min, xg_max }); + box.emplace_back(xg_min, xg_max); } else { // inject into full range in other directions box.push_back(Range::All); @@ -235,7 +232,7 @@ namespace user { // define indice range to reset fields boundaries_t incl_ghosts; for (auto d = 0; d < M::Dim; ++d) { - incl_ghosts.push_back({ false, false }); + incl_ghosts.emplace_back(false, false); } // define box to reset fields @@ -243,7 +240,7 @@ namespace user { // loop over all dimension for (auto d = 0u; d < M::Dim; ++d) { if (d == 0) { - purge_box.push_back({ xmin, global_xmax }); + purge_box.emplace_back(xmin, global_xmax); } else { purge_box.push_back(Range::All); } @@ -258,11 +255,11 @@ namespace user { Kokkos::parallel_for("ResetFields", CreateRangePolicy(x_min, x_max), - arch::SetEMFields_kernel { + arch::SetEMFields_kernel { domain.fields.em, init_flds, domain.mesh.metric }); - global_domain.CommunicateFields(domain, Comm::E | Comm::B); + metadomain.CommunicateFields(domain, Comm::E | Comm::B); /* tag particles inside the injection zone as dead @@ -305,7 +302,7 @@ namespace user { // loop over all dimension for (auto d = 0u; d < M::Dim; ++d) { if (d == 0) { - inj_box.push_back({ xmin, xmax }); + inj_box.emplace_back(xmin, xmax); } else { inj_box.push_back(Range::All); } diff --git a/pgens/streaming/pgen.hpp b/pgens/streaming/pgen.hpp index 9c87d7d97..6983d249d 100644 --- a/pgens/streaming/pgen.hpp +++ b/pgens/streaming/pgen.hpp @@ -5,14 +5,14 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/pgen.h" #include "utils/error.h" #include "utils/numeric.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "archetypes/utils.h" #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" +#include "framework/parameters/parameters.h" namespace user { using namespace ntt; @@ -51,20 +51,16 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { - + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator - static constexpr auto engines = - arch::traits::pgen::compatible_with::value; + static constexpr auto engines = ::traits::pgen::compatible_with {}; static constexpr auto metrics = - arch::traits::pgen::compatible_with::value; + ::traits::pgen::compatible_with {}; static constexpr auto dimensions = - arch::traits::pgen::compatible_with::value; + ::traits::pgen::compatible_with {}; - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + const SimulationParams& params; prmvec_t drifts_in_x, drifts_in_y, drifts_in_z; prmvec_t densities, temperatures; @@ -72,18 +68,22 @@ namespace user { real_t Btheta, Bphi, Bmag; InitFields init_flds; - inline PGen(const SimulationParams& p, const Metadomain& global_domain) - : arch::ProblemGenerator { p } - , drifts_in_x { p.template get("setup.drifts_in_x", prmvec_t {}) } - , drifts_in_y { p.template get("setup.drifts_in_y", prmvec_t {}) } - , drifts_in_z { p.template get("setup.drifts_in_z", prmvec_t {}) } - , Bmag { p.template get("setup.Bmag", ZERO) } - , Btheta { p.template get("setup.Btheta", ZERO) } - , Bphi { p.template get("setup.Bphi", ZERO) } + PGen(const SimulationParams& p, const Metadomain& global_domain) + : params { p } + , drifts_in_x { params.template get("setup.drifts_in_x", + prmvec_t {}) } + , drifts_in_y { params.template get("setup.drifts_in_y", + prmvec_t {}) } + , drifts_in_z { params.template get("setup.drifts_in_z", + prmvec_t {}) } + , Bmag { params.template get("setup.Bmag", ZERO) } + , Btheta { params.template get("setup.Btheta", ZERO) } + , Bphi { params.template get("setup.Bphi", ZERO) } , init_flds { Bmag, Btheta, Bphi } - , densities { p.template get("setup.densities", prmvec_t {}) } - , temperatures { p.template get("setup.temperatures", prmvec_t {}) } { - const auto nspec = p.template get("particles.nspec"); + , densities { params.template get("setup.densities", prmvec_t {}) } + , temperatures { params.template get("setup.temperatures", + prmvec_t {}) } { + const auto nspec = params.template get("particles.nspec"); raise::ErrorIf(nspec % 2 != 0, "Number of species must be even for this setup", HERE); @@ -117,7 +117,7 @@ namespace user { HERE); } - inline void InitPrtls(Domain& domain) { + void InitPrtls(Domain& domain) { const auto nspec = domain.species.size(); for (auto n = 0u; n < nspec; n += 2) { const auto drift_1 = prmvec_t { drifts_in_x[n], diff --git a/pgens/turbulence/pgen.hpp b/pgens/turbulence/pgen.hpp index 17093d86a..706c603da 100644 --- a/pgens/turbulence/pgen.hpp +++ b/pgens/turbulence/pgen.hpp @@ -5,12 +5,11 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/pgen.h" #include "utils/error.h" #include "utils/numeric.h" #include "archetypes/energy_dist.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "archetypes/utils.h" #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" @@ -107,7 +106,7 @@ namespace user { #endif // MPI_ENABLED return new_seed; } else { - return static_cast(seed); + return seed; } } @@ -169,15 +168,20 @@ namespace user { auto k_host = Kokkos::create_mirror_view(k); if constexpr (D == Dim::_2D) { for (auto i = 0u; i < n_modes; i++) { - k_host(0, i) = constant::TWO_PI * wavenumbers[i][0] / Lx; - k_host(1, i) = constant::TWO_PI * wavenumbers[i][1] / Ly; + k_host(0, i) = static_cast(constant::TWO_PI) * + wavenumbers[i][0] / Lx; + k_host(1, i) = static_cast(constant::TWO_PI) * + wavenumbers[i][1] / Ly; } } if constexpr (D == Dim::_3D) { for (auto i = 0u; i < n_modes; i++) { - k_host(0, i) = constant::TWO_PI * wavenumbers[i][0] / Lx; - k_host(1, i) = constant::TWO_PI * wavenumbers[i][1] / Ly; - k_host(2, i) = constant::TWO_PI * wavenumbers[i][2] / Lz; + k_host(0, i) = static_cast(constant::TWO_PI) * + wavenumbers[i][0] / Lx; + k_host(1, i) = static_cast(constant::TWO_PI) * + wavenumbers[i][1] / Ly; + k_host(2, i) = static_cast(constant::TWO_PI) * + wavenumbers[i][2] / Lz; } } // initializing initial complex amplitudes @@ -197,12 +201,13 @@ namespace user { auto k_perp = math::sqrt( k_host(0, i) * k_host(0, i) + k_host(1, i) * k_host(1, i)); real_t phase = static_cast(rand()) / - static_cast(RAND_MAX) * constant::TWO_PI; + static_cast(RAND_MAX) * + static_cast(constant::TWO_PI); A0_host(i) = dB / math::sqrt((real_t)n_modes) / k_perp * prefac; a_real_host(i) = A0_host(i) * math::cos(phase); a_imag_host(i) = A0_host(i) * math::sin(phase); phase = static_cast(rand()) / static_cast(RAND_MAX) * - constant::TWO_PI; + static_cast(constant::TWO_PI); a_imag_inv_host(i) = A0_host(i) * math::cos(phase); a_real_inv_host(i) = A0_host(i) * math::sin(phase); } @@ -290,20 +295,16 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { - + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator - static constexpr auto engines = - arch::traits::pgen::compatible_with::value; + static constexpr auto engines = ::traits::pgen::compatible_with {}; static constexpr auto metrics = - arch::traits::pgen::compatible_with::value; + ::traits::pgen::compatible_with {}; static constexpr auto dimensions = - arch::traits::pgen::compatible_with::value; + ::traits::pgen::compatible_with {}; - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + const SimulationParams& params; const real_t temperature, dB, omega_0, gamma_0; const real_t Lx, Ly, Lz, escape_dist; @@ -319,14 +320,14 @@ namespace user { ExternalCurrent ext_current; InitFields init_flds; - inline PGen(const SimulationParams& p, const Metadomain& global_domain) - : arch::ProblemGenerator { p } - , temperature { p.template get("setup.temperature") } - , dB { p.template get("setup.dB", ONE) } - , omega_0 { p.template get("setup.omega_0") } - , gamma_0 { p.template get("setup.gamma_0") } + PGen(const SimulationParams& p, const Metadomain& global_domain) + : params { p } + , temperature { params.template get("setup.temperature") } + , dB { params.template get("setup.dB", ONE) } + , omega_0 { params.template get("setup.omega_0") } + , gamma_0 { params.template get("setup.gamma_0") } , wavenumbers { init_wavenumbers() } - , random_seed { p.template get("setup.seed", 0) } + , random_seed { params.template get("setup.seed", 0) } , random_pool { init_pool(random_seed) } , Lx { global_domain.mesh().extent(in::x1).second - global_domain.mesh().extent(in::x1).first } @@ -334,7 +335,7 @@ namespace user { global_domain.mesh().extent(in::x2).first } , Lz { global_domain.mesh().extent(in::x3).second - global_domain.mesh().extent(in::x3).first } - , escape_dist { p.template get("setup.escape_dist", HALF * Lx) } + , escape_dist { params.template get("setup.escape_dist", HALF * Lx) } , ext_current { dB, omega_0, gamma_0, wavenumbers, init_pool(random_seed), Lx, Ly, Lz } , init_flds { ext_current.k, @@ -343,7 +344,7 @@ namespace user { ext_current.a_real_inv, ext_current.a_imag_inv } {}; - inline void InitPrtls(Domain& domain) { + void InitPrtls(Domain& domain) { arch::InjectUniformMaxwellian(params, domain, ONE, temperature, { 1, 2 }); } @@ -400,9 +401,9 @@ namespace user { }); // particle escape (resample velocities) - const auto energy_dist = arch::Maxwellian(domain.mesh.metric, - domain.random_pool(), - temperature); + const auto energy_dist = arch::energy_dist::Maxwellian( + domain.random_pool(), + temperature); for (const auto& sp : { 0, 1 }) { if (domain.species[sp].npld_r() > 1) { const auto& ux1 = domain.species[sp].ux1; diff --git a/pgens/wald/pgen.hpp b/pgens/wald/pgen.hpp index 292f6f7fe..de1b4fd71 100644 --- a/pgens/wald/pgen.hpp +++ b/pgens/wald/pgen.hpp @@ -5,18 +5,17 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/pgen.h" #include "utils/comparators.h" #include "utils/error.h" #include "utils/formatting.h" #include "utils/numeric.h" -#include "archetypes/problem_generator.h" -#include "archetypes/traits.h" #include "framework/domain/metadomain.h" #include -enum InitFieldGeometry { +enum class InitFieldGeometry : uint8_t { Wald, Vertical, }; @@ -229,32 +228,21 @@ namespace user { }; template - struct PGen : public arch::ProblemGenerator { + struct PGen { + static constexpr auto D { M::Dim }; // compatibility traits for the problem generator static constexpr auto engines { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; static constexpr auto metrics { - arch::traits::pgen::compatible_with::value + ::traits::pgen::compatible_with {} }; - static constexpr auto dimensions { - arch::traits::pgen::compatible_with::value - }; - - // for easy access to variables in the child class - using arch::ProblemGenerator::D; - using arch::ProblemGenerator::C; - using arch::ProblemGenerator::params; + static constexpr auto dimensions { ::traits::pgen::compatible_with {} }; - InitFields init_flds; - const Metadomain& global_domain; + InitFields init_flds; - inline PGen(const SimulationParams& p, const Metadomain& m) - : arch::ProblemGenerator { p } - , global_domain { m } - , init_flds { m.mesh().metric, + PGen(const SimulationParams& p, const Metadomain& m) + : init_flds { m.mesh().metric, p.template get("setup.init_field", "wald") } {} }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 443dc0545..1d293e8db 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,11 +24,12 @@ set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(ENTITY ${PROJECT_NAME}.xc) +set(ENTITY ${PROJECT_NAME}${pgen_suffix}.xc) set(SOURCES ${SRC_DIR}/entity.cpp) add_executable(${ENTITY} ${SOURCES}) -set(libs ntt_global ntt_framework ntt_metrics ntt_engines ntt_pgen) +set(libs ntt_global ntt_framework ntt_metrics ntt_engines${pgen_suffix} + ntt_pgen${pgen_suffix}) add_dependencies(${ENTITY} ${libs}) target_link_libraries(${ENTITY} PUBLIC ${libs}) install(TARGETS ${ENTITY} DESTINATION bin) diff --git a/src/archetypes/emission.h b/src/archetypes/emission.h new file mode 100644 index 000000000..f6a9240c1 --- /dev/null +++ b/src/archetypes/emission.h @@ -0,0 +1,492 @@ +/** + * @file archetypes/emission.h + * @brief Emission policy archetypes for radiative processes in particle pushers + * @implements + * - arch::EmissionSynchrotron<> + * - arch::EmissionCompton<> + * @namespaces: + * - arch:: + */ + +#ifndef ARCHETYPES_EMISSION_HPP +#define ARCHETYPES_EMISSION_HPP + +#include "enums.h" +#include "global.h" + +#include "traits/metric.h" +#include "utils/param_container.h" + +#include "framework/containers/particles.h" +#include "kernels/injectors.hpp" + +#include + +#include + +namespace arch { + using namespace ntt; + + template + struct EmissionSynchrotron { + struct Payload { + real_t photon_energy; + }; + + const spidx_t emitted_index; + const real_t species_mass; + const real_t emitted_photon_weight; + const real_t emitted_photon_min_energy; + + const real_t nominal_probability; + const real_t nominal_photon_energy; + + const bool should_drag; + + array_t photon_i1, photon_i2, photon_i3; + array_t photon_dx1, photon_dx2, photon_dx3; + array_t photon_ux1, photon_ux2, photon_ux3; + array_t photon_phi; + array_t photon_weight; + array_t photon_tag; + array_t photon_pld_i; + + array_t photon_idx { "idx" }; + const npart_t photon_offset, photon_cntr, domain_idx; + const bool photon_use_tracking; + + random_number_pool_t random_pool; + + EmissionSynchrotron(Particles& photon_species, + spidx_t emitted_index, + float sp_mass, + float sp_charge, + RadiativeDragFlags radiative_drag_flags, + npart_t domain_idx, + const prm::Parameters& params, + random_number_pool_t& random_pool) + : emitted_index { emitted_index } + , species_mass { static_cast(sp_mass) } + , emitted_photon_weight { params.template get( + "radiation.emission.synchrotron.photon_weight") } + , emitted_photon_min_energy { params.template get( + "radiation.emission.synchrotron.photon_energy_min") } + , nominal_probability { math::abs(sp_charge / species_mass) * + params.template get( + "radiation.emission.synchrotron.nominal_" + "probability") } + , nominal_photon_energy { species_mass * + params.template get( + "radiation.emission.synchrotron." + "nominal_photon_energy") } + , should_drag { static_cast(radiative_drag_flags & + RadiativeDrag::SYNCHROTRON) } + , photon_i1 { photon_species.i1 } + , photon_i2 { photon_species.i2 } + , photon_i3 { photon_species.i3 } + , photon_dx1 { photon_species.dx1 } + , photon_dx2 { photon_species.dx2 } + , photon_dx3 { photon_species.dx3 } + , photon_ux1 { photon_species.ux1 } + , photon_ux2 { photon_species.ux2 } + , photon_ux3 { photon_species.ux3 } + , photon_phi { photon_species.phi } + , photon_weight { photon_species.weight } + , photon_tag { photon_species.tag } + , photon_pld_i { photon_species.pld_i } + , photon_offset { photon_species.npart() } + , photon_cntr { photon_species.counter() } + , domain_idx { domain_idx } + , photon_use_tracking { photon_species.use_tracking() } + , random_pool { random_pool } {} + + auto emitted_species_indices() const -> std::vector { + return { emitted_index }; + } + + auto numbers_injected() const -> std::vector { + auto photon_idx_h = Kokkos::create_mirror_view(photon_idx); + Kokkos::deep_copy(photon_idx_h, photon_idx); + return { photon_idx_h() }; + } + + /** + * + * @brief Determine whether a photon is emitted, the drag is applied, + * and compute its energy and the recoil on the emitting particle + * + * @param x_Cd Position of the particle (code) + * @param x_Ph Position of the particle (physical) + * @param u_Ph Velocity of the particle (physical) + * @param ep Interpolated electric field at the particle position (physical, units of B0) + * @param bp Interpolated magnetic field at the particle position (physical, units of B0) + * @param delta_u_Ph Output parameter for the recoil on the emitting particle (physical units) + * + * @note + * + * probability at each timestep is: + * nominal_probability = omegaB0 * etarec * dt * (gamma_QED / gamma_rad)^2 / photon_weight + * kappaR = (e + beta x b) x b + (beta . e) e + * chiR^2 = (e + beta x b)^2 - (beta . e)^2 + * p_gamma = (q / q0) / (m / m0) * nominal_probability * (-kappaR . beta_hat / gamma^2 + beta chiR^2) + * + * mean energy of the emitted photon [units of m0 c^2]: + * nominal_photon_energy = (1 / gamma_QED)^2 + * e_gamma = (gamma)^2 * (m / m0) * nominal_photon_energy + * + * drag force [in units of m c]: + * du / dt = - photon_weight * p_gamma * e_gamma * u_hat + * + * @returns Pair of booleans to indicate whether a particle should be emitted + * and whether the emitting particle should experience a recoil (i.e. radiative drag) + * + */ + Inline auto shouldEmit(const coord_t&, + const coord_t&, + const vec_t& u_Ph, + const vec_t& ep_Ph, + const vec_t& bp_Ph, + vec_t& delta_u_Ph, + Payload& payload) const -> Kokkos::pair { + const auto u_sqr = NORM_SQR(u_Ph[0], u_Ph[1], u_Ph[2]); + const auto u_mag = math::sqrt(u_sqr); + const auto gamma_sqr = ONE + u_sqr; + const auto gamma = math::sqrt(gamma_sqr); + const auto beta = u_mag / gamma; + + const auto e_plus_beta_cross_b_x1 = + ep_Ph[0] + + CROSS_x1(u_Ph[0], u_Ph[1], u_Ph[2], bp_Ph[0], bp_Ph[1], bp_Ph[2]) / gamma; + const auto e_plus_beta_cross_b_x2 = + ep_Ph[1] + + CROSS_x2(u_Ph[0], u_Ph[1], u_Ph[2], bp_Ph[0], bp_Ph[1], bp_Ph[2]) / gamma; + const auto e_plus_beta_cross_b_x3 = + ep_Ph[2] + + CROSS_x3(u_Ph[0], u_Ph[1], u_Ph[2], bp_Ph[0], bp_Ph[1], bp_Ph[2]) / gamma; + const auto beta_dot_e = + DOT(u_Ph[0], u_Ph[1], u_Ph[2], ep_Ph[0], ep_Ph[1], ep_Ph[2]) / gamma; + + const auto kappaR_x1 = CROSS_x1(e_plus_beta_cross_b_x1, + e_plus_beta_cross_b_x2, + e_plus_beta_cross_b_x3, + bp_Ph[0], + bp_Ph[1], + bp_Ph[2]) + + beta_dot_e * ep_Ph[0]; + const auto kappaR_x2 = CROSS_x2(e_plus_beta_cross_b_x1, + e_plus_beta_cross_b_x2, + e_plus_beta_cross_b_x3, + bp_Ph[0], + bp_Ph[1], + bp_Ph[2]) + + beta_dot_e * ep_Ph[1]; + const auto kappaR_x3 = CROSS_x3(e_plus_beta_cross_b_x1, + e_plus_beta_cross_b_x2, + e_plus_beta_cross_b_x3, + bp_Ph[0], + bp_Ph[1], + bp_Ph[2]) + + beta_dot_e * ep_Ph[2]; + const auto chiR_sqr = NORM_SQR(e_plus_beta_cross_b_x1, + e_plus_beta_cross_b_x2, + e_plus_beta_cross_b_x3) - + SQR(beta_dot_e); + + const auto probability = + nominal_probability * + (-DOT(kappaR_x1, kappaR_x2, kappaR_x3, u_Ph[0], u_Ph[1], u_Ph[2]) / + (gamma_sqr * u_mag) + + beta * chiR_sqr); + + const auto dir_x1 = -kappaR_x1 + gamma * u_Ph[0] * chiR_sqr; + const auto dir_x2 = -kappaR_x2 + gamma * u_Ph[1] * chiR_sqr; + const auto dir_x3 = -kappaR_x3 + gamma * u_Ph[2] * chiR_sqr; + + payload.photon_energy = gamma_sqr * nominal_photon_energy; + + const auto delta_u = -emitted_photon_weight * payload.photon_energy / + (NORM(dir_x1, dir_x2, dir_x3) * species_mass); + + delta_u_Ph[0] = delta_u * dir_x1; + delta_u_Ph[1] = delta_u * dir_x2; + delta_u_Ph[2] = delta_u * dir_x3; + + auto rand_gen = random_pool.get_state(); + // should not emit if photon energy is above 20% of (gamma - 1) m c^2 + const auto should_emit = (Random(rand_gen) < probability) and + (payload.photon_energy < + species_mass * (gamma - ONE) * + static_cast(0.2)); + random_pool.free_state(rand_gen); + + return Kokkos::make_pair( + should_emit and (payload.photon_energy >= emitted_photon_min_energy), + should_drag and should_emit); + } + + Inline void emit(const tuple_t& xi_Cd, + const tuple_t& dxi_Cd, + const vec_t& direction, + real_t weight, + real_t phi, + const Payload& payload) const { + const auto index = Kokkos::atomic_fetch_add(&photon_idx(), 1); + if (not photon_use_tracking) { + kernel::InjectParticle( + photon_offset + index, + photon_i1, + photon_i2, + photon_i3, + photon_dx1, + photon_dx2, + photon_dx3, + photon_ux1, + photon_ux2, + photon_ux3, + photon_phi, + photon_weight, + photon_tag, + photon_pld_i, + xi_Cd, + dxi_Cd, + { direction[0] * payload.photon_energy, + direction[1] * payload.photon_energy, + direction[2] * payload.photon_energy }, + emitted_photon_weight * weight, + phi); + } else { + kernel::InjectParticle( + photon_offset + index, + photon_i1, + photon_i2, + photon_i3, + photon_dx1, + photon_dx2, + photon_dx3, + photon_ux1, + photon_ux2, + photon_ux3, + photon_phi, + photon_weight, + photon_tag, + photon_pld_i, + xi_Cd, + dxi_Cd, + { direction[0] * payload.photon_energy, + direction[1] * payload.photon_energy, + direction[2] * payload.photon_energy }, + emitted_photon_weight * weight, + phi, + domain_idx, + photon_cntr + index); + } + } + }; + + template + struct EmissionCompton { + struct Payload { + real_t photon_energy; + }; + + const spidx_t emitted_index; + const real_t species_mass; + const real_t emitted_photon_weight; + const real_t emitted_photon_min_energy; + + const real_t nominal_probability; + const real_t nominal_photon_energy; + + const bool should_drag; + + array_t photon_i1, photon_i2, photon_i3; + array_t photon_dx1, photon_dx2, photon_dx3; + array_t photon_ux1, photon_ux2, photon_ux3; + array_t photon_phi; + array_t photon_weight; + array_t photon_tag; + array_t photon_pld_i; + + array_t photon_idx { "idx" }; + const npart_t photon_offset, photon_cntr, domain_idx; + const bool photon_use_tracking; + + random_number_pool_t random_pool; + + EmissionCompton(Particles& photon_species, + spidx_t emitted_index, + float sp_mass, + float sp_charge, + RadiativeDragFlags radiative_drag_flags, + npart_t domain_idx, + const prm::Parameters& params, + random_number_pool_t& random_pool) + : emitted_index { emitted_index } + , species_mass { static_cast(sp_mass) } + , emitted_photon_weight { params.template get( + "radiation.emission.compton.photon_weight") } + , emitted_photon_min_energy { params.template get( + "radiation.emission.compton.photon_energy_min") } + , nominal_probability { math::abs(sp_charge / species_mass) * + params.template get( + "radiation.emission.compton.nominal_" + "probability") } + , nominal_photon_energy { species_mass * params.template get( + "radiation.emission.compton." + "nominal_photon_energy") } + , should_drag { static_cast(radiative_drag_flags & + RadiativeDrag::COMPTON) } + , photon_i1 { photon_species.i1 } + , photon_i2 { photon_species.i2 } + , photon_i3 { photon_species.i3 } + , photon_dx1 { photon_species.dx1 } + , photon_dx2 { photon_species.dx2 } + , photon_dx3 { photon_species.dx3 } + , photon_ux1 { photon_species.ux1 } + , photon_ux2 { photon_species.ux2 } + , photon_ux3 { photon_species.ux3 } + , photon_phi { photon_species.phi } + , photon_weight { photon_species.weight } + , photon_tag { photon_species.tag } + , photon_pld_i { photon_species.pld_i } + , photon_offset { photon_species.npart() } + , photon_cntr { photon_species.counter() } + , domain_idx { domain_idx } + , photon_use_tracking { photon_species.use_tracking() } + , random_pool { random_pool } {} + + auto emitted_species_indices() const -> std::vector { + return { emitted_index }; + } + + auto numbers_injected() const -> std::vector { + auto photon_idx_h = Kokkos::create_mirror_view(photon_idx); + Kokkos::deep_copy(photon_idx_h, photon_idx); + return { photon_idx_h() }; + } + + /** + * + * @brief Determine whether a photon is emitted, the drag is applied, + * and compute its energy and the recoil on the emitting particle + * + * @param x_Cd Position of the particle (code) + * @param x_Ph Position of the particle (physical) + * @param u_Ph Velocity of the particle (physical) + * @param ep Interpolated electric field at the particle position (physical) + * @param bp Interpolated magnetic field at the particle position (physical) + * @param delta_u_Ph Output parameter for the recoil on the emitting particle (physical units) + * + * @note + * + * probability at each timestep is: + * nominal_probability = omegaB0 * etarec * dt * (gamma_QED / gamma_rad)^2 / photon_weight + * p_gamma = (q / q0) / (m / m0) * beta * nominal_probability + * + * mean energy of the emitted photon [units of m0 c^2]: + * nominal_photon_energy = (1 / gamma_QED)^2 + * e_gamma = (gamma)^2 * (m / m0) * nominal_photon_energy + * + * drag acceleration * dt: + * du = - photon_weight * p_gamma * e_gamma * u_hat / (m / m0) + * + * @returns Pair of booleans to indicate whether a particle should be emitted + * and whether the emitting particle should experience a recoil (i.e. radiative drag) + * + */ + Inline auto shouldEmit(const coord_t&, + const coord_t&, + const vec_t& u_Ph, + const vec_t&, + const vec_t&, + vec_t& delta_u_Ph, + Payload& payload) const -> Kokkos::pair { + const auto u_sqr = NORM_SQR(u_Ph[0], u_Ph[1], u_Ph[2]); + const auto gamma_sqr = ONE + u_sqr; + + payload.photon_energy = gamma_sqr * nominal_photon_energy; + + const auto delta_u = -emitted_photon_weight * payload.photon_energy / + (math::sqrt(u_sqr) * species_mass); + + delta_u_Ph[0] = delta_u * u_Ph[0]; + delta_u_Ph[1] = delta_u * u_Ph[1]; + delta_u_Ph[2] = delta_u * u_Ph[2]; + + auto rand_gen = random_pool.get_state(); + // should not emit if photon energy is above 20% of (gamma - 1) m c^2 + const auto should_emit = (Random(rand_gen) < + (nominal_probability * + math::sqrt(u_sqr / gamma_sqr))) and + (payload.photon_energy < + species_mass * (math::sqrt(gamma_sqr) - ONE) * + static_cast(0.2)); + random_pool.free_state(rand_gen); + + return Kokkos::make_pair( + should_emit and (payload.photon_energy >= emitted_photon_min_energy), + should_drag and should_emit); + } + + Inline void emit(const tuple_t& xi_Cd, + const tuple_t& dxi_Cd, + const vec_t& direction, + real_t weight, + real_t phi, + const Payload& payload) const { + const auto index = Kokkos::atomic_fetch_add(&photon_idx(), 1); + if (not photon_use_tracking) { + kernel::InjectParticle( + photon_offset + index, + photon_i1, + photon_i2, + photon_i3, + photon_dx1, + photon_dx2, + photon_dx3, + photon_ux1, + photon_ux2, + photon_ux3, + photon_phi, + photon_weight, + photon_tag, + photon_pld_i, + xi_Cd, + dxi_Cd, + { direction[0] * payload.photon_energy, + direction[1] * payload.photon_energy, + direction[2] * payload.photon_energy }, + emitted_photon_weight * weight, + phi); + } else { + kernel::InjectParticle( + photon_offset + index, + photon_i1, + photon_i2, + photon_i3, + photon_dx1, + photon_dx2, + photon_dx3, + photon_ux1, + photon_ux2, + photon_ux3, + photon_phi, + photon_weight, + photon_tag, + photon_pld_i, + xi_Cd, + dxi_Cd, + { direction[0] * payload.photon_energy, + direction[1] * payload.photon_energy, + direction[2] * payload.photon_energy }, + emitted_photon_weight * weight, + phi, + domain_idx, + photon_cntr + index); + } + } + }; + +} // namespace arch + +#endif // ARCHETYPES_EMISSION_HPP \ No newline at end of file diff --git a/src/archetypes/energy_dist.h b/src/archetypes/energy_dist.h index a493d7d85..11fe04369 100644 --- a/src/archetypes/energy_dist.h +++ b/src/archetypes/energy_dist.h @@ -1,18 +1,12 @@ /** - * @file archetypes/energy_dist.hpp + * @file archetypes/energy_dist.h * @brief Defines an archetype for energy distributions * @implements - * - arch::EnergyDistribution<> - * - arch::Cold<> : arch::EnergyDistribution<> - * - arch::Powerlaw<> : arch::EnergyDistribution<> - * - arch::Maxwellian<> : arch::EnergyDistribution<> + * - arch::energy_dist::Cold<> + * - arch::energy_dist::Powerlaw<> + * - arch::energy_dist::Maxwellian<> * @namespaces: - * - arch:: - * @note - * The class returns a random velocity according to a coded distribution - * For Cartesian: the returned velocity is in the global Cartesian basis - * For non-Cartesian SR: the returned velocity is in the tetrad basis - * For GR: the returned velocity is in the covariant basis + * - arch::energy_dist:: */ #ifndef ARCHETYPES_ENERGY_DIST_HPP @@ -26,30 +20,15 @@ #include "utils/error.h" #include "utils/numeric.h" -#include "metrics/traits.h" - #include #include -namespace arch { +namespace arch::energy_dist { using namespace ntt; - template - requires metric::traits::HasD - struct EnergyDistribution { - static constexpr auto D = M::Dim; - - EnergyDistribution(const M& metric) : metric { metric } {} - - protected: - const M metric; - }; - - template - struct Cold : public EnergyDistribution { - Cold(const M& metric) : EnergyDistribution { metric } {} - - Inline void operator()(const coord_t&, vec_t& v) const { + template + struct Cold { + Inline void operator()(const coord_t&, vec_t& v) const { v[0] = ZERO; v[1] = ZERO; @@ -57,22 +36,16 @@ namespace arch { } }; - template - struct Powerlaw : public EnergyDistribution { - using EnergyDistribution::metric; + template + struct Powerlaw { - Powerlaw(const M& metric, - random_number_pool_t& pool, - real_t g_min, - real_t g_max, - real_t pl_ind) - : EnergyDistribution { metric } - , g_min { g_min } + Powerlaw(random_number_pool_t& pool, real_t g_min, real_t g_max, real_t pl_ind) + : g_min { g_min } , g_max { g_max } , pl_ind { pl_ind } , pool { pool } {} - Inline void operator()(const coord_t&, vec_t& v) const { + Inline void operator()(const coord_t&, vec_t& v) const { auto rand_gen = pool.get_state(); auto rand_X1 = Random(rand_gen); auto rand_gam = ONE; @@ -92,8 +65,8 @@ namespace arch { auto rand_X3 = Random(rand_gen); v[0] = rand_u * (TWO * rand_X2 - ONE); v[2] = TWO * rand_u * math::sqrt(rand_X2 * (ONE - rand_X2)); - v[1] = v[2] * math::cos(constant::TWO_PI * rand_X3); - v[2] = v[2] * math::sin(constant::TWO_PI * rand_X3); + v[1] = v[2] * math::cos(static_cast(constant::TWO_PI) * rand_X3); + v[2] = v[2] * math::sin(static_cast(constant::TWO_PI) * rand_X3); pool.free_state(rand_gen); } @@ -115,7 +88,7 @@ namespace arch { randX1 = Random(rand_gen); } randX1 = math::sqrt(-TWO * math::log(randX1)); - randX2 = constant::TWO_PI * Random(rand_gen); + randX2 = static_cast(constant::TWO_PI) * Random(rand_gen); v[0] = randX1 * math::cos(randX2) * math::sqrt(temp); randX1 = Random(rand_gen); @@ -123,7 +96,7 @@ namespace arch { randX1 = Random(rand_gen); } randX1 = math::sqrt(-TWO * math::log(randX1)); - randX2 = constant::TWO_PI * Random(rand_gen); + randX2 = static_cast(constant::TWO_PI) * Random(rand_gen); v[1] = randX1 * math::cos(randX2) * math::sqrt(temp); randX1 = Random(rand_gen); @@ -131,7 +104,7 @@ namespace arch { randX1 = Random(rand_gen); } randX1 = math::sqrt(-TWO * math::log(randX1)); - randX2 = constant::TWO_PI * Random(rand_gen); + randX2 = static_cast(constant::TWO_PI) * Random(rand_gen); v[2] = randX1 * math::cos(randX2) * math::sqrt(temp); } else { // Juttner-Synge distribution using the Sobol method - relativistic @@ -155,13 +128,13 @@ namespace arch { randX2 = Random(rand_gen); v[0] = randu * (TWO * randX1 - ONE); v[2] = TWO * randu * math::sqrt(randX1 * (ONE - randX1)); - v[1] = v[2] * math::cos(constant::TWO_PI * randX2); - v[2] = v[2] * math::sin(constant::TWO_PI * randX2); + v[1] = v[2] * math::cos(static_cast(constant::TWO_PI) * randX2); + v[2] = v[2] * math::sin(static_cast(constant::TWO_PI) * randX2); } pool.free_state(rand_gen); } - template + template Inline void SampleFromMaxwellian(vec_t& v, const random_number_pool_t& pool, real_t temperature, @@ -200,16 +173,12 @@ namespace arch { } } - template - struct Maxwellian : public EnergyDistribution { - using EnergyDistribution::metric; - - Maxwellian(const M& metric, - random_number_pool_t& pool, + template + struct Maxwellian { + Maxwellian(random_number_pool_t& pool, real_t temperature, const std::vector& drift_four_vel = { ZERO, ZERO, ZERO }) - : EnergyDistribution { metric } - , pool { pool } + : pool { pool } , temperature { temperature } { raise::ErrorIf(drift_four_vel.size() != 3, "Maxwellian: Drift velocity must be a 3D vector", @@ -217,7 +186,7 @@ namespace arch { raise::ErrorIf(temperature < ZERO, "Maxwellian: Temperature must be non-negative", HERE); - if constexpr (M::CoordType == Coord::Cart) { + if constexpr (C == Coord::Cartesian) { drift_4vel = NORM(drift_four_vel[0], drift_four_vel[1], drift_four_vel[2]); if (cmp::AlmostZero_host(drift_4vel)) { drift_dir = 0; @@ -235,7 +204,7 @@ namespace arch { const auto dnext = (d + 1) % 3; if (cmp::AlmostZero_host(drift_four_vel[dprev]) and cmp::AlmostZero_host(drift_four_vel[dnext])) { - drift_dir = SIGN(drift_four_vel[d]) * (d + 1); + drift_dir = SIGN(drift_four_vel[d]) * (static_cast(d + 1)); break; } } @@ -244,13 +213,13 @@ namespace arch { "Maxwellian: Incorrect drift direction", HERE); raise::ErrorIf( - drift_dir != 0 and (M::CoordType != Coord::Cart), + drift_dir != 0 and (C != Coord::Cartesian), "Maxwellian: Boosting is only supported in Cartesian coordinates", HERE); } } - Inline void operator()(const coord_t&, vec_t& v) const { + Inline void operator()(const coord_t&, vec_t& v) const { if (cmp::AlmostZero(temperature)) { v[0] = ZERO; v[1] = ZERO; @@ -259,7 +228,7 @@ namespace arch { JuttnerSinge(v, temperature, pool); } // @note: boost only when using cartesian coordinates - if constexpr (M::CoordType == Coord::Cart) { + if constexpr (C == Coord::Cartesian) { if (drift_dir != 0) { // Boost an isotropic Maxwellian with a drift velocity using // flipping method https://arxiv.org/pdf/1504.03910.pdf @@ -322,6 +291,6 @@ namespace arch { short drift_dir { 0 }; }; -} // namespace arch +} // namespace arch::energy_dist #endif // ARCHETYPES_ENERGY_DIST_HPP diff --git a/src/archetypes/field_setter.h b/src/archetypes/field_setter.h index a9e45d2d6..be3015a2f 100644 --- a/src/archetypes/field_setter.h +++ b/src/archetypes/field_setter.h @@ -1,19 +1,21 @@ /** - * @file archetypes/field_setter.hpp - * @brief Defines a kernel which populates the EM fields with user-defined values + * @file archetypes/field_setter.h + * @brief Defines kernels which populate the EM fields with user-defined values + * conditionally or unconditionally * @implements - * - arch::SetEMFields_kernel + * - arch::SetEMFields_kernel<> + * - arch::CustomSetEMFields_kernel<> * @namespaces: * - arch:: * @note - * The functor accepts a class I as a template argument, which must contain + * The functors accept a class F as a template argument, which must contain * one of the ex1, ex2, ex3, bx1, bx2, bx3 methods (dx1, dx2, dx3 for GR). * * `coord_t` --> [ I ] --> `real_t` --> [ SetEMFields_kernel ] --> ... * * ^ ^ ^ - * * (physical) (SR: hatted basis ) (SR & GR: cntrv) + * * (physical) (SR: tetrad basis ) (SR & GR: cntrv) * * (GR: phys. cntrv ) * @note - * The functor automatically takes care of the staggering, ghost cells and + * The functors automatically take care of the staggering, ghost cells and * conversion to contravariant basis. */ @@ -24,44 +26,34 @@ #include "global.h" #include "arch/kokkos_aliases.h" -#include "arch/traits.h" +#include "traits/archetypes.h" +#include "traits/metric.h" #include "utils/numeric.h" -#include "metrics/traits.h" - #include namespace arch { using namespace ntt; - template - requires metric::traits::HasD && - (((S == SimEngine::SRPIC) && metric::traits::HasConvert && - metric::traits::HasTransform_i) || - ((S == SimEngine::GRPIC) && metric::traits::HasConvert_i)) && - (((S == SimEngine::SRPIC) && - (::traits::fieldsetter::HasEx1 || - ::traits::fieldsetter::HasEx2 || - ::traits::fieldsetter::HasEx3 || - ::traits::fieldsetter::HasBx1 || - ::traits::fieldsetter::HasBx2 || - ::traits::fieldsetter::HasBx3)) || - ((S == SimEngine::GRPIC) && - ((::traits::fieldsetter::HasDx1 && - ::traits::fieldsetter::HasDx2 && - ::traits::fieldsetter::HasDx3) || - (::traits::fieldsetter::HasBx1 && - ::traits::fieldsetter::HasBx2 && - ::traits::fieldsetter::HasBx3)))) + template FS> class SetEMFields_kernel { - static constexpr Dimension D = M::Dim; + static constexpr auto D = M::Dim; + static constexpr auto HasEx1 = ::traits::fieldsetter::HasEx1; + static constexpr auto HasEx2 = ::traits::fieldsetter::HasEx2; + static constexpr auto HasEx3 = ::traits::fieldsetter::HasEx3; + static constexpr auto HasBx1 = ::traits::fieldsetter::HasBx1; + static constexpr auto HasBx2 = ::traits::fieldsetter::HasBx2; + static constexpr auto HasBx3 = ::traits::fieldsetter::HasBx3; + static constexpr auto HasDx1 = ::traits::fieldsetter::HasDx1; + static constexpr auto HasDx2 = ::traits::fieldsetter::HasDx2; + static constexpr auto HasDx3 = ::traits::fieldsetter::HasDx3; ndfield_t EM; - const I finit; + const FS finit; const M metric; public: - SetEMFields_kernel(ndfield_t& EM, const I& finit, const M& metric) + SetEMFields_kernel(ndfield_t& EM, const FS& finit, const M& metric) : EM { EM } , finit { finit } , metric { metric } {} @@ -73,37 +65,37 @@ namespace arch { const auto i1_ = COORD(i1); coord_t x_Phys { ZERO }; if constexpr (S == SimEngine::SRPIC) { - if constexpr (::traits::fieldsetter::HasEx1) { + if constexpr (HasEx1) { metric.template convert({ i1_ + HALF }, x_Phys); EM(i1, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ + HALF }, finit.ex1(x_Phys)); } - if constexpr (::traits::fieldsetter::HasEx2) { + if constexpr (HasEx2) { metric.template convert({ i1_ }, x_Phys); EM(i1, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( { i1_ }, finit.ex2(x_Phys)); } - if constexpr (::traits::fieldsetter::HasEx3) { + if constexpr (HasEx3) { metric.template convert({ i1_ }, x_Phys); EM(i1, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( { i1_ }, finit.ex3(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx1) { + if constexpr (HasBx1) { metric.template convert({ i1_ }, x_Phys); EM(i1, em::bx1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ }, finit.bx1(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx2) { + if constexpr (HasBx2) { metric.template convert({ i1_ + HALF }, x_Phys); EM(i1, em::bx2) = metric.template transform<2, Idx::T, Idx::U>( { i1_ + HALF }, finit.bx2(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx3) { + if constexpr (HasBx3) { metric.template convert({ i1_ + HALF }, x_Phys); EM(i1, em::bx3) = metric.template transform<3, Idx::T, Idx::U>( { i1_ + HALF }, @@ -124,37 +116,37 @@ namespace arch { // srpic if constexpr (S == SimEngine::SRPIC) { coord_t x_Phys { ZERO }; - if constexpr (::traits::fieldsetter::HasEx1) { + if constexpr (HasEx1) { metric.template convert({ i1_ + HALF, i2_ }, x_Phys); EM(i1, i2, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ + HALF, i2_ }, finit.ex1(x_Phys)); } - if constexpr (::traits::fieldsetter::HasEx2) { + if constexpr (HasEx2) { metric.template convert({ i1_, i2_ + HALF }, x_Phys); EM(i1, i2, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( { i1_, i2_ + HALF }, finit.ex2(x_Phys)); } - if constexpr (::traits::fieldsetter::HasEx3) { + if constexpr (HasEx3) { metric.template convert({ i1_, i2_ }, x_Phys); EM(i1, i2, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( { i1_, i2_ }, finit.ex3(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx1) { + if constexpr (HasBx1) { metric.template convert({ i1_, i2_ + HALF }, x_Phys); EM(i1, i2, em::bx1) = metric.template transform<1, Idx::T, Idx::U>( { i1_, i2_ + HALF }, finit.bx1(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx2) { + if constexpr (HasBx2) { metric.template convert({ i1_ + HALF, i2_ }, x_Phys); EM(i1, i2, em::bx2) = metric.template transform<2, Idx::T, Idx::U>( { i1_ + HALF, i2_ }, finit.bx2(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx3) { + if constexpr (HasBx3) { metric.template convert({ i1_ + HALF, i2_ + HALF }, x_Phys); EM(i1, i2, em::bx3) = metric.template transform<3, Idx::T, Idx::U>( @@ -163,9 +155,7 @@ namespace arch { } } else if constexpr (S == SimEngine::GRPIC) { // grpic - if constexpr (::traits::fieldsetter::HasDx1 && - ::traits::fieldsetter::HasDx2 && - ::traits::fieldsetter::HasDx3) { + if constexpr (HasDx1 && HasDx2 && HasDx3) { const real_t x1_0 { metric.template convert<1, Crd::Cd, Crd::Ph>(i1_) }; const real_t x1_H { metric.template convert<1, Crd::Cd, Crd::Ph>( i1_ + HALF) }; @@ -182,9 +172,7 @@ namespace arch { EM(i1, i2, em::dx3) = finit.dx3({ x1_0, x2_0 }); } } - if constexpr (::traits::fieldsetter::HasBx1 && - ::traits::fieldsetter::HasBx2 && - ::traits::fieldsetter::HasBx3) { + if constexpr (HasBx1 && HasBx2 && HasBx3) { const real_t x1_0 { metric.template convert<1, Crd::Cd, Crd::Ph>(i1_) }; const real_t x1_H { metric.template convert<1, Crd::Cd, Crd::Ph>( i1_ + HALF) }; @@ -217,28 +205,28 @@ namespace arch { coord_t x_Phys { ZERO }; if constexpr (S == SimEngine::SRPIC) { // srpic - if constexpr (::traits::fieldsetter::HasEx1) { + if constexpr (HasEx1) { metric.template convert({ i1_ + HALF, i2_, i3_ }, x_Phys); EM(i1, i2, i3, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( { i1_ + HALF, i2_, i3_ }, finit.ex1(x_Phys)); } - if constexpr (::traits::fieldsetter::HasEx2) { + if constexpr (HasEx2) { metric.template convert({ i1_, i2_ + HALF, i3_ }, x_Phys); EM(i1, i2, i3, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( { i1_, i2_ + HALF, i3_ }, finit.ex2(x_Phys)); } - if constexpr (::traits::fieldsetter::HasEx3) { + if constexpr (HasEx3) { metric.template convert({ i1_, i2_, i3_ + HALF }, x_Phys); EM(i1, i2, i3, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( { i1_, i2_, i3_ + HALF }, finit.ex3(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx1) { + if constexpr (HasBx1) { metric.template convert( { i1_, i2_ + HALF, i3_ + HALF }, x_Phys); @@ -246,7 +234,7 @@ namespace arch { { i1_, i2_ + HALF, i3_ + HALF }, finit.bx1(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx2) { + if constexpr (HasBx2) { metric.template convert( { i1_ + HALF, i2_, i3_ + HALF }, x_Phys); @@ -254,7 +242,7 @@ namespace arch { { i1_ + HALF, i2_, i3_ + HALF }, finit.bx2(x_Phys)); } - if constexpr (::traits::fieldsetter::HasBx3) { + if constexpr (HasBx3) { metric.template convert( { i1_ + HALF, i2_ + HALF, i3_ }, x_Phys); @@ -274,9 +262,7 @@ namespace arch { const real_t x3_H { metric.template convert<3, Crd::Cd, Crd::Ph>( i3_ + HALF) }; - if constexpr (::traits::fieldsetter::HasDx1 && - ::traits::fieldsetter::HasDx2 && - ::traits::fieldsetter::HasDx3) { + if constexpr (HasDx1 && HasDx2 && HasDx3) { { // dx1 EM(i1, i2, i3, em::dx1) = finit.dx1({ x1_H, x2_0, x3_0 }); } @@ -287,9 +273,7 @@ namespace arch { EM(i1, i2, i3, em::dx3) = finit.dx3({ x1_0, x2_0, x3_H }); } } - if constexpr (::traits::fieldsetter::HasBx1 && - ::traits::fieldsetter::HasBx2 && - ::traits::fieldsetter::HasBx3) { + if constexpr (HasBx1 && HasBx2 && HasBx3) { { // bx1 EM(i1, i2, i3, em::bx1) = finit.bx1({ x1_0, x2_H, x3_H }); } @@ -309,6 +293,531 @@ namespace arch { } }; + template FS> + struct CustomSetEMFields_kernel { + static constexpr auto D = M::Dim; + static constexpr auto HasEx1 = ::traits::fieldsetter::HasConditionalEx1; + static constexpr auto HasEx2 = ::traits::fieldsetter::HasConditionalEx2; + static constexpr auto HasEx3 = ::traits::fieldsetter::HasConditionalEx3; + static constexpr auto HasBx1 = ::traits::fieldsetter::HasConditionalBx1; + static constexpr auto HasBx2 = ::traits::fieldsetter::HasConditionalBx2; + static constexpr auto HasBx3 = ::traits::fieldsetter::HasConditionalBx3; + + M metric; + ndfield_t fields; + ndfield_t buffer; + + const FS fieldsetter; + + CustomSetEMFields_kernel(const M& metric, + ndfield_t& fields, + const ndfield_t& /*buffer*/, + const FS& fieldsetter) + : metric { metric } + , fields { fields } + , fieldsetter { fieldsetter } {} + + Inline void operator()(index_t i1) const { + if constexpr (D == Dim::_1D) { + const auto i1_ = COORD(i1); + coord_t x_Ph { ZERO }; + + vec_t e_U { ZERO }; + vec_t b_U { ZERO }; + + vec_t e_T { ZERO }; + vec_t b_T { ZERO }; + + if constexpr (HasEx1 or HasBx2 or HasBx3) { + metric.template convert({ i1_ + HALF }, x_Ph); + + e_U[0] = buffer(i1, em::ex1); + e_U[1] = HALF * (buffer(i1, em::ex2) + buffer(i1 + 1, em::ex2)); + e_U[2] = HALF * (buffer(i1, em::ex3) + buffer(i1 + 1, em::ex3)); + + b_U[0] = HALF * (buffer(i1, em::bx1) + buffer(i1 + 1, em::bx1)); + b_U[1] = buffer(i1, em::bx2); + b_U[2] = buffer(i1, em::bx3); + + metric.template transform({ i1_ + HALF }, e_U, e_T); + metric.template transform({ i1_ + HALF }, b_U, b_T); + + if constexpr (HasEx1) { + const auto ex1_setter = fieldsetter.ex1(x_Ph, e_T, b_T); + + if (ex1_setter.first) { + fields(i1, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( + { i1_ + HALF }, + ex1_setter.second); + } + } + if constexpr (HasBx2) { + const auto bx2_setter = fieldsetter.bx2(x_Ph, e_T, b_T); + + if (bx2_setter.first) { + fields(i1, em::bx2) = metric.template transform<2, Idx::T, Idx::U>( + { i1_ + HALF }, + bx2_setter.second); + } + } + if constexpr (HasBx3) { + const auto bx3_setter = fieldsetter.bx3(x_Ph, e_T, b_T); + + if (bx3_setter.first) { + fields(i1, em::bx3) = metric.template transform<3, Idx::T, Idx::U>( + { i1_ + HALF }, + bx3_setter.second); + } + } + } + if constexpr (HasEx2 or HasEx3 or HasBx1) { + metric.template convert({ i1_ }, x_Ph); + + e_U[0] = HALF * (buffer(i1, em::ex1) + buffer(i1 - 1, em::ex1)); + e_U[1] = buffer(i1, em::ex2); + e_U[2] = buffer(i1, em::ex3); + + b_U[0] = buffer(i1, em::bx1); + b_U[1] = HALF * (buffer(i1, em::bx2) + buffer(i1 - 1, em::bx2)); + b_U[2] = HALF * (buffer(i1, em::bx3) + buffer(i1 - 1, em::bx3)); + + metric.template transform({ i1_ }, e_U, e_T); + metric.template transform({ i1_ }, b_U, b_T); + + if constexpr (HasEx2) { + const auto ex2_setter = fieldsetter.ex2(x_Ph, e_T, b_T); + + if (ex2_setter.first) { + fields(i1, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( + { i1_ }, + ex2_setter.second); + } + } + if constexpr (HasEx3) { + const auto ex3_setter = fieldsetter.ex3(x_Ph, e_T, b_T); + + if (ex3_setter.first) { + fields(i1, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( + { i1_ }, + ex3_setter.second); + } + } + if constexpr (HasBx1) { + const auto bx1_setter = fieldsetter.bx1(x_Ph, e_T, b_T); + + if (bx1_setter.first) { + fields(i1, em::bx1) = metric.template transform<1, Idx::T, Idx::U>( + { i1_ }, + bx1_setter.second); + } + } + } + } else { + raise::KernelError(HERE, "CustomEMFields_kernel 1D called for 2D/3D"); + } + } + + Inline void operator()(index_t i1, index_t i2) const { + if constexpr (D == Dim::_2D) { + const auto i1_ = COORD(i1); + const auto i2_ = COORD(i2); + coord_t x_Ph { ZERO }; + + vec_t e_U { ZERO }; + vec_t b_U { ZERO }; + + vec_t e_T { ZERO }; + vec_t b_T { ZERO }; + + if constexpr (HasEx1 or HasBx2) { + metric.template convert({ i1_ + HALF, i2_ }, x_Ph); + + e_U[0] = buffer(i1, i2, em::ex1); + e_U[1] = INV_4 * (buffer(i1, i2, em::ex2) + buffer(i1 + 1, i2, em::ex2) + + buffer(i1, i2 - 1, em::ex2) + + buffer(i1 + 1, i2 - 1, em::ex2)); + e_U[2] = HALF * (buffer(i1, i2, em::ex3) + buffer(i1 + 1, i2, em::ex3)); + + b_U[0] = INV_4 * (buffer(i1, i2, em::bx1) + buffer(i1 + 1, i2, em::bx1) + + buffer(i1, i2 - 1, em::bx1) + + buffer(i1 + 1, i2 - 1, em::bx1)); + b_U[1] = buffer(i1, i2, em::bx2); + b_U[2] = HALF * (buffer(i1, i2, em::bx3) + buffer(i1, i2 - 1, em::bx3)); + + metric.template transform({ i1_ + HALF, i2_ }, e_U, e_T); + metric.template transform({ i1_ + HALF, i2_ }, b_U, b_T); + if constexpr (HasEx1) { + const auto ex1_setter = fieldsetter.ex1(x_Ph, e_T, b_T); + + if (ex1_setter.first) { + fields(i1, i2, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( + { i1_ + HALF, i2_ }, + ex1_setter.second); + } + } + if constexpr (HasBx2) { + const auto bx2_setter = fieldsetter.bx2(x_Ph, e_T, b_T); + + if (bx2_setter.first) { + fields(i1, i2, em::bx2) = metric.template transform<2, Idx::T, Idx::U>( + { i1_ + HALF, i2_ }, + bx2_setter.second); + } + } + } + if constexpr (HasEx2 or HasBx1) { + metric.template convert({ i1_, i2_ + HALF }, x_Ph); + + e_U[0] = INV_4 * (buffer(i1, i2, em::ex1) + buffer(i1 - 1, i2, em::ex1) + + buffer(i1, i2 + 1, em::ex1) + + buffer(i1 - 1, i2 + 1, em::ex1)); + e_U[1] = buffer(i1, i2, em::ex2); + e_U[2] = HALF * (buffer(i1, i2, em::ex3) + buffer(i1, i2 + 1, em::ex3)); + + b_U[0] = buffer(i1, i2, em::bx1); + b_U[1] = INV_4 * (buffer(i1, i2, em::bx2) + buffer(i1 - 1, i2, em::bx2) + + buffer(i1, i2 + 1, em::bx2) + + buffer(i1 - 1, i2 + 1, em::bx2)); + b_U[2] = HALF * (buffer(i1, i2, em::bx3) + buffer(i1 - 1, i2, em::bx3)); + + metric.template transform({ i1_, i2_ + HALF }, e_U, e_T); + metric.template transform({ i1_, i2_ + HALF }, b_U, b_T); + if constexpr (HasEx2) { + const auto ex2_setter = fieldsetter.ex2(x_Ph, e_T, b_T); + + if (ex2_setter.first) { + fields(i1, i2, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( + { i1_, i2_ + HALF }, + ex2_setter.second); + } + } + if constexpr (HasBx1) { + const auto bx1_setter = fieldsetter.bx1(x_Ph, e_T, b_T); + + if (bx1_setter.first) { + fields(i1, i2, em::bx1) = metric.template transform<1, Idx::T, Idx::U>( + { i1_, i2_ + HALF }, + bx1_setter.second); + } + } + } + if constexpr (HasEx3) { + metric.template convert({ i1_, i2_ }, x_Ph); + + e_U[0] = HALF * (buffer(i1, i2, em::ex1) + buffer(i1 - 1, i2, em::ex1)); + e_U[1] = HALF * (buffer(i1, i2, em::ex2) + buffer(i1, i2 - 1, em::ex2)); + e_U[2] = buffer(i1, i2, em::ex3); + + b_U[0] = HALF * (buffer(i1, i2, em::bx1) + buffer(i1, i2 - 1, em::bx1)); + b_U[1] = HALF * (buffer(i1, i2, em::bx2) + buffer(i1 - 1, i2, em::bx2)); + b_U[2] = INV_4 * (buffer(i1, i2, em::bx3) + buffer(i1 - 1, i2, em::bx3) + + buffer(i1, i2 - 1, em::bx3) + + buffer(i1 - 1, i2 - 1, em::bx3)); + + metric.template transform({ i1_, i2_ }, e_U, e_T); + metric.template transform({ i1_, i2_ }, b_U, b_T); + const auto ex3_setter = fieldsetter.ex3(x_Ph, e_T, b_T); + + if (ex3_setter.first) { + fields(i1, i2, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( + { i1_, i2_ }, + ex3_setter.second); + } + } + if constexpr (HasBx3) { + metric.template convert({ i1_ + HALF, i2_ + HALF }, + x_Ph); + + e_U[0] = HALF * (buffer(i1, i2, em::ex1) + buffer(i1, i2 + 1, em::ex1)); + e_U[1] = HALF * (buffer(i1, i2, em::ex2) + buffer(i1 + 1, i2, em::ex2)); + e_U[2] = INV_4 * (buffer(i1, i2, em::ex3) + buffer(i1 + 1, i2, em::ex3) + + buffer(i1, i2 + 1, em::ex3) + + buffer(i1 + 1, i2 + 1, em::ex3)); + + b_U[0] = HALF * (buffer(i1, i2, em::bx1) + buffer(i1 + 1, i2, em::bx1)); + b_U[1] = HALF * (buffer(i1, i2, em::bx2) + buffer(i1, i2 + 1, em::bx2)); + b_U[2] = buffer(i1, i2, em::bx3); + + metric.template transform({ i1_ + HALF, i2_ + HALF }, + e_U, + e_T); + metric.template transform({ i1_ + HALF, i2_ + HALF }, + b_U, + b_T); + const auto bx3_setter = fieldsetter.bx3(x_Ph, e_T, b_T); + + if (bx3_setter.first) { + fields(i1, i2, em::bx3) = metric.template transform<3, Idx::T, Idx::U>( + { i1_ + HALF, i2_ + HALF }, + bx3_setter.second); + } + } + } else { + raise::KernelError(HERE, "CustomEMFields_kernel 2D called for 1D/3D"); + } + } + + Inline void operator()(index_t i1, index_t i2, index_t i3) const { + if constexpr (D == Dim::_3D) { + const auto i1_ = COORD(i1); + const auto i2_ = COORD(i2); + const auto i3_ = COORD(i3); + coord_t x_Ph { ZERO }; + + vec_t e_U { ZERO }; + vec_t b_U { ZERO }; + + vec_t e_T { ZERO }; + vec_t b_T { ZERO }; + if constexpr (HasEx1) { + metric.template convert({ i1_ + HALF, i2_, i3_ }, x_Ph); + + e_U[0] = buffer(i1, i2, i3, em::ex1); + e_U[1] = INV_4 * (buffer(i1, i2, i3, em::ex2) + + buffer(i1 + 1, i2, i3, em::ex2) + + buffer(i1, i2 - 1, i3, em::ex2) + + buffer(i1 + 1, i2 - 1, i3, em::ex2)); + e_U[2] = INV_4 * (buffer(i1, i2, i3, em::ex3) + + buffer(i1 + 1, i2, i3, em::ex3) + + buffer(i1, i2, i3 - 1, em::ex3) + + buffer(i1 + 1, i2, i3 - 1, em::ex3)); + + b_U[0] = INV_8 * (buffer(i1, i2, i3, em::bx1) + + buffer(i1 + 1, i2, i3, em::bx1) + + buffer(i1, i2 - 1, i3, em::bx1) + + buffer(i1 + 1, i2 - 1, i3, em::bx1) + + buffer(i1, i2, i3 - 1, em::bx1) + + buffer(i1 + 1, i2, i3 - 1, em::bx1) + + buffer(i1, i2 - 1, i3 - 1, em::bx1) + + buffer(i1 + 1, i2 - 1, i3 - 1, em::bx1)); + b_U[1] = HALF * (buffer(i1, i2, i3, em::bx2) + + buffer(i1, i2, i3 - 1, em::bx2)); + b_U[2] = HALF * (buffer(i1, i2, i3, em::bx3) + + buffer(i1, i2 - 1, i3, em::bx3)); + + metric.template transform({ i1_ + HALF, i2_, i3_ }, + e_U, + e_T); + metric.template transform({ i1_ + HALF, i2_, i3_ }, + b_U, + b_T); + const auto ex1_setter = fieldsetter.ex1(x_Ph, e_T, b_T); + + if (ex1_setter.first) { + fields(i1, i2, i3, em::ex1) = metric.template transform<1, Idx::T, Idx::U>( + { i1_ + HALF, i2_, i3_ }, + ex1_setter.second); + } + } + if constexpr (HasEx2) { + metric.template convert({ i1_, i2_ + HALF, i3_ }, x_Ph); + + e_U[0] = INV_4 * (buffer(i1, i2, i3, em::ex1) + + buffer(i1 - 1, i2, i3, em::ex1) + + buffer(i1, i2 + 1, i3, em::ex1) + + buffer(i1 - 1, i2 + 1, i3, em::ex1)); + e_U[1] = buffer(i1, i2, i3, em::ex2); + e_U[2] = INV_4 * (buffer(i1, i2, i3, em::ex3) + + buffer(i1, i2 + 1, i3, em::ex3) + + buffer(i1, i2, i3 - 1, em::ex3) + + buffer(i1, i2 + 1, i3 - 1, em::ex3)); + + b_U[0] = HALF * (buffer(i1, i2, i3, em::bx1) + + buffer(i1, i2, i3 - 1, em::bx1)); + b_U[1] = INV_8 * (buffer(i1, i2, i3, em::bx2) + + buffer(i1 - 1, i2, i3, em::bx2) + + buffer(i1, i2 + 1, i3, em::bx2) + + buffer(i1 - 1, i2 + 1, i3, em::bx2) + + buffer(i1, i2, i3 - 1, em::bx2) + + buffer(i1 - 1, i2, i3 - 1, em::bx2) + + buffer(i1, i2 + 1, i3 - 1, em::bx2) + + buffer(i1 - 1, i2 + 1, i3 - 1, em::bx2)); + b_U[2] = HALF * (buffer(i1, i2, i3, em::bx3) + + buffer(i1 - 1, i2, i3, em::bx3)); + + metric.template transform({ i1_, i2_ + HALF, i3_ }, + e_U, + e_T); + metric.template transform({ i1_, i2_ + HALF, i3_ }, + b_U, + b_T); + const auto ex2_setter = fieldsetter.ex2(x_Ph, e_T, b_T); + + if (ex2_setter.first) { + fields(i1, i2, i3, em::ex2) = metric.template transform<2, Idx::T, Idx::U>( + { i1_, i2_ + HALF, i3_ }, + ex2_setter.second); + } + } + if constexpr (HasEx3) { + metric.template convert({ i1_, i2_, i3_ + HALF }, x_Ph); + + e_U[0] = INV_4 * (buffer(i1, i2, i3, em::ex1) + + buffer(i1 - 1, i2, i3, em::ex1) + + buffer(i1, i2, i3 + 1, em::ex1) + + buffer(i1 - 1, i2, i3 + 1, em::ex1)); + e_U[1] = INV_4 * (buffer(i1, i2, i3, em::ex2) + + buffer(i1, i2 - 1, i3, em::ex2) + + buffer(i1, i2, i3 + 1, em::ex2) + + buffer(i1, i2 - 1, i3 + 1, em::ex2)); + e_U[2] = buffer(i1, i2, i3, em::ex3); + + b_U[0] = HALF * (buffer(i1, i2, i3, em::bx1) + + buffer(i1, i2 - 1, i3, em::bx1)); + b_U[1] = HALF * (buffer(i1, i2, i3, em::bx2) + + buffer(i1 - 1, i2, i3, em::bx2)); + b_U[2] = INV_8 * (buffer(i1, i2, i3, em::bx3) + + buffer(i1 - 1, i2, i3, em::bx3) + + buffer(i1, i2, i3 + 1, em::bx3) + + buffer(i1 - 1, i2, i3 + 1, em::bx3) + + buffer(i1, i2 - 1, i3, em::bx3) + + buffer(i1 - 1, i2 - 1, i3, em::bx3) + + buffer(i1, i2 - 1, i3 + 1, em::bx3) + + buffer(i1 - 1, i2 - 1, i3 + 1, em::bx3)); + + metric.template transform({ i1_, i2_, i3_ + HALF }, + e_U, + e_T); + metric.template transform({ i1_, i2_, i3_ + HALF }, + b_U, + b_T); + const auto ex3_setter = fieldsetter.ex3(x_Ph, e_T, b_T); + + if (ex3_setter.first) { + fields(i1, i2, i3, em::ex3) = metric.template transform<3, Idx::T, Idx::U>( + { i1_, i2_, i3_ + HALF }, + ex3_setter.second); + } + } + if constexpr (HasBx1) { + metric.template convert({ i1_, i2_ + HALF, i3_ + HALF }, + x_Ph); + + e_U[0] = INV_8 * (buffer(i1, i2, i3, em::ex1) + + buffer(i1 - 1, i2, i3, em::ex1) + + buffer(i1, i2 + 1, i3, em::ex1) + + buffer(i1 - 1, i2 + 1, i3, em::ex1) + + buffer(i1, i2, i3 + 1, em::ex1) + + buffer(i1 - 1, i2, i3 + 1, em::ex1) + + buffer(i1, i2 + 1, i3 + 1, em::ex1) + + buffer(i1 - 1, i2 + 1, i3 + 1, em::ex1)); + e_U[1] = HALF * (buffer(i1, i2, i3, em::ex2) + + buffer(i1, i2, i3 + 1, em::ex2)); + e_U[2] = HALF * (buffer(i1, i2, i3, em::ex3) + + buffer(i1, i2 + 1, i3, em::ex3)); + + b_U[0] = buffer(i1, i2, i3, em::bx1); + b_U[1] = INV_4 * (buffer(i1, i2, i3, em::bx2) + + buffer(i1 - 1, i2, i3, em::bx2) + + buffer(i1, i2 + 1, i3, em::bx2) + + buffer(i1 - 1, i2 + 1, i3, em::bx2)); + b_U[2] = INV_4 * (buffer(i1, i2, i3, em::bx3) + + buffer(i1 - 1, i2, i3, em::bx3) + + buffer(i1, i2, i3 + 1, em::bx3) + + buffer(i1 - 1, i2, i3 + 1, em::bx3)); + + metric.template transform({ i1_, i2_ + HALF, i3_ + HALF }, + e_U, + e_T); + metric.template transform({ i1_, i2_ + HALF, i3_ + HALF }, + b_U, + b_T); + const auto bx1_setter = fieldsetter.bx1(x_Ph, e_T, b_T); + + if (bx1_setter.first) { + fields(i1, i2, i3, em::bx1) = metric.template transform<1, Idx::T, Idx::U>( + { i1_, i2_ + HALF, i3_ + HALF }, + bx1_setter.second); + } + } + if constexpr (HasBx2) { + metric.template convert({ i1_ + HALF, i2_, i3_ + HALF }, + x_Ph); + + e_U[0] = HALF * (buffer(i1, i2, i3, em::ex1) + + buffer(i1, i2, i3 + 1, em::ex1)); + e_U[1] = INV_8 * (buffer(i1, i2, i3, em::ex2) + + buffer(i1 + 1, i2, i3, em::ex2) + + buffer(i1, i2 - 1, i3, em::ex2) + + buffer(i1 + 1, i2 - 1, i3, em::ex2) + + buffer(i1, i2, i3 + 1, em::ex2) + + buffer(i1 + 1, i2, i3 + 1, em::ex2) + + buffer(i1, i2 - 1, i3 + 1, em::ex2) + + buffer(i1 + 1, i2 - 1, i3 + 1, em::ex2)); + e_U[2] = HALF * (buffer(i1, i2, i3, em::ex3) + + buffer(i1 + 1, i2, i3, em::ex3)); + + b_U[0] = INV_4 * (buffer(i1, i2, i3, em::bx1) + + buffer(i1 + 1, i2, i3, em::bx1) + + buffer(i1, i2 - 1, i3, em::bx1) + + buffer(i1 + 1, i2 - 1, i3, em::bx1)); + b_U[1] = buffer(i1, i2, i3, em::bx2); + b_U[2] = INV_4 * (buffer(i1, i2, i3, em::bx3) + + buffer(i1, i2 - 1, i3, em::bx3) + + buffer(i1, i2, i3 + 1, em::bx3) + + buffer(i1, i2 - 1, i3 + 1, em::bx3)); + + metric.template transform({ i1_ + HALF, i2_, i3_ + HALF }, + e_U, + e_T); + metric.template transform({ i1_ + HALF, i2_, i3_ + HALF }, + b_U, + b_T); + const auto bx2_setter = fieldsetter.bx2(x_Ph, e_T, b_T); + + if (bx2_setter.first) { + fields(i1, i2, i3, em::bx2) = metric.template transform<2, Idx::T, Idx::U>( + { i1_ + HALF, i2_, i3_ + HALF }, + bx2_setter.second); + } + } + if constexpr (HasBx3) { + metric.template convert({ i1_ + HALF, i2_ + HALF, i3_ }, + x_Ph); + + e_U[0] = HALF * (buffer(i1, i2, i3, em::ex1) + + buffer(i1, i2 + 1, i3, em::ex1)); + e_U[1] = HALF * (buffer(i1, i2, i3, em::ex2) + + buffer(i1 + 1, i2, i3, em::ex2)); + e_U[2] = INV_8 * (buffer(i1, i2, i3, em::ex3) + + buffer(i1 + 1, i2, i3, em::ex3) + + buffer(i1, i2 + 1, i3, em::ex3) + + buffer(i1 + 1, i2 + 1, i3, em::ex3) + + buffer(i1, i2, i3 - 1, em::ex3) + + buffer(i1 + 1, i2, i3 - 1, em::ex3) + + buffer(i1, i2 + 1, i3 - 1, em::ex3) + + buffer(i1 + 1, i2 + 1, i3 - 1, em::ex3)); + + b_U[0] = INV_4 * (buffer(i1, i2, i3, em::bx1) + + buffer(i1 + 1, i2, i3, em::bx1) + + buffer(i1, i2, i3 - 1, em::bx1) + + buffer(i1 + 1, i2, i3 - 1, em::bx1)); + b_U[1] = INV_4 * (buffer(i1, i2, i3, em::bx2) + + buffer(i1, i2 + 1, i3, em::bx2) + + buffer(i1, i2, i3 - 1, em::bx2) + + buffer(i1, i2 + 1, i3 - 1, em::bx2)); + b_U[2] = buffer(i1, i2, i3, em::bx3); + + metric.template transform({ i1_ + HALF, i2_ + HALF, i3_ }, + e_U, + e_T); + metric.template transform({ i1_ + HALF, i2_ + HALF, i3_ }, + b_U, + b_T); + const auto bx3_setter = fieldsetter.bx3(x_Ph, e_T, b_T); + + if (bx3_setter.first) { + fields(i1, i2, i3, em::bx3) = metric.template transform<3, Idx::T, Idx::U>( + { i1_ + HALF, i2_ + HALF, i3_ }, + bx3_setter.second); + } + } + } else { + raise::KernelError(HERE, "CustomEMFields_kernel 3D called for 1D/2D"); + } + } + }; + } // namespace arch #endif // ARCHETYPES_FIELD_SETTER_HPP diff --git a/src/archetypes/particle_injector.h b/src/archetypes/particle_injector.h index 322eb9516..7adb8c5b5 100644 --- a/src/archetypes/particle_injector.h +++ b/src/archetypes/particle_injector.h @@ -19,11 +19,11 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/archetypes.h" +#include "traits/metric.h" #include "utils/error.h" #include "utils/numeric.h" -#include "metrics/traits.h" - #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" #include "kernels/injectors.hpp" @@ -53,8 +53,7 @@ namespace arch { * - array_t: minimum coordinates of the region in computational coords * - array_t: maximum coordinates of the region in computational coords */ - template - requires metric::traits::HasD && metric::traits::HasConvert + template auto DeduceRegion(const Domain& domain, const boundaries_t& box) -> std::tuple, array_t> { if (not domain.mesh.Intersects(box)) { @@ -80,7 +79,8 @@ namespace arch { domain.mesh.metric.template convert(xCorner_max_Ph, xCorner_max_Cd); - array_t xi_min { "xi_min", M::Dim }, xi_max { "xi_max", M::Dim }; + const array_t xi_min { "xi_min", M::Dim }, + xi_max { "xi_max", M::Dim }; auto xi_min_h = Kokkos::create_mirror_view(xi_min); auto xi_max_h = Kokkos::create_mirror_view(xi_max); @@ -108,8 +108,7 @@ namespace arch { * - array_t: minimum coordinates of the region in computational coords * - array_t: maximum coordinates of the region in computational coords */ - template - requires metric::traits::HasD + template auto ComputeNumInject(const SimulationParams& params, const Domain& domain, real_t number_density, @@ -159,7 +158,7 @@ namespace arch { if (xi < xsurf - ds or xi >= xsurf) { return ZERO; } else { - if constexpr (C == Coord::Cart) { + if constexpr (C == Coord::Cartesian) { return nmax * math::exp(-(xsurf - xi) / height); } else { raise::KernelError( @@ -173,7 +172,7 @@ namespace arch { if (xi < xsurf or xi >= xsurf + ds) { return ZERO; } else { - if constexpr (C == Coord::Cart) { + if constexpr (C == Coord::Cartesian) { return nmax * math::exp(-(xi - xsurf) / height); } else { return nmax * math::exp(-(xsurf / height) * (ONE - (xsurf / xi))); @@ -200,9 +199,7 @@ namespace arch { * @tparam ED1 Energy distribution type for species 1 * @tparam ED2 Energy distribution type for species 2 */ - template - requires metric::traits::HasD && traits::energydist::IsValid && - traits::energydist::IsValid + template ED1, EnrgDistClass ED2> inline void InjectUniform(const SimulationParams& params, Domain& domain, const std::pair& species, @@ -210,10 +207,10 @@ namespace arch { real_t number_density, bool use_weights = false, const boundaries_t& box = {}) { - raise::ErrorIf((M::CoordType != Coord::Cart) && (not use_weights), + raise::ErrorIf((M::CoordType != Coord::Cartesian) && (not use_weights), "Weights must be used for non-Cartesian coordinates", HERE); - raise::ErrorIf((M::CoordType == Coord::Cart) && use_weights, + raise::ErrorIf((M::CoordType == Coord::Cartesian) && use_weights, "Weights should not be used for Cartesian coordinates", HERE); raise::ErrorIf(params.template get("particles.use_weights") != use_weights, @@ -230,7 +227,7 @@ namespace arch { boundaries_t nonempty_box; for (auto d { 0u }; d < M::Dim; ++d) { if (d < box.size()) { - nonempty_box.push_back({ box[d].first, box[d].second }); + nonempty_box.emplace_back(box[d].first, box[d].second); } else { nonempty_box.push_back(Range::All); } @@ -248,7 +245,6 @@ namespace arch { kernel::UniformInjector_kernel( domain.species[species.first - 1], domain.species[species.second - 1], - nparticles, domain.index(), domain.mesh.metric, xi_min, @@ -276,7 +272,7 @@ namespace arch { * @param data Map containing all the coordinates/velocities of particles to inject * @param use_weights Boolean toggle to use weights or not */ - template + template inline void InjectGlobally(const Metadomain& global_domain, Domain& local_domain, spidx_t spidx, @@ -313,9 +309,11 @@ namespace arch { * @tparam ED2 Energy distribution type for species 2 * @tparam SD Spatial distribution type */ - template - requires metric::traits::HasD && traits::energydist::IsValid && - traits::energydist::IsValid && traits::spatialdist::IsValid + template ED1, + EnrgDistClass ED2, + SpatialDistClass SD> inline void InjectNonUniform(const SimulationParams& params, Domain& domain, const std::pair& species, @@ -324,10 +322,10 @@ namespace arch { real_t number_density, bool use_weights = false, const boundaries_t& box = {}) { - raise::ErrorIf((M::CoordType != Coord::Cart) && (not use_weights), + raise::ErrorIf((M::CoordType != Coord::Cartesian) && (not use_weights), "Weights must be used for non-Cartesian coordinates", HERE); - raise::ErrorIf((M::CoordType == Coord::Cart) && use_weights, + raise::ErrorIf((M::CoordType == Coord::Cartesian) && use_weights, "Weights should not be used for Cartesian coordinates", HERE); raise::ErrorIf( @@ -345,7 +343,7 @@ namespace arch { } { range_t cell_range; - if (box.size() == 0) { + if (box.empty()) { cell_range = domain.mesh.rangeActiveCells(); } else { boundaries_t reduced_box(box); @@ -357,7 +355,7 @@ namespace arch { HERE); boundaries_t incl_ghosts; for (auto d = 0; d < M::Dim; ++d) { - incl_ghosts.push_back({ false, false }); + incl_ghosts.emplace_back(false, false); } const auto extent = domain.mesh.ExtentToRange(reduced_box, incl_ghosts); tuple_t x_min { 0 }, x_max { 0 }; diff --git a/src/archetypes/piston.h b/src/archetypes/piston.h index 171d1728d..4cb33bf89 100644 --- a/src/archetypes/piston.h +++ b/src/archetypes/piston.h @@ -11,31 +11,27 @@ #ifndef ARCHETYPES_PISTON_H #define ARCHETYPES_PISTON_H -#include "enums.h" #include "global.h" -#include "archetypes/energy_dist.h" -#include "framework/domain/domain.h" -#include "framework/domain/metadomain.h" -#include "framework/parameters/parameters.h" +#include "traits/metric.h" -#include +#include "kernels/pushers/context.h" /* -------------------------------------------------------------------------- */ /* Local macros (same as in particle_pusher_sr.hpp) */ /* -------------------------------------------------------------------------- */ #define from_Xi_to_i(XI, I) \ { \ - I = static_cast((XI + 1)) - 1; \ + (I) = static_cast(((XI) + 1)) - 1; \ } #define from_Xi_to_i_di(XI, I, DI) \ { \ from_Xi_to_i((XI), (I)); \ - DI = static_cast((XI)) - static_cast(I); \ + (DI) = static_cast((XI)) - static_cast(I); \ } -#define i_di_to_Xi(I, DI) static_cast((I)) + static_cast((DI)) +#define i_di_to_Xi(I, DI) (static_cast((I)) + static_cast((DI))) /* -------------------------------------------------------------------------- */ @@ -46,34 +42,32 @@ namespace arch { * piston, called to correct patticle position * * @param p Index of particle - * @param pusher Particle pusher engine for particle update - * @param piston_position Position of the piston at the start of timestep in - * global coordinates + * @param dt Timestep + * @param particles Particle data arrays + * @param metric Metric object for coordinate transformations + * @param piston_position Position of the piston at the start of timestep in global coordinates * @param piston_v Velocity of piston at current timestep + * @param massive Whether the particle is massive or massless (e.g. photon) */ - template - Inline void PistonUpdate(const index_t p, - const PusherKernel& pusher, - const real_t piston_position, - const real_t piston_v, - const bool massive) { - - coord_t xp_Cd { ZERO }; - pusher.getParticlePosition(p, xp_Cd); - + template + Inline void PistonUpdate(index_t p, + real_t dt, + const kernel::PusherArrays& particles, + const M& metric, + real_t piston_position, + real_t piston_v, + bool massive) { // step 1: calculate the particle 3 velocity const real_t gamma_p { - massive - ? (math::sqrt( - ONE + SQR(pusher.ux1(p)) + SQR(pusher.ux2(p)) + SQR(pusher.ux3(p)))) - : (math::sqrt(SQR(pusher.ux1(p)) + SQR(pusher.ux2(p)) + SQR(pusher.ux3(p)))) + massive ? U2GAMMA(particles.ux1(p), particles.ux2(p), particles.ux3(p)) + : NORM(particles.ux1(p), particles.ux2(p), particles.ux3(p)) }; - const real_t beta_x_p = pusher.ux1(p) / gamma_p; - const real_t xp_prev = i_di_to_Xi(pusher.i1_prev(p), pusher.dx1_prev(p)); + const real_t beta_x_p = particles.ux1(p) / gamma_p; + const real_t xp_prev = i_di_to_Xi(particles.i1_prev(p), particles.dx1_prev(p)); - const real_t piston_position_local = - pusher.metric.template convert<1, Crd::Ph, Crd::Cd>(piston_position); + const real_t piston_position_local = metric.template convert<1, Crd::Ph, Crd::Cd>( + piston_position); int i_w; prtldx_t dx_w; @@ -82,96 +76,85 @@ namespace arch { const real_t piston_gamma = ONE / math::sqrt(ONE - SQR(piston_v)); // step 2: calculate the time for the particle to reach the piston - const int delta_i1_to_piston = (i_w - pusher.i1_prev(p)); - const prtldx_t delta_dx1_to_piston = (dx_w - pusher.dx1_prev(p)); + const int delta_i1_to_piston = (i_w - particles.i1_prev(p)); + const prtldx_t delta_dx1_to_piston = (dx_w - particles.dx1_prev(p)); const real_t dx_to_piston = i_di_to_Xi(delta_i1_to_piston, delta_dx1_to_piston); const real_t dt_to_piston = dx_to_piston / - pusher.metric.template transform<1, Idx::XYZ, Idx::U>( - xp_Cd, + metric.template transform<1, Idx::XYZ, Idx::U>( + {}, beta_x_p - piston_v); // step 3: calculate remaining time after the collision - const real_t remaining_dt = pusher.dt - dt_to_piston; - // step 4: update the particle's velocity and position after the collision + const real_t remaining_dt = dt - dt_to_piston; - pusher.ux1(p) = -SQR(piston_gamma) * ((ONE + SQR(piston_v)) * pusher.ux1(p) - - TWO * piston_v * gamma_p); + // step 4: update the particle's velocity and position after the collision + particles.ux1(p) = -SQR(piston_gamma) * + ((ONE + SQR(piston_v)) * particles.ux1(p) - + TWO * piston_v * gamma_p); const real_t remaining_dt_inv_energy { massive - ? (remaining_dt / math::sqrt(ONE + SQR(pusher.ux1(p)) + - SQR(pusher.ux2(p)) + SQR(pusher.ux3(p)))) - : (remaining_dt / math::sqrt(SQR(pusher.ux1(p)) + SQR(pusher.ux2(p)) + - SQR(pusher.ux3(p)))) + ? (remaining_dt / + math::sqrt( + ONE + U2GAMMA(particles.ux1(p), particles.ux2(p), particles.ux3(p)))) + : (remaining_dt / math::sqrt(SQR(particles.ux1(p)) + SQR(particles.ux2(p)) + + SQR(particles.ux3(p)))) }; // define piston integer and fractional coordinate int i_w_coll = i_w; - prtldx_t dx_w_coll = dx_w + - pusher.metric.template transform<1, Idx::XYZ, Idx::U>( - xp_Cd, - piston_v) * - dt_to_piston; - - i_w_coll += static_cast(dx_w_coll >= ONE) - - static_cast(dx_w_coll < ZERO); - dx_w_coll -= (dx_w_coll >= ONE); - dx_w_coll += (dx_w_coll < ZERO); - - pusher.i1(p) = i_w_coll; - pusher.dx1(p) = pusher.metric.template transform<1, Idx::XYZ, Idx::U>( - xp_Cd, - pusher.ux1(p)) * - remaining_dt_inv_energy + - dx_w_coll; - - pusher.i1(p) += static_cast(pusher.dx1(p) >= ONE) - - static_cast(pusher.dx1(p) < ZERO); - pusher.dx1(p) -= (pusher.dx1(p) >= ONE); - pusher.dx1(p) += (pusher.dx1(p) < ZERO); + prtldx_t dx_w_coll = dx_w + metric.template transform<1, Idx::XYZ, Idx::U>( + {}, + piston_v) * + dt_to_piston; + + i_w_coll += static_cast(dx_w_coll >= ONE) - + static_cast(dx_w_coll < ZERO); + dx_w_coll -= static_cast(dx_w_coll >= ONE); + dx_w_coll += static_cast(dx_w_coll < ZERO); + + particles.i1(p) = i_w_coll; + particles.dx1( + p) = metric.template transform<1, Idx::XYZ, Idx::U>({}, particles.ux1(p)) * + remaining_dt_inv_energy + + dx_w_coll; + + particles.i1(p) += static_cast(particles.dx1(p) >= ONE) - + static_cast(particles.dx1(p) < ZERO); + particles.dx1(p) -= static_cast(particles.dx1(p) >= ONE); + particles.dx1(p) += static_cast(particles.dx1(p) < ZERO); } /** * @brief Checks whether a particle reflects off a moving piston, called after particle has been moved by regular pusher * * @param p Index of particle - * @param pusher Particle pusher engine for particle update + * @param dt Timestep + * @param particles Particle data arrays + * @param metric Metric object for coordinate transformations * @param piston_position Position of the piston at the start of timestep in global coordinates * @param piston_v Velocity of piston at current timestep * @param is_left Is piston on the left side of the box or right side of the box */ - template - Inline bool CrossesPiston(const index_t p, - const PusherKernel& pusher, - const real_t piston_position, - const real_t piston_v, - const bool is_left) { - const real_t x1_Cd = i_di_to_Xi(pusher.i1(p), pusher.dx1(p)); - coord_t xp_Cd { ZERO }; - pusher.getParticlePosition(p, xp_Cd); + template + Inline bool CrossesPiston(index_t p, + real_t dt, + const kernel::PusherArrays& particles, + const M& metric, + real_t piston_position, + real_t piston_v, + bool is_left) { + const real_t x1_Cd = i_di_to_Xi(particles.i1(p), particles.dx1(p)); // x1_Cd_wallmove is not the actual particle coordinate // it is particle position minus how much the wall has moved in this // timestep This is a computational trick const real_t x1_Cd_wallmove = - x1_Cd - - pusher.metric.template transform<1, Idx::XYZ, Idx::U>(xp_Cd, piston_v) * - pusher.dt; - const real_t x1_Ph_wallmove = pusher.metric.template convert<1, Crd::Cd, Crd::Ph>( + x1_Cd - metric.template transform<1, Idx::XYZ, Idx::U>({}, piston_v) * dt; + const real_t x1_Ph_wallmove = metric.template convert<1, Crd::Cd, Crd::Ph>( x1_Cd_wallmove); - if (is_left) { // if piston is moving from left, ask if particle is to the left of piston - if (piston_position > x1_Ph_wallmove) { - return true; - } else { - return false; - } - } else { // if piston is moving from the right, so ask is particle to right of piston - if (piston_position < x1_Ph_wallmove) { - return true; - } else { - return false; - } - } + return is_left ? piston_position > x1_Ph_wallmove + : piston_position < x1_Ph_wallmove; } } // namespace arch diff --git a/src/archetypes/problem_generator.h b/src/archetypes/problem_generator.h deleted file mode 100644 index fc22428e6..000000000 --- a/src/archetypes/problem_generator.h +++ /dev/null @@ -1,48 +0,0 @@ -/** - * @file archetypes/problem_generator.hpp - * @brief Base class for all problem generators - * @implements - * - arch::ProblemGenerator<> - * @namespace - * - arch:: - * @note - * To have easier access to variables inside ProblemGenerator in children - * classes, one should simply add: - * ```c++ - * using ProblemGenerator::D; - * using ProblemGenerator::C; - * using ProblemGenerator::params; - * ``` - * in the child class. This will allow to access these variables without - * the need to use `this->` or `ProblemGenerator::` prefix. - */ - -#ifndef ARCHETYPES_PROBLEM_GENERATOR_HPP -#define ARCHETYPES_PROBLEM_GENERATOR_HPP - -#include "enums.h" -#include "global.h" - -#include "metrics/traits.h" - -#include "framework/parameters/parameters.h" - -namespace arch { - using namespace ntt; - - template - requires metric::traits::HasD and metric::traits::HasCoordType - struct ProblemGenerator { - static constexpr Dimension D { M::Dim }; - static constexpr Coord C { M::CoordType }; - - const SimulationParams& params; - - ProblemGenerator(const SimulationParams& p) : params { p } {} - - ~ProblemGenerator() = default; - }; - -} // namespace arch - -#endif // ARCHETYPES_PROBLEM_GENERATOR_HPP diff --git a/src/archetypes/spatial_dist.h b/src/archetypes/spatial_dist.h index bf8abe9cd..e5022c5db 100644 --- a/src/archetypes/spatial_dist.h +++ b/src/archetypes/spatial_dist.h @@ -1,13 +1,12 @@ /** - * @file archetypes/spatial_dist.hpp + * @file archetypes/spatial_dist.h * @brief Spatial distribution class passed to injectors * @implements - * - arch::SpatialDistribution<> - * - arch::Uniform<> : arch::SpatialDistribution<> - * - arch::Replenish<> : arch::SpatialDistribution<> - * - arch::ReplenishUniform<> : arch::SpatialDistribution<> - * @namespace - * - arch:: + * - arch::spatial_dist::Uniform<> + * - arch::spatial_dist::Replenish<> + * - arch::spatial_dist::ReplenishUniform<> + * @namespaces: + * - arch::spatial_dist:: * @note * Instances of these functors take coordinate position in code units * and return a number between 0 and 1 that represents the spatial distribution @@ -16,41 +15,28 @@ #ifndef ARCHETYPES_SPATIAL_DIST_HPP #define ARCHETYPES_SPATIAL_DIST_HPP -#include "enums.h" #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/metric.h" #include "utils/error.h" #include "utils/numeric.h" -#include "metrics/traits.h" - -namespace arch { +namespace arch::spatial_dist { using namespace ntt; - template - requires metric::traits::HasD - struct SpatialDistribution { - static constexpr auto D = M::Dim; - - SpatialDistribution(const M& metric) : metric { metric } {} - - protected: - const M metric; - }; - - template - struct Uniform : public SpatialDistribution { - Uniform(const M& metric) : SpatialDistribution { metric } {} + template + struct Uniform { - Inline auto operator()(const coord_t&) const -> real_t { + Inline auto operator()(const coord_t&) const -> real_t { return ONE; } }; - template - struct Replenish : public SpatialDistribution { - using SpatialDistribution::metric; + template + struct Replenish { + const M metric; + const ndfield_t density; const idx_t idx; @@ -62,7 +48,7 @@ namespace arch { idx_t idx, const T& target_density, real_t target_max_density) - : SpatialDistribution { metric } + : metric { metric } , density { density } , idx { idx } , target_density { target_density } @@ -95,9 +81,9 @@ namespace arch { } }; - template - struct ReplenishUniform : public SpatialDistribution { - using SpatialDistribution::metric; + template + struct ReplenishUniform { + const M metric; const ndfield_t density; const idx_t idx; @@ -107,7 +93,7 @@ namespace arch { const ndfield_t& density, idx_t idx, real_t target_density) - : SpatialDistribution { metric } + : metric { metric } , density { density } , idx { idx } , target_density { target_density } {} @@ -138,6 +124,6 @@ namespace arch { } }; -} // namespace arch +} // namespace arch::spatial_dist #endif // ARCHETYPES_SPATIAL_DIST_HPP diff --git a/src/archetypes/traits.h b/src/archetypes/traits.h deleted file mode 100644 index 45ba642b6..000000000 --- a/src/archetypes/traits.h +++ /dev/null @@ -1,183 +0,0 @@ -/** - * @file archetypes/traits.h - * @brief Defines a set of traits to check if archetype classes satisfy certain conditions - * @implements - * - arch::traits::energydist::IsValid<> - checks if energy distribution class has required operator() - * - arch::traits::spatialdist::IsValid<> - checks if spatial distribution class has required operator() - * - arch::traits::pgen::check_compatibility<> - checks if problem generator is compatible with given enums - * - arch::traits::pgen::compatible_with<> - defines compatible enums for problem generator - * - arch::traits::pgen::HasD<> - checks if problem generator has Dim static member - * - arch::traits::pgen::HasInitFlds<> - checks if problem generator has init_flds member - * - arch::traits::pgen::HasInitPrtls<> - checks if problem generator has InitPrtls method - * - arch::traits::pgen::HasExternalFields<> - checks if problem generator has ExternalFields method - * - arch::traits::pgen::HasExtCurrent<> - checks if problem generator has ext_current member - * - arch::traits::pgen::HasAtmFields<> - checks if problem generator has AtmFields method - * - arch::traits::pgen::HasMatchFields<> - checks if problem generator has MatchFields method - * - arch::traits::pgen::HasMatchFieldsInX1<> - checks if problem generator has MatchFieldsInX1 method - * - arch::traits::pgen::HasMatchFieldsInX2<> - checks if problem generator has MatchFieldsInX2 method - * - arch::traits::pgen::HasMatchFieldsInX3<> - checks if problem generator has MatchFieldsInX3 method - * - arch::traits::pgen::HasFixFieldsConst<> - checks if problem generator has FixFieldsConst method - * - arch::traits::pgen::HasCustomPostStep<> - checks if problem generator has CustomPostStep method - * - arch::traits::pgen::HasCustomFieldOutput<> - checks if problem generator has CustomFieldOutput method - * - arch::traits::pgen::HasCustomStatOutput<> - checks if problem generator has CustomStat method - * @namespaces: - * - arch::traits:: - */ -#ifndef ARCHETYPES_TRAITS_H -#define ARCHETYPES_TRAITS_H - -#include "global.h" - -#include "arch/kokkos_aliases.h" - -namespace arch { - namespace traits { - - namespace energydist { - - template - concept IsValid = requires(const ED& edist, - const coord_t& x_Ph, - vec_t& v) { - { edist(x_Ph, v) } -> std::same_as; - }; - - } // namespace energydist - - namespace spatialdist { - - template - concept IsValid = requires(const SD& sdist, const coord_t& x_Ph) { - { sdist(x_Ph) } -> std::convertible_to; - }; - - } // namespace spatialdist - - namespace pgen { - - // checking compat for the problem generator + engine - template - struct check_compatibility { - template - static constexpr bool value(std::integer_sequence) { - return ((Is == N) || ...); - } - }; - - template - struct compatible_with { - static constexpr auto value = std::integer_sequence {}; - }; - - template - concept HasD = requires { - { PG::D } -> std::convertible_to; - }; - - template - concept HasInitFlds = requires(const PG& pgen) { pgen.init_flds; }; - - template - concept HasEmissionPolicy = requires(const PG& pgen, - simtime_t time, - spidx_t sp, - D& domain) { - pgen.EmissionPolicy(time, sp, domain); - }; - - template - concept HasCustomPrtlUpdate = requires(const PG& pgen, - simtime_t time, - spidx_t sp, - D& domain) { - pgen.CustomParticleUpdate(time, sp, domain); - }; - - template - concept HasInitPrtls = requires(PG& pgen, D& domain) { - { pgen.InitPrtls(domain) } -> std::same_as; - }; - - template - concept HasExternalFields = requires(const PG& pgen, - simtime_t time, - spidx_t sp, - D& domain) { - requires std::same_as; - pgen.ExternalFields(time, sp, domain).second; - }; - - template - concept HasExtCurrent = requires(const PG& pgen) { pgen.ext_current; }; - - template - concept HasAtmFields = requires(const PG& pgen, simtime_t time) { - pgen.AtmFields(time); - }; - - template - concept HasMatchFields = requires(const PG& pgen, simtime_t time) { - pgen.MatchFields(time); - }; - - template - concept HasMatchFieldsInX1 = requires(const PG& pgen, simtime_t time) { - pgen.MatchFieldsInX1(time); - }; - - template - concept HasMatchFieldsInX2 = requires(const PG& pgen, simtime_t time) { - pgen.MatchFieldsInX2(time); - }; - - template - concept HasMatchFieldsInX3 = requires(const PG& pgen, simtime_t time) { - pgen.MatchFieldsInX3(time); - }; - - template - concept HasFixFieldsConst = requires(const PG& pgen, - simtime_t time, - const bc_in& bc, - ntt::em comp) { - { - pgen.FixFieldsConst(time, bc, comp) - } -> std::convertible_to>; - }; - - template - concept HasCustomPostStep = requires(PG& pgen, - timestep_t s, - simtime_t t, - D& domain) { - { pgen.CustomPostStep(s, t, domain) } -> std::same_as; - }; - - template - concept HasCustomFieldOutput = requires(PG& pgen, - const std::string& name, - ndfield_t& buff, - index_t idx, - timestep_t step, - simtime_t time, - const D& dom) { - { - pgen.CustomFieldOutput(name, buff, idx, step, time, dom) - } -> std::same_as; - }; - - template - concept HasCustomStatOutput = requires(PG& pgen, - const std::string& name, - timestep_t s, - simtime_t t, - const D& dom) { - { pgen.CustomStat(name, s, t, dom) } -> std::convertible_to; - }; - - } // namespace pgen - } // namespace traits -} // namespace arch - -#endif // ARCHETYPES_TRAITS_H diff --git a/src/archetypes/utils.h b/src/archetypes/utils.h index 3518ab4c0..ff88d5385 100644 --- a/src/archetypes/utils.h +++ b/src/archetypes/utils.h @@ -15,11 +15,13 @@ #include "enums.h" #include "global.h" +#include "traits/metric.h" + #include "archetypes/energy_dist.h" +#include "archetypes/field_setter.h" #include "archetypes/particle_injector.h" #include "framework/domain/domain.h" #include "framework/parameters/parameters.h" -#include "kernels/fieldsetter.hpp" #include "kernels/particle_moments.hpp" #include @@ -41,7 +43,7 @@ namespace arch { * @tparam S Simulation engine type * @tparam M Metric type */ - template + template inline void InjectUniformMaxwellians( const SimulationParams& params, Domain& domain, @@ -57,14 +59,14 @@ namespace arch { const auto temperature_1 = temperatures.first / mass_1; const auto temperature_2 = temperatures.second / mass_2; - const auto maxwellian_1 = arch::Maxwellian(domain.mesh.metric, - domain.random_pool(), - temperature_1, - drift_four_vels.first); - const auto maxwellian_2 = arch::Maxwellian(domain.mesh.metric, - domain.random_pool(), - temperature_2, - drift_four_vels.second); + const auto maxwellian_1 = arch::energy_dist::Maxwellian( + domain.random_pool(), + temperature_1, + drift_four_vels.first); + const auto maxwellian_2 = arch::energy_dist::Maxwellian( + domain.random_pool(), + temperature_2, + drift_four_vels.second); arch::InjectUniform( params, @@ -91,7 +93,7 @@ namespace arch { * @tparam S Simulation engine type * @tparam M Metric type */ - template + template inline void InjectUniformMaxwellian( const SimulationParams& params, Domain& domain, @@ -129,12 +131,11 @@ namespace arch { * @tparam F Field ID for the moment to compute (e.g. FldsID::N, FldsID::T, etc.) * @tparam N Last dimension of the buffer (e.g. 3 or 6) */ - template - requires metric::traits::HasD + template inline void ComputeMomentWithSpecies( const SimulationParams& params, Domain& domain, - const std::vector species, + const std::vector& species, ndfield_t& buffer, const std::vector& components = {}, idx_t buffer_idx = 0u, @@ -177,19 +178,17 @@ namespace arch { Kokkos::Experimental::contribute(buffer, scatter_buff); } - template - requires ::metric::traits::HasD - inline void UpdateEMFields(const SimulationParams& params, - Domain& domain, - const F& fieldsetter) { + template + inline void UpdateEMFields(Domain& domain, const F& fieldsetter) { if constexpr (S == SimEngine::SRPIC) { Kokkos::deep_copy(domain.fields.bckp, domain.fields.em); - Kokkos::parallel_for("UpdateEMFields", - domain.mesh.rangeActiveCells(), - kernel::CustomFieldsetter(domain.mesh.metric, - domain.fields.em, - domain.fields.bckp, - fieldsetter)); + Kokkos::parallel_for( + "UpdateEMFields", + domain.mesh.rangeActiveCells(), + arch::CustomSetEMFields_kernel(domain.mesh.metric, + domain.fields.em, + domain.fields.bckp, + fieldsetter)); // comm here } else { raise::Error("Custom fieldsetter is only implemented for SRPIC", HERE); diff --git a/src/engines/CMakeLists.txt b/src/engines/CMakeLists.txt index 5b2edebdc..f5aafcb22 100644 --- a/src/engines/CMakeLists.txt +++ b/src/engines/CMakeLists.txt @@ -31,18 +31,20 @@ set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(SOURCES ${SRC_DIR}/reporter.cpp) -add_library(ntt_engines ${SOURCES}) +set(ENGINES ntt_engines${pgen_suffix}) +add_library(${ENGINES} ${SOURCES}) set(libs ntt_global ntt_framework ntt_metrics ntt_archetypes ntt_kernels - ntt_pgen) + ntt_pgen${pgen_suffix}) if(${output}) list(APPEND libs ntt_output) endif() -add_dependencies(ntt_engines ${libs}) -target_link_libraries(ntt_engines PUBLIC ${libs}) -target_compile_definitions(ntt_engines PUBLIC PGEN=\"${PGEN}\") +add_dependencies(${ENGINES} ${libs}) +target_link_libraries(${ENGINES} PUBLIC ${libs}) +target_compile_definitions(${ENGINES} PUBLIC PGEN=\"${PGEN}\") target_include_directories( - ntt_engines + ${ENGINES} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/../) +target_precompile_headers(${ENGINES} REUSE_FROM ntt_global) diff --git a/src/engines/engine.hpp b/src/engines/engine.hpp index 0d0948e79..8d54a5ec3 100644 --- a/src/engines/engine.hpp +++ b/src/engines/engine.hpp @@ -17,14 +17,14 @@ #include "global.h" #include "arch/mpi_aliases.h" +#include "traits/metric.h" +#include "traits/pgen.h" #include "utils/diag.h" #include "utils/reporter.h" #include "utils/timer.h" #include "archetypes/field_setter.h" -#include "archetypes/traits.h" #include "engines/reporter.h" -#include "engines/traits.h" #include "framework/containers/species.h" #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" @@ -57,8 +57,12 @@ namespace ntt { - template - requires traits::engine::IsCompatibleWithEngine + template + concept PGenClass = requires(SimulationParams& p, Metadomain& m) { + PG { p, m }; + }; + + template class Engine { protected: @@ -111,11 +115,10 @@ namespace ntt { , time { start_time } , step { start_step } {} - ~Engine() = default; - void init(); void print_report() const; + virtual ~Engine() = default; virtual void step_forward(timer::Timers&, Domain&) = 0; void run(); @@ -128,8 +131,7 @@ namespace ntt { } }; - template - requires traits::engine::IsCompatibleWithEngine + template void Engine::init() { m_metadomain.InitStatsWriter(m_params, is_resuming); #if defined(OUTPUT_ENABLED) @@ -140,20 +142,19 @@ namespace ntt { if (not is_resuming) { // start a new simulation with initial conditions logger::Checkpoint("Loading initial conditions", HERE); - if constexpr (arch::traits::pgen::HasInitFlds>) { + if constexpr (::traits::pgen::HasInitFlds>) { logger::Checkpoint("Initializing fields from problem generator", HERE); m_metadomain.runOnLocalDomains([&](auto& loc_dom) { Kokkos::parallel_for( "InitFields", loc_dom.mesh.rangeActiveCells(), - arch::SetEMFields_kernel { + arch::SetEMFields_kernel { loc_dom.fields.em, m_pgen.init_flds, loc_dom.mesh.metric }); }); } - if constexpr ( - arch::traits::pgen::HasInitPrtls, Domain>) { + if constexpr (::traits::pgen::HasInitPrtls, Domain>) { logger::Checkpoint("Initializing particles from problem generator", HERE); m_metadomain.runOnLocalDomains([&](auto& loc_dom) { m_pgen.InitPrtls(loc_dom); @@ -177,12 +178,11 @@ namespace ntt { print_report(); } - template - requires traits::engine::IsCompatibleWithEngine + template void Engine::print_report() const { const auto colored_stdout = m_params.template get( "diagnostics.colored_stdout"); - std::string report = ""; + std::string report; CallOnce( [&](auto& metadomain, auto& params) { report += reporter::Backend(); @@ -196,8 +196,9 @@ namespace ntt { metadomain.ndomains_per_dim(), metadomain.ndomains()); const auto pgen_name = std::string(PGEN); - report += ReportPgenConfig>(m_pgen, - pgen_name); + report += ReportPgenConfig>( + m_pgen, + pgen_name); if (metadomain.species_params().size() > 0) { report += "\n"; reporter::AddCategory(report, 4, "Particles"); @@ -237,8 +238,7 @@ namespace ntt { } } - template - requires traits::engine::IsCompatibleWithEngine + template void Engine::run() { init(); @@ -270,7 +270,7 @@ namespace ntt { }); // poststep (if defined) if constexpr ( - arch::traits::pgen::HasCustomPostStep>) { + ::traits::pgen::HasCustomPostStep>) { timers.start("Custom"); m_metadomain.runOnLocalDomains([&timers, this](auto& dom) { m_pgen.CustomPostStep(step, time, dom); @@ -289,7 +289,7 @@ namespace ntt { #if defined(OUTPUT_ENABLED) timers.start("Output"); if constexpr ( - arch::traits::pgen::HasCustomFieldOutput>) { + ::traits::pgen::HasCustomFieldOutput>) { auto lambda_custom_field_output = [&](const std::string& name, ndfield_t& buff, index_t idx, @@ -308,7 +308,7 @@ namespace ntt { print_output &= m_metadomain.Write(m_params, step, step - 1, time, time - dt); } if constexpr ( - arch::traits::pgen::HasCustomStatOutput>) { + ::traits::pgen::HasCustomStatOutput>) { auto lambda_custom_stat = [&](const std::string& name, timestep_t step, simtime_t time, diff --git a/src/engines/grpic.hpp b/src/engines/grpic.hpp deleted file mode 100644 index 7952153ba..000000000 --- a/src/engines/grpic.hpp +++ /dev/null @@ -1,1148 +0,0 @@ -/** - * @file engines/grpic.hpp - * @brief Simulation engien class which specialized on GRPIC - * @implements - * - ntt::GRPICEngine<> : ntt::Engine<> - * @cpp: - * - grpic.cpp - * @namespaces: - * - ntt:: - */ - -#ifndef ENGINES_GRPIC_GRPIC_H -#define ENGINES_GRPIC_GRPIC_H - -#include "enums.h" -#include "global.h" - -#include "arch/kokkos_aliases.h" -#include "utils/log.h" -#include "utils/numeric.h" -#include "utils/timer.h" - -#include "framework/domain/domain.h" -#include "framework/parameters/parameters.h" -#include "kernels/ampere_gr.hpp" -#include "kernels/aux_fields_gr.hpp" -#include "kernels/currents_deposit.hpp" -#include "kernels/digital_filter.hpp" -#include "kernels/faraday_gr.hpp" -#include "kernels/fields_bcs.hpp" -#include "kernels/particle_pusher_gr.hpp" - -#include "engines/engine.hpp" -#include "pgen.hpp" - -#include -#include -#include - -#include -#include - -namespace ntt { - - enum class gr_getE { - D0_B, - D_B0 - }; - enum class gr_getH { - D_B0, - D0_B0 - }; - enum class gr_faraday { - aux, - main - }; - enum class gr_ampere { - init, - aux, - main - }; - enum class gr_bc { - main, - aux, - curr - }; - - template - requires traits::engine::IsCompatibleWithGRPICEngine - class GRPICEngine : public Engine { - using base_t = Engine; - using pgen_t = user::PGen; - using domain_t = Domain; - // contents - using base_t::m_metadomain; - using base_t::m_params; - using base_t::m_pgen; - // methods - using base_t::init; - // variables - using base_t::dt; - using base_t::max_steps; - using base_t::runtime; - using base_t::step; - using base_t::time; - - public: - static constexpr auto S { SimEngine::GRPIC }; - - GRPICEngine(SimulationParams& params) : base_t { params } {} - - ~GRPICEngine() = default; - - void step_forward(timer::Timers& timers, domain_t& dom) override { - const auto fieldsolver_enabled = m_params.template get( - "algorithms.fieldsolver.enable"); - const auto deposit_enabled = m_params.template get( - "algorithms.deposit.enable"); - const auto clear_interval = m_params.template get( - "particles.clear_interval"); - - if (step == 0) { - if (fieldsolver_enabled) { - // communicate fields and apply BCs on the first timestep - /** - * Initially: em0::B -- - * em0::D -- - * em::B at -1/2 - * em::D at -1/2 - * - * cur0::J -- - * cur::J -- - * - * aux::E -- - * aux::H -- - * - * x_prtl at -1/2 - * u_prtl at -1/2 - */ - - /** - * em0::D, em::D, em0::B, em::B <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, - Comm::B | Comm::B0 | Comm::D | Comm::D0); - FieldBoundaries(dom, BC::B | BC::D, gr_bc::main); - - /** - * em0::B <- em::B - * em0::D <- em::D - * - * Now: em0::B & em0::D at -1/2 - */ - CopyFields(dom); - - /** - * aux::E <- alpha * em::D + beta x em0::B - * aux::H <- alpha * em::B0 - beta x em::D - * - * Now: aux::E & aux::H at -1/2 - */ - ComputeAuxE(dom, gr_getE::D_B0); - ComputeAuxH(dom, gr_getH::D_B0); - - /** - * aux::E, aux::H <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::H | Comm::E); - FieldBoundaries(dom, BC::H | BC::E, gr_bc::aux); - - /** - * em0::B <- (em0::B) <- -curl aux::E - * - * Now: em0::B at 0 - */ - Faraday(dom, gr_faraday::aux, HALF); - - /** - * em0::B, em::B <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); - FieldBoundaries(dom, BC::B, gr_bc::main); - - /** - * em::D <- (em0::D) <- curl aux::H - * - * Now: em::D at 0 - */ - Ampere(dom, gr_ampere::init, HALF); - - /** - * em0::D, em::D <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); - FieldBoundaries(dom, BC::D, gr_bc::main); - - /** - * aux::E <- alpha * em::D + beta x em0::B - * aux::H <- alpha * em0::B - beta x em::D - * - * Now: aux::E & aux::H at 0 - */ - ComputeAuxE(dom, gr_getE::D_B0); - ComputeAuxH(dom, gr_getH::D_B0); - - /** - * aux::E, aux::H <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::H | Comm::E); - FieldBoundaries(dom, BC::H | BC::E, gr_bc::aux); - - // !ADD: GR -- particles? - - /** - * em0::B <- (em::B) <- -curl aux::E - * - * Now: em0::B at 1/2 - */ - Faraday(dom, gr_faraday::main, ONE); - /** - * em0::B, em::B <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); - FieldBoundaries(dom, BC::B, gr_bc::main); - - /** - * em0::D <- (em0::D) <- curl aux::H - * - * Now: em0::D at 1/2 - */ - Ampere(dom, gr_ampere::aux, ONE); - /** - * em0::D, em::D <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); - FieldBoundaries(dom, BC::D, gr_bc::main); - - /** - * aux::H <- alpha * em0::B - beta x em0::D - * - * Now: aux::H at 1/2 - */ - ComputeAuxH(dom, gr_getH::D0_B0); - /** - * aux::H <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::H); - FieldBoundaries(dom, BC::H, gr_bc::aux); - - /** - * em0::D <- (em::D) <- curl aux::H - * - * Now: em0::D at 1 - * em::D at 0 - */ - Ampere(dom, gr_ampere::main, ONE); - /** - * em0::D, em::D <- boundary conditions - */ - m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); - FieldBoundaries(dom, BC::D, gr_bc::main); - - /** - * em::D <-> em0::D - * em::B <-> em0::B - * em::J <-> em0::J - */ - SwapFields(dom); - /** - * Finally: em0::B at -1/2 - * em0::D at 0 - * em::B at 1/2 - * em::D at 1 - * - * cur0::J -- - * cur::J -- - * - * aux::E -- - * aux::H -- - * - * x_prtl at 1 - * u_prtl at 1/2 - */ - } else { - /** - * em0::B <- em::B - * em0::D <- em::D - * - * Now: em0::B & em0::D at -1/2 - */ - CopyFields(dom); - } - } - - /** - * Initially: em0::B at n-3/2 - * em0::D at n-1 - * em::B at n-1/2 - * em::D at n - * - * cur0::J -- - * cur::J at n-1/2 - * - * aux::E -- - * aux::H -- - * - * x_prtl at n - * u_prtl at n-1/2 - */ - - if (fieldsolver_enabled) { - timers.start("FieldSolver"); - /** - * em0::D <- (em0::D + em::D) / 2 - * em0::B <- (em0::B + em::B) / 2 - * - * Now: em0::D at n-1/2 - * em0::B at n-1 - */ - TimeAverageDB(dom); - /** - * aux::E <- alpha * em0::D + beta x em::B - * - * Now: aux::E at n-1/2 - */ - ComputeAuxE(dom, gr_getE::D0_B); - timers.stop("FieldSolver"); - - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::E); - timers.stop("Communications"); - timers.start("FieldBoundaries"); - /** - * aux::E <- boundary conditions - */ - FieldBoundaries(dom, BC::E, gr_bc::aux); - timers.stop("FieldBoundaries"); - - timers.start("FieldSolver"); - /** - * em0::B <- (em0::B) <- -curl aux::E - * - * Now: em0::B at n - */ - Faraday(dom, gr_faraday::aux, ONE); - timers.stop("FieldSolver"); - - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); - timers.stop("Communications"); - /** - * em0::B, em::B <- boundary conditions - */ - timers.start("FieldBoundaries"); - FieldBoundaries(dom, BC::B, gr_bc::main); - timers.stop("FieldBoundaries"); - - timers.start("FieldSolver"); - /** - * aux::H <- alpha * em0::B - beta x em::D - * - * Now: aux::H at n - */ - ComputeAuxH(dom, gr_getH::D_B0); - timers.stop("FieldSolver"); - - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::H); - timers.stop("Communications"); - timers.start("FieldBoundaries"); - /** - * aux::H <- boundary conditions - */ - FieldBoundaries(dom, BC::H, gr_bc::aux); - timers.stop("FieldBoundaries"); - } - - { - /** - * x_prtl, u_prtl <- em::D, em0::B - * - * Now: x_prtl at n + 1, u_prtl at n + 1/2 - */ - timers.start("ParticlePusher"); - ParticlePush(dom); - timers.stop("ParticlePusher"); - - /** - * cur0::J <- current deposition - * - * Now: cur0::J at n+1/2 - */ - if (deposit_enabled) { - timers.start("CurrentDeposit"); - Kokkos::deep_copy(dom.fields.cur0, ZERO); - CurrentsDeposit(dom); - timers.stop("CurrentDeposit"); - - timers.start("Communications"); - m_metadomain.SynchronizeFields(dom, Comm::J); - m_metadomain.CommunicateFields(dom, Comm::J); - timers.stop("Communications"); - - timers.start("FieldBoundaries"); - FieldBoundaries(dom, BC::J, gr_bc::curr); - timers.stop("FieldBoundaries"); - - timers.start("CurrentFiltering"); - CurrentsFilter(dom); - timers.stop("CurrentFiltering"); - } - - timers.start("Communications"); - m_metadomain.CommunicateParticles(dom); - timers.stop("Communications"); - } - - if (fieldsolver_enabled) { - timers.start("FieldSolver"); - if (deposit_enabled) { - /** - * cur::J <- (cur0::J + cur::J) / 2 - * - * Now: cur::J at n - */ - TimeAverageJ(dom); - } - /** - * aux::Е <- alpha * em::D + beta x em0::B - * - * Now: aux::Е at n - */ - ComputeAuxE(dom, gr_getE::D_B0); - timers.stop("FieldSolver"); - - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::E); - timers.stop("Communications"); - timers.start("FieldBoundaries"); - /** - * aux::Е <- boundary conditions - */ - FieldBoundaries(dom, BC::E, gr_bc::aux); - timers.stop("FieldBoundaries"); - - timers.start("FieldSolver"); - /** - * em0::B <- (em::B) <- -curl aux::E - * - * Now: em0::B at n+1/2 - * em::B at n-1/2 - */ - Faraday(dom, gr_faraday::main, ONE); - timers.stop("FieldSolver"); - - /** - * em0::B, em::B <- boundary conditions - */ - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); - timers.stop("Communications"); - timers.start("FieldBoundaries"); - FieldBoundaries(dom, BC::B, gr_bc::main); - timers.stop("FieldBoundaries"); - - timers.start("FieldSolver"); - /** - * em0::D <- (em0::D) <- curl aux::H - * - * Now: em0::D at n+1/2 - */ - Ampere(dom, gr_ampere::aux, ONE); - timers.stop("FieldSolver"); - - if (deposit_enabled) { - timers.start("FieldSolver"); - /** - * em0::D <- (em0::D) <- cur::J - * - * Now: em0::D at n+1/2 - */ - AmpereCurrents(dom, gr_ampere::aux); - timers.stop("FieldSolver"); - } - - /** - * em0::D, em::D <- boundary conditions - */ - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); - timers.stop("Communications"); - timers.start("FieldBoundaries"); - FieldBoundaries(dom, BC::D, gr_bc::main); - timers.stop("FieldBoundaries"); - - timers.start("FieldSolver"); - /** - * aux::H <- alpha * em0::B - beta x em0::D - * - * Now: aux::H at n+1/2 - */ - ComputeAuxH(dom, gr_getH::D0_B0); - timers.stop("FieldSolver"); - - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::H); - timers.stop("Communications"); - timers.start("FieldBoundaries"); - /** - * aux::H <- boundary conditions - */ - FieldBoundaries(dom, BC::B, gr_bc::aux); - timers.stop("FieldBoundaries"); - - timers.start("FieldSolver"); - /** - * em0::D <- (em::D) <- curl aux::H - * - * Now: em0::D at n+1 - * em::D at n - */ - Ampere(dom, gr_ampere::main, ONE); - timers.stop("FieldSolver"); - - if (deposit_enabled) { - timers.start("FieldSolver"); - /** - * em0::D <- (em0::D) <- cur0::J - * - * Now: em0::D at n+1 - */ - AmpereCurrents(dom, gr_ampere::main); - timers.stop("FieldSolver"); - } - timers.start("FieldSolver"); - /** - * em::D <-> em0::D - * em::B <-> em0::B - * cur::J <-> cur0::J - */ - SwapFields(dom); - timers.stop("FieldSolver"); - - /** - * em0::D, em::D <- boundary conditions - */ - timers.start("Communications"); - m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); - timers.stop("Communications"); - timers.start("FieldBoundaries"); - FieldBoundaries(dom, BC::D, gr_bc::main); - timers.stop("FieldBoundaries"); - } - - timers.start("ParticleSort"); - m_metadomain.SortParticles(time, step, m_params, dom); - timers.stop("ParticleSort"); - - /** - * Finally: em0::B at n-1/2 - * em0::D at n - * em::B at n+1/2 - * em::D at n+1 - * - * cur0::J (at n) - * cur::J at n+1/2 - * - * aux::E (at n+1/2) - * aux::H (at n) - * - * x_prtl at n+1 - * u_prtl at n+1/2 - */ - } - - /* algorithm substeps --------------------------------------------------- */ - void FieldBoundaries(domain_t& domain, BCTags tags, const gr_bc& g) { - if (g == gr_bc::main) { - for (auto& direction : dir::Directions::orth) { - if (m_metadomain.mesh().flds_bc_in(direction) == FldsBC::MATCH) { - MatchFieldsIn(direction, domain, tags, g); - } else if (domain.mesh.flds_bc_in(direction) == FldsBC::AXIS) { - AxisFieldsIn(direction, domain, tags); - } else if (m_metadomain.mesh().flds_bc_in(direction) == FldsBC::CUSTOM) { - CustomFieldsIn(direction, domain, tags, g); - } else if (domain.mesh.flds_bc_in(direction) == FldsBC::HORIZON) { - HorizonFieldsIn(direction, domain, tags, g); - } - } // loop over directions - } else if (g == gr_bc::aux) { - for (auto& direction : dir::Directions::orth) { - if (domain.mesh.flds_bc_in(direction) == FldsBC::HORIZON) { - HorizonFieldsIn(direction, domain, tags, g); - } - } - } else if (g == gr_bc::curr) { - for (auto& direction : dir::Directions::orth) { - if (domain.mesh.prtl_bc_in(direction) == PrtlBC::ABSORB) { - MatchFieldsIn(direction, domain, tags, g); - } - } - } - } - - void MatchFieldsIn(dir::direction_t direction, - domain_t& domain, - BCTags tags, - const gr_bc& g) { - /** - * match boundaries - */ - const auto ds_array = m_params.template get>( - "grid.boundaries.match.ds"); - const auto dim = direction.get_dim(); - real_t xg_min, xg_max, xg_edge; - auto sign = direction.get_sign(); - - raise::ErrorIf(((dim != in::x1) or (sign < 0)) and (g == gr_bc::curr), - "Absorption of currents only possible in +x1 (+r)", - HERE); - - real_t ds; - if (sign > 0) { // + direction - ds = ds_array[(short)dim].second; - xg_max = m_metadomain.mesh().extent(dim).second; - xg_min = xg_max - ds; - xg_edge = xg_max; - } else { // - direction - ds = ds_array[(short)dim].first; - xg_min = m_metadomain.mesh().extent(dim).first; - xg_max = xg_min + ds; - xg_edge = xg_min; - } - boundaries_t box; - boundaries_t incl_ghosts; - for (unsigned short d { 0 }; d < M::Dim; ++d) { - if (d == static_cast(dim)) { - box.push_back({ xg_min, xg_max }); - incl_ghosts.push_back({ false, true }); - } else { - box.push_back(Range::All); - incl_ghosts.push_back({ true, true }); - } - } - if (not domain.mesh.Intersects(box)) { - return; - } - const auto intersect_range = domain.mesh.ExtentToRange(box, incl_ghosts); - tuple_t range_min { 0 }; - tuple_t range_max { 0 }; - - for (unsigned short d { 0 }; d < M::Dim; ++d) { - range_min[d] = intersect_range[d].first; - range_max[d] = intersect_range[d].second; - } - if (dim == in::x1) { - if (g != gr_bc::curr) { - if constexpr (arch::traits::pgen::HasInitFlds) { - Kokkos::parallel_for( - "MatchBoundaries", - CreateRangePolicy(range_min, range_max), - kernel::bc::MatchBoundaries_kernel( - domain.fields.em, - m_pgen.init_flds, - domain.mesh.metric, - xg_edge, - ds, - tags, - domain.mesh.flds_bc())); - Kokkos::parallel_for( - "MatchBoundaries", - CreateRangePolicy(range_min, range_max), - kernel::bc::MatchBoundaries_kernel( - domain.fields.em0, - m_pgen.init_flds, - domain.mesh.metric, - xg_edge, - ds, - tags, - domain.mesh.flds_bc())); - } - } else { - Kokkos::parallel_for( - "AbsorbCurrents", - CreateRangePolicy(range_min, range_max), - kernel::bc::gr::AbsorbCurrents_kernel(domain.fields.cur0, - domain.mesh.metric, - xg_edge, - ds)); - } - } else { - raise::Error("Invalid dimension", HERE); - } - } - - void HorizonFieldsIn(dir::direction_t direction, - domain_t& domain, - BCTags tags, - const gr_bc& g) { - /** - * open boundaries - */ - raise::ErrorIf(M::CoordType == Coord::Cart, - "Invalid coordinate type for horizon BCs", - HERE); - raise::ErrorIf(direction.get_dim() != in::x1, - "Invalid horizon direction, should be x1", - HERE); - const auto i1_min = domain.mesh.i_min(in::x1); - auto range = CreateRangePolicy({ domain.mesh.i_min(in::x2) }, - { domain.mesh.i_max(in::x2) + 1 }); - const auto nfilter = m_params.template get( - "algorithms.current_filters"); - if (g == gr_bc::main) { - Kokkos::parallel_for( - "OpenBCFields", - range, - kernel::bc::gr::HorizonBoundaries_kernel(domain.fields.em, - i1_min, - tags, - nfilter)); - Kokkos::parallel_for( - "OpenBCFields", - range, - kernel::bc::gr::HorizonBoundaries_kernel(domain.fields.em0, - i1_min, - tags, - nfilter)); - } - } - - void AxisFieldsIn(dir::direction_t direction, - domain_t& domain, - BCTags tags) { - /** - * axis boundaries - */ - raise::ErrorIf(M::CoordType == Coord::Cart, - "Invalid coordinate type for axis BCs", - HERE); - raise::ErrorIf(direction.get_dim() != in::x2, - "Invalid axis direction, should be x2", - HERE); - const auto i2_min = domain.mesh.i_min(in::x2); - const auto i2_max = domain.mesh.i_max(in::x2); - if (direction.get_sign() < 0) { - Kokkos::parallel_for( - "AxisBCFields", - domain.mesh.n_all(in::x1), - kernel::bc::AxisBoundaries_kernel(domain.fields.em, - i2_min, - tags)); - Kokkos::parallel_for( - "AxisBCFields", - domain.mesh.n_all(in::x1), - kernel::bc::AxisBoundaries_kernel(domain.fields.em0, - i2_min, - tags)); - } else { - Kokkos::parallel_for( - "AxisBCFields", - domain.mesh.n_all(in::x1), - kernel::bc::AxisBoundaries_kernel(domain.fields.em, - i2_max, - tags)); - Kokkos::parallel_for( - "AxisBCFields", - domain.mesh.n_all(in::x1), - kernel::bc::AxisBoundaries_kernel(domain.fields.em0, - i2_max, - tags)); - } - } - - void CustomFieldsIn(dir::direction_t direction, - domain_t& domain, - BCTags tags, - const gr_bc& g) { - (void)direction; - (void)domain; - (void)tags; - (void)g; - raise::Error("Custom boundaries not implemented", HERE); - // if constexpr ( - // traits::has_member::value) { - // const auto [box, custom_fields] = m_pgen.CustomFields(time); - // if (domain.mesh.Intersects(box)) { - // } - // - // } else { - // raise::Error("Custom boundaries not implemented", HERE); - // } - } - - /** - * @brief Swaps em and em0 fields, cur and cur0 currents. - */ - void SwapFields(domain_t& domain) { - std::swap(domain.fields.em, domain.fields.em0); - std::swap(domain.fields.cur, domain.fields.cur0); - } - - /** - * @brief Copies em fields into em0 - */ - void CopyFields(domain_t& domain) { - Kokkos::deep_copy(domain.fields.em0, domain.fields.em); - } - - void ComputeAuxE(domain_t& domain, const gr_getE& g) { - auto range = range_with_axis_BCs(domain); - if (g == gr_getE::D0_B) { - Kokkos::parallel_for( - "ComputeAuxE", - range, - kernel::gr::ComputeAuxE_kernel(domain.fields.em0, // D - domain.fields.em, // B - domain.fields.aux, // E - domain.mesh.metric)); - } else if (g == gr_getE::D_B0) { - Kokkos::parallel_for("ComputeAuxE", - range, - kernel::gr::ComputeAuxE_kernel(domain.fields.em, - domain.fields.em0, - domain.fields.aux, - domain.mesh.metric)); - } else { - raise::Error("Wrong option for `g`", HERE); - } - } - - void ComputeAuxH(domain_t& domain, const gr_getH& g) { - auto range = range_with_axis_BCs(domain); - if (g == gr_getH::D_B0) { - Kokkos::parallel_for( - "ComputeAuxH", - range, - kernel::gr::ComputeAuxH_kernel(domain.fields.em, // D - domain.fields.em0, // B - domain.fields.aux, // H - domain.mesh.metric)); - } else if (g == gr_getH::D0_B0) { - Kokkos::parallel_for("ComputeAuxH", - range, - kernel::gr::ComputeAuxH_kernel(domain.fields.em0, - domain.fields.em0, - domain.fields.aux, - domain.mesh.metric)); - } else { - raise::Error("Wrong option for `g`", HERE); - } - } - - auto range_with_axis_BCs(const domain_t& domain) -> range_t { - auto range = domain.mesh.rangeActiveCells(); - /** - * @brief taking one extra cell in the x1 and x2 directions if AXIS BCs - */ - if constexpr (M::Dim == Dim::_2D) { - if (domain.mesh.flds_bc_in({ 0, +1 }) == FldsBC::AXIS) { - range = CreateRangePolicy( - { domain.mesh.i_min(in::x1) - 1, domain.mesh.i_min(in::x2) }, - { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); - } else { - range = CreateRangePolicy( - { domain.mesh.i_min(in::x1) - 1, domain.mesh.i_min(in::x2) }, - { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) }); - } - } else if constexpr (M::Dim == Dim::_3D) { - raise::Error("Invalid dimension", HERE); - } - return range; - } - - void Faraday(domain_t& domain, const gr_faraday& g, real_t fraction = ONE) { - logger::Checkpoint("Launching Faraday kernel", HERE); - const auto dT = fraction * - m_params.template get( - "algorithms.timestep.correction") * - dt; - if (g == gr_faraday::aux) { - Kokkos::parallel_for( - "Faraday", - domain.mesh.rangeActiveCells(), - kernel::gr::Faraday_kernel(domain.fields.em0, // Bin - domain.fields.em0, // Bout - domain.fields.aux, // E - domain.mesh.metric, - dT, - domain.mesh.n_active(in::x2), - domain.mesh.flds_bc())); - } else if (g == gr_faraday::main) { - Kokkos::parallel_for( - "Faraday", - domain.mesh.rangeActiveCells(), - kernel::gr::Faraday_kernel(domain.fields.em, - domain.fields.em0, - domain.fields.aux, - domain.mesh.metric, - dT, - domain.mesh.n_active(in::x2), - domain.mesh.flds_bc())); - - } else { - raise::Error("Wrong option for `g`", HERE); - } - } - - void Ampere(domain_t& domain, const gr_ampere& g, real_t fraction = ONE) { - logger::Checkpoint("Launching Ampere kernel", HERE); - const auto dT = fraction * - m_params.template get( - "algorithms.timestep.correction") * - dt; - auto range = CreateRangePolicy( - { domain.mesh.i_min(in::x1), domain.mesh.i_min(in::x2) }, - { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); - const auto ni2 = domain.mesh.n_active(in::x2); - - if (g == gr_ampere::aux) { - // First push, updates D0 with J. - Kokkos::parallel_for("Ampere-1", - range, - kernel::gr::Ampere_kernel(domain.fields.em0, // Din - domain.fields.em0, // Dout - domain.fields.aux, - domain.mesh.metric, - dT, - ni2, - domain.mesh.flds_bc())); - } else if (g == gr_ampere::main) { - // Second push, updates D with J0 but assigns it to D0. - Kokkos::parallel_for("Ampere-2", - range, - kernel::gr::Ampere_kernel(domain.fields.em, - domain.fields.em0, - domain.fields.aux, - domain.mesh.metric, - dT, - ni2, - domain.mesh.flds_bc())); - } else if (g == gr_ampere::init) { - // Second push, updates D with J0 and assigns it to D. - Kokkos::parallel_for("Ampere-3", - range, - kernel::gr::Ampere_kernel(domain.fields.em, - domain.fields.em, - domain.fields.aux, - domain.mesh.metric, - dT, - ni2, - domain.mesh.flds_bc())); - } else { - raise::Error("Wrong option for `g`", HERE); - } - } - - void AmpereCurrents(domain_t& domain, const gr_ampere& g) { - logger::Checkpoint("Launching Ampere kernel for adding currents", HERE); - const auto q0 = m_params.template get("scales.q0"); - const auto B0 = m_params.template get("scales.B0"); - const auto coeff = -dt * q0 / B0; - auto range = CreateRangePolicy( - { domain.mesh.i_min(in::x1), domain.mesh.i_min(in::x2) }, - { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); - const auto ni2 = domain.mesh.n_active(in::x2); - - if (g == gr_ampere::aux) { - // Updates D0 with J: D0(n-1/2) -> (J(n)) -> D0(n+1/2) - Kokkos::parallel_for( - "AmpereCurrentsAux", - range, - kernel::gr::CurrentsAmpere_kernel(domain.fields.em0, - domain.fields.cur, - domain.mesh.metric, - coeff, - ni2, - domain.mesh.flds_bc())); - } else if (g == gr_ampere::main) { - // Updates D0 with J0: D0(n) -> (J0(n+1/2)) -> D0(n+1) - Kokkos::parallel_for( - "AmpereCurrentsMain", - range, - kernel::gr::CurrentsAmpere_kernel(domain.fields.em0, - domain.fields.cur0, - domain.mesh.metric, - coeff, - ni2, - domain.mesh.flds_bc())); - } else { - raise::Error("Wrong option for `g`", HERE); - } - } - - void TimeAverageDB(domain_t& domain) { - Kokkos::parallel_for( - "TimeAverageDB", - domain.mesh.rangeActiveCells(), - kernel::gr::TimeAverageDB_kernel(domain.fields.em, - domain.fields.em0)); - } - - void TimeAverageJ(domain_t& domain) { - Kokkos::parallel_for( - "TimeAverageJ", - domain.mesh.rangeActiveCells(), - kernel::gr::TimeAverageJ_kernel(domain.fields.cur, - domain.fields.cur0)); - } - - void CurrentsDeposit(domain_t& domain) { - auto scatter_cur0 = Kokkos::Experimental::create_scatter_view( - domain.fields.cur0); - for (auto& species : domain.species) { - logger::Checkpoint( - fmt::format("Launching currents deposit kernel for %d [%s] : %lu %f", - species.index(), - species.label().c_str(), - species.npart(), - (double)species.charge()), - HERE); - if (species.npart() == 0 || cmp::AlmostZero(species.charge())) { - continue; - } - Kokkos::parallel_for("CurrentsDeposit", - species.rangeActiveParticles(), - kernel::DepositCurrents_kernel( - scatter_cur0, - species.i1, - species.i2, - species.i3, - species.i1_prev, - species.i2_prev, - species.i3_prev, - species.dx1, - species.dx2, - species.dx3, - species.dx1_prev, - species.dx2_prev, - species.dx3_prev, - species.ux1, - species.ux2, - species.ux3, - species.phi, - species.weight, - species.tag, - domain.mesh.metric, - (real_t)(species.charge()), - dt)); - } - Kokkos::Experimental::contribute(domain.fields.cur0, scatter_cur0); - } - - void CurrentsFilter(domain_t& domain) { - logger::Checkpoint("Launching currents filtering kernels", HERE); - auto range = CreateRangePolicy( - { domain.mesh.i_min(in::x1), domain.mesh.i_min(in::x2) }, - { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); - const auto nfilter = m_params.template get( - "algorithms.current_filters"); - tuple_t size; - size[0] = domain.mesh.n_active(in::x1); - size[1] = domain.mesh.n_active(in::x2); - - // !TODO: this needs to be done more efficiently - for (unsigned short i = 0; i < nfilter; ++i) { - Kokkos::deep_copy(domain.fields.buff, domain.fields.cur0); - Kokkos::parallel_for("CurrentsFilter", - range, - kernel::DigitalFilter_kernel( - domain.fields.cur0, - domain.fields.buff, - size, - domain.mesh.flds_bc())); - m_metadomain.CommunicateFields(domain, Comm::J); // J0 - } - } - - void ParticlePush(domain_t& domain) { - for (auto& species : domain.species) { - species.set_unsorted(); - logger::Checkpoint( - fmt::format("Launching particle pusher kernel for %d [%s] : %lu", - species.index(), - species.label().c_str(), - species.npart()), - HERE); - if (species.npart() == 0) { - continue; - } - const auto q_ovr_m = species.mass() > ZERO - ? species.charge() / species.mass() - : ZERO; - // coeff = q / m (dt / 2) omegaB0 - const auto coeff = q_ovr_m * HALF * dt * - m_params.template get( - "algorithms.timestep.correction") * - m_params.template get("scales.omegaB0"); - const auto eps = m_params.template get( - "algorithms.gr.pusher_eps"); - const auto niter = m_params.template get( - "algorithms.gr.pusher_niter"); - if (species.pusher() == ParticlePusher::PHOTON) { - auto range_policy = - Kokkos::RangePolicy( - 0, - species.npart()); - - // clang-format off - Kokkos::parallel_for( - "ParticlePusher", - range_policy, - kernel::gr::Pusher_kernel( - domain.fields.em, - domain.fields.em0, - species.i1, species.i2, species.i3, - species.i1_prev, species.i2_prev, species.i3_prev, - species.dx1, species.dx2, species.dx3, - species.dx1_prev, species.dx2_prev, species.dx3_prev, - species.ux1, species.ux2, species.ux3, - species.phi, species.tag, - domain.mesh.metric, - coeff, dt, - domain.mesh.n_active(in::x1), - domain.mesh.n_active(in::x2), - domain.mesh.n_active(in::x3), - eps, niter, - domain.mesh.prtl_bc() - )); - // clang-format on - } else if (species.pusher() == ParticlePusher::BORIS) { - auto range_policy = - Kokkos::RangePolicy( - 0, - species.npart()); - // clang-format off - Kokkos::parallel_for( - "ParticlePusher", - range_policy, - kernel::gr::Pusher_kernel( - domain.fields.em, - domain.fields.em0, - species.i1, species.i2, species.i3, - species.i1_prev, species.i2_prev, species.i3_prev, - species.dx1, species.dx2, species.dx3, - species.dx1_prev, species.dx2_prev, species.dx3_prev, - species.ux1, species.ux2, species.ux3, - species.phi, species.tag, - domain.mesh.metric, - coeff, dt, - domain.mesh.n_active(in::x1), - domain.mesh.n_active(in::x2), - domain.mesh.n_active(in::x3), - eps, niter, - domain.mesh.prtl_bc() - )); - // clang-format on - } else if (species.pusher() == ParticlePusher::NONE) { - // do nothing - } else { - raise::Error("not implemented", HERE); - } - } - } - }; -} // namespace ntt - -#endif // ENGINES_GRPIC_GRPIC_H diff --git a/src/engines/grpic/currents.h b/src/engines/grpic/currents.h new file mode 100644 index 000000000..2aed0843f --- /dev/null +++ b/src/engines/grpic/currents.h @@ -0,0 +1,106 @@ +/** + * @file engines/grpic/currents.h + * @brief Current deposition and filtering routines for the GRPIC engine + * @implements + * - ntt::grpic::CurrentsDeposit<> -> void + * - ntt::grpic::CurrentsFilter<> -> void + * @namespaces: + * - ntt::grpic:: + */ + +#ifndef ENGINES_GRPIC_CURRENTS_H +#define ENGINES_GRPIC_CURRENTS_H + +#include "enums.h" + +#include "traits/metric.h" +#include "utils/log.h" +#include "utils/param_container.h" + +#include "framework/domain/domain.h" +#include "framework/domain/metadomain.h" +#include "framework/parameters/parameters.h" +#include "kernels/currents_deposit.hpp" +#include "kernels/digital_filter.hpp" + +namespace ntt { + namespace grpic { + + template + void CurrentsDeposit(Domain& domain, + const prm::Parameters& engine_params) { + auto scatter_cur0 = Kokkos::Experimental::create_scatter_view( + domain.fields.cur0); + const auto dt = engine_params.get("dt"); + for (auto& species : domain.species) { + logger::Checkpoint( + fmt::format("Launching currents deposit kernel for %d [%s] : %lu %f", + species.index(), + species.label().c_str(), + species.npart(), + (double)species.charge()), + HERE); + if (species.npart() == 0 || cmp::AlmostZero(species.charge())) { + continue; + } + Kokkos::parallel_for("CurrentsDeposit", + species.rangeActiveParticles(), + kernel::DepositCurrents_kernel( + scatter_cur0, + species.i1, + species.i2, + species.i3, + species.i1_prev, + species.i2_prev, + species.i3_prev, + species.dx1, + species.dx2, + species.dx3, + species.dx1_prev, + species.dx2_prev, + species.dx3_prev, + species.ux1, + species.ux2, + species.ux3, + species.phi, + species.weight, + species.tag, + domain.mesh.metric, + (real_t)(species.charge()), + dt)); + } + Kokkos::Experimental::contribute(domain.fields.cur0, scatter_cur0); + } + + template + void CurrentsFilter(Metadomain& metadomain, + Domain& domain, + const SimulationParams& params) { + logger::Checkpoint("Launching currents filtering kernels", HERE); + auto range = CreateRangePolicy( + { domain.mesh.i_min(in::x1), domain.mesh.i_min(in::x2) }, + { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); + const auto nfilter = params.template get( + "algorithms.current_filters"); + tuple_t size; + size[0] = domain.mesh.n_active(in::x1); + size[1] = domain.mesh.n_active(in::x2); + + // !TODO: this needs to be done more efficiently + for (unsigned short i = 0; i < nfilter; ++i) { + Kokkos::deep_copy(domain.fields.buff, domain.fields.cur0); + Kokkos::parallel_for("CurrentsFilter", + range, + kernel::DigitalFilter_kernel( + domain.fields.cur0, + domain.fields.buff, + size, + domain.mesh.flds_bc())); + metadomain.CommunicateFields(domain, Comm::J); // J0 + } + } + + } // namespace grpic +} // namespace ntt + +#endif // ENGINES_GRPIC_CURRENTS_H \ No newline at end of file diff --git a/src/engines/grpic/fields_bcs.h b/src/engines/grpic/fields_bcs.h new file mode 100644 index 000000000..6520769f4 --- /dev/null +++ b/src/engines/grpic/fields_bcs.h @@ -0,0 +1,271 @@ +/** + * @file engines/grpic/fields_bcs.h + * @brief Field boundary condition routines for the GRPIC engine + * @implements + * - enum ntt::grpic::gr_bc + * - ntt::grpic::MatchFieldsIn<> -> void + * - ntt::grpic::HorizonFieldsIn<> -> void + * - ntt::grpic::AxisFieldsIn<> -> void + * - ntt::grpic::CustomFieldsIn<> -> void + * - ntt::grpic::FieldBoundaries<> -> void + * @namespaces: + * - ntt::grpic:: + */ + +#ifndef ENGINES_GRPIC_FIELDS_BCS_H +#define ENGINES_GRPIC_FIELDS_BCS_H + +#include "enums.h" +#include "global.h" + +#include "traits/metric.h" +#include "traits/pgen.h" + +#include "framework/domain/domain.h" +#include "framework/parameters/parameters.h" +#include "kernels/fields_bcs.hpp" + +#include "engines/engine.hpp" + +#include + +namespace ntt { + namespace grpic { + + enum class gr_bc : uint8_t { + main, + aux, + curr + }; + + template PG> + void MatchFieldsIn(dir::direction_t direction, + Domain& domain, + const Grid& global_grid, + const PG& pgen, + const SimulationParams& params, + BCTags tags, + const gr_bc& g) { + /** + * match boundaries + */ + const auto ds_array = params.template get>( + "grid.boundaries.match.ds"); + const auto dim = direction.get_dim(); + real_t xg_min, xg_max, xg_edge; + auto sign = direction.get_sign(); + + raise::ErrorIf(((dim != in::x1) or (sign < 0)) and (g == gr_bc::curr), + "Absorption of currents only possible in +x1 (+r)", + HERE); + + real_t ds; + if (sign > 0) { // + direction + ds = ds_array[(short)dim].second; + xg_max = global_grid.extent(dim).second; + xg_min = xg_max - ds; + xg_edge = xg_max; + } else { // - direction + ds = ds_array[(short)dim].first; + xg_min = global_grid.extent(dim).first; + xg_max = xg_min + ds; + xg_edge = xg_min; + } + boundaries_t box; + boundaries_t incl_ghosts; + for (unsigned short d { 0 }; d < M::Dim; ++d) { + if (d == static_cast(dim)) { + box.emplace_back(xg_min, xg_max); + incl_ghosts.emplace_back(false, true); + } else { + box.push_back(Range::All); + incl_ghosts.emplace_back(true, true); + } + } + if (not domain.mesh.Intersects(box)) { + return; + } + const auto intersect_range = domain.mesh.ExtentToRange(box, incl_ghosts); + tuple_t range_min { 0 }; + tuple_t range_max { 0 }; + + for (unsigned short d { 0 }; d < M::Dim; ++d) { + range_min[d] = intersect_range[d].first; + range_max[d] = intersect_range[d].second; + } + if (dim == in::x1) { + if (g != gr_bc::curr) { + if constexpr (::traits::pgen::HasInitFlds) { + Kokkos::parallel_for( + "MatchBoundaries", + CreateRangePolicy(range_min, range_max), + kernel::bc::MatchBoundaries_kernel(domain.fields.em, + pgen.init_flds, + domain.mesh.metric, + xg_edge, + ds, + tags, + domain.mesh.flds_bc())); + Kokkos::parallel_for( + "MatchBoundaries", + CreateRangePolicy(range_min, range_max), + kernel::bc::MatchBoundaries_kernel(domain.fields.em0, + pgen.init_flds, + domain.mesh.metric, + xg_edge, + ds, + tags, + domain.mesh.flds_bc())); + } + } else { + Kokkos::parallel_for( + "AbsorbCurrents", + CreateRangePolicy(range_min, range_max), + kernel::bc::gr::AbsorbCurrents_kernel(domain.fields.cur0, + domain.mesh.metric, + xg_edge, + ds)); + } + } else { + raise::Error("Invalid dimension", HERE); + } + } + + template + void HorizonFieldsIn(dir::direction_t direction, + Domain& domain, + const SimulationParams& params, + BCTags tags, + const gr_bc& g) { + /** + * open boundaries + */ + raise::ErrorIf(M::CoordType == Coord::Cartesian, + "Invalid coordinate type for horizon BCs", + HERE); + raise::ErrorIf(direction.get_dim() != in::x1, + "Invalid horizon direction, should be x1", + HERE); + const auto i1_min = domain.mesh.i_min(in::x1); + auto range = CreateRangePolicy({ domain.mesh.i_min(in::x2) }, + { domain.mesh.i_max(in::x2) + 1 }); + const auto nfilter = params.template get( + "algorithms.current_filters"); + if (g == gr_bc::main) { + Kokkos::parallel_for( + "OpenBCFields", + range, + kernel::bc::gr::HorizonBoundaries_kernel(domain.fields.em, + i1_min, + tags, + nfilter)); + Kokkos::parallel_for( + "OpenBCFields", + range, + kernel::bc::gr::HorizonBoundaries_kernel(domain.fields.em0, + i1_min, + tags, + nfilter)); + } + } + + template + void AxisFieldsIn(dir::direction_t direction, + Domain& domain, + BCTags tags) { + /** + * axis boundaries + */ + raise::ErrorIf(M::CoordType == Coord::Cartesian, + "Invalid coordinate type for axis BCs", + HERE); + raise::ErrorIf(direction.get_dim() != in::x2, + "Invalid axis direction, should be x2", + HERE); + const auto i2_min = domain.mesh.i_min(in::x2); + const auto i2_max = domain.mesh.i_max(in::x2); + if (direction.get_sign() < 0) { + Kokkos::parallel_for( + "AxisBCFields", + domain.mesh.n_all(in::x1), + kernel::bc::AxisBoundaries_kernel(domain.fields.em, + i2_min, + tags)); + Kokkos::parallel_for( + "AxisBCFields", + domain.mesh.n_all(in::x1), + kernel::bc::AxisBoundaries_kernel(domain.fields.em0, + i2_min, + tags)); + } else { + Kokkos::parallel_for( + "AxisBCFields", + domain.mesh.n_all(in::x1), + kernel::bc::AxisBoundaries_kernel(domain.fields.em, + i2_max, + tags)); + Kokkos::parallel_for( + "AxisBCFields", + domain.mesh.n_all(in::x1), + kernel::bc::AxisBoundaries_kernel(domain.fields.em0, + i2_max, + tags)); + } + } + + template + void CustomFieldsIn(dir::direction_t direction, + Domain& domain, + BCTags tags, + const gr_bc& g) { + (void)direction; + (void)domain; + (void)tags; + (void)g; + raise::Error("Custom boundaries not implemented", HERE); + } + + template PG> + void FieldBoundaries(Domain& domain, + const Grid& global_grid, + const PG& pgen, + const SimulationParams& params, + BCTags tags, + const gr_bc& g) { + if (g == gr_bc::main) { + for (auto& direction : dir::Directions::orth) { + if (global_grid.flds_bc_in(direction) == FldsBC::MATCH) { + MatchFieldsIn(direction, domain, global_grid, pgen, params, tags, g); + } else if (domain.mesh.flds_bc_in(direction) == FldsBC::AXIS) { + AxisFieldsIn(direction, domain, tags); + } else if (global_grid.flds_bc_in(direction) == FldsBC::CUSTOM) { + CustomFieldsIn(direction, domain, tags, g); + } else if (domain.mesh.flds_bc_in(direction) == FldsBC::HORIZON) { + HorizonFieldsIn(direction, domain, params, tags, g); + } + } // loop over directions + } else if (g == gr_bc::aux) { + for (auto& direction : dir::Directions::orth) { + if (domain.mesh.flds_bc_in(direction) == FldsBC::HORIZON) { + HorizonFieldsIn(direction, domain, params, tags, g); + } + } + } else if (g == gr_bc::curr) { + for (auto& direction : dir::Directions::orth) { + if (domain.mesh.prtl_bc_in(direction) == PrtlBC::ABSORB) { + MatchFieldsIn(direction, domain, global_grid, pgen, params, tags, g); + } + } + } + } + + } // namespace grpic +} // namespace ntt + +#endif // ENGINES_GRPIC_FIELDS_BCS_H \ No newline at end of file diff --git a/src/engines/grpic/fieldsolvers.h b/src/engines/grpic/fieldsolvers.h new file mode 100644 index 000000000..476583908 --- /dev/null +++ b/src/engines/grpic/fieldsolvers.h @@ -0,0 +1,267 @@ +/** + * @file engines/grpic/fieldsolvers.h + * @brief Field solver routines (Faraday, Ampere, auxiliary fields) for the GRPIC engine + * @implements + * - enum ntt::grpic::gr_getE + * - enum ntt::grpic::gr_getH + * - enum ntt::grpic::gr_faraday + * - enum ntt::grpic::gr_ampere + * - ntt::grpic::range_with_axis_BCs<> -> auto + * - ntt::grpic::ComputeAuxE<> -> void + * - ntt::grpic::ComputeAuxH<> -> void + * - ntt::grpic::Faraday<> -> void + * - ntt::grpic::Ampere<> -> void + * - ntt::grpic::AmpereCurrents<> -> void + * @namespaces: + * - ntt::grpic:: + */ + +#ifndef ENGINES_GRPIC_FIELDSOLVERS_H +#define ENGINES_GRPIC_FIELDSOLVERS_H + +#include "enums.h" + +#include "utils/log.h" + +#include "framework/domain/domain.h" +#include "framework/parameters/parameters.h" +#include "kernels/ampere_gr.hpp" +#include "kernels/aux_fields_gr.hpp" +#include "kernels/faraday_gr.hpp" + +#include + +namespace ntt { + namespace grpic { + + enum class gr_getE : uint8_t { + D0_B, + D_B0 + }; + enum class gr_getH : uint8_t { + D_B0, + D0_B0 + }; + enum class gr_faraday : uint8_t { + aux, + main + }; + enum class gr_ampere : uint8_t { + init, + aux, + main + }; + + template + auto range_with_axis_BCs(const Domain& domain) + -> range_t { + auto range = domain.mesh.rangeActiveCells(); + /** + * @brief taking one extra cell in the x1 and x2 directions if AXIS BCs + */ + if constexpr (M::Dim == Dim::_2D) { + if (domain.mesh.flds_bc_in({ 0, +1 }) == FldsBC::AXIS) { + range = CreateRangePolicy( + { domain.mesh.i_min(in::x1) - 1, domain.mesh.i_min(in::x2) }, + { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); + } else { + range = CreateRangePolicy( + { domain.mesh.i_min(in::x1) - 1, domain.mesh.i_min(in::x2) }, + { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) }); + } + } else if constexpr (M::Dim == Dim::_3D) { + raise::Error("Invalid dimension", HERE); + } + return range; + } + + template + void ComputeAuxE(Domain& domain, const gr_getE& g) { + auto range = range_with_axis_BCs(domain); + if (g == gr_getE::D0_B) { + Kokkos::parallel_for( + "ComputeAuxE", + range, + kernel::gr::ComputeAuxE_kernel(domain.fields.em0, // D + domain.fields.em, // B + domain.fields.aux, // E + domain.mesh.metric)); + } else if (g == gr_getE::D_B0) { + Kokkos::parallel_for("ComputeAuxE", + range, + kernel::gr::ComputeAuxE_kernel(domain.fields.em, + domain.fields.em0, + domain.fields.aux, + domain.mesh.metric)); + } else { + raise::Error("Wrong option for `g`", HERE); + } + } + + template + void ComputeAuxH(Domain& domain, const gr_getH& g) { + auto range = range_with_axis_BCs(domain); + if (g == gr_getH::D_B0) { + Kokkos::parallel_for( + "ComputeAuxH", + range, + kernel::gr::ComputeAuxH_kernel(domain.fields.em, // D + domain.fields.em0, // B + domain.fields.aux, // H + domain.mesh.metric)); + } else if (g == gr_getH::D0_B0) { + Kokkos::parallel_for("ComputeAuxH", + range, + kernel::gr::ComputeAuxH_kernel(domain.fields.em0, + domain.fields.em0, + domain.fields.aux, + domain.mesh.metric)); + } else { + raise::Error("Wrong option for `g`", HERE); + } + } + + template + void Faraday(Domain& domain, + const SimulationParams& params, + const prm::Parameters& engine_params, + const gr_faraday& g, + real_t fraction = ONE) { + logger::Checkpoint("Launching Faraday kernel", HERE); + const auto dt = engine_params.get("dt"); + const auto dT = fraction * + params.template get( + "algorithms.timestep.correction") * + dt; + if (g == gr_faraday::aux) { + Kokkos::parallel_for( + "Faraday", + domain.mesh.rangeActiveCells(), + kernel::gr::Faraday_kernel(domain.fields.em0, // Bin + domain.fields.em0, // Bout + domain.fields.aux, // E + domain.mesh.metric, + dT, + domain.mesh.n_active(in::x2), + domain.mesh.flds_bc())); + } else if (g == gr_faraday::main) { + Kokkos::parallel_for( + "Faraday", + domain.mesh.rangeActiveCells(), + kernel::gr::Faraday_kernel(domain.fields.em, + domain.fields.em0, + domain.fields.aux, + domain.mesh.metric, + dT, + domain.mesh.n_active(in::x2), + domain.mesh.flds_bc())); + + } else { + raise::Error("Wrong option for `g`", HERE); + } + } + + template + void Ampere(Domain& domain, + const SimulationParams& params, + const prm::Parameters& engine_params, + const gr_ampere& g, + real_t fraction = ONE) { + logger::Checkpoint("Launching Ampere kernel", HERE); + + const auto dt = engine_params.get("dt"); + const auto dT = fraction * + params.template get( + "algorithms.timestep.correction") * + dt; + auto range = CreateRangePolicy( + { domain.mesh.i_min(in::x1), domain.mesh.i_min(in::x2) }, + { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); + const auto ni2 = domain.mesh.n_active(in::x2); + + if (g == gr_ampere::aux) { + // First push, updates D0 with J. + Kokkos::parallel_for("Ampere-1", + range, + kernel::gr::Ampere_kernel(domain.fields.em0, // Din + domain.fields.em0, // Dout + domain.fields.aux, + domain.mesh.metric, + dT, + ni2, + domain.mesh.flds_bc())); + } else if (g == gr_ampere::main) { + // Second push, updates D with J0 but assigns it to D0. + Kokkos::parallel_for("Ampere-2", + range, + kernel::gr::Ampere_kernel(domain.fields.em, + domain.fields.em0, + domain.fields.aux, + domain.mesh.metric, + dT, + ni2, + domain.mesh.flds_bc())); + } else if (g == gr_ampere::init) { + // Second push, updates D with J0 and assigns it to D. + Kokkos::parallel_for("Ampere-3", + range, + kernel::gr::Ampere_kernel(domain.fields.em, + domain.fields.em, + domain.fields.aux, + domain.mesh.metric, + dT, + ni2, + domain.mesh.flds_bc())); + } else { + raise::Error("Wrong option for `g`", HERE); + } + } + + template + void AmpereCurrents(Domain& domain, + const SimulationParams& params, + const prm::Parameters& engine_params, + const gr_ampere& g) { + logger::Checkpoint("Launching Ampere kernel for adding currents", HERE); + + const auto dt = engine_params.get("dt"); + + const auto q0 = params.template get("scales.q0"); + const auto B0 = params.template get("scales.B0"); + const auto coeff = -dt * q0 / B0; + auto range = CreateRangePolicy( + { domain.mesh.i_min(in::x1), domain.mesh.i_min(in::x2) }, + { domain.mesh.i_max(in::x1), domain.mesh.i_max(in::x2) + 1 }); + const auto ni2 = domain.mesh.n_active(in::x2); + + if (g == gr_ampere::aux) { + // Updates D0 with J: D0(n-1/2) -> (J(n)) -> D0(n+1/2) + Kokkos::parallel_for( + "AmpereCurrentsAux", + range, + kernel::gr::CurrentsAmpere_kernel(domain.fields.em0, + domain.fields.cur, + domain.mesh.metric, + coeff, + ni2, + domain.mesh.flds_bc())); + } else if (g == gr_ampere::main) { + // Updates D0 with J0: D0(n) -> (J0(n+1/2)) -> D0(n+1) + Kokkos::parallel_for( + "AmpereCurrentsMain", + range, + kernel::gr::CurrentsAmpere_kernel(domain.fields.em0, + domain.fields.cur0, + domain.mesh.metric, + coeff, + ni2, + domain.mesh.flds_bc())); + } else { + raise::Error("Wrong option for `g`", HERE); + } + } + + } // namespace grpic +} // namespace ntt + +#endif // ENGINES_GRPIC_FIELDSOLVERS_H \ No newline at end of file diff --git a/src/engines/grpic/grpic.hpp b/src/engines/grpic/grpic.hpp new file mode 100644 index 000000000..cf7d19cf1 --- /dev/null +++ b/src/engines/grpic/grpic.hpp @@ -0,0 +1,638 @@ +/** + * @file engines/grpic/grpic.hpp + * @brief Simulation engine class which specializes on GRPIC + * @implements + * - ntt::GRPICEngine<> : ntt::Engine<> + * @cpp: + * - grpic.cpp + * @namespaces: + * - ntt:: + */ + +#ifndef ENGINES_GRPIC_GRPIC_HPP +#define ENGINES_GRPIC_GRPIC_HPP + +#include "enums.h" +#include "global.h" + +#include "traits/metric.h" +#include "utils/numeric.h" +#include "utils/timer.h" + +#include "engines/grpic/currents.h" +#include "engines/grpic/fields_bcs.h" +#include "engines/grpic/fieldsolvers.h" +#include "engines/grpic/particle_pusher.h" +#include "engines/grpic/utils.h" +#include "framework/domain/domain.h" +#include "framework/parameters/parameters.h" + +#include "engines/engine.hpp" +#include "pgen.hpp" + +#include +#include +#include + +#include + +namespace ntt { + + template + class GRPICEngine : public Engine { + using base_t = Engine; + using pgen_t = user::PGen; + using domain_t = Domain; + // contents + using base_t::m_metadomain; + using base_t::m_params; + using base_t::m_pgen; + // methods + using base_t::init; + // variables + using base_t::dt; + using base_t::max_steps; + using base_t::runtime; + using base_t::step; + using base_t::time; + + public: + static constexpr auto S { SimEngine::GRPIC }; + + GRPICEngine(SimulationParams& params) : base_t { params } {} + + ~GRPICEngine() override = default; + + void step_forward(timer::Timers& timers, domain_t& dom) override { + const auto fieldsolver_enabled = m_params.template get( + "algorithms.fieldsolver.enable"); + const auto deposit_enabled = m_params.template get( + "algorithms.deposit.enable"); + const auto clear_interval = m_params.template get( + "particles.clear_interval"); + + if (step == 0) { + if (fieldsolver_enabled) { + // communicate fields and apply BCs on the first timestep + /** + * Initially: em0::B -- + * em0::D -- + * em::B at -1/2 + * em::D at -1/2 + * + * cur0::J -- + * cur::J -- + * + * aux::E -- + * aux::H -- + * + * x_prtl at -1/2 + * u_prtl at -1/2 + */ + + /** + * em0::D, em::D, em0::B, em::B <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, + Comm::B | Comm::B0 | Comm::D | Comm::D0); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::B | BC::D, + grpic::gr_bc::main); + + /** + * em0::B <- em::B + * em0::D <- em::D + * + * Now: em0::B & em0::D at -1/2 + */ + grpic::CopyFields(dom); + + /** + * aux::E <- alpha * em::D + beta x em0::B + * aux::H <- alpha * em::B0 - beta x em::D + * + * Now: aux::E & aux::H at -1/2 + */ + grpic::ComputeAuxE(dom, grpic::gr_getE::D_B0); + grpic::ComputeAuxH(dom, grpic::gr_getH::D_B0); + + /** + * aux::E, aux::H <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::H | Comm::E); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::H | BC::E, + grpic::gr_bc::aux); + + /** + * em0::B <- (em0::B) <- -curl aux::E + * + * Now: em0::B at 0 + */ + grpic::Faraday(dom, + m_params, + this->engineParams(), + grpic::gr_faraday::aux, + HALF); + + /** + * em0::B, em::B <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::B, + grpic::gr_bc::main); + + /** + * em::D <- (em0::D) <- curl aux::H + * + * Now: em::D at 0 + */ + grpic::Ampere(dom, + m_params, + this->engineParams(), + grpic::gr_ampere::init, + HALF); + + /** + * em0::D, em::D <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::D, + grpic::gr_bc::main); + + /** + * aux::E <- alpha * em::D + beta x em0::B + * aux::H <- alpha * em0::B - beta x em::D + * + * Now: aux::E & aux::H at 0 + */ + grpic::ComputeAuxE(dom, grpic::gr_getE::D_B0); + grpic::ComputeAuxH(dom, grpic::gr_getH::D_B0); + + /** + * aux::E, aux::H <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::H | Comm::E); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::H | BC::E, + grpic::gr_bc::aux); + + // !ADD: GR -- particles? + + /** + * em0::B <- (em::B) <- -curl aux::E + * + * Now: em0::B at 1/2 + */ + grpic::Faraday(dom, + m_params, + this->engineParams(), + grpic::gr_faraday::main, + ONE); + /** + * em0::B, em::B <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::B, + grpic::gr_bc::main); + + /** + * em0::D <- (em0::D) <- curl aux::H + * + * Now: em0::D at 1/2 + */ + grpic::Ampere(dom, m_params, this->engineParams(), grpic::gr_ampere::aux, ONE); + /** + * em0::D, em::D <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::D, + grpic::gr_bc::main); + + /** + * aux::H <- alpha * em0::B - beta x em0::D + * + * Now: aux::H at 1/2 + */ + grpic::ComputeAuxH(dom, grpic::gr_getH::D0_B0); + /** + * aux::H <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::H); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::H, + grpic::gr_bc::aux); + + /** + * em0::D <- (em::D) <- curl aux::H + * + * Now: em0::D at 1 + * em::D at 0 + */ + grpic::Ampere(dom, m_params, this->engineParams(), grpic::gr_ampere::main, ONE); + /** + * em0::D, em::D <- boundary conditions + */ + m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::D, + grpic::gr_bc::main); + + /** + * em::D <-> em0::D + * em::B <-> em0::B + * em::J <-> em0::J + */ + grpic::SwapFields(dom); + /** + * Finally: em0::B at -1/2 + * em0::D at 0 + * em::B at 1/2 + * em::D at 1 + * + * cur0::J -- + * cur::J -- + * + * aux::E -- + * aux::H -- + * + * x_prtl at 1 + * u_prtl at 1/2 + */ + } else { + /** + * em0::B <- em::B + * em0::D <- em::D + * + * Now: em0::B & em0::D at -1/2 + */ + grpic::CopyFields(dom); + } + } + + /** + * Initially: em0::B at n-3/2 + * em0::D at n-1 + * em::B at n-1/2 + * em::D at n + * + * cur0::J -- + * cur::J at n-1/2 + * + * aux::E -- + * aux::H -- + * + * x_prtl at n + * u_prtl at n-1/2 + */ + + if (fieldsolver_enabled) { + timers.start("FieldSolver"); + /** + * em0::D <- (em0::D + em::D) / 2 + * em0::B <- (em0::B + em::B) / 2 + * + * Now: em0::D at n-1/2 + * em0::B at n-1 + */ + grpic::TimeAverageDB(dom); + /** + * aux::E <- alpha * em0::D + beta x em::B + * + * Now: aux::E at n-1/2 + */ + grpic::ComputeAuxE(dom, grpic::gr_getE::D0_B); + timers.stop("FieldSolver"); + + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::E); + timers.stop("Communications"); + timers.start("FieldBoundaries"); + /** + * aux::E <- boundary conditions + */ + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::E, + grpic::gr_bc::aux); + timers.stop("FieldBoundaries"); + + timers.start("FieldSolver"); + /** + * em0::B <- (em0::B) <- -curl aux::E + * + * Now: em0::B at n + */ + grpic::Faraday(dom, m_params, this->engineParams(), grpic::gr_faraday::aux, ONE); + timers.stop("FieldSolver"); + + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); + timers.stop("Communications"); + /** + * em0::B, em::B <- boundary conditions + */ + timers.start("FieldBoundaries"); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::B, + grpic::gr_bc::main); + timers.stop("FieldBoundaries"); + + timers.start("FieldSolver"); + /** + * aux::H <- alpha * em0::B - beta x em::D + * + * Now: aux::H at n + */ + grpic::ComputeAuxH(dom, grpic::gr_getH::D_B0); + timers.stop("FieldSolver"); + + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::H); + timers.stop("Communications"); + timers.start("FieldBoundaries"); + /** + * aux::H <- boundary conditions + */ + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::H, + grpic::gr_bc::aux); + timers.stop("FieldBoundaries"); + } + + { + /** + * x_prtl, u_prtl <- em::D, em0::B + * + * Now: x_prtl at n + 1, u_prtl at n + 1/2 + */ + timers.start("ParticlePusher"); + grpic::ParticlePush(dom, m_params, this->engineParams()); + timers.stop("ParticlePusher"); + + /** + * cur0::J <- current deposition + * + * Now: cur0::J at n+1/2 + */ + if (deposit_enabled) { + timers.start("CurrentDeposit"); + Kokkos::deep_copy(dom.fields.cur0, ZERO); + grpic::CurrentsDeposit(dom, this->engineParams()); + timers.stop("CurrentDeposit"); + + timers.start("Communications"); + m_metadomain.SynchronizeFields(dom, Comm::J); + m_metadomain.CommunicateFields(dom, Comm::J); + timers.stop("Communications"); + + timers.start("FieldBoundaries"); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::J, + grpic::gr_bc::curr); + timers.stop("FieldBoundaries"); + + timers.start("CurrentFiltering"); + grpic::CurrentsFilter(m_metadomain, dom, m_params); + timers.stop("CurrentFiltering"); + } + + timers.start("Communications"); + m_metadomain.CommunicateParticles(dom); + timers.stop("Communications"); + } + + if (fieldsolver_enabled) { + timers.start("FieldSolver"); + if (deposit_enabled) { + /** + * cur::J <- (cur0::J + cur::J) / 2 + * + * Now: cur::J at n + */ + grpic::TimeAverageJ(dom); + } + /** + * aux::Е <- alpha * em::D + beta x em0::B + * + * Now: aux::Е at n + */ + grpic::ComputeAuxE(dom, grpic::gr_getE::D_B0); + timers.stop("FieldSolver"); + + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::E); + timers.stop("Communications"); + timers.start("FieldBoundaries"); + /** + * aux::Е <- boundary conditions + */ + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::E, + grpic::gr_bc::aux); + timers.stop("FieldBoundaries"); + + timers.start("FieldSolver"); + /** + * em0::B <- (em::B) <- -curl aux::E + * + * Now: em0::B at n+1/2 + * em::B at n-1/2 + */ + grpic::Faraday(dom, m_params, this->engineParams(), grpic::gr_faraday::main, ONE); + timers.stop("FieldSolver"); + + /** + * em0::B, em::B <- boundary conditions + */ + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::B | Comm::B0); + timers.stop("Communications"); + timers.start("FieldBoundaries"); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::B, + grpic::gr_bc::main); + timers.stop("FieldBoundaries"); + + timers.start("FieldSolver"); + /** + * em0::D <- (em0::D) <- curl aux::H + * + * Now: em0::D at n+1/2 + */ + grpic::Ampere(dom, m_params, this->engineParams(), grpic::gr_ampere::aux, ONE); + timers.stop("FieldSolver"); + + if (deposit_enabled) { + timers.start("FieldSolver"); + /** + * em0::D <- (em0::D) <- cur::J + * + * Now: em0::D at n+1/2 + */ + grpic::AmpereCurrents(dom, + m_params, + this->engineParams(), + grpic::gr_ampere::aux); + timers.stop("FieldSolver"); + } + + /** + * em0::D, em::D <- boundary conditions + */ + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); + timers.stop("Communications"); + timers.start("FieldBoundaries"); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::D, + grpic::gr_bc::main); + timers.stop("FieldBoundaries"); + + timers.start("FieldSolver"); + /** + * aux::H <- alpha * em0::B - beta x em0::D + * + * Now: aux::H at n+1/2 + */ + grpic::ComputeAuxH(dom, grpic::gr_getH::D0_B0); + timers.stop("FieldSolver"); + + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::H); + timers.stop("Communications"); + timers.start("FieldBoundaries"); + /** + * aux::H <- boundary conditions + */ + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::H, + grpic::gr_bc::aux); + timers.stop("FieldBoundaries"); + + timers.start("FieldSolver"); + /** + * em0::D <- (em::D) <- curl aux::H + * + * Now: em0::D at n+1 + * em::D at n + */ + grpic::Ampere(dom, m_params, this->engineParams(), grpic::gr_ampere::main, ONE); + timers.stop("FieldSolver"); + + if (deposit_enabled) { + timers.start("FieldSolver"); + /** + * em0::D <- (em0::D) <- cur0::J + * + * Now: em0::D at n+1 + */ + grpic::AmpereCurrents(dom, + m_params, + this->engineParams(), + grpic::gr_ampere::main); + timers.stop("FieldSolver"); + } + timers.start("FieldSolver"); + /** + * em::D <-> em0::D + * em::B <-> em0::B + * cur::J <-> cur0::J + */ + grpic::SwapFields(dom); + timers.stop("FieldSolver"); + + /** + * em0::D, em::D <- boundary conditions + */ + timers.start("Communications"); + m_metadomain.CommunicateFields(dom, Comm::D | Comm::D0); + timers.stop("Communications"); + timers.start("FieldBoundaries"); + grpic::FieldBoundaries(dom, + m_metadomain.mesh(), + m_pgen, + m_params, + BC::D, + grpic::gr_bc::main); + timers.stop("FieldBoundaries"); + } + + timers.start("ParticleSort"); + m_metadomain.SortParticles(time, step, m_params, dom); + timers.stop("ParticleSort"); + + /** + * Finally: em0::B at n-1/2 + * em0::D at n + * em::B at n+1/2 + * em::D at n+1 + * + * cur0::J (at n) + * cur::J at n+1/2 + * + * aux::E (at n+1/2) + * aux::H (at n) + * + * x_prtl at n+1 + * u_prtl at n+1/2 + */ + } + }; +} // namespace ntt + +#endif // ENGINES_GRPIC_GRPIC_HPP diff --git a/src/engines/grpic/particle_pusher.h b/src/engines/grpic/particle_pusher.h new file mode 100644 index 000000000..bfcec8089 --- /dev/null +++ b/src/engines/grpic/particle_pusher.h @@ -0,0 +1,94 @@ +/** + * @file engines/grpic/particle_pusher.h + * @brief Particle pusher routines for the GRPIC engine + * @implements + * - ntt::grpic::ParticlePush<> -> void + * @namespaces: + * - ntt::grpic:: + */ + +#ifndef ENGINES_GRPIC_PARTICLE_PUSHER_H +#define ENGINES_GRPIC_PARTICLE_PUSHER_H + +#include "enums.h" + +#include "traits/metric.h" +#include "utils/log.h" +#include "utils/param_container.h" + +#include "framework/domain/domain.h" +#include "framework/parameters/parameters.h" +#include "kernels/pushers/gr.hpp" + +namespace ntt { + namespace grpic { + + template + void ParticlePush(Domain& domain, + const SimulationParams& params, + const prm::Parameters& engine_params) { + const auto dt = engine_params.get("dt"); + for (auto& species : domain.species) { + species.set_unsorted(); + logger::Checkpoint( + fmt::format("Launching particle pusher kernel for %d [%s] : %lu", + species.index(), + species.label().c_str(), + species.npart()), + HERE); + if ((species.npart() == 0) or (species.pusher() == ParticlePusher::NONE)) { + continue; + } + const auto pusher_ctx = kernel::gr::PusherContext { + species.mass(), + species.charge(), + dt, + params.template get("scales.omegaB0"), + params.template get("algorithms.gr.pusher_eps"), + params.template get("algorithms.gr.pusher_niter"), + static_cast(domain.mesh.n_active(in::x1)), + static_cast(domain.mesh.n_active(in::x2)), + static_cast(domain.mesh.n_active(in::x3)) + }; + + const auto pusher_boundaries = kernel::gr::PusherBoundaries { + domain.mesh.prtl_bc() + }; + auto pusher_arrays = species.PusherKernelArrays(); + + if (species.pusher() == ParticlePusher::PHOTON) { + const auto range_policy = + Kokkos::RangePolicy( + 0, + species.npart()); + Kokkos::parallel_for("ParticlePusher", + range_policy, + kernel::gr::Pusher_kernel(pusher_ctx, + pusher_boundaries, + pusher_arrays, + domain.fields.em, + domain.fields.em0, + domain.mesh.metric)); + } else if (species.pusher() == ParticlePusher::BORIS) { + const auto range_policy = + Kokkos::RangePolicy( + 0, + species.npart()); + Kokkos::parallel_for("ParticlePusher", + range_policy, + kernel::gr::Pusher_kernel(pusher_ctx, + pusher_boundaries, + pusher_arrays, + domain.fields.em, + domain.fields.em0, + domain.mesh.metric)); + } else { + raise::Error("not implemented", HERE); + } + } + } + + } // namespace grpic +} // namespace ntt + +#endif // ENGINES_GRPIC_PARTICLE_PUSHER_H \ No newline at end of file diff --git a/src/engines/grpic/utils.h b/src/engines/grpic/utils.h new file mode 100644 index 000000000..c6f43ec2c --- /dev/null +++ b/src/engines/grpic/utils.h @@ -0,0 +1,66 @@ +/** + * @file engines/grpic/utils.h + * @brief Utility functions used by the GRPIC engine + * @implements + * - ntt::grpic::SwapFields<> -> void + * - ntt::grpic::CopyFields<> -> void + * - ntt::grpic::TimeAverageDB<> -> void + * - ntt::grpic::TimeAverageJ<> -> void + * @namespaces: + * - ntt::grpic:: + */ + +#ifndef ENGINES_GRPIC_UTILS_H +#define ENGINES_GRPIC_UTILS_H + +#include "enums.h" + +#include "traits/metric.h" + +#include "framework/domain/domain.h" +#include "kernels/aux_fields_gr.hpp" + +#include + +namespace ntt { + namespace grpic { + + /** + * @brief Swaps em and em0 fields, cur and cur0 currents. + */ + template + void SwapFields(Domain& domain) { + std::swap(domain.fields.em, domain.fields.em0); + std::swap(domain.fields.cur, domain.fields.cur0); + } + + /** + * @brief Copies em fields into em0 + */ + template + void CopyFields(Domain& domain) { + Kokkos::deep_copy(domain.fields.em0, domain.fields.em); + } + + template + void TimeAverageDB(Domain& domain) { + Kokkos::parallel_for( + "TimeAverageDB", + domain.mesh.rangeActiveCells(), + kernel::gr::TimeAverageDB_kernel(domain.fields.em, + domain.fields.em0)); + } + + template + void TimeAverageJ(Domain& domain) { + Kokkos::parallel_for( + "TimeAverageJ", + domain.mesh.rangeActiveCells(), + kernel::gr::TimeAverageJ_kernel(domain.fields.cur, + domain.fields.cur0)); + } + + } // namespace grpic +} // namespace ntt + +#endif // ENGINES_GRPIC_UTILS_H \ No newline at end of file diff --git a/src/engines/reporter.cpp b/src/engines/reporter.cpp index 191e5b732..f56874d23 100644 --- a/src/engines/reporter.cpp +++ b/src/engines/reporter.cpp @@ -1,7 +1,9 @@ #include "engines/reporter.h" #include "enums.h" +#include "global.h" +#include "utils/formatting.h" #include "utils/reporter.h" #include "framework/parameters/parameters.h" @@ -19,7 +21,7 @@ namespace ntt { timestep_t max_steps, const std::vector& ndomains_per_dim, unsigned int ndomains) -> std::string { - std::string report = ""; + std::string report; /* * Simulation configs */ @@ -29,7 +31,7 @@ namespace ntt { "Name", "%s", params.template get("simulation.name").c_str()); - reporter::AddParam(report, 4, "Engine", "%s", S.to_string()); + reporter::AddParam(report, 4, "Engine", "%s", SimEngine(S).to_string()); reporter::AddParam(report, 4, "Metric", "%s", M.to_string()); #if SHAPE_ORDER == 0 reporter::AddParam(report, 4, "Deposit", "%s", "zigzag"); diff --git a/src/engines/reporter.h b/src/engines/reporter.h index 9cbcf3a3d..1cc0a3495 100644 --- a/src/engines/reporter.h +++ b/src/engines/reporter.h @@ -1,11 +1,21 @@ +/** + * @file engines/reporter.h + * @brief Functions for reporting simulation and problem generator configuration + * @implements + * - ntt::ReportSimulationConfig -> std::string + * - ntt::ReportPgenConfig<> -> std::string + * @namespaces: + * - ntt:: + */ + #ifndef ENGINES_REPORTER_H #define ENGINES_REPORTER_H #include "enums.h" +#include "traits/pgen.h" #include "utils/reporter.h" -#include "archetypes/traits.h" #include "framework/parameters/parameters.h" #include @@ -22,11 +32,11 @@ namespace ntt { const std::vector&, unsigned int) -> std::string; - template - inline auto ReportPgenConfig(const PG& pgen, const std::string& pgen_name) + template + inline auto ReportPgenConfig(const PG& /*pgen*/, const std::string& pgen_name) -> std::string { - std::string report = ""; - report += "\n"; + std::string report; + report += "\n"; reporter::AddCategory(report, 4, "Problem generator"); reporter::AddParam(report, 6, "Name", "%s", pgen_name.c_str()); reporter::AddSubcategory(report, 6, "Methods defined"); @@ -39,74 +49,73 @@ namespace ntt { 8, "InitFlds", "%s", - BoolToOnOff(arch::traits::pgen::HasInitFlds)); + BoolToOnOff(::traits::pgen::HasInitFlds)); reporter::AddParam(report, 8, "InitPrtls", "%s", - BoolToOnOff(arch::traits::pgen::HasInitPrtls)); + BoolToOnOff(::traits::pgen::HasInitPrtls)); reporter::AddParam(report, 8, "CustomPostStep", "%s", - BoolToOnOff(arch::traits::pgen::HasCustomPostStep)); + BoolToOnOff(::traits::pgen::HasCustomPostStep)); reporter::AddParam(report, 8, "ExternalFields", "%s", - BoolToOnOff(arch::traits::pgen::HasExternalFields)); + BoolToOnOff(::traits::pgen::HasExternalFields)); reporter::AddParam(report, 8, "ext_current", "%s", - BoolToOnOff(arch::traits::pgen::HasExtCurrent)); + BoolToOnOff(::traits::pgen::HasExtCurrent)); reporter::AddParam(report, 8, "AtmFields", "%s", - BoolToOnOff(arch::traits::pgen::HasAtmFields)); + BoolToOnOff(::traits::pgen::HasAtmFields)); reporter::AddParam(report, 8, "MatchFields", "%s", - BoolToOnOff(arch::traits::pgen::HasMatchFields)); + BoolToOnOff(::traits::pgen::HasMatchFields)); reporter::AddParam(report, 8, "MatchFieldsInX1", "%s", - BoolToOnOff(arch::traits::pgen::HasMatchFieldsInX1)); + BoolToOnOff(::traits::pgen::HasMatchFieldsInX1)); reporter::AddParam(report, 8, "MatchFieldsInX2", "%s", - BoolToOnOff(arch::traits::pgen::HasMatchFieldsInX2)); + BoolToOnOff(::traits::pgen::HasMatchFieldsInX2)); reporter::AddParam(report, 8, "MatchFieldsInX3", "%s", - BoolToOnOff(arch::traits::pgen::HasMatchFieldsInX3)); + BoolToOnOff(::traits::pgen::HasMatchFieldsInX3)); reporter::AddParam(report, 8, "FixFieldsConst", "%s", - BoolToOnOff(arch::traits::pgen::HasFixFieldsConst)); + BoolToOnOff(::traits::pgen::HasFixFieldsConst)); reporter::AddParam(report, 8, "EmissionPolicy", "%s", - BoolToOnOff(arch::traits::pgen::HasEmissionPolicy)); + BoolToOnOff(::traits::pgen::HasEmissionPolicy)); reporter::AddParam( report, 8, "CustomFieldOutput", "%s", - BoolToOnOff(arch::traits::pgen::HasCustomFieldOutput)); - reporter::AddParam( - report, - 8, - "CustomStatOutput", - "%s", - BoolToOnOff(arch::traits::pgen::HasCustomStatOutput)); + BoolToOnOff(::traits::pgen::HasCustomFieldOutput)); + reporter::AddParam(report, + 8, + "CustomStatOutput", + "%s", + BoolToOnOff(::traits::pgen::HasCustomStatOutput)); return report; } diff --git a/src/engines/srpic/currents.h b/src/engines/srpic/currents.h index 5733875b4..3afabea8a 100644 --- a/src/engines/srpic/currents.h +++ b/src/engines/srpic/currents.h @@ -1,3 +1,14 @@ +/** + * @file engines/srpic/currents.h + * @brief Current deposition and filtering routines for the SRPIC engine + * @implements + * - ntt::srpic::CallDepositKernel<> -> void + * - ntt::srpic::CurrentsDeposit<> -> void + * - ntt::srpic::CurrentsFilter<> -> void + * @namespaces: + * - ntt::srpic:: + */ + #ifndef ENGINES_SRPIC_CURRENTS_H #define ENGINES_SRPIC_CURRENTS_H @@ -5,11 +16,10 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/metric.h" #include "utils/log.h" #include "utils/param_container.h" -#include "metrics/traits.h" - #include "engines/srpic/utils.h" #include "framework/domain/domain.h" #include "framework/domain/metadomain.h" @@ -19,8 +29,7 @@ namespace ntt { namespace srpic { - template - requires metric::traits::HasD && metric::traits::HasCoordType + template void CallDepositKernel(const Particles& species, const M& local_metric, const scatter_ndfield_t& scatter_cur, @@ -52,7 +61,7 @@ namespace ntt { dt)); } - template + template void CurrentsDeposit(Domain& domain, const prm::Parameters& engine_params) { const auto dt = engine_params.get("dt"); @@ -77,8 +86,7 @@ namespace ntt { Kokkos::Experimental::contribute(domain.fields.cur, scatter_cur); } - template - requires metric::traits::HasD && metric::traits::HasCoordType + template void CurrentsFilter(Metadomain& metadomain, Domain& domain, const SimulationParams& params) { diff --git a/src/engines/srpic/fields_bcs.h b/src/engines/srpic/fields_bcs.h index a02909cdf..7552dc5e3 100644 --- a/src/engines/srpic/fields_bcs.h +++ b/src/engines/srpic/fields_bcs.h @@ -1,3 +1,19 @@ +/** + * @file engines/srpic/fields_bcs.h + * @brief Field boundary condition routines for the SRPIC engine + * @implements + * - ntt::srpic::CallMatchFields<> -> void + * - ntt::srpic::MatchFieldsIn<> -> void + * - ntt::srpic::AxisFieldsIn<> -> void + * - ntt::srpic::FixedFieldsIn<> -> void + * - ntt::srpic::PerfectConductorFieldsIn<> -> void + * - ntt::srpic::AtmosphereFieldsIn<> -> void + * - ntt::srpic::CustomFieldsIn<> -> void + * - ntt::srpic::FieldBoundaries<> -> void + * @namespaces: + * - ntt::srpic:: + */ + #ifndef ENGINES_SRPIC_FIELDS_BCS_H #define ENGINES_SRPIC_FIELDS_BCS_H @@ -5,21 +21,21 @@ #include "global.h" #include "arch/directions.h" +#include "traits/metric.h" +#include "traits/pgen.h" #include "utils/numeric.h" -#include "metrics/traits.h" - -#include "archetypes/traits.h" #include "engines/srpic/utils.h" #include "framework/domain/domain.h" #include "framework/parameters/parameters.h" #include "kernels/fields_bcs.hpp" +#include "engines/engine.hpp" + namespace ntt { namespace srpic { - template - requires metric::traits::HasD + template void CallMatchFields(ndfield_t& fields, const boundaries_t& boundaries, const T& match_fields, @@ -32,7 +48,7 @@ namespace ntt { Kokkos::parallel_for( "MatchFields", CreateRangePolicy(range_min, range_max), - kernel::bc::MatchBoundaries_kernel(fields, + kernel::bc::MatchBoundaries_kernel(fields, match_fields, metric, xg_edge, @@ -41,15 +57,14 @@ namespace ntt { boundaries)); } - template - requires metric::traits::HasD - void MatchFieldsIn(dir::direction_t direction, - Domain& domain, - const Grid& global_grid, - const PG& pgen, - const prm::Parameters& engine_params, - const SimulationParams& params, - BCTags tags) { + template PG> + void MatchFieldsIn(const dir::direction_t& direction, + Domain& domain, + const Grid& global_grid, + const PG& pgen, + const prm::Parameters& engine_params, + const SimulationParams& params, + BCTags tags) { const auto time = engine_params.get("time"); /** * matching boundaries @@ -75,15 +90,15 @@ namespace ntt { boundaries_t incl_ghosts; for (dim_t d { 0 }; d < M::Dim; ++d) { if (d == static_cast(dim)) { - box.push_back({ xg_min, xg_max }); + box.emplace_back(xg_min, xg_max); if (sign > 0) { - incl_ghosts.push_back({ false, true }); + incl_ghosts.emplace_back(false, true); } else { - incl_ghosts.push_back({ true, false }); + incl_ghosts.emplace_back(true, false); } } else { box.push_back(Range::All); - incl_ghosts.push_back({ true, true }); + incl_ghosts.emplace_back(true, true); } } if (not domain.mesh.Intersects(box)) { @@ -99,7 +114,7 @@ namespace ntt { } if (dim == in::x1) { - if constexpr (arch::traits::pgen::HasMatchFields) { + if constexpr (::traits::pgen::HasMatchFields) { auto match_fields = pgen.MatchFields(time); CallMatchFields(domain.fields.em, domain.mesh.flds_bc(), @@ -110,7 +125,7 @@ namespace ntt { tags, range_min, range_max); - } else if constexpr (arch::traits::pgen::HasMatchFieldsInX1) { + } else if constexpr (::traits::pgen::HasMatchFieldsInX1) { auto match_fields = pgen.MatchFieldsInX1(time); CallMatchFields(domain.fields.em, domain.mesh.flds_bc(), @@ -124,7 +139,7 @@ namespace ntt { } } else if (dim == in::x2) { if constexpr (M::Dim == Dim::_2D or M::Dim == Dim::_3D) { - if constexpr (arch::traits::pgen::HasMatchFields) { + if constexpr (::traits::pgen::HasMatchFields) { auto match_fields = pgen.MatchFields(time); CallMatchFields( domain.fields.em, @@ -136,7 +151,7 @@ namespace ntt { tags, range_min, range_max); - } else if constexpr (arch::traits::pgen::HasMatchFieldsInX2) { + } else if constexpr (::traits::pgen::HasMatchFieldsInX2) { auto match_fields = pgen.MatchFieldsInX2(time); CallMatchFields( domain.fields.em, @@ -154,7 +169,7 @@ namespace ntt { } } else if (dim == in::x3) { if constexpr (M::Dim == Dim::_3D) { - if constexpr (arch::traits::pgen::HasMatchFields) { + if constexpr (::traits::pgen::HasMatchFields) { auto match_fields = pgen.MatchFields(time); CallMatchFields( domain.fields.em, @@ -166,7 +181,7 @@ namespace ntt { tags, range_min, range_max); - } else if constexpr (arch::traits::pgen::HasMatchFieldsInX3) { + } else if constexpr (::traits::pgen::HasMatchFieldsInX3) { auto match_fields = pgen.MatchFieldsInX3(time); CallMatchFields( domain.fields.em, @@ -185,15 +200,14 @@ namespace ntt { } } - template - requires metric::traits::HasD - void AxisFieldsIn(dir::direction_t direction, - Domain& domain, - BCTags tags) { + template + void AxisFieldsIn(const dir::direction_t& direction, + Domain& domain, + BCTags tags) { /** * axis boundaries */ - if constexpr (M::CoordType != Coord::Cart) { + if constexpr (M::CoordType != Coord::Cartesian) { raise::ErrorIf(direction.get_dim() != in::x2, "Invalid axis direction, should be x2", HERE); @@ -222,22 +236,20 @@ namespace ntt { } } - template - requires metric::traits::HasD and metric::traits::HasCoordType and - metric::traits::HasTransform_i - void FixedFieldsIn(dir::direction_t direction, - Domain& domain, - const PG& pgen, - const prm::Parameters& engine_params, - BCTags tags) { - if constexpr (arch::traits::pgen::HasFixFieldsConst) { + template PG> + void FixedFieldsIn(const dir::direction_t& direction, + Domain& domain, + const PG& pgen, + const prm::Parameters& engine_params, + BCTags tags) { + if constexpr (::traits::pgen::HasFixFieldsConst) { const auto time = engine_params.get("time"); /** * fixed field boundaries */ const auto sign = direction.get_sign(); const auto dim = direction.get_dim(); - raise::ErrorIf(dim != in::x1 and M::CoordType != Coord::Cart, + raise::ErrorIf(dim != in::x1 and M::CoordType != Coord::Cartesian, "Fixed BCs only implemented for x1 in " "non-cartesian coordinates", HERE); @@ -273,7 +285,7 @@ namespace ntt { comps.push_back(em::bx2); comps.push_back(em::bx3); } - raise::ErrorIf(M::CoordType != Coord::Cart and dim != in::x1, + raise::ErrorIf(M::CoordType != Coord::Cartesian and dim != in::x1, "FixedFields cannot be used for non-cartesian metric", HERE); for (const auto& comp : comps) { @@ -336,15 +348,14 @@ namespace ntt { } } - template - requires metric::traits::HasD - void PerfectConductorFieldsIn(dir::direction_t direction, - Domain& domain, - BCTags tags) { + template + void PerfectConductorFieldsIn(const dir::direction_t& direction, + Domain& domain, + BCTags tags) { /** * perfect conductor field boundaries */ - if constexpr (M::CoordType != Coord::Cart) { + if constexpr (M::CoordType != Coord::Cartesian) { (void)direction; (void)domain; (void)tags; @@ -459,19 +470,19 @@ namespace ntt { } } - template - void AtmosphereFieldsIn(dir::direction_t direction, - Domain& domain, - const M& global_metric, - const Grid& global_grid, - const PG& pgen, - const SimulationParams& params, - const prm::Parameters& engine_params, - BCTags tags) { + template PG> + void AtmosphereFieldsIn(const dir::direction_t& direction, + Domain& domain, + const M& global_metric, + const Grid& global_grid, + const PG& pgen, + const SimulationParams& params, + const prm::Parameters& engine_params, + BCTags tags) { /** * atmosphere field boundaries */ - if constexpr (arch::traits::pgen::HasAtmFields) { + if constexpr (::traits::pgen::HasAtmFields) { const auto time = engine_params.get("time"); const auto [sign, dim, xg_min, xg_max] = GetAtmosphereExtent(direction, global_metric, @@ -483,15 +494,15 @@ namespace ntt { boundaries_t incl_ghosts; for (auto d { 0u }; d < M::Dim; ++d) { if (d == dd) { - box.push_back({ xg_min, xg_max }); + box.emplace_back(xg_min, xg_max); if (sign > 0) { - incl_ghosts.push_back({ false, true }); + incl_ghosts.emplace_back(false, true); } else { - incl_ghosts.push_back({ true, false }); + incl_ghosts.emplace_back(true, false); } } else { box.push_back(Range::All); - incl_ghosts.push_back({ true, true }); + incl_ghosts.emplace_back(true, true); } } if (not domain.mesh.Intersects(box)) { @@ -518,7 +529,7 @@ namespace ntt { Kokkos::parallel_for( "AtmosphereBCFields", range, - kernel::bc::EnforcedBoundaries_kernel( + kernel::bc::EnforcedBoundaries_kernel( domain.fields.em, atm_fields, domain.mesh.metric, @@ -528,7 +539,7 @@ namespace ntt { Kokkos::parallel_for( "AtmosphereBCFields", range, - kernel::bc::EnforcedBoundaries_kernel( + kernel::bc::EnforcedBoundaries_kernel( domain.fields.em, atm_fields, domain.mesh.metric, @@ -541,7 +552,7 @@ namespace ntt { Kokkos::parallel_for( "AtmosphereBCFields", range, - kernel::bc::EnforcedBoundaries_kernel( + kernel::bc::EnforcedBoundaries_kernel( domain.fields.em, atm_fields, domain.mesh.metric, @@ -551,7 +562,7 @@ namespace ntt { Kokkos::parallel_for( "AtmosphereBCFields", range, - kernel::bc::EnforcedBoundaries_kernel( + kernel::bc::EnforcedBoundaries_kernel( domain.fields.em, atm_fields, domain.mesh.metric, @@ -567,7 +578,7 @@ namespace ntt { Kokkos::parallel_for( "AtmosphereBCFields", range, - kernel::bc::EnforcedBoundaries_kernel( + kernel::bc::EnforcedBoundaries_kernel( domain.fields.em, atm_fields, domain.mesh.metric, @@ -577,7 +588,7 @@ namespace ntt { Kokkos::parallel_for( "AtmosphereBCFields", range, - kernel::bc::EnforcedBoundaries_kernel( + kernel::bc::EnforcedBoundaries_kernel( domain.fields.em, atm_fields, domain.mesh.metric, @@ -597,19 +608,17 @@ namespace ntt { } } - template - requires metric::traits::HasD - void CustomFieldsIn(dir::direction_t direction, - Domain& domain, - BCTags tags) { + template + void CustomFieldsIn(const dir::direction_t& direction, + Domain& domain, + BCTags tags) { (void)direction; (void)domain; (void)tags; raise::Error("Custom boundaries not implemented", HERE); } - template - requires metric::traits::HasD + template PG> void FieldBoundaries(Domain& domain, const M& global_metric, const Grid& global_grid, diff --git a/src/engines/srpic/fieldsolvers.h b/src/engines/srpic/fieldsolvers.h index bc68cee3a..3a2fb4479 100644 --- a/src/engines/srpic/fieldsolvers.h +++ b/src/engines/srpic/fieldsolvers.h @@ -1,14 +1,25 @@ +/** + * @file engines/srpic/fieldsolvers.h + * @brief Field solver routines (Faraday, Ampere) for the SRPIC engine + * @implements + * - ntt::srpic::Faraday<> -> void + * - ntt::srpic::Ampere<> -> void + * - ntt::srpic::CurrentsAmpere<> -> void + * @namespaces: + * - ntt::srpic:: + */ + #ifndef ENGINES_SRPIC_FIELDSOLVERS_H #define ENGINES_SRPIC_FIELDSOLVERS_H #include "enums.h" #include "global.h" +#include "traits/pgen.h" #include "utils/log.h" #include "utils/numeric.h" #include "utils/param_container.h" -#include "archetypes/traits.h" #include "engines/srpic/utils.h" #include "framework/domain/domain.h" #include "framework/parameters/parameters.h" @@ -17,10 +28,12 @@ #include "kernels/faraday_mink.hpp" #include "kernels/faraday_sr.hpp" +#include "engines/engine.hpp" + namespace ntt { namespace srpic { - template + template void Faraday(Domain& domain, const prm::Parameters& engine_params, const SimulationParams& params, @@ -32,7 +45,7 @@ namespace ntt { params.template get( "algorithms.timestep.correction") * dt; - if constexpr (M::CoordType == Coord::Cart) { + if constexpr (M::CoordType == Coord::Cartesian) { // minkowski case const auto dx = math::sqrt(domain.mesh.metric.template h_<1, 1>({})); const auto deltax = params.template get( @@ -85,7 +98,7 @@ namespace ntt { } } - template + template void Ampere(Domain& domain, const prm::Parameters& engine_params, const SimulationParams& params, @@ -98,7 +111,7 @@ namespace ntt { "algorithms.timestep.correction") * dt; auto range = RangeWithAxisBCs(domain); - if constexpr (M::CoordType == Coord::Cart) { + if constexpr (M::CoordType == Coord::Cartesian) { // minkowski case const auto dx = math::sqrt(domain.mesh.metric.template h_<1, 1>({})); real_t coeff1, coeff2; @@ -126,7 +139,7 @@ namespace ntt { } } - template + template PG> void CurrentsAmpere(Domain& domain, const prm::Parameters& engine_params, const SimulationParams& params, @@ -137,12 +150,12 @@ namespace ntt { const auto q0 = params.template get("scales.q0"); const auto n0 = params.template get("scales.n0"); const auto B0 = params.template get("scales.B0"); - if constexpr (M::CoordType == Coord::Cart) { + if constexpr (M::CoordType == Coord::Cartesian) { // minkowski case const auto V0 = params.template get("scales.V0"); const auto ppc0 = params.template get("particles.ppc0"); const auto coeff = -dt * q0 / (B0 * V0); - if constexpr (arch::traits::pgen::HasExtCurrent) { + if constexpr (::traits::pgen::HasExtCurrent) { const std::vector xmin { domain.mesh.extent(in::x1).first, domain.mesh.extent(in::x2).first, domain.mesh.extent(in::x3).first }; diff --git a/src/engines/srpic/particle_pusher.h b/src/engines/srpic/particle_pusher.h index a63cd4134..42b26780f 100644 --- a/src/engines/srpic/particle_pusher.h +++ b/src/engines/srpic/particle_pusher.h @@ -1,282 +1,38 @@ +/** + * @file engines/srpic/particle_pusher.h + * @brief Particle pusher routines for the SRPIC engine + * @implements + * - ntt::srpic::ParticlePush<> -> void + * @namespaces: + * - ntt::srpic:: + */ + #ifndef ENGINES_SRPIC_PARTICLE_PUSHER_H #define ENGINES_SRPIC_PARTICLE_PUSHER_H #include "enums.h" #include "global.h" -#include "utils/comparators.h" +#include "traits/metric.h" +#include "traits/policies.h" #include "utils/log.h" #include "utils/numeric.h" #include "utils/param_container.h" -#include "metrics/traits.h" - -#include "archetypes/traits.h" #include "engines/srpic/utils.h" #include "framework/domain/domain.h" #include "framework/domain/grid.h" #include "framework/parameters/parameters.h" -#include "kernels/emission/compton.hpp" -#include "kernels/emission/emission.hpp" -#include "kernels/emission/synchrotron.hpp" -#include "kernels/emission/traits.h" -#include "kernels/particle_pusher_sr.hpp" +#include "kernels/pushers/context.h" +#include "kernels/pushers/sr.hpp" +#include "kernels/pushers/sr_policies.h" + +#include "engines/engine.hpp" namespace ntt { namespace srpic { - template - void CallPusher_WithExternalFieldFlag( - Domain& domain, - const SimulationParams& params, - const kernel::sr::PusherParams& pusher_params, - kernel::sr::PusherArrays& pusher_arrays, - EmissionTypeFlag emission_policy_flag, - const range_t& range, - const ndfield_t& EB, - const M& metric, - const PG& pgen, - const F& external_fields) { - auto get_custom_prtl_update = [&]() { - if constexpr ( - arch::traits::pgen::HasCustomPrtlUpdate) { - return pgen.CustomParticleUpdate(pusher_params.time, - pusher_params.species_index, - domain); - } else { - return kernel::sr::NoCustomPrtlUpdate_t {}; - } - }; - const auto custom_prtl_update = get_custom_prtl_update(); - - if (emission_policy_flag == EmissionType::NONE) { - const auto no_emission = kernel::NoEmissionPolicy_t {}; - Kokkos::parallel_for( - "ParticlePusher", - range, - kernel::sr::Pusher_kernel( - pusher_params, - pusher_arrays, - EB, - metric, - external_fields, - no_emission, - custom_prtl_update)); - } else if (emission_policy_flag == EmissionType::SYNCHROTRON) { - const auto photon_species = params.get( - "radiation.emission.synchrotron.photon_species"); - raise::ErrorIf(photon_species > domain.species.size(), - "Invalid photon_species for Synchrotron emission", - HERE); - auto& emitted_species = domain.species[photon_species - 1]; - raise::ErrorIf(not cmp::AlmostZero_host(emitted_species.mass()), - "Emitted photon species must have zero mass", - HERE); - raise::ErrorIf(not cmp::AlmostZero_host(emitted_species.charge()), - "Emitted photon species must have zero charge", - HERE); - const auto emission_policy = kernel::emission::Synchrotron( - emitted_species, - photon_species, - pusher_params.mass, - pusher_params.charge, - pusher_params.radiative_drag_flags, - domain.index(), - params, - domain.random_pool()); - static_assert( - kernel::traits::emission::IsValid, M>, - "Synchrotron emission policy does not satisfy the required " - "interface"); - Kokkos::parallel_for( - "ParticlePusher", - range, - kernel::sr::Pusher_kernel( - pusher_params, - pusher_arrays, - EB, - metric, - external_fields, - emission_policy, - custom_prtl_update)); - const auto n_inj = emission_policy.numbers_injected(); - raise::ErrorIf(n_inj.size() != 1, - "Synchrotron emission should only inject one species", - HERE); - domain.species[photon_species - 1].set_npart( - emitted_species.npart() + n_inj[0]); - domain.species[photon_species - 1].set_counter( - emitted_species.counter() + n_inj[0]); - } else if (emission_policy_flag == EmissionType::COMPTON) { - const auto photon_species = params.get( - "radiation.emission.compton.photon_species"); - raise::ErrorIf(photon_species > domain.species.size(), - "Invalid photon_species for Compton emission", - HERE); - auto& emitted_species = domain.species[photon_species - 1]; - raise::ErrorIf(not cmp::AlmostZero_host(emitted_species.mass()), - "Emitted photon species must have zero mass", - HERE); - raise::ErrorIf(not cmp::AlmostZero_host(emitted_species.charge()), - "Emitted photon species must have zero charge", - HERE); - const auto emission_policy = kernel::emission::Compton( - emitted_species, - photon_species, - pusher_params.mass, - pusher_params.charge, - pusher_params.radiative_drag_flags, - domain.index(), - params, - domain.random_pool()); - static_assert( - kernel::traits::emission::IsValid, M>, - "Compton emission policy does not satisfy the required interface"); - Kokkos::parallel_for( - "ParticlePusher", - range, - kernel::sr::Pusher_kernel( - pusher_params, - pusher_arrays, - EB, - metric, - external_fields, - emission_policy, - custom_prtl_update)); - const auto n_inj = emission_policy.numbers_injected(); - raise::ErrorIf(n_inj.size() != 1, - "Compton emission should only inject one species", - HERE); - domain.species[photon_species - 1].set_npart( - emitted_species.npart() + n_inj[0]); - domain.species[photon_species - 1].set_counter( - emitted_species.counter() + n_inj[0]); - } else if (emission_policy_flag == EmissionType::CUSTOM) { - if constexpr (arch::traits::pgen::HasEmissionPolicy) { - const auto emission_policy = pgen.EmissionPolicy(pusher_params.time, - pusher_params.species_index, - domain); - static_assert( - kernel::traits::emission::IsValid, - "Custom emission policy does not satisfy the required " - "interface"); - Kokkos::parallel_for( - "ParticlePusher", - range, - kernel::sr::Pusher_kernel( - pusher_params, - pusher_arrays, - EB, - metric, - external_fields, - emission_policy, - custom_prtl_update)); - const auto emitted_species = emission_policy.emitted_species_indices(); - const auto n_inj = emission_policy.numbers_injected(); - raise::ErrorIf(emitted_species.size() != n_inj.size(), - "Emission policy emitted_species_indices and " - "numbers_injected must have the same size", - HERE); - for (auto i = 0u; i < emitted_species.size(); ++i) { - const auto sp_idx = emitted_species[i]; - raise::ErrorIf(sp_idx > domain.species.size(), - "Invalid emitted species index from custom " - "emission policy", - HERE); - domain.species[sp_idx - 1].set_npart( - domain.species[sp_idx - 1].npart() + n_inj[i]); - domain.species[sp_idx - 1].set_counter( - domain.species[sp_idx - 1].counter() + n_inj[i]); - } - } else { - raise::Error("Custom emission policy flag is set but problem " - "generator does not define an emission policy", - HERE); - } - } else { - raise::Error("Unrecognized emission policy flag", HERE); - } - } - - template - void CallPusher_WithAtmFlag(Domain& domain, - const SimulationParams& params, - const kernel::sr::PusherParams& pusher_params, - kernel::sr::PusherArrays& pusher_arrays, - EmissionTypeFlag emission_policy_flag, - const range_t& range, - const ndfield_t& EB, - const M& metric, - const PG& pgen) { - if constexpr (arch::traits::pgen::HasExternalFields) { - auto [apply_extfields, - external_fields] = pgen.ExternalFields(pusher_params.time, - pusher_params.species_index, - domain); - if (apply_extfields) { - CallPusher_WithExternalFieldFlag( - domain, - params, - pusher_params, - pusher_arrays, - emission_policy_flag, - range, - domain.fields.em, - domain.mesh.metric, - pgen, - external_fields); - return; - } - } - CallPusher_WithExternalFieldFlag( - domain, - params, - pusher_params, - pusher_arrays, - emission_policy_flag, - range, - domain.fields.em, - domain.mesh.metric, - pgen, - kernel::sr::NoField_t {}); - } - - template - void CallPusher(Domain& domain, - const SimulationParams& params, - const kernel::sr::PusherParams& pusher_params, - kernel::sr::PusherArrays& pusher_arrays, - EmissionTypeFlag emission_policy_flag, - const range_t& range, - const ndfield_t& EB, - const M& metric, - const PG& pgen, - bool has_atmosphere) { - if (not has_atmosphere) { - CallPusher_WithAtmFlag(domain, - params, - pusher_params, - pusher_arrays, - emission_policy_flag, - range, - domain.fields.em, - domain.mesh.metric, - pgen); - } else { - CallPusher_WithAtmFlag(domain, - params, - pusher_params, - pusher_arrays, - emission_policy_flag, - range, - domain.fields.em, - domain.mesh.metric, - pgen); - } - } - - template - requires metric::traits::HasD + template PG> void ParticlePush(Domain& domain, const Grid& global_grid, const M& global_metric, @@ -334,61 +90,99 @@ namespace ntt { species.npart()), HERE); - kernel::sr::PusherParams pusher_params {}; - pusher_params.species_index = species.index(); - pusher_params.pusher_flags = species.pusher(); - pusher_params.radiative_drag_flags = species.radiative_drag_flags(); - pusher_params.mass = species.mass(); - pusher_params.charge = species.charge(); - pusher_params.time = time; - pusher_params.dt = dt; - pusher_params.omegaB0 = params.template get("scales.omegaB0"); - pusher_params.ni1 = domain.mesh.n_active(in::x1); - pusher_params.ni2 = domain.mesh.n_active(in::x2); - pusher_params.ni3 = domain.mesh.n_active(in::x3); - pusher_params.boundaries = domain.mesh.prtl_bc(); - - if (has_atmosphere) { - pusher_params.atmosphere_params.set("gx1", gx1); - pusher_params.atmosphere_params.set("gx2", gx2); - pusher_params.atmosphere_params.set("gx3", gx3); - pusher_params.atmosphere_params.set("x_surf", x_surf); - pusher_params.atmosphere_params.set("ds", ds); - } + kernel::sr::PusherContext pusher_ctx { + species.index(), + species.pusher(), + species.radiative_drag_flags(), + species.mass(), + species.charge(), + time, + dt, + params.template get("scales.omegaB0"), + static_cast(domain.mesh.n_active(in::x1)), + static_cast(domain.mesh.n_active(in::x2)), + static_cast(domain.mesh.n_active(in::x3)) + }; if (species.pusher() & ParticlePusher::GCA) { - pusher_params.gca_params.set( - "larmor_max", - params.template get("algorithms.gca.larmor_max")); - pusher_params.gca_params.set( - "e_ovr_b_max", + pusher_ctx.gca = kernel::sr::PusherGCAContext( + params.template get("algorithms.gca.larmor_max"), params.template get("algorithms.gca.e_ovr_b_max")); } + if (has_atmosphere) { + pusher_ctx.atmosphere = kernel::sr::PusherAtmosphereContext(gx1, + gx2, + gx3, + x_surf, + ds); + } + if (species.radiative_drag_flags() & RadiativeDrag::SYNCHROTRON) { - pusher_params.radiative_drag_params.set( - "synchrotron_gamma_rad", - params.template get( - "radiation.drag.synchrotron.gamma_rad")); + pusher_ctx.synchrotron_drag = kernel::sr::PusherSynchrotronDragContext( + dt, + pusher_ctx.omegaB0, + params.template get("radiation.drag.synchrotron.gamma_rad"), + species.mass()); } if (species.radiative_drag_flags() & RadiativeDrag::COMPTON) { - pusher_params.radiative_drag_params.set( - "compton_gamma_rad", - params.template get("radiation.drag.compton.gamma_rad")); + pusher_ctx.compton_drag = kernel::sr::PusherComptonDragContext( + dt, + pusher_ctx.omegaB0, + params.template get("radiation.drag.compton.gamma_rad"), + species.mass()); } + + auto pusher_boundaries = kernel::sr::PusherBoundaries { + domain.mesh.prtl_bc() + }; + auto pusher_arrays = species.PusherKernelArrays(); - CallPusher(domain, - params, - pusher_params, - pusher_arrays, - species.emission_policy_flag(), - species.rangeActiveParticles(), - domain.fields.em, - domain.mesh.metric, - pgen, - has_atmosphere); + kernel::sr::MakePusherPolicy( + pgen, + domain, + params, + pusher_ctx, + species.emission_policy_flag(), + has_atmosphere, + [&](const auto& policies) { + using policy_t = std::decay_t; + Kokkos::parallel_for( + "ParticlePusher", + species.rangeActiveParticles(), + kernel::sr::Pusher_kernel { pusher_ctx, + pusher_boundaries, + pusher_arrays, + domain.fields.em, + domain.mesh.metric, + policies }); + // if emission takes place, update the npart and counter of emitted species + if constexpr ( + not ::traits::emission::IsNoPolicy) { + const auto& emission_policy = policies.emission_policy; + const auto emitted_species = emission_policy.emitted_species_indices(); + const auto n_inj = emission_policy.numbers_injected(); + raise::ErrorIf(emitted_species.size() != n_inj.size(), + "Emission policy emitted_species_indices and " + "numbers_injected must have the same size", + HERE); + for (auto i = 0u; i < emitted_species.size(); ++i) { + const auto sp_idx = emitted_species[i]; + raise::ErrorIf(sp_idx > domain.species.size(), + "Invalid emitted species index from custom " + "emission policy", + HERE); + domain.species[sp_idx - 1].set_npart( + domain.species[sp_idx - 1].npart() + n_inj[i]); + domain.species[sp_idx - 1].set_counter( + domain.species[sp_idx - 1].counter() + n_inj[i]); + } + } + }); } } diff --git a/src/engines/srpic/particles_bcs.h b/src/engines/srpic/particles_bcs.h index 5d444385f..d7b3b9a47 100644 --- a/src/engines/srpic/particles_bcs.h +++ b/src/engines/srpic/particles_bcs.h @@ -1,3 +1,13 @@ +/** + * @file engines/srpic/particles_bcs.h + * @brief Particle boundary condition routines for the SRPIC engine + * @implements + * - ntt::srpic::AtmosphereParticlesIn<> -> void + * - ntt::srpic::ParticleInjector<> -> void + * @namespaces: + * - ntt::srpic:: + */ + #ifndef ENGINES_SRPIC_PARTICLES_BCS_H #define ENGINES_SRPIC_PARTICLES_BCS_H @@ -5,10 +15,9 @@ #include "global.h" #include "arch/directions.h" +#include "traits/metric.h" #include "utils/numeric.h" -#include "metrics/traits.h" - #include "archetypes/energy_dist.h" #include "archetypes/particle_injector.h" #include "archetypes/spatial_dist.h" @@ -21,9 +30,8 @@ namespace ntt { namespace srpic { - template - requires metric::traits::HasD && metric::traits::HasCoordType - void AtmosphereParticlesIn(dir::direction_t direction, + template + void AtmosphereParticlesIn(const dir::direction_t& direction, Metadomain& metadomain, Domain& domain, const SimulationParams& params, @@ -49,7 +57,7 @@ namespace ntt { Kokkos::deep_copy(domain.fields.bckp, ZERO); auto scatter_bckp = Kokkos::Experimental::create_scatter_view( domain.fields.bckp); - const auto use_weights = M::CoordType != Coord::Cart; + const auto use_weights = M::CoordType != Coord::Cartesian; const auto ni2 = domain.mesh.n_active(in::x2); const auto inv_n0 = ONE / params.template get("scales.n0"); @@ -101,194 +109,54 @@ namespace ntt { metadomain.SynchronizeFields(domain, Comm::Bckp, { 0, 1 }); } - const auto maxwellian = arch::Maxwellian { - domain.mesh.metric, + const auto maxwellian = arch::energy_dist::Maxwellian { domain.random_pool(), temp }; + auto do_inject = [&]() { + auto target_density = + arch::AtmosphereDensityProfile { nmax, + height, + x_surf, + ds }; + const auto spatial_dist = + arch::spatial_dist::Replenish { + domain.mesh.metric, + domain.fields.bckp, + 0, + target_density, + nmax + }; + arch::InjectNonUniform( + params, + domain, + { species.first, species.second }, + { maxwellian, maxwellian }, + spatial_dist, + nmax, + use_weights); + }; + if (dim == in::x1) { - if (sign > 0) { - auto target_density = - arch::AtmosphereDensityProfile { - nmax, - height, - x_surf, - ds - }; - const auto spatial_dist = - arch::Replenish { - domain.mesh.metric, - domain.fields.bckp, - 0, - target_density, - nmax - }; - arch::InjectNonUniform( - params, - domain, - { species.first, species.second }, - { maxwellian, maxwellian }, - spatial_dist, - nmax, - use_weights); - } else { - auto target_density = - arch::AtmosphereDensityProfile { - nmax, - height, - x_surf, - ds - }; - const auto spatial_dist = - arch::Replenish { - domain.mesh.metric, - domain.fields.bckp, - 0, - target_density, - nmax - }; - arch::InjectNonUniform( - params, - domain, - { species.first, species.second }, - { maxwellian, maxwellian }, - spatial_dist, - nmax, - use_weights); - } + sign > 0 ? do_inject.template operator()() + : do_inject.template operator()(); } else if (dim == in::x2) { - if (sign > 0) { - auto target_density = - arch::AtmosphereDensityProfile { - nmax, - height, - x_surf, - ds - }; - const auto spatial_dist = - arch::Replenish { - domain.mesh.metric, - domain.fields.bckp, - 0, - target_density, - nmax - }; - arch::InjectNonUniform( - params, - domain, - { species.first, species.second }, - { maxwellian, maxwellian }, - spatial_dist, - nmax, - use_weights); - } else { - auto target_density = - arch::AtmosphereDensityProfile { - nmax, - height, - x_surf, - ds - }; - const auto spatial_dist = - arch::Replenish { - domain.mesh.metric, - domain.fields.bckp, - 0, - target_density, - nmax - }; - arch::InjectNonUniform( - params, - domain, - { species.first, species.second }, - { maxwellian, maxwellian }, - spatial_dist, - nmax, - use_weights); - } + sign > 0 ? do_inject.template operator()() + : do_inject.template operator()(); } else if (dim == in::x3) { - if (sign > 0) { - auto target_density = - arch::AtmosphereDensityProfile { - nmax, - height, - x_surf, - ds - }; - const auto spatial_dist = - arch::Replenish { - domain.mesh.metric, - domain.fields.bckp, - 0, - target_density, - nmax - }; - arch::InjectNonUniform( - params, - domain, - { species.first, species.second }, - { maxwellian, maxwellian }, - spatial_dist, - nmax, - use_weights); - } else { - auto target_density = - arch::AtmosphereDensityProfile { - nmax, - height, - x_surf, - ds - }; - const auto spatial_dist = - arch::Replenish { - domain.mesh.metric, - domain.fields.bckp, - 0, - target_density, - nmax - }; - arch::InjectNonUniform( - params, - domain, - { species.first, species.second }, - { maxwellian, maxwellian }, - spatial_dist, - nmax, - use_weights); - } + sign > 0 ? do_inject.template operator()() + : do_inject.template operator()(); } else { raise::Error("Invalid dimension", HERE); } - return; } - template - requires metric::traits::HasD + template void ParticleInjector(Metadomain& metadomain, Domain& domain, const SimulationParams& params, diff --git a/src/engines/srpic/srpic.hpp b/src/engines/srpic/srpic.hpp index 246f7f056..94a8949c1 100644 --- a/src/engines/srpic/srpic.hpp +++ b/src/engines/srpic/srpic.hpp @@ -1,21 +1,21 @@ /** - * @file engines/srpic.hpp - * @brief Simulation engien class which specialized on SRPIC + * @file engines/srpic/srpic.hpp + * @brief Simulation engine class which specializes on SRPIC * @implements * - ntt::SRPICEngine<> : ntt::Engine<> * @cpp: * - srpic.cpp * @namespaces: * - ntt:: - * @macros: */ -#ifndef ENGINES_SRPIC_SRPIC_H -#define ENGINES_SRPIC_SRPIC_H +#ifndef ENGINES_SRPIC_SRPIC_HPP +#define ENGINES_SRPIC_SRPIC_HPP #include "enums.h" #include "global.h" +#include "traits/metric.h" #include "utils/numeric.h" #include "utils/timer.h" @@ -24,7 +24,6 @@ #include "engines/srpic/fieldsolvers.h" #include "engines/srpic/particle_pusher.h" #include "engines/srpic/particles_bcs.h" -#include "engines/traits.h" #include "framework/domain/domain.h" #include "framework/parameters/parameters.h" @@ -37,8 +36,7 @@ namespace ntt { - template - requires traits::engine::IsCompatibleWithSRPICEngine + template class SRPICEngine : public Engine { using base_t = Engine; @@ -62,7 +60,7 @@ namespace ntt { SRPICEngine(const SimulationParams& params) : base_t { params } {} - ~SRPICEngine() = default; + ~SRPICEngine() override = default; void step_forward(timer::Timers& timers, domain_t& dom) override { const auto fieldsolver_enabled = m_params.template get( @@ -192,4 +190,4 @@ namespace ntt { } // namespace ntt -#endif // ENGINES_SRPIC_SRPIC_H +#endif // ENGINES_SRPIC_SRPIC_HPP diff --git a/src/engines/srpic/utils.h b/src/engines/srpic/utils.h index 9779e55e4..0f8926a43 100644 --- a/src/engines/srpic/utils.h +++ b/src/engines/srpic/utils.h @@ -1,3 +1,12 @@ +/** + * @file engines/srpic/utils.h + * @brief Utility functions used by the SRPIC engine + * @implements + * - ntt::srpic::GetAtmosphereExtent<> -> std::tuple + * @namespaces: + * - ntt::srpic:: + */ + #ifndef ENGINES_SRPIC_UTILS_H #define ENGINES_SRPIC_UTILS_H @@ -5,10 +14,9 @@ #include "global.h" #include "arch/directions.h" +#include "traits/metric.h" #include "utils/numeric.h" -#include "metrics/traits.h" - #include "framework/domain/domain.h" #include "framework/domain/grid.h" #include "framework/parameters/parameters.h" @@ -36,12 +44,11 @@ namespace ntt { * * in this case the function returns { -1, in::x1, xg_min, xg_max } */ - template - requires metric::traits::HasD && metric::traits::HasConvert - auto GetAtmosphereExtent(dir::direction_t direction, - const M& global_metric, - const Grid& global_grid, - const SimulationParams& params) + template + auto GetAtmosphereExtent(const dir::direction_t& direction, + const M& global_metric, + const Grid& global_grid, + const SimulationParams& params) -> std::tuple { const auto sign = direction.get_sign(); const auto dim = direction.get_dim(); @@ -49,7 +56,7 @@ namespace ntt { "algorithms.current_filters") + 2; const auto buffer_ncells = min_buff > 5 ? min_buff : 5; - if (M::CoordType != Coord::Cart and (dim != in::x1 or sign > 0)) { + if (M::CoordType != Coord::Cartesian and (dim != in::x1 or sign > 0)) { raise::Error("For non-cartesian coordinates atmosphere BCs is " "possible only in -x1 (@ rmin)", HERE); @@ -93,11 +100,11 @@ namespace ntt { return { sign, dim, xg_min, xg_max }; } - template + template auto RangeWithAxisBCs(const Domain& domain) -> range_t { auto range = domain.mesh.rangeActiveCells(); - if constexpr (M::CoordType != Coord::Cart) { + if constexpr (M::CoordType != Coord::Cartesian) { /** * @brief taking one extra cell in the x2 direction if AXIS BCs */ diff --git a/src/engines/traits.h b/src/engines/traits.h deleted file mode 100644 index 4050049b5..000000000 --- a/src/engines/traits.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file engine/traits.h - * @brief Defines a set of traits to check if an engine class satisfies certain - * conditions - * @implements - * - ntt::traits::engine::HasRun<> - checks if an engine has a run() method - * - ntt::traits::engine::IsCompatibleWithEngine<> - checks if a metric and - * pgen are compatible with a given simulation engine - * - ntt::traits::engine::IsCompatibleWithSRPICEngine<> - checks if a metric - * and pgen are compatible with the SRPIC engine - * - ntt::traits::engine::IsCompatibleWithGRPICEngine<> - checks if a metric - * and pgen are compatible with the GRPIC engine - * @namespaces: - * - ntt::traits::engine:: - */ -#ifndef ENGINES_TRAITS_H -#define ENGINES_TRAITS_H - -#include "metrics/traits.h" - -#include "archetypes/traits.h" - -#include - -namespace ntt { - namespace traits { - namespace engine { - - template class PG> - concept IsCompatibleWithEngine = - metric::traits::HasD and - arch::traits::pgen::check_compatibility::value(PG::engines) and - arch::traits::pgen::check_compatibility::value( - PG::metrics) and - arch::traits::pgen::check_compatibility::value(PG::dimensions); - - template class PG> - concept IsCompatibleWithSRPICEngine = - IsCompatibleWithEngine && - metric::traits::HasH_ij && metric::traits::HasConvert_i && - metric::traits::HasSqrtH_ij; - - template class PG> - concept IsCompatibleWithGRPICEngine = - IsCompatibleWithEngine; - - template - concept HasRun = requires(E& engine) { - { engine.run() } -> std::same_as; - }; - - } // namespace engine - } // namespace traits -} // namespace ntt - -#endif // ENGINES_TRAITS_H diff --git a/src/entity.cpp b/src/entity.cpp index 9afb37cb2..c05c6c18a 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -1,16 +1,20 @@ #include "enums.h" +#include "global.h" #include "arch/traits.h" +#include "traits/metric.h" +#include "traits/pgen.h" #include "utils/error.h" -#include "archetypes/traits.h" #include "framework/simulation.h" #include "framework/specialization_registry.h" -#include "engines/grpic.hpp" +#include "engines/grpic/grpic.hpp" #include "engines/srpic/srpic.hpp" #include "pgen.hpp" +#include +#include #include namespace ntt { @@ -19,23 +23,27 @@ namespace ntt { template <> struct EngineSelector { - template + template using type = SRPICEngine; }; template <> struct EngineSelector { - template + template using type = GRPICEngine; }; } // namespace ntt +template +static constexpr bool is_compatible(::traits::pgen::compatible_with) { + return ((N == Is) || ...); +} + template class M, Dimension D> static constexpr bool should_compile { - arch::traits::pgen::check_compatibility::value(user::PGen>::engines) && - arch::traits::pgen::check_compatibility::MetricType>::value( - user::PGen>::metrics) && - arch::traits::pgen::check_compatibility::value(user::PGen>::dimensions) + is_compatible(user::PGen>::engines) and + is_compatible::MetricType>(user::PGen>::metrics) and + is_compatible(user::PGen>::dimensions) }; template class M, Dimension D> @@ -45,47 +53,50 @@ void dispatch_engine(ntt::Simulation& sim) { } else if constexpr (S == SimEngine::GRPIC) { sim.run::template type, M, D>(); } else { - static_assert( - ::traits::always_false>::value, - "Unsupported engine"); + static_assert(::traits::always_false>::value, + "Unsupported engine"); } } auto main(int argc, char* argv[]) -> int { - ntt::Simulation sim { argc, argv }; - - auto matched = false; - auto launched = false; - - ntt::for_each_specialization([&](auto spec) { - using Spec = decltype(spec); - const auto requested_e = sim.requested_engine(); - const auto requested_m = sim.requested_metric(); - const auto requested_d = sim.requested_dimension(); - - if (requested_e == Spec::engine && requested_m == Spec::metric && - requested_d == Spec::dimension) { - matched = true; - if constexpr ( - should_compile) { - dispatch_engine( - sim); - launched = true; - } else { - raise::Fatal( - "Requested configuration is not available for this problem generator", - HERE); + try { + ntt::Simulation sim { argc, argv }; + + auto matched = false; + auto launched = false; + ntt::for_each_specialization([&](auto spec) { + using Spec = decltype(spec); + const auto requested_e = sim.requested_engine(); + const auto requested_m = sim.requested_metric(); + const auto requested_d = sim.requested_dimension(); + + if (requested_e == Spec::engine && requested_m == Spec::metric && + requested_d == Spec::dimension) { + matched = true; + if constexpr ( + should_compile) { + dispatch_engine( + sim); + launched = true; + } else { + raise::Fatal("Requested configuration is not available for this " + "problem generator", + HERE); + } } + }); + + if (not matched) { + raise::Fatal("Invalid engine, metric, or dimension combination", HERE); } - }); - if (not matched) { - raise::Fatal("Invalid engine, metric, or dimension combination", HERE); - } + if (not launched) { + raise::Fatal("Requested combination is not enabled in this build", HERE); + } - if (not launched) { - raise::Fatal("Requested combination is not enabled in this build", HERE); + return 0; + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << '\n'; + return 1; } - - return 0; } diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index df2bf4c69..032e507fb 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -74,7 +74,7 @@ add_library(ntt_framework ${SOURCES}) set(libs ntt_global ntt_metrics ntt_kernels ntt_output) add_dependencies(ntt_framework ${libs}) target_link_libraries(ntt_framework PUBLIC ${libs}) -if (NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") +if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "AppleClang") target_link_libraries(ntt_framework PRIVATE stdc++fs) endif() @@ -82,3 +82,4 @@ target_include_directories( ntt_framework PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../ INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/../) +target_precompile_headers(ntt_framework REUSE_FROM ntt_global) diff --git a/src/framework/containers/fields.cpp b/src/framework/containers/fields.cpp index 7202ff282..6d379ee34 100644 --- a/src/framework/containers/fields.cpp +++ b/src/framework/containers/fields.cpp @@ -46,10 +46,10 @@ namespace ntt { } } - template struct Fields; - template struct Fields; - template struct Fields; - template struct Fields; - template struct Fields; + template struct Fields; + template struct Fields; + template struct Fields; + template struct Fields; + template struct Fields; } // namespace ntt diff --git a/src/framework/containers/fields_io.cpp b/src/framework/containers/fields_io.cpp index 8c7029a7e..9cf5f8806 100644 --- a/src/framework/containers/fields_io.cpp +++ b/src/framework/containers/fields_io.cpp @@ -9,10 +9,6 @@ #include -#if defined(MPI_ENABLED) - #include -#endif - #include namespace ntt { diff --git a/src/framework/containers/particles.cpp b/src/framework/containers/particles.cpp index 7682718a3..aaaede0e2 100644 --- a/src/framework/containers/particles.cpp +++ b/src/framework/containers/particles.cpp @@ -6,6 +6,7 @@ #include "arch/kokkos_aliases.h" #include "framework/containers/species.h" +#include "kernels/pushers/context.h" #include #include @@ -79,15 +80,14 @@ namespace ntt { pld_i = array_t { label + "_pld_i", maxnpart, npld_i }; } - if ((D == Dim::_2D) && (C != Coord::Cart)) { + if ((D == Dim::_2D) && (C != Coord::Cartesian)) { phi = array_t { label + "_phi", maxnpart }; } } template - auto Particles::PusherKernelArrays() -> kernel::sr::PusherArrays { - kernel::sr::PusherArrays pusher_arrays {}; - pusher_arrays.sp = index(); + auto Particles::PusherKernelArrays() -> kernel::PusherArrays { + kernel::PusherArrays pusher_arrays { index() }; pusher_arrays.i1 = i1; pusher_arrays.i2 = i2; pusher_arrays.i3 = i3; @@ -109,12 +109,12 @@ namespace ntt { return pusher_arrays; } - template struct Particles; - template struct Particles; - template struct Particles; - template struct Particles; - template struct Particles; - template struct Particles; - template struct Particles; + template struct Particles; + template struct Particles; + template struct Particles; + template struct Particles; + template struct Particles; + template struct Particles; + template struct Particles; } // namespace ntt diff --git a/src/framework/containers/particles.h b/src/framework/containers/particles.h index c3ee1c1b5..45e38228e 100644 --- a/src/framework/containers/particles.h +++ b/src/framework/containers/particles.h @@ -8,6 +8,8 @@ * - particles_io.cpp * - particles_comm.cpp * - particles_sort.cpp + * @namespaces: + * - ntt:: * @macros: * - MPI_ENABLED */ @@ -20,12 +22,13 @@ #include "arch/directions.h" #include "arch/kokkos_aliases.h" +#include "traits/metric.h" #include "utils/error.h" #include "utils/formatting.h" #include "framework/containers/species.h" #include "framework/domain/grid.h" -#include "kernels/particle_pusher_sr.hpp" +#include "kernels/pushers/context.h" #include @@ -140,7 +143,7 @@ namespace ntt { * @brief Loop over all active particles * @returns A 1D Kokkos range policy of size of `npart` */ - inline auto rangeActiveParticles() const -> range_t { + auto rangeActiveParticles() const -> range_t { return CreateParticleRangePolicy(0u, npart()); } @@ -148,7 +151,7 @@ namespace ntt { * @brief Loop over all particles * @returns A 1D Kokkos range policy of size of `npart` */ - inline auto rangeAllParticles() const -> range_t { + auto rangeAllParticles() const -> range_t { return CreateParticleRangePolicy(0u, maxnpart()); } @@ -275,7 +278,7 @@ namespace ntt { * @brief Get the arrays required for the particle pusher kernel * @returns The struct of arrays for the particle pusher kernel */ - auto PusherKernelArrays() -> kernel::sr::PusherArrays; + auto PusherKernelArrays() -> kernel::PusherArrays; #if defined(MPI_ENABLED) /** @@ -298,7 +301,7 @@ namespace ntt { #if defined(OUTPUT_ENABLED) void OutputDeclare(adios2::IO&) const; - template + template void OutputWrite(adios2::IO&, adios2::Engine&, npart_t, diff --git a/src/framework/containers/particles_comm.cpp b/src/framework/containers/particles_comm.cpp index 1cf17efef..8fe9d1ec5 100644 --- a/src/framework/containers/particles_comm.cpp +++ b/src/framework/containers/particles_comm.cpp @@ -224,7 +224,7 @@ namespace ntt { // number of arrays of each type to send/recv const unsigned short NREALS = 4 + static_cast( - D == Dim::_2D and C != Coord::Cart); + D == Dim::_2D and C != Coord::Cartesian); const unsigned short NINTS = 2 * static_cast(D); const unsigned short NPRTLDX = 2 * static_cast(D); const unsigned short NPLDS_R = npld_r(); @@ -381,13 +381,13 @@ namespace ntt { const dir::map_t&, \ const dir::map_t&); - PARTICLES_COMM(Dim::_1D, Coord::Cart) - PARTICLES_COMM(Dim::_2D, Coord::Cart) - PARTICLES_COMM(Dim::_3D, Coord::Cart) - PARTICLES_COMM(Dim::_2D, Coord::Sph) - PARTICLES_COMM(Dim::_2D, Coord::Qsph) - PARTICLES_COMM(Dim::_3D, Coord::Sph) - PARTICLES_COMM(Dim::_3D, Coord::Qsph) + PARTICLES_COMM(Dim::_1D, Coord::Cartesian) + PARTICLES_COMM(Dim::_2D, Coord::Cartesian) + PARTICLES_COMM(Dim::_3D, Coord::Cartesian) + PARTICLES_COMM(Dim::_2D, Coord::Spherical) + PARTICLES_COMM(Dim::_2D, Coord::Qspherical) + PARTICLES_COMM(Dim::_3D, Coord::Spherical) + PARTICLES_COMM(Dim::_3D, Coord::Qspherical) #undef PARTICLES_COMM } // namespace ntt diff --git a/src/framework/containers/particles_io.cpp b/src/framework/containers/particles_io.cpp index fb72ccc5f..42f31e18c 100644 --- a/src/framework/containers/particles_io.cpp +++ b/src/framework/containers/particles_io.cpp @@ -1,6 +1,8 @@ #include "enums.h" #include "global.h" +#include "arch/kokkos_aliases.h" +#include "traits/metric.h" #include "utils/error.h" #include "utils/formatting.h" #include "utils/log.h" @@ -15,17 +17,24 @@ #include #if defined(MPI_ENABLED) + #include "arch/mpi_aliases.h" + #include + + #include #endif +#include + namespace ntt { /* * * * * * * * * * Output * * * * * * * * */ template void Particles::OutputDeclare(adios2::IO& io) const { - const auto n_addition_coords = ((D == Dim::_2D) and (C != Coord::Cart)) ? 1 - : 0; + const auto n_addition_coords = ((D == Dim::_2D) and (C != Coord::Cartesian)) + ? 1 + : 0; for (auto d { 0u }; d < D + n_addition_coords; ++d) { io.DefineVariable(fmt::format("pX%d_%d", d + 1, index()), { adios2::UnknownDim }, @@ -78,7 +87,7 @@ namespace ntt { } template - template + template void Particles::OutputWrite(adios2::IO& io, adios2::Engine& writer, npart_t prtl_stride, @@ -107,7 +116,7 @@ namespace ntt { }, nout); out_indices = array_t { "out_indices", nout }; - array_t out_counter { "out_counter" }; + const array_t out_counter { "out_counter" }; Kokkos::parallel_for( "RecordOutputIndices", rangeActiveParticles(), @@ -120,13 +129,15 @@ namespace ntt { }); } - npart_t nout_offset = 0; - npart_t nout_total = nout; #if !defined(MPI_ENABLED) + const npart_t nout_offset = 0; + const npart_t nout_total = nout; (void)domains_total; (void)domains_offset; #else - auto nout_total_vec = std::vector(domains_total); + npart_t nout_offset = 0; + npart_t nout_total = nout; + auto nout_total_vec = std::vector(domains_total); MPI_Allgather(&nout, 1, mpi::get_type(), @@ -154,7 +165,7 @@ namespace ntt { if constexpr (D == Dim::_2D or D == Dim::_3D) { buff_x2 = array_t { "x2", nout }; } - if constexpr (D == Dim::_3D or ((D == Dim::_2D) and (C != Coord::Cart))) { + if constexpr (D == Dim::_3D or ((D == Dim::_2D) and (C != Coord::Cartesian))) { buff_x3 = array_t { "x3", nout }; } array_t buff_pldr; @@ -250,7 +261,7 @@ namespace ntt { nout_total, nout_offset); } - if constexpr (D == Dim::_3D or ((D == Dim::_2D) and (C != Coord::Cart))) { + if constexpr (D == Dim::_3D or ((D == Dim::_2D) and (C != Coord::Cartesian))) { out::Write1DArray(io, writer, fmt::format("pX3_%d", index()), @@ -374,7 +385,7 @@ namespace ntt { { adios2::UnknownDim }); } - if constexpr (D == Dim::_2D and C != ntt::Coord::Cart) { + if constexpr (D == Dim::_2D and C != ntt::Coord::Cartesian) { io.DefineVariable(fmt::format("s%d_phi", index()), { adios2::UnknownDim }, { adios2::UnknownDim }, @@ -421,9 +432,8 @@ namespace ntt { raise::ErrorIf(npart() > 0, "Particles already initialized before reading checkpoint", HERE); - npart_t npart_offset = 0u; - npart_t npart_read; + npart_t npart_read; out::ReadVariable(io, reader, fmt::format("s%d_npart", index()), @@ -432,8 +442,10 @@ namespace ntt { set_npart(npart_read); #if !defined(MPI_ENABLED) + const npart_t npart_offset = 0u; (void)domains_total; #else + npart_t npart_offset = 0u; { const auto npart_send = npart(); std::vector glob_nparts(domains_total); @@ -536,7 +548,7 @@ namespace ntt { npart_offset); } - if constexpr (D == Dim::_2D and C != Coord::Cart) { + if constexpr (D == Dim::_2D and C != Coord::Cartesian) { out::Read1DArray(io, reader, fmt::format("s%d_phi", index()), @@ -735,7 +747,7 @@ namespace ntt { npart_offset); } - if constexpr (D == Dim::_2D and C != Coord::Cart) { + if constexpr (D == Dim::_2D and C != Coord::Cartesian) { out::Write1DArray(io, writer, fmt::format("s%d_phi", index()), @@ -803,16 +815,17 @@ namespace ntt { } } + // NOLINTBEGIN(bugprone-macro-parentheses) #define PARTICLES_OUTPUT_DECLARE(D, C) \ template void Particles::OutputDeclare(adios2::IO&) const; - PARTICLES_OUTPUT_DECLARE(Dim::_1D, Coord::Cart) - PARTICLES_OUTPUT_DECLARE(Dim::_2D, Coord::Cart) - PARTICLES_OUTPUT_DECLARE(Dim::_3D, Coord::Cart) - PARTICLES_OUTPUT_DECLARE(Dim::_2D, Coord::Sph) - PARTICLES_OUTPUT_DECLARE(Dim::_2D, Coord::Qsph) - PARTICLES_OUTPUT_DECLARE(Dim::_3D, Coord::Sph) - PARTICLES_OUTPUT_DECLARE(Dim::_3D, Coord::Qsph) + PARTICLES_OUTPUT_DECLARE(Dim::_1D, Coord::Cartesian) + PARTICLES_OUTPUT_DECLARE(Dim::_2D, Coord::Cartesian) + PARTICLES_OUTPUT_DECLARE(Dim::_3D, Coord::Cartesian) + PARTICLES_OUTPUT_DECLARE(Dim::_2D, Coord::Spherical) + PARTICLES_OUTPUT_DECLARE(Dim::_2D, Coord::Qspherical) + PARTICLES_OUTPUT_DECLARE(Dim::_3D, Coord::Spherical) + PARTICLES_OUTPUT_DECLARE(Dim::_3D, Coord::Qspherical) #undef PARTICLES_OUTPUT_DECLARE #define PARTICLES_OUTPUT_WRITE(S, M, D) \ @@ -838,13 +851,14 @@ namespace ntt { std::size_t, \ std::size_t) const; - PARTICLES_CHECKPOINTS(Dim::_1D, Coord::Cart) - PARTICLES_CHECKPOINTS(Dim::_2D, Coord::Cart) - PARTICLES_CHECKPOINTS(Dim::_3D, Coord::Cart) - PARTICLES_CHECKPOINTS(Dim::_2D, Coord::Sph) - PARTICLES_CHECKPOINTS(Dim::_2D, Coord::Qsph) - PARTICLES_CHECKPOINTS(Dim::_3D, Coord::Sph) - PARTICLES_CHECKPOINTS(Dim::_3D, Coord::Qsph) + PARTICLES_CHECKPOINTS(Dim::_1D, Coord::Cartesian) + PARTICLES_CHECKPOINTS(Dim::_2D, Coord::Cartesian) + PARTICLES_CHECKPOINTS(Dim::_3D, Coord::Cartesian) + PARTICLES_CHECKPOINTS(Dim::_2D, Coord::Spherical) + PARTICLES_CHECKPOINTS(Dim::_2D, Coord::Qspherical) + PARTICLES_CHECKPOINTS(Dim::_3D, Coord::Spherical) + PARTICLES_CHECKPOINTS(Dim::_3D, Coord::Qspherical) #undef PARTICLES_CHECKPOINTS + // NOLINTEND(bugprone-macro-parentheses) } // namespace ntt diff --git a/src/framework/containers/particles_sort.cpp b/src/framework/containers/particles_sort.cpp index 2fd0e6011..e89c71877 100644 --- a/src/framework/containers/particles_sort.cpp +++ b/src/framework/containers/particles_sort.cpp @@ -2,6 +2,7 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "utils/error.h" #include "utils/sorting.h" #include "framework/containers/particles.h" @@ -12,6 +13,7 @@ #include #include +#include #include namespace ntt { @@ -46,8 +48,8 @@ namespace ntt { } // count the offsets on the host and copy to device - array_t tag_offsets("tag_offsets", num_tags - 3); - auto tag_offsets_h = Kokkos::create_mirror_view(tag_offsets); + const array_t tag_offsets("tag_offsets", num_tags - 3); + auto tag_offsets_h = Kokkos::create_mirror_view(tag_offsets); tag_offsets_h(0) = npptag_vec[2]; // offset for tag = 3 for (auto t { 1u }; t < num_tags - 3; ++t) { @@ -60,8 +62,8 @@ namespace ntt { template void RemoveDeadInArray(array_t& arr, const array_t& indices_alive) { - npart_t n_alive = indices_alive.extent(0); - auto buffer = Kokkos::View("buffer", n_alive); + const npart_t n_alive = indices_alive.extent(0); + auto buffer = Kokkos::View("buffer", n_alive); Kokkos::parallel_for( "PopulateBufferAlive", n_alive, @@ -74,8 +76,8 @@ namespace ntt { template void RemoveDeadInArray(array_t& arr, const array_t& indices_alive) { - npart_t n_alive = indices_alive.extent(0); - auto buffer = array_t { "buffer", n_alive, arr.extent(1) }; + const npart_t n_alive = indices_alive.extent(0); + auto buffer = array_t { "buffer", n_alive, arr.extent(1) }; Kokkos::parallel_for( "PopulateBufferAlive", CreateRangePolicy({ 0, 0 }, { n_alive, arr.extent(1) }), @@ -106,8 +108,8 @@ namespace ntt { n_alive, n_dead); - array_t indices_alive { "indices_alive", n_alive }; - array_t alive_counter { "counter_alive", 1 }; + const array_t indices_alive { "indices_alive", n_alive }; + const array_t alive_counter { "counter_alive", 1 }; Kokkos::parallel_for( "AliveIndices", @@ -153,7 +155,7 @@ namespace ntt { RemoveDeadInArray(ux3, indices_alive); RemoveDeadInArray(weight, indices_alive); - if constexpr (D == Dim::_2D && C != Coord::Cart) { + if constexpr (D == Dim::_2D && C != Coord::Cartesian) { RemoveDeadInArray(phi, indices_alive); } @@ -225,7 +227,7 @@ namespace ntt { sorter.sort(Kokkos::subview(ux3, slice)); sorter.sort(Kokkos::subview(weight, slice)); sorter.sort(Kokkos::subview(tag, slice)); - if constexpr (D == Dim::_2D and C != Coord::Cart) { + if constexpr (D == Dim::_2D and C != Coord::Cartesian) { sorter.sort(Kokkos::subview(phi, slice)); } for (auto pldr { 0u }; pldr < npld_r(); ++pldr) { @@ -242,13 +244,13 @@ namespace ntt { template void Particles::RemoveDead(); \ template void Particles::SortSpatially(const Grid&); - PARTICLES_SORT(Dim::_1D, Coord::Cart) - PARTICLES_SORT(Dim::_2D, Coord::Cart) - PARTICLES_SORT(Dim::_3D, Coord::Cart) - PARTICLES_SORT(Dim::_2D, Coord::Sph) - PARTICLES_SORT(Dim::_2D, Coord::Qsph) - PARTICLES_SORT(Dim::_3D, Coord::Sph) - PARTICLES_SORT(Dim::_3D, Coord::Qsph) + PARTICLES_SORT(Dim::_1D, Coord::Cartesian) + PARTICLES_SORT(Dim::_2D, Coord::Cartesian) + PARTICLES_SORT(Dim::_3D, Coord::Cartesian) + PARTICLES_SORT(Dim::_2D, Coord::Spherical) + PARTICLES_SORT(Dim::_2D, Coord::Qspherical) + PARTICLES_SORT(Dim::_3D, Coord::Spherical) + PARTICLES_SORT(Dim::_3D, Coord::Qspherical) #undef PARTICLES_SORT } // namespace ntt diff --git a/src/framework/containers/species.h b/src/framework/containers/species.h index 699a39ac5..411630da2 100644 --- a/src/framework/containers/species.h +++ b/src/framework/containers/species.h @@ -56,7 +56,6 @@ namespace ntt { public: ParticleSpecies() : m_index { 0u } - , m_label { "" } , m_mass { 0.0 } , m_charge { 0.0 } , m_maxnpart { 0 } @@ -98,7 +97,7 @@ namespace ntt { unsigned short npld_r, unsigned short npld_i) : m_index { index } - , m_label { std::move(label) } + , m_label { label } , m_mass { m } , m_charge { ch } , m_maxnpart { maxnpart } @@ -198,7 +197,7 @@ namespace ntt { /* reporter -------------------------------------------------------------- */ auto Report() const -> std::string { - std::string report = ""; + std::string report; reporter::AddSubcategory(report, 4, fmt::format("Species #%d", index()).c_str()); diff --git a/src/framework/domain/domain.h b/src/framework/domain/domain.h index 06ffd302d..747ab4576 100644 --- a/src/framework/domain/domain.h +++ b/src/framework/domain/domain.h @@ -6,6 +6,10 @@ * as well as pointers to neighboring domains. * @implements * - ntt::Domain<> + * @cpp: + * - domain.cpp + * @namespaces: + * - ntt:: * @macros: * - MPI_ENABLED * @note @@ -43,12 +47,11 @@ #include "global.h" #include "arch/directions.h" +#include "traits/metric.h" #include "utils/formatting.h" #include "utils/numeric.h" #include "utils/reporter.h" -#include "metrics/traits.h" - #include "framework/containers/fields.h" #include "framework/containers/particles.h" #include "framework/containers/species.h" @@ -62,8 +65,7 @@ namespace ntt { - template - requires metric::traits::HasD + template struct Domain { static constexpr Dimension D { M::Dim }; @@ -151,9 +153,9 @@ namespace ntt { [[nodiscard]] auto random_pool() -> random_number_pool_t& { - raise::ErrorIf(not m_random_number_pool.has_value(), - "random_pool() called on a placeholder domain", - HERE); + if (not m_random_number_pool.has_value()) { + raise::Error("random_pool() called on a placeholder domain", HERE); + } return m_random_number_pool.value(); } @@ -165,7 +167,7 @@ namespace ntt { /* printer overload ----------------------------------------------------- */ auto Report() const -> std::string { - std::string report = ""; + std::string report; reporter::AddSubcategory(report, 4, fmt::format("Domain #%d", index()).c_str()); @@ -234,13 +236,13 @@ namespace ntt { // neighboring domain indices dir::map_t m_neighbor_idx; // MPI rank of the domain (used only when MPI enabled) - int m_mpi_rank; + int m_mpi_rank { -1 }; // random number pool for the domain std::optional m_random_number_pool; }; - template + template inline auto operator<<(std::ostream& os, const Domain& domain) -> std::ostream& { os << "Domain #" << domain.index(); diff --git a/src/framework/domain/grid.cpp b/src/framework/domain/grid.cpp index 1e223cbc5..0b09f98a6 100644 --- a/src/framework/domain/grid.cpp +++ b/src/framework/domain/grid.cpp @@ -1,5 +1,6 @@ #include "framework/domain/grid.h" +#include "enums.h" #include "global.h" #include "arch/kokkos_aliases.h" @@ -9,42 +10,34 @@ namespace ntt { template <> auto Grid::rangeAllCells() const -> range_t { - box_region_t region { CellLayer::allLayer }; - return rangeCells(region); + return rangeCells({ CellLayer::allLayer }); } template <> auto Grid::rangeAllCells() const -> range_t { - box_region_t region { CellLayer::allLayer, CellLayer::allLayer }; - return rangeCells(region); + return rangeCells({ CellLayer::allLayer, CellLayer::allLayer }); } template <> auto Grid::rangeAllCells() const -> range_t { - box_region_t region { CellLayer::allLayer, - CellLayer::allLayer, - CellLayer::allLayer }; - return rangeCells(region); + return rangeCells( + { CellLayer::allLayer, CellLayer::allLayer, CellLayer::allLayer }); } template <> auto Grid::rangeActiveCells() const -> range_t { - box_region_t region { CellLayer::activeLayer }; - return rangeCells(region); + return rangeCells({ CellLayer::activeLayer }); } template <> auto Grid::rangeActiveCells() const -> range_t { - box_region_t region { CellLayer::activeLayer, CellLayer::activeLayer }; - return rangeCells(region); + return rangeCells({ CellLayer::activeLayer, CellLayer::activeLayer }); } template <> auto Grid::rangeActiveCells() const -> range_t { - box_region_t region { CellLayer::activeLayer, - CellLayer::activeLayer, - CellLayer::activeLayer }; - return rangeCells(region); + return rangeCells( + { CellLayer::activeLayer, CellLayer::activeLayer, CellLayer::activeLayer }); } template @@ -123,42 +116,34 @@ namespace ntt { template <> auto Grid::rangeAllCellsOnHost() const -> range_h_t { - box_region_t region { CellLayer::allLayer }; - return rangeCellsOnHost(region); + return rangeCellsOnHost({ CellLayer::allLayer }); } template <> auto Grid::rangeAllCellsOnHost() const -> range_h_t { - box_region_t region { CellLayer::allLayer, CellLayer::allLayer }; - return rangeCellsOnHost(region); + return rangeCellsOnHost({ CellLayer::allLayer, CellLayer::allLayer }); } template <> auto Grid::rangeAllCellsOnHost() const -> range_h_t { - box_region_t region { CellLayer::allLayer, - CellLayer::allLayer, - CellLayer::allLayer }; - return rangeCellsOnHost(region); + return rangeCellsOnHost( + { CellLayer::allLayer, CellLayer::allLayer, CellLayer::allLayer }); } template <> auto Grid::rangeActiveCellsOnHost() const -> range_h_t { - box_region_t region { CellLayer::activeLayer }; - return rangeCellsOnHost(region); + return rangeCellsOnHost({ CellLayer::activeLayer }); } template <> auto Grid::rangeActiveCellsOnHost() const -> range_h_t { - box_region_t region { CellLayer::activeLayer, CellLayer::activeLayer }; - return rangeCellsOnHost(region); + return rangeCellsOnHost({ CellLayer::activeLayer, CellLayer::activeLayer }); } template <> auto Grid::rangeActiveCellsOnHost() const -> range_h_t { - box_region_t region { CellLayer::activeLayer, - CellLayer::activeLayer, - CellLayer::activeLayer }; - return rangeCellsOnHost(region); + return rangeCellsOnHost( + { CellLayer::activeLayer, CellLayer::activeLayer, CellLayer::activeLayer }); } template diff --git a/src/framework/domain/grid.h b/src/framework/domain/grid.h index 6fbe2fae3..2e725eabf 100644 --- a/src/framework/domain/grid.h +++ b/src/framework/domain/grid.h @@ -106,7 +106,7 @@ namespace ntt { auto i_min(in i) const -> ncells_t { switch (i) { case in::x1: - return (m_resolution.size() > 0) ? N_GHOSTS : 0; + return (not m_resolution.empty()) ? N_GHOSTS : 0; case in::x2: return (m_resolution.size() > 1) ? N_GHOSTS : 0; case in::x3: @@ -121,7 +121,7 @@ namespace ntt { auto i_max(in i) const -> ncells_t { switch (i) { case in::x1: - return (m_resolution.size() > 0) ? (m_resolution[0] + N_GHOSTS) : 1; + return (not m_resolution.empty()) ? (m_resolution[0] + N_GHOSTS) : 1; case in::x2: return (m_resolution.size() > 1) ? (m_resolution[1] + N_GHOSTS) : 1; case in::x3: @@ -136,7 +136,7 @@ namespace ntt { auto n_active(in i) const -> ncells_t { switch (i) { case in::x1: - return (m_resolution.size() > 0) ? m_resolution[0] : 1; + return (not m_resolution.empty()) ? m_resolution[0] : 1; case in::x2: return (m_resolution.size() > 1) ? m_resolution[1] : 1; case in::x3: @@ -165,7 +165,7 @@ namespace ntt { auto n_all(in i) const -> ncells_t { switch (i) { case in::x1: - return (m_resolution.size() > 0) ? (m_resolution[0] + 2 * N_GHOSTS) : 1; + return (not m_resolution.empty()) ? (m_resolution[0] + 2 * N_GHOSTS) : 1; case in::x2: return (m_resolution.size() > 1) ? (m_resolution[1] + 2 * N_GHOSTS) : 1; case in::x3: @@ -178,9 +178,9 @@ namespace ntt { [[nodiscard]] auto n_all() const -> std::vector { - std::vector nall; + std::vector nall(D); for (auto i = 0u; i < D; ++i) { - nall.push_back(m_resolution[i] + 2 * N_GHOSTS); + nall[i] = m_resolution[i] + 2 * N_GHOSTS; } return nall; } @@ -248,8 +248,9 @@ namespace ntt { auto extent(in i) const -> std::pair { switch (i) { case in::x1: - return (m_extent.size() > 0) ? m_extent[0] - : std::pair { ZERO, ZERO }; + return (not m_extent.empty()) + ? m_extent[0] + : std::pair { ZERO, ZERO }; case in::x2: return (m_extent.size() > 1) ? m_extent[1] : std::pair { ZERO, ZERO }; @@ -290,11 +291,11 @@ namespace ntt { } /* setters -------------------------------------------------------------- */ - inline void set_flds_bc(const dir::direction_t& direction, const FldsBC& bc) { + void set_flds_bc(const dir::direction_t& direction, const FldsBC& bc) { m_flds_bc.insert_or_assign(direction, bc); } - inline void set_prtl_bc(const dir::direction_t& direction, const PrtlBC& bc) { + void set_prtl_bc(const dir::direction_t& direction, const PrtlBC& bc) { m_prtl_bc.insert_or_assign(direction, bc); } diff --git a/src/framework/domain/mesh.h b/src/framework/domain/mesh.h index adff231f4..07d1d1500 100644 --- a/src/framework/domain/mesh.h +++ b/src/framework/domain/mesh.h @@ -16,12 +16,11 @@ #include "enums.h" #include "global.h" +#include "traits/metric.h" #include "utils/comparators.h" #include "utils/error.h" #include "utils/numeric.h" -#include "metrics/traits.h" - #include "framework/domain/grid.h" #include @@ -31,8 +30,7 @@ namespace ntt { - template - requires metric::traits::HasD && metric::traits::HasConvert_i + template struct Mesh : public Grid { static constexpr Dimension D { M::Dim }; using base_t = Grid; @@ -66,7 +64,8 @@ namespace ntt { * @note pass Range::All to select the entire dimension */ [[nodiscard]] - auto Intersection(boundaries_t box) const -> boundaries_t { + auto Intersection(const boundaries_t& box) const + -> boundaries_t { raise::ErrorIf(box.size() != M::Dim, "Invalid box dimension", HERE); boundaries_t intersection; auto d = 0; @@ -86,7 +85,7 @@ namespace ntt { } else { x_max = std::max(extent()[d].first, std::min(extent()[d].second, b.second)); - intersection.push_back({ x_min, x_max }); + intersection.emplace_back(x_min, x_max); } } ++d; @@ -101,7 +100,7 @@ namespace ntt { * @note pass Range::All to select the entire dimension */ [[nodiscard]] - auto Intersects(boundaries_t box) const -> bool { + auto Intersects(const boundaries_t& box) const -> bool { raise::ErrorIf(box.size() != M::Dim, "Invalid box dimension", HERE); const auto intersection = Intersection(box); for (const auto& i : intersection) { @@ -123,7 +122,8 @@ namespace ntt { * @note indices are already shifted by N_GHOSTS (i.e. they start at N_GHOSTS not 0) */ [[nodiscard]] - auto ExtentToRange(boundaries_t box, boundaries_t incl_ghosts) const + auto ExtentToRange(const boundaries_t& box, + const boundaries_t& incl_ghosts) const -> boundaries_t { raise::ErrorIf(box.size() != M::Dim, "Invalid box dimension", HERE); raise::ErrorIf(incl_ghosts.size() != M::Dim, @@ -132,7 +132,7 @@ namespace ntt { boundaries_t range; if (not Intersects(box)) { for (auto i { 0u }; i < box.size(); ++i) { - range.push_back({ 0, 0 }); + range.emplace_back(0, 0); } return range; } @@ -183,13 +183,14 @@ namespace ntt { xi_max_Cd = std::min(xi_max_Cd, static_cast(this->n_active()[d])); } - range.push_back({ static_cast(xi_min_Cd) + - (incl_ghosts[d].first ? 0 : N_GHOSTS), - static_cast(xi_max_Cd) + - (incl_ghosts[d].second ? 2 * N_GHOSTS : N_GHOSTS) }); + range.emplace_back(static_cast(xi_min_Cd) + + (incl_ghosts[d].first ? 0 : N_GHOSTS), + static_cast(xi_max_Cd) + + (incl_ghosts[d].second ? 2 * N_GHOSTS : N_GHOSTS)); } ++d; } + return range; } }; diff --git a/src/framework/domain/metadomain.cpp b/src/framework/domain/metadomain.cpp index e31015c24..b0eacb5dc 100644 --- a/src/framework/domain/metadomain.cpp +++ b/src/framework/domain/metadomain.cpp @@ -7,17 +7,21 @@ #include "arch/mpi_aliases.h" #endif +#include "arch/directions.h" +#include "traits/metric.h" #include "utils/comparators.h" #include "utils/error.h" +#include "utils/numeric.h" #include "utils/tools.h" -#include "framework/domain/domain.h" +#include "framework/containers/species.h" #include "framework/specialization_registry.h" #if defined(MPI_ENABLED) #include #endif +#include #include #include #include @@ -25,8 +29,7 @@ namespace ntt { - template - requires IsCompatibleWithMetadomain + template Metadomain::Metadomain(unsigned int global_ndomains, const std::vector& global_decomposition, const std::vector& global_ncells, @@ -57,8 +60,7 @@ namespace ntt { metricCompatibilityCheck(); } - template - requires IsCompatibleWithMetadomain + template void Metadomain::initialValidityCheck() const { // ensure everything has the correct shape raise::ErrorIf(g_decomposition.size() != (std::size_t)D, @@ -95,8 +97,7 @@ namespace ntt { #endif // MPI_ENABLED } - template - requires IsCompatibleWithMetadomain + template void Metadomain::createEmptyDomains() { /* decompose and compute cell & domain offsets ------------------------ */ auto d_ncells = tools::Decompose(g_ndomains, g_mesh.n_active(), g_decomposition); @@ -191,8 +192,7 @@ namespace ntt { } } - template - requires IsCompatibleWithMetadomain + template void Metadomain::redefineNeighbors() { for (unsigned int idx { 0 }; idx < g_ndomains; ++idx) { // offset of the subdomain[idx] @@ -232,8 +232,7 @@ namespace ntt { } } - template - requires IsCompatibleWithMetadomain + template void Metadomain::redefineBoundaries() { for (unsigned int idx { 0 }; idx < g_ndomains; ++idx) { // offset of the subdomain[idx] @@ -282,7 +281,7 @@ namespace ntt { current_domain.mesh.set_prtl_bc(direction, prtl_bc); } // setting boundaries in non-orthogonal (corner) directions - for (auto direction : dir::Directions::all) { + for (const auto& direction : dir::Directions::all) { auto assoc_orth = direction.get_assoc_orth(); if (assoc_orth.size() == 1) { // skip the orthogonal directions @@ -291,7 +290,7 @@ namespace ntt { // if one of the boundaries is not periodic, then use it // otherwise, use periodic FldsBC flds_bc { FldsBC::INVALID }; - for (auto dir : assoc_orth) { + for (const auto& dir : assoc_orth) { const auto fldsbc_in_dir = current_domain.mesh.flds_bc_in(dir); if (fldsbc_in_dir != FldsBC::PERIODIC) { flds_bc = fldsbc_in_dir; @@ -301,7 +300,7 @@ namespace ntt { } } PrtlBC prtl_bc { PrtlBC::INVALID }; - for (auto dir : assoc_orth) { + for (const auto& dir : assoc_orth) { const auto prtlbc_in_dir = current_domain.mesh.prtl_bc_in(dir); if (prtlbc_in_dir != PrtlBC::PERIODIC) { prtl_bc = prtlbc_in_dir; @@ -322,8 +321,7 @@ namespace ntt { } } - template - requires IsCompatibleWithMetadomain + template void Metadomain::finalValidityCheck() const { for (unsigned int idx { 0 }; idx < g_ndomains; ++idx) { const auto& current_domain = g_subdomains[idx]; @@ -368,8 +366,7 @@ namespace ntt { } } - template - requires IsCompatibleWithMetadomain + template void Metadomain::metricCompatibilityCheck() const { const auto epsilon = std::numeric_limits::epsilon() * static_cast(100.0); @@ -403,8 +400,7 @@ namespace ntt { #endif } - template - requires IsCompatibleWithMetadomain + template void Metadomain::setFldsBC(const bc_in& dir, const FldsBC& new_bcs) { if (dir == bc_in::Mx1) { if constexpr (M::Dim == Dim::_1D) { @@ -472,8 +468,7 @@ namespace ntt { redefineBoundaries(); } - template - requires IsCompatibleWithMetadomain + template void Metadomain::setPrtlBC(const bc_in& dir, const PrtlBC& new_bcs) { if (dir == bc_in::Mx1) { if constexpr (M::Dim == Dim::_1D) { @@ -541,10 +536,10 @@ namespace ntt { redefineBoundaries(); } + // NOLINTBEGIN(bugprone-macro-parentheses) #define METADOMAIN_STRUCT(S, M, D) template struct Metadomain>; - NTT_FOREACH_SPECIALIZATION(METADOMAIN_STRUCT) - #undef METADOMAIN_STRUCT + // NOLINTEND(bugprone-macro-parentheses) } // namespace ntt diff --git a/src/framework/domain/metadomain.h b/src/framework/domain/metadomain.h index d04433fcf..9c67a25a5 100644 --- a/src/framework/domain/metadomain.h +++ b/src/framework/domain/metadomain.h @@ -1,6 +1,7 @@ /** * @file framework/domain/metadomain.h - * @brief ... + * @brief Global metadomain class managing domain decomposition, inter-domain + * communication, I/O, and checkpointing * @implements * - ntt::Metadomain<> * @cpp: @@ -23,8 +24,7 @@ #include "global.h" #include "arch/kokkos_aliases.h" - -#include "metrics/traits.h" +#include "traits/metric.h" #include "framework/containers/species.h" #include "framework/domain/domain.h" @@ -46,19 +46,14 @@ #include #include +#include #include #include #include namespace ntt { - template - concept IsCompatibleWithMetadomain = metric::traits::HasD && - metric::traits::HasConvert && - metric::traits::HasTotVolume; - - template - requires IsCompatibleWithMetadomain + template struct Metadomain { static constexpr Dimension D { M::Dim }; @@ -137,12 +132,12 @@ namespace ntt { timestep_t, simtime_t, simtime_t, - std::function&, - index_t, - timestep_t, - simtime_t, - const Domain&)> = nullptr) -> bool; + const std::function&, + index_t, + timestep_t, + simtime_t, + const Domain&)>& = nullptr) -> bool; void InitCheckpointWriter(adios2::ADIOS*, const SimulationParams&); auto WriteCheckpoint(const SimulationParams&, timestep_t, @@ -160,8 +155,9 @@ namespace ntt { timestep_t, simtime_t, simtime_t, - std::function&)> = - nullptr) -> bool; + const std::function< + real_t(const std::string&, timestep_t, simtime_t, const Domain&)>& = nullptr) + -> bool; /* setters -------------------------------------------------------------- */ void setFldsBC(const bc_in&, const FldsBC&); @@ -234,16 +230,16 @@ namespace ntt { } [[nodiscard]] - auto l_npart() const -> std::size_t { + auto l_npart() const -> npart_t { const auto npart = l_npart_perspec(); - return std::accumulate(npart.begin(), npart.end(), 0); + return std::accumulate(npart.begin(), npart.end(), static_cast(0)); } [[nodiscard]] - auto l_ncells() const -> std::size_t { - std::size_t ncells_local = 0; + auto l_ncells() const -> ncells_t { + ncells_t ncells_local = 0; for (const auto& ldidx : l_subdomain_indices()) { - std::size_t ncells = 1; + ncells_t ncells = 1; for (const auto& n : g_subdomains[ldidx].mesh.n_all()) { ncells *= n; } @@ -254,9 +250,9 @@ namespace ntt { [[nodiscard]] auto species_labels() const -> std::vector { - std::vector labels; - for (const auto& sp : g_species_params) { - labels.push_back(sp.label()); + std::vector labels(g_species_params.size()); + for (std::size_t i = 0; i < g_species_params.size(); ++i) { + labels[i] = g_species_params[i].label(); } return labels; } @@ -288,7 +284,7 @@ namespace ntt { #endif #if defined(MPI_ENABLED) - int g_mpi_rank, g_mpi_size; + int g_mpi_rank { -1 }, g_mpi_size { -1 }; #endif }; diff --git a/src/framework/domain/metadomain_chckpt.cpp b/src/framework/domain/metadomain_chckpt.cpp index bd6b87cfd..46aa32b84 100644 --- a/src/framework/domain/metadomain_chckpt.cpp +++ b/src/framework/domain/metadomain_chckpt.cpp @@ -1,6 +1,7 @@ #include "enums.h" #include "global.h" +#include "traits/metric.h" #include "utils/error.h" #include "utils/formatting.h" #include "utils/log.h" @@ -10,10 +11,17 @@ #include "framework/specialization_registry.h" #include "output/checkpoint.h" +#if defined(MPI_ENABLED) + #include +#endif + +#include +#include +#include + namespace ntt { - template - requires IsCompatibleWithMetadomain + template void Metadomain::InitCheckpointWriter(adios2::ADIOS* ptr_adios, const SimulationParams& params) { raise::ErrorIf(ptr_adios == nullptr, "adios == nullptr", HERE); @@ -63,8 +71,7 @@ namespace ntt { } } - template - requires IsCompatibleWithMetadomain + template auto Metadomain::WriteCheckpoint(const SimulationParams& params, timestep_t current_step, timestep_t finished_step, @@ -110,8 +117,7 @@ namespace ntt { return true; } - template - requires IsCompatibleWithMetadomain + template void Metadomain::ContinueFromCheckpoint(adios2::ADIOS* ptr_adios, const SimulationParams& params) { raise::ErrorIf(ptr_adios == nullptr, "adios == nullptr", HERE); @@ -159,6 +165,7 @@ namespace ntt { HERE); } + // NOLINTBEGIN(bugprone-macro-parentheses) #define METADOMAIN_CHECKPOINTS(S, M, D) \ template void Metadomain>::InitCheckpointWriter( \ adios2::ADIOS*, \ @@ -173,5 +180,6 @@ namespace ntt { const SimulationParams&); NTT_FOREACH_SPECIALIZATION(METADOMAIN_CHECKPOINTS) #undef METADOMAIN_CHECKPOINTS + // NOLINTEND(bugprone-macro-parentheses) } // namespace ntt diff --git a/src/framework/domain/metadomain_comm.cpp b/src/framework/domain/metadomain_comm.cpp index 45bf61013..ee7803182 100644 --- a/src/framework/domain/metadomain_comm.cpp +++ b/src/framework/domain/metadomain_comm.cpp @@ -2,10 +2,14 @@ #include "global.h" #include "arch/directions.h" +#include "arch/kokkos_aliases.h" +#include "traits/metric.h" #include "utils/error.h" #include "utils/formatting.h" #include "utils/log.h" +#include "utils/numeric.h" +#include "framework/domain/domain.h" #include "framework/domain/metadomain.h" #include "framework/specialization_registry.h" @@ -19,6 +23,7 @@ #include +#include #include #include @@ -27,10 +32,10 @@ namespace ntt { using address_t = std::pair; using comm_params_t = std::pair>; - template - auto GetSendRecvRanks(const Metadomain* const metadomain, - Domain& domain, - dir::direction_t direction) + template + auto GetSendRecvRanks(const Metadomain* const metadomain, + Domain& domain, + const dir::direction_t& direction) -> std::pair { const Domain* send_to_nghbr_ptr = nullptr; const Domain* recv_from_nghbr_ptr = nullptr; @@ -110,7 +115,7 @@ namespace ntt { }; } - template + template auto GetSendRecvParams(const Metadomain* const metadomain, Domain& domain, dir::direction_t direction, @@ -196,8 +201,7 @@ namespace ntt { }; } - template - requires IsCompatibleWithMetadomain + template void Metadomain::CommunicateFields(Domain& domain, CommTags tags) const { const auto comm_em = ((S == SimEngine::SRPIC) and @@ -213,7 +217,7 @@ namespace ntt { "CommunicateFields called with no task", HERE); - std::string comms = ""; + std::string comms; if (tags & Comm::E) { comms += "E "; } @@ -414,8 +418,7 @@ namespace ntt { } } - template - requires IsCompatibleWithMetadomain + template void Metadomain::SynchronizeFields(Domain& domain, CommTags tags, const range_tuple_t& components) const { @@ -430,7 +433,7 @@ namespace ntt { HERE); const auto synchronize = true; - std::string comms = ""; + std::string comms; if (comm_j) { comms += "J "; } @@ -571,8 +574,7 @@ namespace ntt { } } - template - requires IsCompatibleWithMetadomain + template void Metadomain::CommunicateParticles(Domain& domain) const { #if defined(MPI_ENABLED) logger::Checkpoint("Communicating particles\n", HERE); @@ -663,6 +665,7 @@ namespace ntt { #endif } + // NOLINTBEGIN(bugprone-macro-parentheses) #define METADOMAIN_COMM(S, M, D) \ template void Metadomain>::CommunicateFields(Domain>&, \ CommTags) const; \ @@ -673,7 +676,7 @@ namespace ntt { template void Metadomain>::CommunicateParticles(Domain>&) const; NTT_FOREACH_SPECIALIZATION(METADOMAIN_COMM) - #undef METADOMAIN_COMM + // NOLINTEND(bugprone-macro-parentheses) } // namespace ntt diff --git a/src/framework/domain/metadomain_io.cpp b/src/framework/domain/metadomain_io.cpp index b9f351a0f..2f2c84972 100644 --- a/src/framework/domain/metadomain_io.cpp +++ b/src/framework/domain/metadomain_io.cpp @@ -2,12 +2,14 @@ #include "global.h" #include "arch/kokkos_aliases.h" +#include "traits/metric.h" #include "utils/error.h" #include "utils/log.h" #include "utils/numeric.h" #include "framework/containers/particles.h" #include "framework/domain/domain.h" +#include "framework/domain/mesh.h" #include "framework/domain/metadomain.h" #include "framework/parameters/parameters.h" #include "framework/specialization_registry.h" @@ -20,17 +22,21 @@ #include #if defined(MPI_ENABLED) + #include "arch/mpi_aliases.h" + #include #endif // MPI_ENABLED #include +#include +#include #include +#include #include namespace ntt { - template - requires IsCompatibleWithMetadomain + template void Metadomain::InitWriter(adios2::ADIOS* ptr_adios, const SimulationParams& params) { raise::ErrorIf( @@ -104,7 +110,7 @@ namespace ntt { g_writer.writeAttrs(params); } - template + template void ComputeMoments(const SimulationParams& params, const Mesh& mesh, const std::vector>& prtl_species, @@ -113,7 +119,7 @@ namespace ntt { ndfield_t& buffer, idx_t buff_idx) { std::vector specs = species; - if (specs.size() == 0) { + if (specs.empty()) { // if no species specified, take all massive species for (auto& sp : prtl_species) { if (sp.mass() > 0) { @@ -180,11 +186,11 @@ namespace ntt { } } - template + template void ComputeVectorPotential(ndfield_t& buffer, ndfield_t& EM, unsigned short buff_idx, - const Mesh mesh) { + const Mesh& mesh) { if constexpr (M::Dim == Dim::_2D) { const auto metric = mesh.metric; Kokkos::parallel_for( @@ -244,11 +250,11 @@ namespace ntt { } #if defined(MPI_ENABLED) && defined(OUTPUT_ENABLED) - template + template void ExtractVectorPotential(ndfield_t& buffer, array_t& aphi_r, unsigned short buff_idx, - const Mesh mesh) { + const Mesh& mesh) { Kokkos::parallel_for( "AddVectorPotential", mesh.rangeActiveCells(), @@ -257,8 +263,7 @@ namespace ntt { }); } - template - requires IsCompatibleWithMetadomain + template void Metadomain::CommunicateVectorPotential(unsigned short buff_idx) { if constexpr (M::Dim == Dim::_2D) { auto local_domain = subdomain_ptr(l_subdomain_indices()[0]); @@ -312,20 +317,19 @@ namespace ntt { } #endif - template - requires IsCompatibleWithMetadomain + template auto Metadomain::Write( - const SimulationParams& params, - timestep_t current_step, - timestep_t finished_step, - simtime_t current_time, - simtime_t finished_time, - std::function&, - index_t, - timestep_t, - simtime_t, - const Domain&)> CustomFieldOutput) -> bool { + const SimulationParams& params, + timestep_t current_step, + timestep_t finished_step, + simtime_t current_time, + simtime_t finished_time, + const std::function&, + index_t, + timestep_t, + simtime_t, + const Domain&)>& CustomFieldOutput) -> bool { raise::ErrorIf( l_subdomain_indices().size() != 1, "Output for now is only supported for one subdomain per rank", @@ -392,8 +396,8 @@ namespace ntt { const auto add_ghost = (incl_ghosts ? 2 * N_GHOSTS : 0); const auto add_last = (is_last ? 1 : 0); - array_t xc { "Xc", l_size_dwn + add_ghost }; - array_t xe { "Xe", l_size_dwn + add_ghost + add_last }; + const array_t xc { "Xc", l_size_dwn + add_ghost }; + const array_t xe { "Xe", l_size_dwn + add_ghost + add_last }; const auto offset = (incl_ghosts ? N_GHOSTS : 0); const auto ncells = l_size_dwn; @@ -432,7 +436,7 @@ namespace ntt { Kokkos::deep_copy(local_domain->fields.bckp, ZERO); std::vector names; std::vector addresses; - if (fld.comp.size() == 0 || fld.comp.size() == 1) { // scalar + if (fld.comp.empty() || fld.comp.size() == 1) { // scalar names.push_back(fld.name()); addresses.push_back(0); if (fld.is_moment()) { @@ -732,15 +736,18 @@ namespace ntt { e_min = math::log10(e_min); e_max = math::log10(e_max); } - array_t energy { "energy", n_bins + 1 }; + const array_t energy { "energy", n_bins + 1 }; Kokkos::parallel_for( "GenerateEnergyBins", n_bins + 1, Lambda(index_t e) { if (log_bins) { - energy(e) = math::pow(10.0, e_min + (e_max - e_min) * e / n_bins); + energy(e) = math::pow(static_cast(10), + e_min + (e_max - e_min) * static_cast(e) / + static_cast(n_bins)); } else { - energy(e) = e_min + (e_max - e_min) * e / n_bins; + energy(e) = e_min + (e_max - e_min) * static_cast(e) / + static_cast(n_bins); } }); for (const auto& spec : g_writer.spectraWriters()) { @@ -791,6 +798,7 @@ namespace ntt { return true; } + // NOLINTBEGIN(bugprone-macro-parentheses) #define METADOMAIN_OUTPUT(S, M, D) \ template void Metadomain>::InitWriter(adios2::ADIOS*, \ const SimulationParams&); \ @@ -800,12 +808,12 @@ namespace ntt { timestep_t, \ simtime_t, \ simtime_t, \ - std::function::Dim, 6>&, \ - index_t, \ - timestep_t, \ - simtime_t, \ - const Domain>&)>) -> bool; + const std::function::Dim, 6>&, \ + index_t, \ + timestep_t, \ + simtime_t, \ + const Domain>&)>&) -> bool; NTT_FOREACH_SPECIALIZATION(METADOMAIN_OUTPUT) @@ -819,5 +827,6 @@ namespace ntt { #undef COMMVECTORPOTENTIAL #endif + // NOLINTEND(bugprone-macro-parentheses) } // namespace ntt diff --git a/src/framework/domain/metadomain_sort.cpp b/src/framework/domain/metadomain_sort.cpp index c529ca738..791bf31a8 100644 --- a/src/framework/domain/metadomain_sort.cpp +++ b/src/framework/domain/metadomain_sort.cpp @@ -1,5 +1,9 @@ #include "enums.h" +#include "global.h" +#include "traits/metric.h" + +#include "framework/domain/domain.h" #include "framework/domain/metadomain.h" #include "framework/parameters/parameters.h" #include "framework/specialization_registry.h" @@ -8,8 +12,7 @@ namespace ntt { - template - requires IsCompatibleWithMetadomain + template void Metadomain::SortParticles(simtime_t, timestep_t step, const SimulationParams&, @@ -30,6 +33,7 @@ namespace ntt { } } + // NOLINTBEGIN(bugprone-macro-parentheses) #define METADOMAIN_COMM(S, M, D) \ template void Metadomain>::SortParticles(simtime_t, \ timestep_t, \ @@ -37,7 +41,7 @@ namespace ntt { Domain>&) const; NTT_FOREACH_SPECIALIZATION(METADOMAIN_COMM) - #undef METADOMAIN_COMM + // NOLINTEND(bugprone-macro-parentheses) } // namespace ntt diff --git a/src/framework/domain/metadomain_stats.cpp b/src/framework/domain/metadomain_stats.cpp index 21176a836..9d3ded210 100644 --- a/src/framework/domain/metadomain_stats.cpp +++ b/src/framework/domain/metadomain_stats.cpp @@ -1,6 +1,7 @@ #include "enums.h" #include "global.h" +#include "traits/metric.h" #include "utils/comparators.h" #include "utils/error.h" #include "utils/log.h" @@ -8,6 +9,7 @@ #include "framework/containers/particles.h" #include "framework/domain/domain.h" +#include "framework/domain/mesh.h" #include "framework/domain/metadomain.h" #include "framework/parameters/parameters.h" #include "framework/specialization_registry.h" @@ -18,11 +20,13 @@ #include #include +#include +#include +#include namespace ntt { - template - requires IsCompatibleWithMetadomain + template void Metadomain::InitStatsWriter(const SimulationParams& params, bool is_resuming) { raise::ErrorIf( @@ -62,7 +66,7 @@ namespace ntt { } } - template + template auto ComputeMoments(const SimulationParams& params, const Mesh& mesh, const M& global_metric, @@ -70,7 +74,7 @@ namespace ntt { const std::vector& species, const std::vector& components) -> real_t { std::vector specs = species; - if (specs.size() == 0) { + if (specs.empty()) { // if no species specified, take all massive species for (auto& sp : prtl_species) { if (sp.mass() > 0) { @@ -119,13 +123,13 @@ namespace ntt { } } - template + template auto ReduceFields(Domain* domain, const M& global_metric, const std::vector& components) -> real_t { auto buffer { ZERO }; if constexpr (F == StatsID::JdotE) { - if (components.size() == 0) { + if (components.empty()) { Kokkos::parallel_reduce( "ReduceFields", domain->mesh.rangeActiveCells(), @@ -181,15 +185,15 @@ namespace ntt { return buffer / global_metric.totVolume(); } - template - requires IsCompatibleWithMetadomain + template auto Metadomain::WriteStats( const SimulationParams& params, timestep_t current_step, timestep_t finished_step, simtime_t current_time, simtime_t finished_time, - std::function&)> CustomStat) + const std::function< + real_t(const std::string&, timestep_t, simtime_t, const Domain&)>& CustomStat) -> bool { if (not(params.template get("output.stats.enable") and g_stats_writer.shouldWrite(finished_step, finished_time))) { @@ -272,7 +276,7 @@ namespace ntt { } } else { raise::Error("StatsID not implemented for particular SimEngine: " + - std::to_string(static_cast(S)), + std::string(SimEngine(S).to_string()), HERE); } } @@ -280,21 +284,22 @@ namespace ntt { return true; } -#define METADOMAIN_STATS(S, M, D) \ - template void Metadomain>::InitStatsWriter(const SimulationParams&, \ - bool); \ - template auto Metadomain>::WriteStats( \ - const SimulationParams&, \ - timestep_t, \ - timestep_t, \ - simtime_t, \ - simtime_t, \ - std::function< \ - real_t(const std::string&, timestep_t, simtime_t, const Domain>&)>) \ + // NOLINTBEGIN(bugprone-macro-parentheses) +#define METADOMAIN_STATS(S, M, D) \ + template void Metadomain>::InitStatsWriter(const SimulationParams&, \ + bool); \ + template auto Metadomain>::WriteStats( \ + const SimulationParams&, \ + timestep_t, \ + timestep_t, \ + simtime_t, \ + simtime_t, \ + const std::function< \ + real_t(const std::string&, timestep_t, simtime_t, const Domain>&)>&) \ -> bool; NTT_FOREACH_SPECIALIZATION(METADOMAIN_STATS) - #undef METADOMAIN_STATS + // NOLINTEND(bugprone-macro-parentheses) } // namespace ntt diff --git a/src/framework/parameters/algorithms.cpp b/src/framework/parameters/algorithms.cpp index 856d05fc7..4766db965 100644 --- a/src/framework/parameters/algorithms.cpp +++ b/src/framework/parameters/algorithms.cpp @@ -9,6 +9,9 @@ #include +#include +#include + namespace ntt { namespace params { @@ -16,7 +19,7 @@ namespace ntt { const std::map& extra, const toml::value& toml_data) { CFL = toml::find_or(toml_data, "algorithms", "timestep", "CFL", defaults::cfl); - dt = CFL * dx0; + dt = CFL.value() * dx0; dt_correction_factor = toml::find_or(toml_data, "algorithms", "timestep", @@ -36,64 +39,64 @@ namespace ntt { "fieldsolver", "enable", true); - - fieldsolver_stencil_coeffs["delta_x"] = toml::find_or( + fieldsolver_stencil_coeffs.emplace(); + (*fieldsolver_stencil_coeffs)["delta_x"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "delta_x", defaults::fieldsolver::delta_x); - fieldsolver_stencil_coeffs["delta_y"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["delta_y"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "delta_y", defaults::fieldsolver::delta_y); - fieldsolver_stencil_coeffs["delta_z"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["delta_z"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "delta_z", defaults::fieldsolver::delta_z); - fieldsolver_stencil_coeffs["beta_xy"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["beta_xy"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "beta_xy", defaults::fieldsolver::beta_xy); - fieldsolver_stencil_coeffs["beta_yx"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["beta_yx"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "beta_yx", defaults::fieldsolver::beta_yx); - fieldsolver_stencil_coeffs["beta_xz"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["beta_xz"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "beta_xz", defaults::fieldsolver::beta_xz); - fieldsolver_stencil_coeffs["beta_zx"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["beta_zx"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "beta_zx", defaults::fieldsolver::beta_zx); - fieldsolver_stencil_coeffs["beta_yz"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["beta_yz"] = toml::find_or( toml_data, "algorithms", "fieldsolver", "beta_yz", defaults::fieldsolver::beta_yz); - fieldsolver_stencil_coeffs["beta_zy"] = toml::find_or( + (*fieldsolver_stencil_coeffs)["beta_zy"] = toml::find_or( toml_data, "algorithms", "fieldsolver", @@ -129,28 +132,28 @@ namespace ntt { void Algorithms::setParams(const std::map& extra, SimulationParams* params) const { - params->set("algorithms.timestep.CFL", CFL); - params->set("algorithms.timestep.dt", dt); - params->set("algorithms.timestep.correction", dt_correction_factor); + params->set("algorithms.timestep.CFL", CFL.value()); + params->set("algorithms.timestep.dt", dt.value()); + params->set("algorithms.timestep.correction", dt_correction_factor.value()); - params->set("algorithms.current_filters", number_of_current_filters); + params->set("algorithms.current_filters", number_of_current_filters.value()); - params->set("algorithms.deposit.enable", deposit_enable); - params->set("algorithms.deposit.order", deposit_order); + params->set("algorithms.deposit.enable", deposit_enable.value()); + params->set("algorithms.deposit.order", deposit_order.value()); - params->set("algorithms.fieldsolver.enable", fieldsolver_enable); - for (const auto& [key, value] : fieldsolver_stencil_coeffs) { + params->set("algorithms.fieldsolver.enable", fieldsolver_enable.value()); + for (const auto& [key, value] : fieldsolver_stencil_coeffs.value()) { params->set("algorithms.fieldsolver." + key, value); } if (extra.at("gr")) { - params->set("algorithms.gr.pusher_eps", gr_pusher_eps); - params->set("algorithms.gr.pusher_niter", gr_pusher_niter); + params->set("algorithms.gr.pusher_eps", gr_pusher_eps.value()); + params->set("algorithms.gr.pusher_niter", gr_pusher_niter.value()); } if (extra.at("gca")) { - params->set("algorithms.gca.e_ovr_b_max", gca_e_ovr_b_max); - params->set("algorithms.gca.larmor_max", gca_larmor_max); + params->set("algorithms.gca.e_ovr_b_max", gca_e_ovr_b_max.value()); + params->set("algorithms.gca.larmor_max", gca_larmor_max.value()); } } diff --git a/src/framework/parameters/algorithms.h b/src/framework/parameters/algorithms.h index a496cb7b6..97edb244a 100644 --- a/src/framework/parameters/algorithms.h +++ b/src/framework/parameters/algorithms.h @@ -19,32 +19,33 @@ #include #include +#include #include namespace ntt { namespace params { struct Algorithms { - real_t CFL; - real_t dt; - real_t dt_correction_factor; + std::optional CFL; + std::optional dt; + std::optional dt_correction_factor; - unsigned short number_of_current_filters; + std::optional number_of_current_filters; - bool deposit_enable; - unsigned short deposit_order; + std::optional deposit_enable; + std::optional deposit_order; - bool fieldsolver_enable; - std::map fieldsolver_stencil_coeffs; + std::optional fieldsolver_enable; + std::optional> fieldsolver_stencil_coeffs; - real_t gr_pusher_eps; - unsigned short gr_pusher_niter; + std::optional gr_pusher_eps; + std::optional gr_pusher_niter; - real_t gca_e_ovr_b_max; - real_t gca_larmor_max; + std::optional gca_e_ovr_b_max; + std::optional gca_larmor_max; - real_t synchrotron_gamma_rad; - real_t compton_gamma_rad; + std::optional synchrotron_gamma_rad; + std::optional compton_gamma_rad; void read(real_t, const std::map&, const toml::value&); void setParams(const std::map&, SimulationParams*) const; diff --git a/src/framework/parameters/extra.cpp b/src/framework/parameters/extra.cpp index 3e8c2dcd9..dbee39c7c 100644 --- a/src/framework/parameters/extra.cpp +++ b/src/framework/parameters/extra.cpp @@ -1,9 +1,17 @@ #include "framework/parameters/extra.h" #include "defaults.h" +#include "global.h" #include "utils/numeric.h" +#include "framework/parameters/parameters.h" + +#include + +#include +#include + namespace ntt { namespace params { @@ -62,91 +70,94 @@ namespace ntt { params->template get("scales.omegaB0") * static_cast(0.1) * params->template get("algorithms.timestep.dt") * - SQR(synchrotron_gamma_qed / synchrotron_gamma_rad) / - synchrotron_photon_weight; - synchrotron_nominal_photon_energy = ONE / SQR(synchrotron_gamma_qed); + SQR(synchrotron_gamma_qed.value() / synchrotron_gamma_rad.value()) / + synchrotron_photon_weight.value(); + synchrotron_nominal_photon_energy = ONE / + SQR(synchrotron_gamma_qed.value()); } if (extra.at("compton_emission")) { - compton_gamma_rad = toml::find_or(toml_data, + compton_gamma_rad = toml::find_or(toml_data, "radiation", "drag", "compton", "gamma_rad", defaults::compton::gamma_rad); - compton_gamma_qed = toml::find_or(toml_data, + compton_gamma_qed = toml::find_or(toml_data, "radiation", "emission", "compton", "gamma_qed", defaults::compton::gamma_qed); - compton_energy_min = toml::find_or(toml_data, + compton_energy_min = toml::find_or(toml_data, "radiation", "emission", "compton", "photon_energy_min", defaults::compton::energy_min); - compton_photon_weight = toml::find_or(toml_data, + compton_photon_weight = toml::find_or(toml_data, "radiation", "emission", "compton", "photon_weight", ONE); - compton_photon_species = toml::find(toml_data, + compton_photon_species = toml::find(toml_data, "radiation", "emission", "compton", "photon_species"); - compton_nominal_probability = params->template get( - "scales.omegaB0") * - static_cast(0.1) * - params->template get( - "algorithms.timestep.dt") * - SQR(compton_gamma_qed / compton_gamma_rad) / - compton_photon_weight; - compton_nominal_photon_energy = ONE / SQR(compton_gamma_qed); + compton_nominal_probability = + params->template get("scales.omegaB0") * + static_cast(0.1) * + params->template get("algorithms.timestep.dt") * + SQR(compton_gamma_qed.value() / compton_gamma_rad.value()) / + compton_photon_weight.value(); + compton_nominal_photon_energy = ONE / SQR(compton_gamma_qed.value()); } } void Extra::setParams(const std::map& extra, SimulationParams* params) const { if (extra.at("synchrotron_drag")) { - params->set("radiation.drag.synchrotron.gamma_rad", synchrotron_gamma_rad); + params->set("radiation.drag.synchrotron.gamma_rad", + synchrotron_gamma_rad.value()); } if (extra.at("compton_drag")) { - params->set("radiation.drag.compton.gamma_rad", compton_gamma_rad); + params->set("radiation.drag.compton.gamma_rad", compton_gamma_rad.value()); } if (extra.at("synchrotron_emission")) { - params->set("radiation.drag.synchrotron.gamma_rad", synchrotron_gamma_rad); + params->set("radiation.drag.synchrotron.gamma_rad", + synchrotron_gamma_rad.value()); params->set("radiation.emission.synchrotron.gamma_qed", - synchrotron_gamma_qed); + synchrotron_gamma_qed.value()); params->set("radiation.emission.synchrotron.photon_energy_min", - synchrotron_energy_min); + synchrotron_energy_min.value()); params->set("radiation.emission.synchrotron.photon_weight", - synchrotron_photon_weight); + synchrotron_photon_weight.value()); params->set("radiation.emission.synchrotron.photon_species", - synchrotron_photon_species); + synchrotron_photon_species.value()); params->set("radiation.emission.synchrotron.nominal_probability", - synchrotron_nominal_probability); + synchrotron_nominal_probability.value()); params->set("radiation.emission.synchrotron.nominal_photon_energy", - synchrotron_nominal_photon_energy); + synchrotron_nominal_photon_energy.value()); } if (extra.at("compton_emission")) { - params->set("radiation.drag.compton.gamma_rad", compton_gamma_rad); - params->set("radiation.emission.compton.gamma_qed", compton_gamma_qed); + params->set("radiation.drag.compton.gamma_rad", compton_gamma_rad.value()); + params->set("radiation.emission.compton.gamma_qed", + compton_gamma_qed.value()); params->set("radiation.emission.compton.photon_energy_min", - compton_energy_min); + compton_energy_min.value()); params->set("radiation.emission.compton.photon_weight", - compton_photon_weight); + compton_photon_weight.value()); params->set("radiation.emission.compton.photon_species", - compton_photon_species); + compton_photon_species.value()); params->set("radiation.emission.compton.nominal_probability", - compton_nominal_probability); + compton_nominal_probability.value()); params->set("radiation.emission.compton.nominal_photon_energy", - compton_nominal_photon_energy); + compton_nominal_photon_energy.value()); } } } // namespace params diff --git a/src/framework/parameters/extra.h b/src/framework/parameters/extra.h index cf5c0849c..29de4527a 100644 --- a/src/framework/parameters/extra.h +++ b/src/framework/parameters/extra.h @@ -1,5 +1,5 @@ /** - * @file framework/parameters/algorithms.h + * @file framework/parameters/extra.h * @brief Auxiliary functions for reading in extra physics parameters * @implements * - ntt::params::Extra @@ -19,6 +19,7 @@ #include #include +#include #include namespace ntt { @@ -26,23 +27,23 @@ namespace ntt { struct Extra { // radiative drag parameters - real_t synchrotron_gamma_rad; - real_t compton_gamma_rad; + std::optional synchrotron_gamma_rad; + std::optional compton_gamma_rad; // emission parameters - real_t synchrotron_energy_min; - real_t synchrotron_gamma_qed; - real_t synchrotron_photon_weight; - spidx_t synchrotron_photon_species; - real_t synchrotron_nominal_probability; - real_t synchrotron_nominal_photon_energy; - - real_t compton_energy_min; - real_t compton_gamma_qed; - real_t compton_photon_weight; - spidx_t compton_photon_species; - real_t compton_nominal_probability; - real_t compton_nominal_photon_energy; + std::optional synchrotron_energy_min; + std::optional synchrotron_gamma_qed; + std::optional synchrotron_photon_weight; + std::optional synchrotron_photon_species; + std::optional synchrotron_nominal_probability; + std::optional synchrotron_nominal_photon_energy; + + std::optional compton_energy_min; + std::optional compton_gamma_qed; + std::optional compton_photon_weight; + std::optional compton_photon_species; + std::optional compton_nominal_probability; + std::optional compton_nominal_photon_energy; void read(const std::map&, const toml::value&, diff --git a/src/framework/parameters/grid.cpp b/src/framework/parameters/grid.cpp index 9e5d39df5..b701ab98e 100644 --- a/src/framework/parameters/grid.cpp +++ b/src/framework/parameters/grid.cpp @@ -1,6 +1,7 @@ #include "framework/parameters/grid.h" #include "defaults.h" +#include "enums.h" #include "global.h" #include "utils/error.h" @@ -18,9 +19,17 @@ #include +#if defined(MPI_ENABLED) + #include +#endif + +#include +#include +#include #include #include #include +#include #include namespace ntt { @@ -53,7 +62,7 @@ namespace ntt { "boundaries", "fields"); { - raise::ErrorIf(flds_bc.size() < 1 || flds_bc.size() > 3, + raise::ErrorIf(flds_bc.empty() || flds_bc.size() > 3, "invalid `grid.boundaries.fields`", HERE); params->promiseToDefine("grid.boundaries.fields"); @@ -85,7 +94,7 @@ namespace ntt { "boundaries", "particles"); { - raise::ErrorIf(prtl_bc.size() < 1 || prtl_bc.size() > 3, + raise::ErrorIf(prtl_bc.empty() || prtl_bc.size() > 3, "invalid `grid.boundaries.particles`", HERE); params->promiseToDefine("grid.boundaries.particles"); @@ -112,7 +121,7 @@ namespace ntt { } std::vector> flds_bc_enum; std::vector> prtl_bc_enum; - if (coord_enum == Coord::Cart) { + if (coord_enum == Coord::Cartesian) { raise::ErrorIf(flds_bc.size() != (std::size_t)dim, "invalid `grid.boundaries.fields`", HERE); @@ -120,14 +129,14 @@ namespace ntt { "invalid `grid.boundaries.particles`", HERE); for (auto d { 0u }; d < (dim_t)dim; ++d) { - flds_bc_enum.push_back({}); - prtl_bc_enum.push_back({}); - const auto fbc = flds_bc[d]; - const auto pbc = prtl_bc[d]; - raise::ErrorIf(fbc.size() < 1 || fbc.size() > 2, + flds_bc_enum.emplace_back(); + prtl_bc_enum.emplace_back(); + const auto& fbc = flds_bc[d]; + const auto& pbc = prtl_bc[d]; + raise::ErrorIf(fbc.empty() || fbc.size() > 2, "invalid `grid.boundaries.fields`", HERE); - raise::ErrorIf(pbc.size() < 1 || pbc.size() > 2, + raise::ErrorIf(pbc.empty() || pbc.size() > 2, "invalid `grid.boundaries.particles`", HERE); auto fbc_enum = FldsBC::pick(fmt::toLower(fbc[0]).c_str()); @@ -136,35 +145,35 @@ namespace ntt { raise::ErrorIf(fbc_enum != FldsBC::PERIODIC, "invalid `grid.boundaries.fields`", HERE); - flds_bc_enum.back().push_back(FldsBC(FldsBC::PERIODIC)); - flds_bc_enum.back().push_back(FldsBC(FldsBC::PERIODIC)); + flds_bc_enum.back().emplace_back(FldsBC::PERIODIC); + flds_bc_enum.back().emplace_back(FldsBC::PERIODIC); } else { raise::ErrorIf(fbc_enum == FldsBC::PERIODIC, "invalid `grid.boundaries.fields`", HERE); - flds_bc_enum.back().push_back(fbc_enum); + flds_bc_enum.back().emplace_back(fbc_enum); auto fbc_enum = FldsBC::pick(fmt::toLower(fbc[1]).c_str()); raise::ErrorIf(fbc_enum == FldsBC::PERIODIC, "invalid `grid.boundaries.fields`", HERE); - flds_bc_enum.back().push_back(fbc_enum); + flds_bc_enum.back().emplace_back(fbc_enum); } if (pbc.size() == 1) { raise::ErrorIf(pbc_enum != PrtlBC::PERIODIC, "invalid `grid.boundaries.particles`", HERE); - prtl_bc_enum.back().push_back(PrtlBC(PrtlBC::PERIODIC)); - prtl_bc_enum.back().push_back(PrtlBC(PrtlBC::PERIODIC)); + prtl_bc_enum.back().emplace_back(PrtlBC::PERIODIC); + prtl_bc_enum.back().emplace_back(PrtlBC::PERIODIC); } else { raise::ErrorIf(pbc_enum == PrtlBC::PERIODIC, "invalid `grid.boundaries.particles`", HERE); - prtl_bc_enum.back().push_back(pbc_enum); + prtl_bc_enum.back().emplace_back(pbc_enum); auto pbc_enum = PrtlBC::pick(fmt::toLower(pbc[1]).c_str()); raise::ErrorIf(pbc_enum == PrtlBC::PERIODIC, "invalid `grid.boundaries.particles`", HERE); - prtl_bc_enum.back().push_back(pbc_enum); + prtl_bc_enum.back().emplace_back(pbc_enum); } } } else { @@ -232,8 +241,8 @@ namespace ntt { prtl_bc_enum[d].size() != 2, fmt::format("invalid inferred `grid.boundaries.particles[%d]`", d), HERE); - flds_bc_pairwise.push_back({ flds_bc_enum[d][0], flds_bc_enum[d][1] }); - prtl_bc_pairwise.push_back({ prtl_bc_enum[d][0], prtl_bc_enum[d][1] }); + flds_bc_pairwise.emplace_back(flds_bc_enum[d][0], flds_bc_enum[d][1]); + prtl_bc_pairwise.emplace_back(prtl_bc_enum[d][0], prtl_bc_enum[d][1]); } return { flds_bc_pairwise, prtl_bc_pairwise }; } @@ -243,7 +252,8 @@ namespace ntt { const boundaries_t& extent_pairwise, const toml::value& toml_data) { if (needs_match_boundaries) { - if (coord_enum == Coord::Cart) { + match_ds_array.emplace(); + if (coord_enum == Coord::Cartesian) { auto min_extent = std::numeric_limits::max(); for (const auto& e : extent_pairwise) { min_extent = std::min(min_extent, e.second - e.first); @@ -252,7 +262,7 @@ namespace ntt { try { auto ds = toml::find(toml_data, "grid", "boundaries", "match", "ds"); for (auto d = 0u; d < dim; ++d) { - match_ds_array.push_back({ ds, ds }); + match_ds_array->emplace_back(ds, ds); } } catch (...) { try { @@ -267,18 +277,18 @@ namespace ntt { HERE); for (auto d = 0u; d < dim; ++d) { if (ds[d].size() == 1) { - match_ds_array.push_back({ ds[d][0], ds[d][0] }); + match_ds_array->emplace_back(ds[d][0], ds[d][0]); } else if (ds[d].size() == 2) { - match_ds_array.push_back({ ds[d][0], ds[d][1] }); - } else if (ds[d].size() == 0) { - match_ds_array.push_back({}); + match_ds_array->emplace_back(ds[d][0], ds[d][1]); + } else if (ds[d].empty()) { + match_ds_array->emplace_back(); } else { raise::Error("invalid `grid.boundaries.match.ds`", HERE); } } } catch (...) { for (auto d = 0u; d < dim; ++d) { - match_ds_array.push_back({ default_ds, default_ds }); + match_ds_array->emplace_back(default_ds, default_ds); } } } @@ -291,12 +301,12 @@ namespace ntt { "match", "ds", r_extent * defaults::bc::match::ds_frac); - match_ds_array.push_back({ ds, ds }); + match_ds_array->emplace_back(ds, ds); } } if (needs_absorb_boundaries) { - if (coord_enum == Coord::Cart) { + if (coord_enum == Coord::Cartesian) { auto min_extent = std::numeric_limits::max(); for (const auto& e : extent_pairwise) { min_extent = std::min(min_extent, e.second - e.first); @@ -336,7 +346,7 @@ namespace ntt { "density"); atmosphere_ds = toml::find_or(toml_data, "grid", "boundaries", "atmosphere", "ds", ZERO); - atmosphere_g = atmosphere_temperature / atmosphere_height; + atmosphere_g = atmosphere_temperature.value() / atmosphere_height.value(); atmosphere_species = toml::find>( toml_data, "grid", @@ -348,19 +358,21 @@ namespace ntt { void Boundaries::setParams(SimulationParams* params) const { if (needs_match_boundaries) { - params->set("grid.boundaries.match.ds", match_ds_array); + params->set("grid.boundaries.match.ds", match_ds_array.value()); } if (needs_absorb_boundaries) { - params->set("grid.boundaries.absorb.ds", absorb_ds); + params->set("grid.boundaries.absorb.ds", absorb_ds.value()); } if (needs_atmosphere_boundaries) { params->set("grid.boundaries.atmosphere.temperature", - atmosphere_temperature); - params->set("grid.boundaries.atmosphere.density", atmosphere_density); - params->set("grid.boundaries.atmosphere.height", atmosphere_height); - params->set("grid.boundaries.atmosphere.ds", atmosphere_ds); - params->set("grid.boundaries.atmosphere.g", atmosphere_g); - params->set("grid.boundaries.atmosphere.species", atmosphere_species); + atmosphere_temperature.value()); + params->set("grid.boundaries.atmosphere.density", + atmosphere_density.value()); + params->set("grid.boundaries.atmosphere.height", atmosphere_height.value()); + params->set("grid.boundaries.atmosphere.ds", atmosphere_ds.value()); + params->set("grid.boundaries.atmosphere.g", atmosphere_g.value()); + params->set("grid.boundaries.atmosphere.species", + atmosphere_species.value()); } } @@ -387,16 +399,17 @@ namespace ntt { /* resolution and dimension ------------------------------------------- */ resolution = toml::find>(toml_data, "grid", "resolution"); - raise::ErrorIf(resolution.size() < 1 || resolution.size() > 3, + raise::ErrorIf(resolution->empty() or resolution->size() > 3, "invalid `grid.resolution`", HERE); - dim = static_cast(resolution.size()); + dim = static_cast(resolution->size()); - if (domain_decomposition.size() > dim) { - domain_decomposition.erase(domain_decomposition.begin() + (std::size_t)(dim), - domain_decomposition.end()); + if (domain_decomposition->size() > dim.value()) { + domain_decomposition->erase( + domain_decomposition->begin() + static_cast(dim.value()), + domain_decomposition->end()); } - raise::ErrorIf(domain_decomposition.size() != dim, + raise::ErrorIf(domain_decomposition->size() != dim.value(), "invalid `simulation.domain.decomposition`", HERE); @@ -404,6 +417,8 @@ namespace ntt { metric_enum = Metric::pick( fmt::toLower(toml::find(toml_data, "grid", "metric", "metric")) .c_str()); + metric_params.emplace(); + metric_params_short_.emplace(); std::string coord; if (metric_enum == Metric::Minkowski) { raise::ErrorIf(engine_enum != SimEngine::SRPIC, @@ -413,42 +428,42 @@ namespace ntt { } else if (metric_enum == Metric::QKerr_Schild or metric_enum == Metric::QSpherical) { // quasi-spherical geometry - raise::ErrorIf(dim == Dim::_1D, + raise::ErrorIf(dim.value() == Dim::_1D, "not enough dimensions for qspherical geometry", HERE); - raise::ErrorIf(dim == Dim::_3D, + raise::ErrorIf(dim.value() == Dim::_3D, "3D not implemented for qspherical geometry", HERE); - coord = "qsph"; - metric_params["qsph_r0"] = toml::find_or(toml_data, - "grid", - "metric", - "qsph_r0", - defaults::qsph::r0); - metric_params["qsph_h"] = toml::find_or(toml_data, - "grid", - "metric", - "qsph_h", - defaults::qsph::h); + coord = "qsph"; + (*metric_params)["qsph_r0"] = toml::find_or(toml_data, + "grid", + "metric", + "qsph_r0", + defaults::qsph::r0); + (*metric_params)["qsph_h"] = toml::find_or(toml_data, + "grid", + "metric", + "qsph_h", + defaults::qsph::h); } else { // spherical geometry - raise::ErrorIf(dim == Dim::_1D, + raise::ErrorIf(dim.value() == Dim::_1D, "not enough dimensions for spherical geometry", HERE); - raise::ErrorIf(dim == Dim::_3D, + raise::ErrorIf(dim.value() == Dim::_3D, "3D not implemented for spherical geometry", HERE); coord = "sph"; } if ((engine_enum == SimEngine::GRPIC) && (metric_enum != Metric::Kerr_Schild_0)) { - const auto ks_a = toml::find_or(toml_data, + const auto ks_a = toml::find_or(toml_data, "grid", "metric", "ks_a", defaults::ks::a); - metric_params["ks_a"] = ks_a; - metric_params["ks_rh"] = ONE + math::sqrt(ONE - SQR(ks_a)); + (*metric_params)["ks_a"] = ks_a; + (*metric_params)["ks_rh"] = ONE + math::sqrt(ONE - SQR(ks_a)); } coord_enum = Coord::pick(coord.c_str()); @@ -456,74 +471,85 @@ namespace ntt { extent = toml::find>>(toml_data, "grid", "extent"); - - if (extent.size() > dim) { - extent.erase(extent.begin() + (std::size_t)(dim), extent.end()); + extent_pairwise_.emplace(); + if (extent->size() > dim.value()) { + extent->erase(extent->begin() + static_cast(dim.value()), + extent->end()); } - raise::ErrorIf(extent[0].size() != 2, "invalid `grid.extent[0]`", HERE); - if (coord_enum != Coord::Cart) { - raise::ErrorIf(extent.size() > 1, + raise::ErrorIf(extent->at(0).size() != 2, "invalid `grid.extent[0]`", HERE); + if (coord_enum != Coord::Cartesian) { + raise::ErrorIf(extent->size() > 1, "invalid `grid.extent` for non-cartesian geometry", HERE); - extent.push_back({ ZERO, constant::PI }); - if (dim == Dim::_3D) { - extent.push_back({ ZERO, TWO * constant::PI }); + extent->push_back({ ZERO, constant::PI }); + if (dim.value() == Dim::_3D) { + extent->push_back({ ZERO, TWO * constant::PI }); } } - raise::ErrorIf(extent.size() != dim, "invalid inferred `grid.extent`", HERE); - for (auto d { 0u }; d < (dim_t)dim; ++d) { - raise::ErrorIf(extent[d].size() != 2, + raise::ErrorIf(extent->size() != dim.value(), + "invalid inferred `grid.extent`", + HERE); + for (auto d { 0u }; d < (dim_t)(dim.value()); ++d) { + raise::ErrorIf(extent->at(d).size() != 2, fmt::format("invalid inferred `grid.extent[%d]`", d), HERE); - extent_pairwise_.push_back({ extent[d][0], extent[d][1] }); + extent_pairwise_->emplace_back(extent->at(d)[0], extent->at(d)[1]); } /* metric parameters ------------------------------------------------------ */ - if (coord_enum == Coord::Qsph) { - metric_params_short_["r0"] = metric_params["qsph_r0"]; - metric_params_short_["h"] = metric_params["qsph_h"]; + if (coord_enum == Coord::Qspherical) { + (*metric_params_short_)["r0"] = (*metric_params)["qsph_r0"]; + (*metric_params_short_)["h"] = (*metric_params)["qsph_h"]; } if ((engine_enum == SimEngine::GRPIC) && (metric_enum != Metric::Kerr_Schild_0)) { - metric_params_short_["a"] = metric_params["ks_a"]; + (*metric_params_short_)["a"] = (*metric_params)["ks_a"]; } // set("grid.metric.params", params); std::pair dx0_V0; if (metric_enum == Metric::Minkowski) { if (dim == Dim::_1D) { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } else if (dim == Dim::_2D) { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } else { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } } else if (metric_enum == Metric::Spherical) { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } else if (metric_enum == Metric::QSpherical) { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } else if (metric_enum == Metric::Kerr_Schild) { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } else if (metric_enum == Metric::Kerr_Schild_0) { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } else if (metric_enum == Metric::QKerr_Schild) { - dx0_V0 = get_dx0_V0>(resolution, - extent_pairwise_, - metric_params_short_); + dx0_V0 = get_dx0_V0>( + resolution.value(), + extent_pairwise_.value(), + metric_params_short_.value()); } auto [dx0, V0] = dx0_V0; scale_dx0 = dx0; @@ -531,21 +557,21 @@ namespace ntt { } void Grid::setParams(SimulationParams* params) const { - params->set("simulation.domain.number", number_of_domains); - params->set("simulation.domain.decomposition", domain_decomposition); + params->set("simulation.domain.number", number_of_domains.value()); + params->set("simulation.domain.decomposition", domain_decomposition.value()); - params->set("grid.resolution", resolution); - params->set("grid.dim", dim); + params->set("grid.resolution", resolution.value()); + params->set("grid.dim", dim.value()); params->set("grid.metric.metric", metric_enum); params->set("grid.metric.coord", coord_enum); - for (const auto& [key, value] : metric_params) { + for (const auto& [key, value] : metric_params.value()) { params->set("grid.metric." + key, value); } - params->set("grid.metric.params", metric_params_short_); - params->set("grid.extent", extent_pairwise_); + params->set("grid.metric.params", metric_params_short_.value()); + params->set("grid.extent", extent_pairwise_.value()); - params->set("scales.dx0", scale_dx0); - params->set("scales.V0", scale_V0); + params->set("scales.dx0", scale_dx0.value()); + params->set("scales.V0", scale_V0.value()); } } // namespace params diff --git a/src/framework/parameters/grid.h b/src/framework/parameters/grid.h index 978b7f329..3aa6aa090 100644 --- a/src/framework/parameters/grid.h +++ b/src/framework/parameters/grid.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -27,19 +28,19 @@ namespace ntt { namespace params { struct Boundaries { - const bool needs_match_boundaries; - boundaries_t match_ds_array; + const bool needs_match_boundaries; + std::optional> match_ds_array; - const bool needs_absorb_boundaries; - real_t absorb_ds; + const bool needs_absorb_boundaries; + std::optional absorb_ds; - const bool needs_atmosphere_boundaries; - real_t atmosphere_temperature; - real_t atmosphere_height; - real_t atmosphere_density; - real_t atmosphere_g; - real_t atmosphere_ds; - std::pair atmosphere_species; + const bool needs_atmosphere_boundaries; + std::optional atmosphere_temperature; + std::optional atmosphere_height; + std::optional atmosphere_density; + std::optional atmosphere_g; + std::optional atmosphere_ds; + std::optional> atmosphere_species; Boundaries(bool needs_match, bool needs_absorb, bool needs_atmosphere) : needs_match_boundaries { needs_match } @@ -54,22 +55,22 @@ namespace ntt { }; struct Grid { - unsigned int number_of_domains; - std::vector domain_decomposition; + std::optional number_of_domains; + std::optional> domain_decomposition; - std::vector resolution; - Dimension dim; + std::optional> resolution; + std::optional dim; - std::vector> extent; - boundaries_t extent_pairwise_; + std::optional>> extent; + std::optional> extent_pairwise_; - Metric metric_enum = Metric::INVALID; - Coord coord_enum = Coord::INVALID; - std::map metric_params; - std::map metric_params_short_; + Metric metric_enum = Metric::INVALID; + Coord coord_enum = Coord::INVALID; + std::optional> metric_params; + std::optional> metric_params_short_; - real_t scale_dx0; - real_t scale_V0; + std::optional scale_dx0; + std::optional scale_V0; void read(const SimEngine&, const toml::value&); void setParams(SimulationParams*) const; diff --git a/src/framework/parameters/output.cpp b/src/framework/parameters/output.cpp index 547800971..badb6d9bd 100644 --- a/src/framework/parameters/output.cpp +++ b/src/framework/parameters/output.cpp @@ -6,8 +6,14 @@ #include "utils/error.h" #include "utils/log.h" +#include "framework/parameters/parameters.h" + #include +#include +#include +#include + namespace ntt { namespace params { @@ -26,28 +32,29 @@ namespace ntt { "separate_files=false is deprecated", HERE); + categories.emplace(); for (const auto& category : { "fields", "particles", "spectra", "stats" }) { - const auto q_int = toml::find_or(toml_data, + const auto q_int = toml::find_or(toml_data, "output", category, "interval", 0); - const auto q_int_time = toml::find_or(toml_data, + const auto q_int_time = toml::find_or(toml_data, "output", category, "interval_time", -1.0); - categories[category].enable = toml::find_or(toml_data, - "output", - category, - "enable", - true); + (*categories)[category].enable = toml::find_or(toml_data, + "output", + category, + "enable", + true); if ((q_int == 0) and (q_int_time == -1.0)) { - categories[category].interval = global_interval; - categories[category].interval_time = global_interval_time; + (*categories)[category].interval = global_interval.value(); + (*categories)[category].interval_time = global_interval_time.value(); } else { - categories[category].interval = q_int; - categories[category].interval_time = q_int_time; + (*categories)[category].interval = q_int; + (*categories)[category].interval_time = q_int_time; } } @@ -62,7 +69,7 @@ namespace ntt { "fields", "custom", std::vector {}); - if (flds_out.size() == 0) { + if (flds_out.empty()) { raise::Warning("No fields output specified", HERE); } fields_quantities = flds_out; @@ -72,13 +79,14 @@ namespace ntt { "fields", "mom_smooth", defaults::output::mom_smooth); + fields_downsampling.emplace(); try { auto field_dwn_ = toml::find>(toml_data, "output", "fields", "downsampling"); - for (auto i = 0u; i < field_dwn_.size(); ++i) { - fields_downsampling.push_back(field_dwn_[i]); + for (const auto& dwn : field_dwn_) { + fields_downsampling->push_back(dwn); } } catch (...) { try { @@ -87,22 +95,22 @@ namespace ntt { "fields", "downsampling"); for (auto i = 0u; i < dim; ++i) { - fields_downsampling.push_back(field_dwn_); + fields_downsampling->push_back(field_dwn_); } } catch (...) { for (auto i = 0u; i < dim; ++i) { - fields_downsampling.push_back(1u); + fields_downsampling->push_back(1u); } } } - raise::ErrorIf(fields_downsampling.size() > 3, + raise::ErrorIf(fields_downsampling->size() > 3, "invalid `output.fields.downsampling`", HERE); - if (fields_downsampling.size() > dim) { - fields_downsampling.erase(fields_downsampling.begin() + (std::size_t)(dim), - fields_downsampling.end()); + if (fields_downsampling->size() > dim) { + fields_downsampling->erase(fields_downsampling->begin() + dim, + fields_downsampling->end()); } - for (const auto& dwn : fields_downsampling) { + for (const auto& dwn : fields_downsampling.value()) { raise::ErrorIf(dwn == 0, "downsampling factor must be nonzero", HERE); } @@ -159,8 +167,8 @@ namespace ntt { /* Debug ---------------------------------------------------------------- */ debug_as_is = toml::find_or(toml_data, "output", "debug", "as_is", false); debug_ghosts = toml::find_or(toml_data, "output", "debug", "ghosts", false); - if (debug_ghosts) { - for (const auto& dwn : fields_downsampling) { + if (debug_ghosts.value()) { + for (const auto& dwn : fields_downsampling.value()) { raise::ErrorIf( dwn != 1, "full resolution required when outputting with ghost cells", @@ -170,34 +178,34 @@ namespace ntt { } void Output::setParams(SimulationParams* params) const { - params->set("output.format", format); - params->set("output.interval", global_interval); - params->set("output.interval_time", global_interval_time); - for (const auto& [category, cat_params] : categories) { + params->set("output.format", format.value()); + params->set("output.interval", global_interval.value()); + params->set("output.interval_time", global_interval_time.value()); + for (const auto& [category, cat_params] : categories.value()) { params->set("output." + category + ".enable", cat_params.enable); params->set("output." + category + ".interval", cat_params.interval); params->set("output." + category + ".interval_time", cat_params.interval_time); } - params->set("output.fields.quantities", fields_quantities); - params->set("output.fields.custom", fields_custom_quantities); - params->set("output.fields.mom_smooth", fields_mom_smooth); - params->set("output.fields.downsampling", fields_downsampling); + params->set("output.fields.quantities", fields_quantities.value()); + params->set("output.fields.custom", fields_custom_quantities.value()); + params->set("output.fields.mom_smooth", fields_mom_smooth.value()); + params->set("output.fields.downsampling", fields_downsampling.value()); - params->set("output.particles.species", particles_species); - params->set("output.particles.stride", particles_stride); + params->set("output.particles.species", particles_species.value()); + params->set("output.particles.stride", particles_stride.value()); - params->set("output.spectra.e_min", spectra_e_min); - params->set("output.spectra.e_max", spectra_e_max); - params->set("output.spectra.log_bins", spectra_log_bins); - params->set("output.spectra.n_bins", spectra_n_bins); + params->set("output.spectra.e_min", spectra_e_min.value()); + params->set("output.spectra.e_max", spectra_e_max.value()); + params->set("output.spectra.log_bins", spectra_log_bins.value()); + params->set("output.spectra.n_bins", spectra_n_bins.value()); - params->set("output.stats.quantities", stats_quantities); - params->set("output.stats.custom", stats_custom_quantities); + params->set("output.stats.quantities", stats_quantities.value()); + params->set("output.stats.custom", stats_custom_quantities.value()); - params->set("output.debug.as_is", debug_as_is); - params->set("output.debug.ghosts", debug_ghosts); + params->set("output.debug.as_is", debug_as_is.value()); + params->set("output.debug.ghosts", debug_ghosts.value()); } } // namespace params diff --git a/src/framework/parameters/output.h b/src/framework/parameters/output.h index e301fc745..94afff324 100644 --- a/src/framework/parameters/output.h +++ b/src/framework/parameters/output.h @@ -18,7 +18,9 @@ #include +#include #include +#include #include #include @@ -32,31 +34,31 @@ namespace ntt { }; struct Output { - std::string format; + std::optional format; - timestep_t global_interval; - simtime_t global_interval_time; + std::optional global_interval; + std::optional global_interval_time; - std::map categories; + std::optional> categories; - std::vector fields_quantities; - std::vector fields_custom_quantities; - unsigned short fields_mom_smooth; - std::vector fields_downsampling; + std::optional> fields_quantities; + std::optional> fields_custom_quantities; + std::optional fields_mom_smooth; + std::optional> fields_downsampling; - std::vector particles_species; - npart_t particles_stride; + std::optional> particles_species; + std::optional particles_stride; - real_t spectra_e_min; - real_t spectra_e_max; - bool spectra_log_bins; - std::size_t spectra_n_bins; + std::optional spectra_e_min; + std::optional spectra_e_max; + std::optional spectra_log_bins; + std::optional spectra_n_bins; - std::vector stats_quantities; - std::vector stats_custom_quantities; + std::optional> stats_quantities; + std::optional> stats_custom_quantities; - bool debug_as_is; - bool debug_ghosts; + std::optional debug_as_is; + std::optional debug_ghosts; void read(Dimension, std::size_t, const toml::value&); void setParams(SimulationParams*) const; diff --git a/src/framework/parameters/parameters.cpp b/src/framework/parameters/parameters.cpp index 085e6bc39..e56c68e5e 100644 --- a/src/framework/parameters/parameters.cpp +++ b/src/framework/parameters/parameters.cpp @@ -4,6 +4,7 @@ #include "enums.h" #include "global.h" +#include "arch/mpi_aliases.h" #include "utils/error.h" #include "utils/formatting.h" #include "utils/numeric.h" @@ -17,12 +18,9 @@ #include -#if defined(MPI_ENABLED) - #include -#endif - +#include #include -#include +#include #include #include #include @@ -142,9 +140,9 @@ namespace ntt { spidx_t idxM1 = 0; for (const auto& sp : species_tab) { - const auto maxnpart_real = toml::find(sp, "maxnpart"); - const auto maxnpart = static_cast(maxnpart_real); - const auto particle_species = species[idxM1]; + const auto maxnpart_real = toml::find(sp, "maxnpart"); + const auto maxnpart = static_cast(maxnpart_real); + const auto& particle_species = species[idxM1]; new_species.emplace_back(particle_species.index(), particle_species.label(), particle_species.mass(), @@ -216,8 +214,8 @@ namespace ntt { boundaries_params.setParams(this); /* [algorithms] --------------------------------------------------------- */ - params::Algorithms alg_params {}; - std::map alg_extra_flags = { + params::Algorithms alg_params {}; + const std::map alg_extra_flags = { { "gr", engine_enum == SimEngine::GRPIC }, { "gca", isPromised("algorithms.gca.e_ovr_b_max") }, }; @@ -225,8 +223,8 @@ namespace ntt { alg_params.setParams(alg_extra_flags, this); /* extra physics ------------------------------------------------------ */ - params::Extra extra_params {}; - std::map extra_extra_flags = { + params::Extra extra_params {}; + const std::map extra_extra_flags = { { "synchrotron_drag",isPromised("radiation.drag.synchrotron.gamma_rad") }, { "compton_drag", isPromised("radiation.drag.compton.gamma_rad") }, { "synchrotron_emission", @@ -237,7 +235,7 @@ namespace ntt { extra_params.setParams(extra_extra_flags, this); // @TODO: disabling stats for non-Cartesian - if (coord_enum != Coord::Cart) { + if (coord_enum != Coord::Cartesian) { set("output.stats.enable", false); } } @@ -256,19 +254,19 @@ namespace ntt { set("setup." + key, (std::string)(val.as_string())); } else if (val.is_array()) { const auto val_arr = val.as_array(); - if (val_arr.size() == 0) { + if (val_arr.empty()) { continue; } else { if (val_arr[0].is_integer()) { std::vector vec; for (const auto& v : val_arr) { - vec.push_back(v.as_integer()); + vec.push_back(static_cast(v.as_integer())); } set("setup." + key, vec); } else if (val_arr[0].is_floating()) { std::vector vec; for (const auto& v : val_arr) { - vec.push_back(v.as_floating()); + vec.push_back(static_cast(v.as_floating())); } set("setup." + key, vec); } else if (val_arr[0].is_boolean()) { @@ -284,14 +282,14 @@ namespace ntt { } set("setup." + key, vec); } else if (val_arr[0].is_array()) { - if (val_arr[0].as_array().size() == 0) { + if (val_arr[0].as_array().empty()) { raise::Error("empty inner arrays not allowed in [setup]", HERE); } else if (val_arr[0][0].is_integer()) { std::vector> vec; for (const auto& v1 : val_arr) { std::vector inner_vec; for (const auto& v2 : v1.as_array()) { - inner_vec.push_back(v2.as_integer()); + inner_vec.push_back(static_cast(v2.as_integer())); } vec.push_back(inner_vec); } @@ -301,7 +299,7 @@ namespace ntt { for (const auto& v1 : val_arr) { std::vector inner_vec; for (const auto& v2 : v1.as_array()) { - inner_vec.push_back(v2.as_floating()); + inner_vec.push_back(static_cast(v2.as_floating())); } vec.push_back(inner_vec); } @@ -356,7 +354,7 @@ namespace ntt { std::ofstream metadata; metadata.open(path); metadata << fmt::format("[metadata]\n time = %f\n\n", time) << data() - << std::endl; + << '\n'; metadata.close(); }); } diff --git a/src/framework/parameters/parameters.h b/src/framework/parameters/parameters.h index a9eadc042..0c9a7d847 100644 --- a/src/framework/parameters/parameters.h +++ b/src/framework/parameters/parameters.h @@ -32,9 +32,9 @@ namespace ntt { SimulationParams(const SimulationParams&) = default; SimulationParams& operator=(const SimulationParams& other) { - vars = std::move(other.vars); - promises = std::move(other.promises); - raw_data = std::move(other.raw_data); + vars = other.vars; + promises = other.promises; + raw_data = other.raw_data; return *this; } diff --git a/src/framework/parameters/particles.cpp b/src/framework/parameters/particles.cpp index afc795a9a..6f1a23c03 100644 --- a/src/framework/parameters/particles.cpp +++ b/src/framework/parameters/particles.cpp @@ -12,6 +12,7 @@ #include +#include #include namespace ntt { diff --git a/src/framework/simulation.cpp b/src/framework/simulation.cpp index 4e0da3eda..d037aae64 100644 --- a/src/framework/simulation.cpp +++ b/src/framework/simulation.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace ntt { @@ -48,9 +49,7 @@ namespace ntt { const auto res = toml::find>(raw_params, "grid", "resolution"); - raise::ErrorIf(res.size() < 1 || res.size() > 3, - "invalid `grid.resolution`", - HERE); + raise::ErrorIf(res.empty() || res.size() > 3, "invalid `grid.resolution`", HERE); m_requested_dimension = static_cast(res.size()); m_params.setRawData(raw_params); diff --git a/src/framework/simulation.h b/src/framework/simulation.h index 7387d62dd..9a5526d66 100644 --- a/src/framework/simulation.h +++ b/src/framework/simulation.h @@ -18,7 +18,6 @@ #include "utils/error.h" -#include "engines/traits.h" #include "framework/parameters/parameters.h" #include @@ -37,8 +36,7 @@ namespace ntt { ~Simulation(); template