diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index fe4124896..f2c360037 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -80,7 +80,8 @@ jobs: cmake $GITHUB_WORKSPACE \ -DENABLE_SAMRAI_TESTS=OFF -DCMAKE_C_COMPILER_LAUNCHER=ccache \ -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DlowResourceTests=ON \ - -DCMAKE_CXX_FLAGS="-DPHARE_DIAG_DOUBLES=1 -O2" + -DCMAKE_CXX_FLAGS="-DPHARE_DIAG_DOUBLES=1 -O2" \ + -DtestDuringBuild=ON - name: Build working-directory: ${{runner.workspace}}/build diff --git a/.gitignore b/.gitignore index b270b8a47..1f4a3d847 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.mkn* bin build subprojects diff --git a/CMakeLists.txt b/CMakeLists.txt index 32dab9d3e..871112f06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required (VERSION 3.9) project(PHARE VERSION 0.1 LANGUAGES CXX) +set(PHARE_PROJECT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +include("${PHARE_PROJECT_DIR}/res/cmake/policies.cmake" NO_POLICY_SCOPE) +include(CheckCXXCompilerFlag) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "" FORCE) @@ -10,31 +14,21 @@ if(NOT CMAKE_BUILD_TYPE) endif() endif() -if (POLICY CMP0074) # hides warning about ${PACKAGE}_ROOT variables - cmake_policy(SET CMP0074 NEW) -endif() -if (POLICY CMP0069) # allow LTO if available, requires cmake 3.9 (released August 2017) - cmake_policy(SET CMP0069 NEW) -endif() +find_package(PythonInterp 3.0 REQUIRED) +include("${PHARE_PROJECT_DIR}/res/cmake/options.cmake") +include("${PHARE_PROJECT_DIR}/res/cmake/def.cmake") +include("${PHARE_PROJECT_DIR}/res/cmake/coverage.cmake") +include("${PHARE_PROJECT_DIR}/res/cmake/dep.cmake") # Enables -fPIC typically required for shared libraries with dependencies. e.g. pybind modules set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -include(CheckCXXCompilerFlag) -find_program(Git git) -find_package(PythonInterp 3.0 REQUIRED) - -set(PHARE_PROJECT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(CMAKE_EXPORT_COMPILE_COMMANDS 1) include_directories(${PHARE_PROJECT_DIR}/src) -include("${PHARE_PROJECT_DIR}/res/cmake/options.cmake") -include("${PHARE_PROJECT_DIR}/res/cmake/def.cmake") -include("${PHARE_PROJECT_DIR}/res/cmake/coverage.cmake") -include("${PHARE_PROJECT_DIR}/res/cmake/dep.cmake") include("${PHARE_PROJECT_DIR}/res/cmake/cppcheck.cmake") +# set standard after dependencies to avoid potential conflicts set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/mkn.cuda.sh b/mkn.cuda.sh new file mode 100755 index 000000000..bacdc1f11 --- /dev/null +++ b/mkn.cuda.sh @@ -0,0 +1,36 @@ +#! /usr/bin/env bash + +set -ex + +CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +mkdir -p $CWD/build +[ ! -d "$CWD/build" ] && echo "mkn expects cmake configured build directory" && exit 1 + +MKN_X_FILE=${MKN_X_FILE:-settings} +MKN_MPI_X_FILE=${MKN_MPI_X_FILE:-res/mkn/mpi} +MKN_GPU_X_FILE=${MKN_GPU_X_FILE:-res/mkn/clang_cuda} +export MKN_LIB_LINK_LIB=1 + +exit 0 + +# verify compiler setup +# mkn clean build -x ${MKN_GPU_X_FILE} -ga -DKUL_GPU_CUDA dbg -p mkn.gpu_depositor run test + +[ ! -d ./subprojects/thrust ] && \ + git clone https://github.com/NVIDIA/thrust \ + -b main subprojects/thrust --depth 10 --shallow-submodules --recursive +[ ! -d ./subprojects/cuda-samples ] && \ + git clone https://github.com/NVIDIA/cuda-samples \ + -b main subprojects/cuda-samples --depth 10 --shallow-submodules --recursive + + +# gtest doens't like mpi compilers +#mkn clean build -x ${MKN_X_FILE} -p test_diagnostics -tKOd google.test,+ + +#mkn clean build -x ${MKN_MPI_X_FILE} -dtKOp py + +# mkn clean build -x ${MKN_MPI_X_FILE} -p test_diagnostics -KO 9 test run +# mkn clean build -x ${MKN_GPU_X_FILE} -Oa -DKUL_GPU_CUDA run + +#mkn clean build -x ${MKN_GPU_X_FILE} -Op cpp diff --git a/mkn.rocm.sh b/mkn.rocm.sh new file mode 100755 index 000000000..8f19dc8f6 --- /dev/null +++ b/mkn.rocm.sh @@ -0,0 +1,28 @@ +#! /usr/bin/env bash + +set -ex + +CWD="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +MKN_X_FILE=${MKN_X_FILE:-settings} +MKN_MPI_X_FILE=${MKN_MPI_X_FILE:-res/mkn/mpi} +MKN_GPU_X_FILE=${MKN_GPU_X_FILE:-res/mkn/hip} + +mkdir -p $CWD/build +[ ! -d "$CWD/build" ] && echo "mkn expects cmake configured build directory" && exit 1 + +export MKN_LIB_LINK_LIB=1 + +# verify compiler setup +# mkn clean build -x ${MKN_GPU_X_FILE} -ga -DKUL_GPU_ROCM dbg -p mkn.gpu_depositor run test + +# git clone https://github.com/NVIDIA/thrust -b 1.13.0 subprojects/thrust --depth 10 --shallow-submodules --recursive + +# gtest doens't like mpi compilers +mkn clean build -x ${MKN_X_FILE} -p test_diagnostics -tKOd google.test,+ + +mkn clean build -x ${MKN_MPI_X_FILE} -dtKOp py + +mkn clean build -x ${MKN_MPI_X_FILE} -p test_diagnostics -KO 9 test run + +mkn clean build -x ${MKN_GPU_X_FILE} -Oa -DKUL_GPU_ROCM run diff --git a/mkn.yaml b/mkn.yaml new file mode 100644 index 000000000..d2a40be0d --- /dev/null +++ b/mkn.yaml @@ -0,0 +1,214 @@ +#! clean build -x mpi -dtKOp py + +name: phare + +property: + MPI_INC: /usr/include #/hdf5/mpich #/usr/include/hdf5/openmpi #/usr/local/netcdf/include + MPI_LIB: hdf5 + MPI_PTH: /usr/local/lib #/opt/conda/envs/vscode/lib + mkn.gpu: -DPHARE_HAVE_MKN_GPU=1 -DMKN_GPU_CUDA=1 -DPHARE_WITH_KUL=1 + gpu.args: -DPHARE_HAVE_RAJA=1 -DPHARE_WITH_GPU=1 ${mkn.gpu} -DHAVE_RAJA=1 -DHAVE_CUDA=1 + +profile: +- name: umpira_raja_base + arg: -DPHARE_HAVE_RAJA=1 -DPHARE_HAVE_UMPIRE=1 + +- name: base + arg: -Wno-macro-redefined -Wno-unknown-cuda-version -DPHARE_HAS_HIGHFIVE=0 + +- name: gpu_base + parent: base + dep: mkn.gpu llnl.raja[lib] + arg: ${gpu.args} + +- name: core + parent: base + inc: src subprojects + src: src/core + # dep: mkn.kul + out: phare_core + + +- name: test_base + inc: . + dep: google.test + +- name: test_core + parent: test_base + self: core + # test: tests/core/(\w)/(test_.*).cpp + +- name: amr + parent: base + self: core + dep: llnl.samrai(https://github.com/mkn-nix/llnl.samrai) #llnl.raja[lib] mkn.gpu #llnl.umpire[lib] + lib: hdf5_openmpi + src: src/amr + out: phare_amr + +- name: test_amr + parent: test_base + self: amr + +- name: diagnostic + self: amr + dep: hpc.highfive(https://github.com/mkn-nix/hpc.highfive) + inc: build/subprojects/highfive/include + ${MPI_INC} + lib: ${MPI_LIB} + path: ${MPI_PTH} + +- name: simulator + parent: base + self: diagnostic + out: phare_simulator + +- name: exe + parent: gpu_base + self: simulator init + dep: lang.pybind11 + main: src/phare/phare.cpp + mod: lang.python3 + env: PYTHONPATH=${MKN_ROOT}:${MKN_ROOT}/tests/diagnostic:${MKN_ROOT}/build:${MKN_ROOT}/pyphare + +- name: init + src: src/initializer/data_provider.cpp + self: core + out: phare_initializer + mode: shared # static singleton dict lives here + +- name: pybind + # parent: base + mode: shared + dep: lang.pybind11 + mod: lang.pybind11 lang.python3 + +- name: dictator + parent: pybind + self: init + src: src/initializer/dictator.cpp + install: build/pybindlibs + out: dictator + +- name: cpp + parent: pybind + # lib: numa + # arg: -DPHARE_WITH_GPU -DKUL_GPU_CUDA + arg: ${gpu.args} + + self: simulator init gpu_base + src: src/python3/cpp_simulator.cpp + install: build/pybindlibs + #test: tests/amr/solvers/ppc/test_gpu_solverppc.cpp + out: cpp + +- name: cpp_etc + parent: pybind + self: simulator + src: src/python3/cpp_etc.cpp + install: build/pybindlibs + out: cpp_etc + arg: -DPHARE_HAS_HIGHFIVE=0 + +- name: py + self: dictator cpp_etc simulator + +- name: test + parent: base + self: init simulator + dep: lang.pybind11 google.test + mod: lang.python3 + inc: . + link: -pthread + +- name: test_diagnostics + parent: test + mode: none + mod: lang.python3 + test: + tests/diagnostic/test-diagnostics_1d.cpp + tests/diagnostic/test-diagnostics_2d.cpp + env: PYTHONPATH=${MKN_ROOT}:${MKN_ROOT}/build/tests/diagnostic:${MKN_ROOT}/build:${MKN_ROOT}/pyphare + +- name: bench + self: core + inc: . tools + dep: google.benchmark + main: tools/bench/core/data/particles/interop.cpp + link: -pthread + +- name: gpu_pusher + parent: test + self: bench + inc: . + dep: mkn.gpu + arg: -DPHARE_WITH_GPU + env: PYTHONPATH=${MKN_ROOT}:${MKN_ROOT}/tools/bench/core/numerics/pusher:${MKN_ROOT}/build:${MKN_ROOT}/pyphare + +- name: rocm_pusher + parent: gpu_pusher + main: tools/bench/core/numerics/pusher/gpu.cpp + arg: -DKUL_GPU_ROCM + env: PYTHONPATH=${MKN_ROOT}:${MKN_ROOT}/tools/bench/core/numerics/pusher:${MKN_ROOT}/build:${MKN_ROOT}/pyphare + +- name: cuda_pusher + parent: gpu_pusher + main: tests/amr/solvers/ppc/test_gpu_solverppc.cpp #tools/bench/core/numerics/pusher/gpu.cpp + arg: -DKUL_GPU_CUDA + inc: subprojects/thrust + env: PYTHONPATH=${MKN_ROOT}:${MKN_ROOT}/tools/bench/core/numerics/pusher:${MKN_ROOT}/build:${MKN_ROOT}/pyphare + +# - name: cuda_bandwidth_test +# main: subprojects/cuda-samples/Samples/bandwidthTest/bandwidthTest.cu +# inc: subprojects/cuda-samples/Common + +- name: c_depositor + test: tools/hw/gpu/bench/depositor.cpp + +- name: mkn.gpu_depositor + dep: mkn.gpu + test: tools/hw/gpu/bench/depositor.mkn.gpu.cpp + +# mkn clean build test run -p mkn.sheath.hip -x res/mkn/hcc.yaml -O +- name: mkn.sheath.hip + dep: mkn.gpu + test: tools/hw/gpu/bench/sheath-gpu.hip.cpp + +- name: mkn.sheath.cuda + dep: mkn.gpu + test: tools/hw/gpu/bench/sheath-gpu.cuda.cpp + +- name: test_core_gpu_mkn + self: core + arg: -DPHARE_WITH_GPU=1 -DPHARE_HAVE_MKN_GPU=1 -DMKN_GPU_CUDA=1 + dep: google.test mkn.gpu nvidia.thrust + main: tests/core/data/test_vector.cpp + +- name: gpu_test + parent: test + self: simulator init + arg: | + -DHAVE_RAJA + -DHAVE_UMPIRE + -DRAJA_ENABLE_CUDA=1 + -DUMPIRE_ENABLE_CUDA=1 + -DPHARE_WITH_GPU=1 + -DPHARE_HAS_HIGHFIVE=1 + -DPHARE_WITH_KUL=1 + -DKUL_GPU_CUDA=1 + test: + tests/core/data/ndarray/test_main.cpp + +# tests/core/numerics/ion_updater/test_updater.cpp +# tests/core/numerics/interpolator/test_main.cpp +# tests/core/data/field/test_field.cpp +# tests/core/data/vecfield/test_main.cpp +# tests/core/data/ions/test_ions.cpp +# tests/core/data/electrons/test_electrons.cpp +# tests/core/numerics/ampere/test_main.cpp +# tests/core/numerics/faraday/test_main.cpp +# tests/core/numerics/ohm/test_main.cpp + +- name: format + mod: | + clang.format{init{style: file, paths: src}} diff --git a/pyphare/pyphare/core/phare_utilities.py b/pyphare/pyphare/core/phare_utilities.py index 78961c364..308df5222 100644 --- a/pyphare/pyphare/core/phare_utilities.py +++ b/pyphare/pyphare/core/phare_utilities.py @@ -1,18 +1,24 @@ import math import numpy as np + + +def is_nd_array(arg): + return isinstance(arg, np.ndarray) + + def all_iterables(*args): """ return true if all arguments are either lists or tuples """ - return all([isinstance(arg, list) or isinstance(arg, tuple) for arg in args]) + return all([isinstance(arg, (tuple, list)) or is_nd_array(arg) for arg in args]) def none_iterable(*args): """ return true if none of the arguments are either lists or tuples """ - return all([not isinstance(arg, list) and not isinstance(arg, tuple) for arg in args]) + return all([not isinstance(arg, (tuple, list)) and not is_nd_array(arg) for arg in args]) def equal_size(*args): @@ -44,11 +50,9 @@ def listify(arg): def is_scalar(arg): - return not isinstance(arg, (list, tuple)) and not is_nd_array(arg) + return none_iterable(arg) -def is_nd_array(arg): - return isinstance(arg, np.ndarray) def np_array_ify(arg, size = 1): @@ -159,3 +163,8 @@ def deep_copy(item, memo, excludes=[]): setattr(that, key, deepcopy(value, memo)) return that + +def NO_GUI(): + import matplotlib as mpl + mpl.use("Agg") + diff --git a/requirements.txt b/requirements.txt index 2ecd76178..e333f5e5d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ +# wheel might be need to build other deps +wheel dill ddt h5py diff --git a/res/cmake/bench.cmake b/res/cmake/bench.cmake index 6810fec34..694038ac3 100644 --- a/res/cmake/bench.cmake +++ b/res/cmake/bench.cmake @@ -8,28 +8,6 @@ if (bench) - if (NOT DEFINED PHARE_BENCH_PROFILING) - set(PHARE_BENCH_PROFILE 0) - endif() - - if(DEFINED GBENCH_ROOT) - set(GBENCH_ROOT ${GBENCH_ROOT} CACHE PATH "Path to googlebenchmark") - find_package(benchmark REQUIRED) - set(GBENCH_LIBS benchmark::benchmark) - else() - set(GBENCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/subprojects/googlebench) - - if (NOT EXISTS ${GBENCH_ROOT}) - execute_process(COMMAND ${Git} clone https://github.com/google/benchmark ${GBENCH_ROOT} --depth 1) - endif() - - option(BENCHMARK_ENABLE_TESTING "Enable building the unit tests which depend on gtest" OFF) - option(BENCHMARK_ENABLE_GTEST_TESTS "Enable building the unit tests which depend on gtest" OFF) - add_subdirectory(subprojects/googlebench) - set(GBENCH_LIBS "benchmark") - - endif() - function(add_phare_cpp_benchmark_ exec_level target file directory) add_executable(${target} ${file}) target_compile_options(${target} PRIVATE ${PHARE_WERROR_FLAGS} -DPHARE_HAS_HIGHFIVE=${PHARE_HAS_HIGHFIVE}) diff --git a/res/cmake/def.cmake b/res/cmake/def.cmake index f7f2d3d47..5608fe34f 100644 --- a/res/cmake/def.cmake +++ b/res/cmake/def.cmake @@ -180,25 +180,6 @@ if (test AND ${PHARE_EXEC_LEVEL_MIN} GREATER 0) # 0 = no tests endfunction(add_phare_build_test) - if(DEFINED GTEST_ROOT) - set(GTEST_ROOT ${GTEST_ROOT} CACHE PATH "Path to googletest") - find_package(GTest REQUIRED) - set(GTEST_LIBS GTest::GTest GTest::Main) - else() - set(GTEST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/subprojects/googletest) - - if (NOT EXISTS ${GTEST_ROOT}) - execute_process(COMMAND ${Git} clone https://github.com/google/googletest ${GTEST_ROOT}) - endif() - - add_subdirectory(subprojects/googletest) - set(GTEST_INCLUDE_DIRS - $ - $) - set(GTEST_LIBS gtest gmock) - - endif() - function(phare_exec level target exe directory) if(${level} GREATER_EQUAL ${PHARE_EXEC_LEVEL_MIN} AND ${level} LESS_EQUAL ${PHARE_EXEC_LEVEL_MAX}) add_test(NAME ${target} COMMAND ${exe} WORKING_DIRECTORY ${directory}) diff --git a/res/cmake/dep.cmake b/res/cmake/dep.cmake index 8234fa25c..a5b12ecd9 100644 --- a/res/cmake/dep.cmake +++ b/res/cmake/dep.cmake @@ -1,7 +1,11 @@ +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +find_program(Git git) # SAMRAI build option include("${PHARE_PROJECT_DIR}/res/cmake/dep/samrai.cmake") +include("${PHARE_PROJECT_DIR}/res/cmake/dep/raja_umpire.cmake") # caliper build option @@ -15,3 +19,6 @@ include("${PHARE_PROJECT_DIR}/res/cmake/dep/pybind.cmake") # HighFive include("${PHARE_PROJECT_DIR}/res/cmake/dep/highfive.cmake") + +include("${PHARE_PROJECT_DIR}/res/cmake/dep/gtest.cmake") +include("${PHARE_PROJECT_DIR}/res/cmake/dep/gbench.cmake") diff --git a/res/cmake/dep/gbench.cmake b/res/cmake/dep/gbench.cmake new file mode 100644 index 000000000..71604ecc2 --- /dev/null +++ b/res/cmake/dep/gbench.cmake @@ -0,0 +1,27 @@ + +if (bench) + + if(DEFINED GBENCH_ROOT) + set(GBENCH_ROOT ${GBENCH_ROOT} CACHE PATH "Path to googlebenchmark") + find_package(benchmark REQUIRED) + set(GBENCH_LIBS benchmark::benchmark) + else() + set(GBENCH_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/subprojects/googlebench) + + if (NOT EXISTS ${GBENCH_ROOT}) + execute_process(COMMAND ${Git} clone https://github.com/google/benchmark ${GBENCH_ROOT} --depth 1) + else() + if(devMode) + message("downloading latest googlebench updates") + execute_process(COMMAND ${Git} pull origin master WORKING_DIRECTORY ${GBENCH_ROOT}) + endif(devMode) + endif() + + option(BENCHMARK_ENABLE_TESTING "Enable building the unit tests which depend on gtest" OFF) + option(BENCHMARK_ENABLE_GTEST_TESTS "Enable building the unit tests which depend on gtest" OFF) + add_subdirectory(subprojects/googlebench) + set(GBENCH_LIBS "benchmark") + + endif() + +endif() diff --git a/res/cmake/dep/gtest.cmake b/res/cmake/dep/gtest.cmake new file mode 100644 index 000000000..4da793c19 --- /dev/null +++ b/res/cmake/dep/gtest.cmake @@ -0,0 +1,31 @@ + + +if (test AND ${PHARE_EXEC_LEVEL_MIN} GREATER 0) # 0 = no tests + + if(DEFINED GTEST_ROOT) + set(GTEST_ROOT ${GTEST_ROOT} CACHE PATH "Path to googletest") + find_package(GTest REQUIRED) + set(GTEST_LIBS GTest::GTest GTest::Main Threads::Threads) + else() + set(GTEST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/subprojects/googletest) + + if (NOT EXISTS ${GTEST_ROOT}) + execute_process(COMMAND ${Git} clone https://github.com/google/googletest ${GTEST_ROOT}) + else() + if(devMode) + message("downloading latest googletest updates") + execute_process(COMMAND ${Git} pull origin main WORKING_DIRECTORY ${GTEST_ROOT}) + endif(devMode) + endif() + + add_subdirectory(subprojects/googletest) + set(GTEST_INCLUDE_DIRS + $ + $) + set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIRS} ${PHARE_PROJECT_DIR}) + set(GTEST_LIBS gtest gmock Threads::Threads) + + endif() + +endif() + diff --git a/res/cmake/dep/raja_umpire.cmake b/res/cmake/dep/raja_umpire.cmake new file mode 100644 index 000000000..07f651a79 --- /dev/null +++ b/res/cmake/dep/raja_umpire.cmake @@ -0,0 +1,23 @@ + + +if(DEFINED WITH_UMPIRE AND DEFINED WITH_RAJA) + find_package(RAJA PATHS ${WITH_RAJA} REQUIRED) + if (NOT RAJA_FOUND) + message(FATAL_ERROR "RAJA is needed") + endif() + + find_package(umpire PATHS ${WITH_UMPIRE} REQUIRED) + if (NOT umpire_FOUND) + message(FATAL_ERROR "umpire is needed") + endif() + + set (PHARE_BASE_LIBS ${PHARE_BASE_LIBS} RAJA umpire cudart) + set (PHARE_FLAGS ${PHARE_FLAGS} -DHAVE_RAJA=1 -DHAVE_UMPIRE=1) + + set (THRUST_DIR ${PHARE_PROJECT_DIR}/subprojects/thrust) + if (NOT EXISTS ${THRUST_DIR}) + execute_process(COMMAND ${Git} clone https://github.com/NVIDIA/thrust -b main ${THRUST_DIR} --depth 5 --shallow-submodules --recursive WORKING_DIRECTORY ${PHARE_PROJECT_DIR}) + endif() + include_directories(${THRUST_DIR}) + +endif() diff --git a/res/cmake/policies.cmake b/res/cmake/policies.cmake new file mode 100644 index 000000000..8ad8e498f --- /dev/null +++ b/res/cmake/policies.cmake @@ -0,0 +1,10 @@ + + +if(POLICY CMP0074) # hides warning about ${PACKAGE}_ROOT variables + cmake_policy(SET CMP0074 NEW) +endif() + +if (POLICY CMP0069) # allow LTO if available, requires cmake 3.9 (released August 2017) + cmake_policy(SET CMP0069 NEW) +endif() + diff --git a/res/cpp/CPPLINT.cfg b/res/cpp/CPPLINT.cfg new file mode 100644 index 000000000..51daba8a5 --- /dev/null +++ b/res/cpp/CPPLINT.cfg @@ -0,0 +1,3 @@ +set noparent +linelength=120 +filter=-readability/todo,-legal/copyright,-build/include,-runtime/int,-runtime/references,-whitespace/line_length,-build/c++11 diff --git a/res/cpp/run_cpplint.py b/res/cpp/run_cpplint.py new file mode 100644 index 000000000..a04893092 --- /dev/null +++ b/res/cpp/run_cpplint.py @@ -0,0 +1,34 @@ + +import pathlib + +CPPLINT_DIRS = [ + 'src', +] + +try: + import cpplint as cl + + cl_state = cl._cpplint_state + error_count = 0 + + for dir in CPPLINT_DIRS: + print("Processing {}".format(dir)) + + cl_state.ResetErrorCounts() + filenames = list(pathlib.Path(dir).glob('**/*.h')) + \ + list(pathlib.Path(dir).glob('**/*.cpp')) + + for filename in filenames: + cl.ProcessFile(str(filename), cl_state.verbose_level) + cl_state.PrintErrorCounts() + + error_count += cl_state.error_count + print('') + + if error_count > 0: + raise RuntimeError("Codestyle check by cpplint failed") + +except ImportError: + warnings.warn("Stylecheck by cpplint failed because cpplint " + "is not installed as a Python module") + diff --git a/res/mkn/clang_asan.yaml b/res/mkn/clang_asan.yaml new file mode 100644 index 000000000..6fe8b5730 --- /dev/null +++ b/res/mkn/clang_asan.yaml @@ -0,0 +1,18 @@ +property: + cxx_flags: -std=c++17 -fsanitize=address -fno-omit-frame-pointer +local: + repo: /mkn/r + mod-repo: /mkn/m +remote: + repo: git@github.com:mkn/ + mod-repo: git@github.com:mkn-mod/ +path: | + /usr/lib/llvm-14/lib/clang/14.0.6/lib/linux +env: | # replace with $(clang++ -print-file-name=libclang_rt.asan-x86_64.so) + LD_PRELOAD=/usr/lib/llvm-14/lib/clang/14.0.6/lib/linux/libclang_rt.asan-x86_64.so + ASAN_OPTIONS=detect_leaks=0 +file: + - type: cpp:cxx:cc:c:S + archiver: ar -cr + compiler: ccache clang++ ${cxx_flags} + linker: clang++ -Bmold -lclang_rt.asan-x86_64 diff --git a/res/mkn/clang_cuda.yaml b/res/mkn/clang_cuda.yaml new file mode 100644 index 000000000..113ad24e2 --- /dev/null +++ b/res/mkn/clang_cuda.yaml @@ -0,0 +1,18 @@ +property: + cxx.flags: --std=c++17 -fPIC -fsized-deallocation #-fsanitize=address -fno-omit-frame-pointer +env: + OMPI_CXX=clang++ + PATH=/usr/bin:/home/deegan/chain/clang_14.0.6/bin:${PATH} +path: | + /home/deegan/hpc_sdk/Linux_x86_64/22.7/cuda/11.7/targets/x86_64-linux/lib +inc: | + /home/deegan/hpc_sdk/Linux_x86_64/22.7/cuda/11.0/targets/x86_64-linux/include +compiler: + mask: + g++: + mpicxx +file: + - type: cpp:cxx:cc + archiver: ar -cr + compiler: mpicxx ${cxx.flags} -x cuda --cuda-gpu-arch=sm_80 #--cuda-path=/home/deegan/hpc_sdk/Linux_x86_64/22.7/cuda/11.7 + linker: mpicxx -Wl,-z,defs -fuse-ld=gold -lcudart #-lclang_rt.asan-x86_64 diff --git a/res/mkn/clang_mold.yaml b/res/mkn/clang_mold.yaml new file mode 100644 index 000000000..b3274d968 --- /dev/null +++ b/res/mkn/clang_mold.yaml @@ -0,0 +1,13 @@ +property: + cxx_flags: -std=c++17 +local: + repo: /mkn/r + mod-repo: /mkn/m +remote: + repo: git@github.com:mkn/ + mod-repo: git@github.com:mkn-mod/ +file: + - type: cpp:cxx:cc:c:S + archiver: ar -cr + compiler: ccache clang++ ${cxx_flags} + linker: clang++ -Bmold diff --git a/res/mkn/gcc_asan.yaml b/res/mkn/gcc_asan.yaml new file mode 100644 index 000000000..bf48a224a --- /dev/null +++ b/res/mkn/gcc_asan.yaml @@ -0,0 +1,13 @@ +property: + cxx_flags: -std=c++17 -fsanitize=address -fno-omit-frame-pointer +local: + repo: /mkn/r + mod-repo: /mkn/m +remote: + repo: git@github.com:mkn/ + mod-repo: git@github.com:mkn-mod/ +file: + - type: cpp:cxx:cc:c:S + archiver: ar -cr + compiler: ccache g++ ${cxx_flags} + linker: g++ ${cxx_flags} diff --git a/res/mkn/hip.yaml b/res/mkn/hip.yaml new file mode 100644 index 000000000..a104598f7 --- /dev/null +++ b/res/mkn/hip.yaml @@ -0,0 +1,19 @@ +property: + cxx_flags: --std=c++17 -fsized-deallocation +local: + repo: /mkn/r + mod-repo: /mkn/m +remote: + repo: git@github.com:mkn/ + mod-repo: git@github.com:mkn-mod/ +env: | + PATH=/opt/rocm/bin:${PATH} +compiler: + mask: + g++: + hipcc +file: + - type: cpp:cxx:cc:c:S + archiver: ar -cr + compiler: hipcc ${cxx_flags} + linker: hipcc diff --git a/res/mkn/hip_mpi.yaml b/res/mkn/hip_mpi.yaml new file mode 100644 index 000000000..20139e869 --- /dev/null +++ b/res/mkn/hip_mpi.yaml @@ -0,0 +1,20 @@ +property: + cxx_flags: --std=c++17 -fsized-deallocation +local: + repo: /mkn/r + mod-repo: /mkn/m +remote: + repo: git@github.com:mkn/ + mod-repo: git@github.com:mkn-mod/ +env: | + PATH=/opt/rocm/bin:${PATH} + OMPI_CXX=hipcc +compiler: + mask: + g++: + mpicxx +file: + - type: cpp:cxx:cc:c:S + archiver: ar -cr + compiler: mpicxx ${cxx_flags} + linker: mpicxx diff --git a/res/mkn/mpi.yaml b/res/mkn/mpi.yaml new file mode 100644 index 000000000..8528f12cd --- /dev/null +++ b/res/mkn/mpi.yaml @@ -0,0 +1,18 @@ +local: + repo: /mkn/r + mod-repo: /mkn/m +remote: + repo: git@github.com:mkn/ + mod-repo: git@github.com:mkn-mod/ +compiler: + mask: + g++: + mpicxx +# env: | +# PATH=/opt/conda/envs/vscode/bin:${PATH} +# OMPI_CXX=clang++ +file: + - type: cpp:cxx:cc + archiver: ar -cr + compiler: ccache mpicxx -std=c++17 -fPIC + linker: mpicxx #-fuse-ld=gold -Wl,--threads -Wl,--thread-count=10 diff --git a/res/mkn/mpi_asan.yaml b/res/mkn/mpi_asan.yaml new file mode 100644 index 000000000..b35cc31e6 --- /dev/null +++ b/res/mkn/mpi_asan.yaml @@ -0,0 +1,19 @@ +property: + cxx_flags: -std=c++17 -fsanitize=address -fno-omit-frame-pointer +local: + repo: /mkn/r + mod-repo: /mkn/m +remote: + repo: git@github.com:mkn/ + mod-repo: git@github.com:mkn-mod/ +env: | + ASAN_OPTIONS=detect_leaks=0 +compiler: + mask: + g++: + mpicxx +file: + - type: cpp:cxx:cc + archiver: ar -cr + compiler: ccache mpicxx ${cxx_flags} + linker: mpicxx ${cxx_flags} #-fuse-ld=gold -Wl,--threads -Wl,--thread-count=10 diff --git a/src/amr/CMakeLists.txt b/src/amr/CMakeLists.txt index d496c219a..d042f8341 100644 --- a/src/amr/CMakeLists.txt +++ b/src/amr/CMakeLists.txt @@ -68,7 +68,7 @@ set( SOURCES_CPP ) add_library(${PROJECT_NAME} ${SOURCES_INC} ${SOURCES_CPP}) -target_compile_options(${PROJECT_NAME} PRIVATE ${PHARE_WERROR_FLAGS}) +target_compile_options(${PROJECT_NAME} PRIVATE ${PHARE_FLAGS} ${PHARE_WERROR_FLAGS}) set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION ${PHARE_INTERPROCEDURAL_OPTIMIZATION}) diff --git a/src/amr/amr_constants.hpp b/src/amr/amr_constants.hpp index a7c43570d..38f55f28e 100644 --- a/src/amr/amr_constants.hpp +++ b/src/amr/amr_constants.hpp @@ -2,7 +2,8 @@ #define AMR_CONSTANTS_HPP #include -namespace PHARE::amr { +namespace PHARE::amr +{ static std::size_t constexpr refinementRatio = 2; } diff --git a/src/amr/data/field/coarsening/field_coarsen_operator.hpp b/src/amr/data/field/coarsening/field_coarsen_operator.hpp index 975fafde4..4398d9b22 100644 --- a/src/amr/data/field/coarsening/field_coarsen_operator.hpp +++ b/src/amr/data/field/coarsening/field_coarsen_operator.hpp @@ -40,10 +40,10 @@ namespace amr { } - FieldCoarsenOperator(FieldCoarsenOperator const&) = delete; - FieldCoarsenOperator(FieldCoarsenOperator&&) = delete; + FieldCoarsenOperator(FieldCoarsenOperator const&) = delete; + FieldCoarsenOperator(FieldCoarsenOperator&&) = delete; FieldCoarsenOperator& operator=(FieldCoarsenOperator const&) = delete; - FieldCoarsenOperator&& operator=(FieldCoarsenOperator&&) = delete; + FieldCoarsenOperator&& operator=(FieldCoarsenOperator&&) = delete; virtual ~FieldCoarsenOperator() = default; diff --git a/src/amr/data/field/field_data.hpp b/src/amr/data/field/field_data.hpp index 1d33586aa..14a201806 100644 --- a/src/amr/data/field/field_data.hpp +++ b/src/amr/data/field/field_data.hpp @@ -72,9 +72,9 @@ namespace amr { } - FieldData() = delete; - FieldData(FieldData const&) = delete; - FieldData(FieldData&&) = default; + FieldData() = delete; + FieldData(FieldData const&) = delete; + FieldData(FieldData&&) = default; FieldData& operator=(FieldData const&) = delete; @@ -83,14 +83,25 @@ namespace amr { Super::getFromRestart(restart_db); - restart_db->getVector("field_" + field.name(), field.vector()); + if constexpr (std::decay_t::is_host_mem) + restart_db->getVector("field_" + field.name(), field.vector()); + else + { + std::abort(); + } + field.reset(); // :( } void putToRestart(std::shared_ptr const& restart_db) const override { Super::putToRestart(restart_db); - restart_db->putVector("field_" + field.name(), field.vector()); + if constexpr (std::decay_t::is_host_mem) + restart_db->putVector("field_" + field.name(), field.vector()); + else + { + std::abort(); + } }; diff --git a/src/amr/data/field/field_variable_fill_pattern.hpp b/src/amr/data/field/field_variable_fill_pattern.hpp index 6ebe06e63..45beb4b58 100644 --- a/src/amr/data/field/field_variable_fill_pattern.hpp +++ b/src/amr/data/field/field_variable_fill_pattern.hpp @@ -110,10 +110,13 @@ class FieldFillPattern : public SAMRAI::xfer::VariableFillPattern transformation); } - std::string const& getPatternName() const { return s_name_id; } + std::string const& getPatternName() const + { + return s_name_id; + } private: - FieldFillPattern(FieldFillPattern const&) = delete; + FieldFillPattern(FieldFillPattern const&) = delete; FieldFillPattern& operator=(FieldFillPattern const&) = delete; static const inline std::string s_name_id = "BOX_GEOMETRY_FILL_PATTERN"; diff --git a/src/amr/data/particles/particles_data.hpp b/src/amr/data/particles/particles_data.hpp index a9f836b39..42cdc684c 100644 --- a/src/amr/data/particles/particles_data.hpp +++ b/src/amr/data/particles/particles_data.hpp @@ -1,11 +1,11 @@ #ifndef PHARE_SRC_AMR_DATA_PARTICLES_PARTICLES_DATA_HPP #define PHARE_SRC_AMR_DATA_PARTICLES_PARTICLES_DATA_HPP -#include +#include #include #include +#include #include -#include #include #include @@ -16,16 +16,21 @@ #include "SAMRAI/hier/Transformation.h" +#include "core/logger.hpp" +#include "core/vector.hpp" + #include "core/data/ions/ion_population/particle_pack.hpp" #include "core/data/particles/particle.hpp" #include "core/data/particles/particle_array.hpp" #include "core/data/particles/particle_packer.hpp" -#include "amr/resources_manager/amr_utils.hpp" -#include "amr/utilities/box/amr_box.hpp" #include "core/utilities/point/point.hpp" +#include "core/utilities/partitionner/partitionner.hpp" -#include "core/logger.hpp" +#include "hdf5/detail/hdf5_utils.hpp" + +#include "amr/resources_manager/amr_utils.hpp" +#include "amr/utilities/box/amr_box.hpp" namespace PHARE @@ -33,46 +38,6 @@ namespace PHARE namespace amr { - - template - inline bool isInBox(SAMRAI::hier::Box const& box, Particle const& particle) - { - constexpr auto dim = Particle::dimension; - - auto const& iCell = particle.iCell; - - auto const& lower = box.lower(); - auto const& upper = box.upper(); - - - if (iCell[0] >= lower(0) && iCell[0] <= upper(0)) - { - if constexpr (dim > 1) - { - if (iCell[1] >= lower(1) && iCell[1] <= upper(1)) - { - if constexpr (dim > 2) - { - if (iCell[2] >= lower(2) && iCell[2] <= upper(2)) - { - return true; - } - } - else - { - return true; - } - } - } - else - { - return true; - } - } - return false; - } - - /** @brief ParticlesData is a concrete SAMRAI::hier::PatchData subclass to store Particle data * * This class encapsulates particle storage known by the module core, and by being derived @@ -98,65 +63,167 @@ namespace amr /** * @brief The ParticlesData class */ - template + template class ParticlesData : public SAMRAI::hier::PatchData { - using Super = SAMRAI::hier::PatchData; - + using This = ParticlesData; + using Super = SAMRAI::hier::PatchData; + using ParticleArray = ParticleArray_; using Particle_t = typename ParticleArray::Particle_t; static constexpr auto dim = ParticleArray::dimension; // add one cell surrounding ghost box to map particles exiting the ghost layer static constexpr int ghostSafeMapLayer = 1; + + auto constexpr construct_particles() + { + if constexpr (ParticleArray::is_mapped) + return ParticleArray{grow(phare_box_from(getGhostBox()), ghostSafeMapLayer)}; + else + return ParticleArray{}; + } + + template + static auto _put_vec_size(std::vector> const& vec) + { + return vec.size() * S; + } + template + static auto _put_vec_size(std::vector const& vec) + { + return vec.size(); + } + template + static auto _put_vec_data(std::vector> const& vec) + { + return vec[0].data(); + } + template + static auto _put_vec_data(std::vector const& vec) + { + return vec.data(); + } + + template + void static putVecArrayToDB(DB const& db, std::vector const& vec, std::string const& key) + { + auto size = _put_vec_size(vec); + auto data = _put_vec_data(vec); + + using V = std::decay_t; + + if constexpr (std::is_same_v) + db->putDoubleArray(key, data, size); + else if constexpr (std::is_same_v) + db->putIntegerArray(key, data, size); + else + throw std::runtime_error("ParticlesData::putVecArrayToDB unhandled type"); + } + + template + static auto _get_vec_size(std::size_t size, std::vector> const&) + { + return size / S; + } + template + static auto _get_vec_size(std::size_t size, std::vector const&) + { + return size; + } + template + static auto _get_vec_data(std::vector>& vec) + { + return vec[0].data(); + } + template + static auto _get_vec_data(std::vector& vec) + { + return vec.data(); + } + + template + void static getVecArrayFromDB(DB const& db, std::vector& vec, std::string const& key) + { + vec.resize(_get_vec_size(db->getArraySize(key), vec)); + + auto size = db->getArraySize(key); + auto data = _get_vec_data(vec); + + using V = std::decay_t; + + if constexpr (std::is_same_v) + db->getDoubleArray(key, data, size); + + else if constexpr (std::is_same_v) + db->getIntegerArray(key, data, size); + else + { + throw std::runtime_error("ParticlesData::getVecArrayFromDB unhandled type"); + } + } + public: ParticlesData(SAMRAI::hier::Box const& box, SAMRAI::hier::IntVector const& ghost) : SAMRAI::hier::PatchData::PatchData(box, ghost) - , domainParticles{grow(phare_box_from(getGhostBox()), ghostSafeMapLayer)} - , patchGhostParticles{grow(phare_box_from(getGhostBox()), ghostSafeMapLayer)} - , levelGhostParticles{grow(phare_box_from(getGhostBox()), ghostSafeMapLayer)} - , levelGhostParticlesOld{grow(phare_box_from(getGhostBox()), ghostSafeMapLayer)} - , levelGhostParticlesNew{grow(phare_box_from(getGhostBox()), ghostSafeMapLayer)} + , domainParticles{construct_particles()} + , patchGhostParticles{construct_particles()} + , levelGhostParticles{construct_particles()} + , levelGhostParticlesOld{construct_particles()} + , levelGhostParticlesNew{construct_particles()} , pack{&domainParticles, &patchGhostParticles, &levelGhostParticles, &levelGhostParticlesOld, &levelGhostParticlesNew} , interiorLocalBox_{AMRToLocal(box, this->getGhostBox())} { } - - - ParticlesData() = delete; ParticlesData(ParticlesData const&) = delete; ParticlesData(ParticlesData&&) = default; - ParticlesData& operator=(ParticlesData const&) = delete; - - // SAMRAI interface - void putToRestart(std::shared_ptr const& restart_db) const override { - Super::putToRestart(restart_db); - using Packer = core::ParticlePacker; + Super::putToRestart(restart_db); + auto putParticles = [&](std::string name, auto& particles) { + auto static constexpr is_host_mem = ParticleArray::is_host_mem; + // SAMRAI errors on writing 0 size arrays if (particles.size() == 0) return; - particles.sortMapping(); + if constexpr (ParticleArray::is_mapped) + particles.sortMapping(); Packer packer(particles); - core::ContiguousParticles soa{particles.size()}; + core::SoAParticleArray soa{particles.size()}; packer.pack(soa); std::size_t part_idx = 0; - core::apply(soa.as_tuple(), [&](auto const& arg) { - restart_db->putVector(name + "_" + packer.keys()[part_idx++], arg); + core::apply(soa.template as_tuple(), [&](auto const& arg) { + using Vector = std::decay_t; + using T = typename Vector::value_type; + + auto& vec = [](auto const& v) -> auto const& + { + if constexpr (is_host_mem) + return v; + else + { + static std::vector put_host_vec; + put_host_vec.resize(v.size()); + PHARE::Vector::copy(put_host_vec, v); + return put_host_vec; + } + } + (arg); + + putVecArrayToDB(restart_db, vec, name + "_" + packer.keys()[part_idx++]); }); }; @@ -170,11 +237,13 @@ namespace amr void getFromRestart(std::shared_ptr const& restart_db) override { - Super::getFromRestart(restart_db); - using Packer = core::ParticlePacker; + Super::getFromRestart(restart_db); + auto getParticles = [&](std::string const name, auto& particles) { + auto static constexpr is_host_mem = ParticleArray::is_host_mem; + auto const keys_exist = core::generate( [&](auto const& key) { return restart_db->keyExists(name + "_" + key); }, Packer::keys()); @@ -192,19 +261,40 @@ namespace amr auto n_particles = restart_db->getArraySize(name + "_" + Packer::arbitrarySingleValueKey()); - core::ContiguousParticles soa{n_particles}; + core::SoAParticleArray soa{n_particles}; { std::size_t part_idx = 0; - core::apply(soa.as_tuple(), [&](auto& arg) { - restart_db->getVector(name + "_" + Packer::keys()[part_idx++], arg); + core::apply(soa.template as_tuple(), [&](auto& arg) { + using Vector = std::decay_t; + using T = typename Vector::value_type; + + auto& vec = [](auto& v) -> auto& + { + if constexpr (is_host_mem) + return v; + else + { + static std::vector get_host_vec; + get_host_vec.clear(); + return get_host_vec; + } + } + (arg); + + getVecArrayFromDB(restart_db, vec, name + "_" + Packer::keys()[part_idx++]); + + if constexpr (not is_host_mem) + PHARE::Vector::copy(arg, vec); }); } assert(particles.size() == 0); particles.reserve(n_particles); for (std::size_t i = 0; i < n_particles; ++i) - particles.push_back(soa.copy(i)); + particles.emplace_back(soa.copy(i)); + + particles.check(); }; getParticles("domainParticles", domainParticles); @@ -327,8 +417,6 @@ namespace amr auto const& pOverlap{dynamic_cast(overlap)}; - std::vector outBuffer; - if (pOverlap.isOverlapEmpty()) { constexpr std::size_t zero = 0; @@ -337,16 +425,26 @@ namespace amr else { SAMRAI::hier::Transformation const& transformation = pOverlap.getTransformation(); - SAMRAI::hier::BoxContainer const& boxContainer - = pOverlap.getDestinationBoxContainer(); + typename ParticleArray::vector_type outBuffer; pack_(pOverlap, transformation, outBuffer); stream << outBuffer.size(); stream.growBufferAsNeeded(); - stream.pack(outBuffer.data(), outBuffer.size()); + this->pack_(stream, outBuffer); } } - + template + void pack_(SAMRAI::tbox::MessageStream& stream, ParticleArray_t const& outBuffer) const + { + if constexpr (ParticleArray_t::is_contiguous) + std::apply( + [&](auto const&... container) { + ((stream.pack(container.data(), outBuffer.size())), ...); + }, + outBuffer.as_tuple()); + else + stream.pack(outBuffer.data(), outBuffer.size()); + } /** * @brief unpackStream is the function that unpacks a stream of particles to our particle @@ -372,8 +470,8 @@ namespace amr // unpack particles into a particle array std::size_t numberParticles = 0; stream >> numberParticles; - std::vector particleArray(numberParticles); - stream.unpack(particleArray.data(), numberParticles); + typename ParticleArray::vector_type particleArray(numberParticles); + unpack_(stream, particleArray); // ok now our goal is to put the particles we have just unpacked // into the particleData and in the proper particleArray : interior or ghost @@ -418,12 +516,22 @@ namespace amr } // end overlap not empty } - + template + void unpack_(SAMRAI::tbox::MessageStream& stream, ParticleArray_t& specie) const + { + if constexpr (ParticleArray_t::is_contiguous) + std::apply( + [&](auto&... container) { + ((stream.unpack(container.data(), specie.size())), ...); + }, + specie.as_tuple()); + else + stream.unpack(specie.data(), specie.size()); + } core::ParticlesPack* getPointer() { return &pack; } - // Core interface // these particles arrays are public because core module is free to use // them easily @@ -437,7 +545,14 @@ namespace amr core::ParticlesPack pack; + using Iterators_t = typename ParticleArray::iterator; + std::vector, Iterators_t>> domain_partition_iterators; + template + void set_iterators(V&& iterators) + { + domain_partition_iterators = std::move(iterators); + } private: //! interiorLocalBox_ is the box, in local index space, that goes from the first to the last @@ -445,147 +560,39 @@ namespace amr //! end index" SAMRAI::hier::Box interiorLocalBox_; - void copy_(SAMRAI::hier::Box const& overlapBox, ParticlesData const& sourceData) - { - auto myDomainBox = this->getBox(); - auto& srcDomainParticles = sourceData.domainParticles; - - PHARE_LOG_START("ParticleData::copy_ DomainToDomain"); + template + void copyMappedParticles(SamraiBox const& overlapBox, ParticlesData const& sourceData); - // first copy particles that fall into our domain array - // they can come from the source domain or patch ghost - auto destBox = myDomainBox * overlapBox; - auto new_size = domainParticles.size(); - - if (!destBox.empty()) - { - auto destBox_p = phare_box_from(destBox); - new_size += srcDomainParticles.nbr_particles_in(destBox_p); - if (domainParticles.capacity() < new_size) - domainParticles.reserve(new_size); - - srcDomainParticles.export_particles(destBox_p, domainParticles); - } + template + void copyPartitionedParticles(SamraiBox const& overlapBox, ParticlesData const& sourceData); - PHARE_LOG_START("ParticlesData::copy_ DomainToGhosts"); - // Now copy particles from the source domain that fall into - // our ghost layer. The ghost layer is the result of removing the domain box - // from the intersection box. - SAMRAI::hier::BoxContainer ghostLayerBoxes{}; - ghostLayerBoxes.removeIntersections(overlapBox, myDomainBox); - new_size = patchGhostParticles.size(); - for (auto& selectionBox : ghostLayerBoxes) - { - if (!selectionBox.empty()) - { - auto selectionBox_p = phare_box_from(selectionBox); - new_size += srcDomainParticles.nbr_particles_in(selectionBox_p); - } - } - if (patchGhostParticles.capacity() < new_size) - patchGhostParticles.reserve(new_size); + void copy_(SAMRAI::hier::Box const& overlapBox, ParticlesData const& sourceData) + { + if constexpr (ParticleArray::is_mapped) + copyMappedParticles(overlapBox, sourceData); + else + copyPartitionedParticles(overlapBox, sourceData); + } + template + void copyMappedParticles(SamraiBox const& overlapBox, ParticlesData const& sourceData, + SAMRAI::hier::Transformation const& transformation); - for (auto const& selectionBox : ghostLayerBoxes) - { - if (!selectionBox.empty()) - { - auto selectionBox_p = phare_box_from(selectionBox); - srcDomainParticles.export_particles(selectionBox_p, patchGhostParticles); - } - } - PHARE_LOG_STOP("ParticlesData::copy_ DomainToGhosts"); - } + template + void copyPartitionedParticles(SamraiBox const& overlapBox, ParticlesData const& sourceData, + SAMRAI::hier::Transformation const& transformation); void copy_(SAMRAI::hier::Box const& overlapBox, ParticlesData const& sourceData, SAMRAI::hier::Transformation const& transformation) { - auto myDomainBox = this->getBox(); - auto& srcDomainParticles = sourceData.domainParticles; - - PHARE_LOG_START("ParticleData::copy_ (transform)"); - - // first copy particles that fall into our domain array - // they can come from the source domain or patch ghost - auto destBox = myDomainBox * overlapBox; - auto new_size = domainParticles.size(); - auto offset = transformation.getOffset(); - auto offseter = [&](auto const& particle) { - // we make a copy because we do not want to - // shift the original particle... - auto shiftedParticle{particle}; - for (std::size_t idir = 0; idir < dim; ++idir) - { - shiftedParticle.iCell[idir] += offset[idir]; - } - return shiftedParticle; - }; - - PHARE_LOG_START("DomainToDomain (transform)"); - if (!destBox.empty()) - { - // we cannot select particles from the intersectDomain box - // right away. The reason is that the transformation may have - // a non-zero offset and particle iCells from the source are in - // the source index space, not in the destination index space - // therefore we need to first modify the destination box to - // be in the source index space - // this is done by applying the INVERSE transformation - // since a *transformation* is from source to destination. - - transformation.inverseTransform(destBox); - auto destBox_p = phare_box_from(destBox); - new_size += srcDomainParticles.nbr_particles_in(destBox_p); - - if (domainParticles.capacity() < new_size) - domainParticles.reserve(new_size); - srcDomainParticles.export_particles(destBox_p, domainParticles, offseter); - } - PHARE_LOG_STOP("DomainToDomain (transform)"); - - - - PHARE_LOG_START("DomainToGhosts (transform)"); - // Now copy particles from the source domain and patchghost that fall into - // our ghost layer. The ghost layer is the result of removing the domain box - // from the intersection box. - SAMRAI::hier::BoxContainer ghostLayerBoxes{}; - ghostLayerBoxes.removeIntersections(overlapBox, myDomainBox); - - new_size = patchGhostParticles.size(); - for (auto& selectionBox : ghostLayerBoxes) - { - if (!selectionBox.empty()) - { - transformation.inverseTransform(selectionBox); - auto selectionBox_p = phare_box_from(selectionBox); - new_size += srcDomainParticles.nbr_particles_in(selectionBox_p); - } - } - if (patchGhostParticles.capacity() < new_size) - patchGhostParticles.reserve(new_size); - - - // ghostLayer boxes already have been inverse transformed - // in previous loop, not to do again... - for (auto const& selectionBox : ghostLayerBoxes) - { - if (!selectionBox.empty()) - { - auto selectionBox_p = phare_box_from(selectionBox); - srcDomainParticles.export_particles(selectionBox_p, patchGhostParticles, - offseter); - } - } - - PHARE_LOG_STOP("DomainToGhosts (transform)"); - PHARE_LOG_STOP("ParticleData::copy_ (transform)"); + if constexpr (ParticleArray::is_mapped) + copyMappedParticles(overlapBox, sourceData, transformation); + else + copyPartitionedParticles(overlapBox, sourceData, transformation); } - - /** * @brief countNumberParticlesIn_ counts the number of particles that lie * within the boxes of an overlap. This function count both patchGhost and @@ -595,14 +602,12 @@ namespace amr std::size_t countNumberParticlesIn_(SAMRAI::pdat::CellOverlap const& overlap) const { PHARE_LOG_SCOPE("ParticleData::countNumberParticlesIn_"); - std::size_t numberParticles = 0; if (overlap.isOverlapEmpty()) - { - return numberParticles; - } + return 0; - auto const& overlapBoxes = overlap.getDestinationBoxContainer(); + std::size_t numberParticles = 0; + auto const& overlapBoxes = overlap.getDestinationBoxContainer(); for (auto const& overlapBox : overlapBoxes) { @@ -617,16 +622,31 @@ namespace amr SAMRAI::hier::Transformation const& transformation = overlap.getTransformation(); transformation.inverseTransform(shiftedOverlapBox); auto shiftedOverlapBox_p = phare_box_from(shiftedOverlapBox); - numberParticles += domainParticles.nbr_particles_in(shiftedOverlapBox_p); + if constexpr (ParticleArray::is_mapped) + numberParticles += domainParticles.nbr_particles_in(shiftedOverlapBox_p); + else + { + throw std::runtime_error("fix"); + } } + return numberParticles; } + template + void packMappedParticles(SAMRAI::pdat::CellOverlap const& overlap, + SAMRAI::hier::Transformation const& transformation, + ParticleArray_t& outBuffer) const; + template + void packPartitionedParticles(SAMRAI::pdat::CellOverlap const& overlap, + SAMRAI::hier::Transformation const& transformation, + ParticleArray_t& outBuffer) const; + template void pack_(SAMRAI::pdat::CellOverlap const& overlap, SAMRAI::hier::Transformation const& transformation, - std::vector& outBuffer) const + ParticleArray_t& outBuffer) const { PHARE_LOG_SCOPE("ParticleData::pack_"); // we want to put particles from our domain and patchghost arrays @@ -636,36 +656,261 @@ namespace amr // destination space. Therefore we need to inverse transform the // overlap box into our index space, intersect each of them with // our ghost box and put export them with the transformation offset - auto overlapBoxes = overlap.getDestinationBoxContainer(); - auto offset = transformation.getOffset(); - std::size_t size = 0; - auto offseter = [&](auto const& particle) { - auto shiftedParticle{particle}; - for (std::size_t idir = 0; idir < dim; ++idir) - { - shiftedParticle.iCell[idir] += offset[idir]; - } - return shiftedParticle; - }; - for (auto const& box : overlapBoxes) - { - auto toTakeFrom{box}; - transformation.inverseTransform(toTakeFrom); - auto toTakeFrom_p = phare_box_from(toTakeFrom); - size += domainParticles.nbr_particles_in(toTakeFrom_p); - } - outBuffer.reserve(size); - for (auto const& box : overlapBoxes) + + if constexpr (ParticleArray::is_mapped) + packMappedParticles(overlap, transformation, outBuffer); + // else + // packPartitionedParticles(overlap, transformation, outBuffer); + } + + template + auto iterator_for_overlap(OverlapBox const& overlap) const + { + auto local_lap = overlap * this->getBox(); + auto local_box = phare_box_from(local_lap); + + PHARE_LOG_LINE_STR("iterator_for_overlap"); + + PHARE_LOG_LINE_STR(phare_box_from(local_lap)); + PHARE_LOG_LINE_STR(phare_box_from(overlap)); + + PHARE_LOG_LINE_STR(domain_partition_iterators.size()); + + for (auto const& box_range : domain_partition_iterators) { - auto toTakeFrom{box}; - transformation.inverseTransform(toTakeFrom); - auto toTakeFrom_p = phare_box_from(toTakeFrom); - domainParticles.export_particles(toTakeFrom_p, outBuffer, offseter); + PHARE_LOG_LINE_STR(box_range.box()); + if (box_range.box() == local_box) + return box_range; } + + throw std::runtime_error("NOO"); } }; -} // namespace amr + +} // namespace amr } // namespace PHARE +namespace PHARE::amr +{ +template +template +void ParticlesData::copyPartitionedParticles( + SamraiBox const& /*overlapBox*/, ParticlesData const& /*sourceData*/) +{ +} + +template +template +void ParticlesData::copyMappedParticles( + SamraiBox const& overlapBox, ParticlesData const& sourceData) +{ + auto myDomainBox = this->getBox(); + auto& srcDomainParticles = sourceData.domainParticles; + + PHARE_LOG_START("ParticleData::copy_ DomainToDomain"); + + // first copy particles that fall into our domain array + // they can come from the source domain or patch ghost + auto destBox = myDomainBox * overlapBox; + auto new_size = domainParticles.size(); + + if (!destBox.empty()) + { + auto destBox_p = phare_box_from(destBox); + new_size += srcDomainParticles.nbr_particles_in(destBox_p); + if (domainParticles.capacity() < new_size) + domainParticles.reserve(new_size); + + srcDomainParticles.export_particles(destBox_p, domainParticles); + } + + PHARE_LOG_START("ParticlesData::copy_ DomainToGhosts"); + // Now copy particles from the source domain that fall into + // our ghost layer. The ghost layer is the result of removing the domain box + // from the intersection box. + SAMRAI::hier::BoxContainer ghostLayerBoxes{}; + ghostLayerBoxes.removeIntersections(overlapBox, myDomainBox); + + new_size = patchGhostParticles.size(); + for (auto& selectionBox : ghostLayerBoxes) + { + if (!selectionBox.empty()) + { + auto selectionBox_p = phare_box_from(selectionBox); + new_size += srcDomainParticles.nbr_particles_in(selectionBox_p); + } + } + if (patchGhostParticles.capacity() < new_size) + patchGhostParticles.reserve(new_size); + + + for (auto const& selectionBox : ghostLayerBoxes) + { + if (!selectionBox.empty()) + { + auto selectionBox_p = phare_box_from(selectionBox); + srcDomainParticles.export_particles(selectionBox_p, patchGhostParticles); + } + } + PHARE_LOG_STOP("ParticlesData::copy_ DomainToGhosts"); +} + +template +template +void ParticlesData::copyPartitionedParticles( + SamraiBox const& /*overlapBox*/, ParticlesData const& /*sourceData*/, + SAMRAI::hier::Transformation const& /*transformation*/) +{ +} + +template +template +void ParticlesData::copyMappedParticles( + SamraiBox const& overlapBox, ParticlesData const& sourceData, + SAMRAI::hier::Transformation const& transformation) +{ + auto myDomainBox = this->getBox(); + auto& srcDomainParticles = sourceData.domainParticles; + + PHARE_LOG_START("ParticleData::copy_ (transform)"); + + // first copy particles that fall into our domain array + // they can come from the source domain or patch ghost + auto destBox = myDomainBox * overlapBox; + auto new_size = domainParticles.size(); + auto offset = transformation.getOffset(); + auto offseter = [&](auto const& particle) { + // we make a copy because we do not want to + // shift the original particle... + auto shiftedParticle{particle}; + for (std::size_t idir = 0; idir < dim; ++idir) + { + shiftedParticle.iCell()[idir] += offset[idir]; + } + return shiftedParticle; + }; + + PHARE_LOG_START("DomainToDomain (transform)"); + if (!destBox.empty()) + { + // we cannot select particles from the intersectDomain box + // right away. The reason is that the transformation may have + // a non-zero offset and particle iCells from the source are in + // the source index space, not in the destination index space + // therefore we need to first modify the destination box to + // be in the source index space + // this is done by applying the INVERSE transformation + // since a *transformation* is from source to destination. + + transformation.inverseTransform(destBox); + auto destBox_p = phare_box_from(destBox); + new_size += srcDomainParticles.nbr_particles_in(destBox_p); + + if (domainParticles.capacity() < new_size) + domainParticles.reserve(new_size); + srcDomainParticles.export_particles(destBox_p, domainParticles, offseter); + } + PHARE_LOG_STOP("DomainToDomain (transform)"); + + + + PHARE_LOG_START("DomainToGhosts (transform)"); + // Now copy particles from the source domain and patchghost that fall into + // our ghost layer. The ghost layer is the result of removing the domain box + // from the intersection box. + SAMRAI::hier::BoxContainer ghostLayerBoxes{}; + ghostLayerBoxes.removeIntersections(overlapBox, myDomainBox); + + new_size = patchGhostParticles.size(); + for (auto& selectionBox : ghostLayerBoxes) + { + if (!selectionBox.empty()) + { + transformation.inverseTransform(selectionBox); + auto selectionBox_p = phare_box_from(selectionBox); + new_size += srcDomainParticles.nbr_particles_in(selectionBox_p); + } + } + if (patchGhostParticles.capacity() < new_size) + patchGhostParticles.reserve(new_size); + + + // ghostLayer boxes already have been inverse transformed + // in previous loop, not to do again... + for (auto const& selectionBox : ghostLayerBoxes) + { + if (!selectionBox.empty()) + { + auto selectionBox_p = phare_box_from(selectionBox); + srcDomainParticles.export_particles(selectionBox_p, patchGhostParticles, offseter); + } + } + + PHARE_LOG_STOP("DomainToGhosts (transform)"); + PHARE_LOG_STOP("ParticleData::copy_ (transform)"); +} + +template +template +void ParticlesData::packPartitionedParticles( + SAMRAI::pdat::CellOverlap const& overlap, SAMRAI::hier::Transformation const& transformation, + ParticleArray_t& outBuffer) const +{ + for (auto box : overlap.getDestinationBoxContainer()) + { + transformation.inverseTransform(box); + auto box_range = iterator_for_overlap(box); + + + outBuffer.insert(outBuffer.end(), + domainParticles.vector().begin() + box_range.begin().idx(), + domainParticles.vector().begin() + box_range.end().idx()); + + + + // void insert(typename std::vector::iterator position, iterator first, + // iterator last) + // { + // particles_.insert(position, first.idx(), last.idx()); + // } + } +} + +template +template +void ParticlesData::packMappedParticles( + SAMRAI::pdat::CellOverlap const& overlap, SAMRAI::hier::Transformation const& transformation, + ParticleArray_t& outBuffer) const +{ + auto overlapBoxes = overlap.getDestinationBoxContainer(); + auto offset = transformation.getOffset(); + std::size_t size = 0; + auto offseter = [&](auto const& particle) { + auto shiftedParticle{particle}; + for (std::size_t idir = 0; idir < dim; ++idir) + { + shiftedParticle.iCell()[idir] += offset[idir]; + } + return shiftedParticle; + }; + for (auto const& box : overlapBoxes) + { + auto toTakeFrom{box}; + transformation.inverseTransform(toTakeFrom); + auto toTakeFrom_p = phare_box_from(toTakeFrom); + size += domainParticles.nbr_particles_in(toTakeFrom_p); + } + outBuffer.reserve(size); + for (auto const& box : overlapBoxes) + { + auto toTakeFrom{box}; + transformation.inverseTransform(toTakeFrom); + auto toTakeFrom_p = phare_box_from(toTakeFrom); + domainParticles.export_particles(toTakeFrom_p, outBuffer, offseter); + } +} + +} // namespace PHARE::amr + #endif diff --git a/src/amr/data/particles/refine/particles_data_split.hpp b/src/amr/data/particles/refine/particles_data_split.hpp index 4a16f0a59..c8acd747a 100644 --- a/src/amr/data/particles/refine/particles_data_split.hpp +++ b/src/amr/data/particles/refine/particles_data_split.hpp @@ -29,18 +29,21 @@ namespace amr }; - template - Particle toFineGrid(Particle toFine) + template + auto toFineGrid(Iterator iterator) { - constexpr auto dim = Particle::dimension; + constexpr auto dim = Iterator::dimension; constexpr auto ratio = PHARE::amr::refinementRatio; + auto toFine = std::make_tuple(iterator.iCell(), iterator.delta()); + auto& [iCell, delta] = toFine; + for (size_t iDim = 0; iDim < dim; ++iDim) { - auto fineDelta = toFine.delta[iDim] * ratio; - int fineDeltaInt = static_cast(fineDelta); - toFine.iCell[iDim] = toFine.iCell[iDim] * ratio + fineDeltaInt; - toFine.delta[iDim] = fineDelta - fineDeltaInt; + auto fineDelta = delta[iDim] * ratio; + int fineDeltaInt = static_cast(fineDelta); + iCell[iDim] = iCell[iDim] * ratio + fineDeltaInt; + delta[iDim] = fineDelta - fineDeltaInt; } return toFine; @@ -135,8 +138,8 @@ namespace amr * an overlap , a ratio and the geometry of both patches, perform the * splitting of coarse particles onto the destination patch */ - void refine_(ParticlesData& destParticlesData, - ParticlesData const& srcParticlesData, + template // template so only exists on usage + void refine_(Data_t& destParticlesData, Data_t const& srcParticlesData, SAMRAI::pdat::CellOverlap const& destFieldOverlap) const { // the source PatchData is a possible restriction of a "real" patchdata @@ -173,21 +176,26 @@ namespace amr std::array particlesArrays{&srcInteriorParticles, &srcGhostParticles}; auto splitBox = getSplitBox(destinationBox); - auto isInDest = [&destinationBox](auto const& particle) // - { return isInBox(destinationBox, particle); }; - + auto isInDest = [&destinationBox](auto const& particle) { + if constexpr (ParticleArray::is_contiguous) + return isInBox(destinationBox, particle.iCell()); + else + return isInBox(destinationBox, particle); + }; for (auto const& sourceParticlesArray : particlesArrays) { - for (auto const& particle : *sourceParticlesArray) + auto start = sourceParticlesArray->begin(); + auto end = sourceParticlesArray->end(); + + for (auto it = start; it != sourceParticlesArray->end(); ++it) { - std::array - refinedParticles; - auto particleRefinedPos = toFineGrid(particle); + typename ParticleArray::template array_type refinedParticles; + auto particleRefinedPos = toFineGrid(it); - if (isInBox(splitBox, particleRefinedPos)) + if (isInBox(splitBox, std::get<0>(particleRefinedPos))) { - split(particleRefinedPos, refinedParticles); + split(particleRefinedPos, it, refinedParticles); // we need to know in which of interior or levelGhostParticlesXXXX diff --git a/src/amr/data/particles/refine/splitter.hpp b/src/amr/data/particles/refine/splitter.hpp index ce6b8dba0..3a99cbf6e 100644 --- a/src/amr/data/particles/refine/splitter.hpp +++ b/src/amr/data/particles/refine/splitter.hpp @@ -41,45 +41,55 @@ class PatternDispatcher { } - template - inline void operator()(Particle const& coarsePartOnRefinedGrid, Particles& refinedParticles, - size_t idx = 0) const + template + inline void operator()(FineiCellDelta const& fine, ParticleIt const& coarseParticle, + Particles& refinedParticles, size_t idx = 0) const { - dispatch(coarsePartOnRefinedGrid, refinedParticles, idx); + dispatch(fine, coarseParticle, refinedParticles, idx); } std::tuple patterns{}; size_t nbRefinedParts{0}; private: - template - void dispatch(Particle const& particle, Particles& particles, size_t idx) const + template + void dispatch(FineiCellDelta const& fine, ParticleIt const& particle, // + Particles& particles, size_t idx) const { - constexpr auto dimension = Particle::dimension; + constexpr auto dimension = ParticleIt::dimension; constexpr auto refRatio = PHARE::amr::refinementRatio; constexpr std::array power{refRatio, refRatio * refRatio, refRatio * refRatio * refRatio}; assert(particles.size() >= idx + nbRefinedParts); - using FineParticle = decltype(particles[0]); // may be a reference + auto const& iCell = std::get<0>(fine); + auto const& delta = std::get<1>(fine); + + auto split = [&](auto& fineParticle, auto& pattern, auto const& rpIndex) { + fineParticle.weight() = particle.weight() * pattern.weight_ * power[dimension - 1]; + fineParticle.charge() = particle.charge(); + fineParticle.iCell() = iCell; + fineParticle.delta() = delta; + fineParticle.v() = particle.v(); + + for (size_t iDim = 0; iDim < dimension; iDim++) + { + fineParticle.delta()[iDim] += pattern.deltas_[rpIndex][iDim]; + float integra = std::floor(fineParticle.delta()[iDim]); + fineParticle.delta()[iDim] -= integra; + fineParticle.iCell()[iDim] += static_cast(integra); + } + }; core::apply(patterns, [&](auto const& pattern) { for (size_t rpIndex = 0; rpIndex < pattern.deltas_.size(); rpIndex++) { - FineParticle fineParticle = particles[idx++]; - fineParticle.weight = particle.weight * pattern.weight_ * power[dimension - 1]; - fineParticle.charge = particle.charge; - fineParticle.iCell = particle.iCell; - fineParticle.delta = particle.delta; - fineParticle.v = particle.v; - - for (size_t iDim = 0; iDim < dimension; iDim++) - { - fineParticle.delta[iDim] += pattern.deltas_[rpIndex][iDim]; - float integra = std::floor(fineParticle.delta[iDim]); - fineParticle.delta[iDim] -= integra; - fineParticle.iCell[iDim] += static_cast(integra); - } + auto fineParticle = particles.begin() + idx++; + + if constexpr (ParticleIt::is_contiguous) + split(fineParticle, pattern, rpIndex); + else + split(*fineParticle, pattern, rpIndex); } }); } diff --git a/src/amr/messengers/messenger_factory.cpp b/src/amr/messengers/messenger_factory.cpp index 326892c84..0e6b024e2 100644 --- a/src/amr/messengers/messenger_factory.cpp +++ b/src/amr/messengers/messenger_factory.cpp @@ -16,4 +16,4 @@ std::vector makeDescriptors(std::vector modelN else throw std::runtime_error("Error max number of models is 2"); } -} +} // namespace PHARE::amr diff --git a/src/amr/multiphysics_integrator.hpp b/src/amr/multiphysics_integrator.hpp index 370ce25e0..0a3bdcef4 100644 --- a/src/amr/multiphysics_integrator.hpp +++ b/src/amr/multiphysics_integrator.hpp @@ -49,7 +49,10 @@ namespace solver }; - inline bool isRootLevel(int levelNumber) { return levelNumber == 0; } + inline bool isRootLevel(int levelNumber) + { + return levelNumber == 0; + } /** diff --git a/src/amr/physical_models/hybrid_model.hpp b/src/amr/physical_models/hybrid_model.hpp index 68e6918c2..0611053eb 100644 --- a/src/amr/physical_models/hybrid_model.hpp +++ b/src/amr/physical_models/hybrid_model.hpp @@ -24,11 +24,14 @@ template { public: - using type_list = PHARE::core::type_list; - using Interface = IPhysicalModel; - using amr_types = AMR_Types; - using patch_t = typename AMR_Types::patch_t; - using level_t = typename AMR_Types::level_t; + // type_list must be kept in sync with template parameters above including order of + using type_list = PHARE::core::type_list; + using Interface = IPhysicalModel; + using amr_types = AMR_Types; + using InitState_t = core::HybridState; + using State_t = typename core::HybridState; + using patch_t = typename AMR_Types::patch_t; + using level_t = typename AMR_Types::level_t; static const std::string model_name; using gridlayout_type = GridLayoutT; using electromag_type = Electromag; @@ -42,7 +45,7 @@ class HybridModel : public IPhysicalModel = core::ParticleInitializerFactory; - core::HybridState state; + State_t state; std::shared_ptr resourcesManager; diff --git a/src/amr/resources_manager/amr_utils.hpp b/src/amr/resources_manager/amr_utils.hpp index 7bdba0435..206b68b08 100644 --- a/src/amr/resources_manager/amr_utils.hpp +++ b/src/amr/resources_manager/amr_utils.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -129,7 +130,9 @@ namespace amr template - GridLayoutT layoutFromPatch(SAMRAI::hier::Patch const& patch) + GridLayoutT layoutFromPatch(SAMRAI::hier::Patch const& patch, + std::shared_ptr const& hierarchy + = nullptr) { int constexpr dimension = GridLayoutT::dimension; @@ -179,7 +182,18 @@ namespace amr nbrCell[iDim] = static_cast(domain.numberCells(iDim)); } - return GridLayoutT{dl, nbrCell, origin, amr::Box{domain}}; + std::vector> neighbors; + if (hierarchy) + { + SAMRAI::hier::HierarchyNeighbors hier_nbrs{*hierarchy, patch.getPatchLevelNumber(), + patch.getPatchLevelNumber()}; + + for (auto const& neighbour_box : + hier_nbrs.getSameLevelNeighbors(domain, patch.getPatchLevelNumber())) + neighbors.emplace_back(amr::Box{domain}); + } + + return GridLayoutT{dl, nbrCell, origin, amr::Box{domain}, neighbors}; } inline auto to_string(SAMRAI::hier::GlobalId const& id) diff --git a/src/amr/resources_manager/resources_guards.hpp b/src/amr/resources_manager/resources_guards.hpp index dff3f3a02..fd8769c30 100644 --- a/src/amr/resources_manager/resources_guards.hpp +++ b/src/amr/resources_manager/resources_guards.hpp @@ -64,11 +64,11 @@ namespace amr // We just need the move constructor for using ResourceManager::createResourcesGuards - ResourcesGuard(ResourcesGuard&&) = default; - ResourcesGuard() = delete; - ResourcesGuard(ResourcesGuard const&) = delete; + ResourcesGuard(ResourcesGuard&&) = default; + ResourcesGuard() = delete; + ResourcesGuard(ResourcesGuard const&) = delete; ResourcesGuard& operator=(ResourcesGuard const& source) = delete; - ResourcesGuard& operator=(ResourcesGuard&&) = delete; + ResourcesGuard& operator=(ResourcesGuard&&) = delete; diff --git a/src/amr/resources_manager/resources_manager.hpp b/src/amr/resources_manager/resources_manager.hpp index db856d65d..abd2691d0 100644 --- a/src/amr/resources_manager/resources_manager.hpp +++ b/src/amr/resources_manager/resources_manager.hpp @@ -110,10 +110,10 @@ namespace amr } - ResourcesManager(ResourcesManager const&) = delete; - ResourcesManager(ResourcesManager&&) = delete; + ResourcesManager(ResourcesManager const&) = delete; + ResourcesManager(ResourcesManager&&) = delete; ResourcesManager& operator=(ResourcesManager const& source) = delete; - ResourcesManager& operator=(ResourcesManager&&) = delete; + ResourcesManager& operator=(ResourcesManager&&) = delete; /** @brief registerResources takes a ResourcesUser to register its resources diff --git a/src/amr/resources_manager/resources_manager_utilities.hpp b/src/amr/resources_manager/resources_manager_utilities.hpp index eea15e83e..04f66c678 100644 --- a/src/amr/resources_manager/resources_manager_utilities.hpp +++ b/src/amr/resources_manager/resources_manager_utilities.hpp @@ -45,8 +45,8 @@ namespace amr */ template struct has_field().getFieldNamesAndQuantities())>> + core::tryToInstanciate< + decltype(std::declval().getFieldNamesAndQuantities())>> : std::true_type { }; @@ -58,8 +58,9 @@ namespace amr * has particles */ template - struct has_particles().getParticleArrayNames())>> + struct has_particles< + ResourcesUser, + core::tryToInstanciate().getParticleArrayNames())>> : std::true_type { }; @@ -71,8 +72,8 @@ namespace amr */ template struct has_runtime_subresourceuser_list< - ResourcesUser, core::tryToInstanciate().getRunTimeResourcesUserList())>> + ResourcesUser, core::tryToInstanciate< + decltype(std::declval().getRunTimeResourcesUserList())>> : std::true_type { }; @@ -84,8 +85,8 @@ namespace amr */ template struct has_compiletime_subresourcesuser_list< - ResourcesUser, core::tryToInstanciate().getCompileTimeResourcesUserList())>> + ResourcesUser, core::tryToInstanciate() + .getCompileTimeResourcesUserList())>> : std::true_type { }; diff --git a/src/amr/solvers/solver_ppc.hpp b/src/amr/solvers/solver_ppc.hpp index 85bdf1c02..0fe4a8ef1 100644 --- a/src/amr/solvers/solver_ppc.hpp +++ b/src/amr/solvers/solver_ppc.hpp @@ -19,6 +19,7 @@ #include "core/numerics/boundary_condition/boundary_condition.hpp" #include "core/numerics/ion_updater/ion_updater.hpp" +#include "core/numerics/ion_updater/ion_updater_per_particle.hpp" #include "core/numerics/ampere/ampere.hpp" #include "core/numerics/faraday/faraday.hpp" #include "core/numerics/ohm/ohm.hpp" @@ -28,8 +29,6 @@ #include "core/data/grid/gridlayout_utils.hpp" -#include - namespace PHARE::solver { // ----------------------------------------------------------------------------- @@ -119,9 +118,9 @@ class SolverPPC : public ISolver void average_(level_t& level, HybridModel& model); - void moveIons_(level_t& level, Ions& ions, Electromag& electromag, ResourcesManager& rm, - Messenger& fromCoarser, double const currentTime, double const newTime, - core::UpdaterMode mode); + void moveIons_(std::shared_ptr const& hierarchy, level_t& level, Ions& ions, + Electromag& electromag, ResourcesManager& rm, Messenger& fromCoarser, + double const currentTime, double const newTime, core::UpdaterMode mode); void saveState_(level_t& level, Ions& ions, ResourcesManager& rm); @@ -138,8 +137,11 @@ class SolverPPC : public ISolver // extend lifespan - std::unordered_map tmpDomain; - std::unordered_map patchGhost; + // GPU -> GPU alloc/copy is bugging inside std vector + using PHARE_Types = core::CPU_Types; + using LocalParticles = typename PHARE_Types::ParticleArray_t; + std::unordered_map tmpDomain; + std::unordered_map patchGhost; }; // end solverPPC @@ -198,6 +200,8 @@ void SolverPPC::saveState_(level_t& level, Ions& ions, R auto _ = rm.setOnPatch(*patch, ions); for (auto& pop : ions) { + pop.domainParticles().check(); + auto key = ss.str() + "_" + pop.name(); if (!tmpDomain.count(key)) tmpDomain.emplace(key, pop.domainParticles()); @@ -223,8 +227,11 @@ void SolverPPC::restoreState_(level_t& level, Ions& ions auto _ = rm.setOnPatch(*patch, ions); for (auto& pop : ions) { - pop.domainParticles() = std::move(tmpDomain.at(ss.str() + "_" + pop.name())); - pop.patchGhostParticles() = std::move(patchGhost.at(ss.str() + "_" + pop.name())); + auto key = ss.str() + "_" + pop.name(); + tmpDomain.at(key).check(); + pop.domainParticles() = std::move(tmpDomain.at(key)); + pop.domainParticles().check(); + pop.patchGhostParticles() = std::move(patchGhost.at(key)); } } } @@ -251,8 +258,8 @@ void SolverPPC::advanceLevel(std::shared_ptr::advanceLevel(std::shared_ptr::average_(level_t& level, HybridModel& mo template -void SolverPPC::moveIons_(level_t& level, Ions& ions, +void SolverPPC::moveIons_(std::shared_ptr const& hierarchy, + level_t& level, Ions& ions, Electromag& electromag, ResourcesManager& rm, Messenger& fromCoarser, double const currentTime, double const newTime, core::UpdaterMode mode) @@ -568,7 +576,7 @@ void SolverPPC::moveIons_(level_t& level, Ions& ions, { auto _ = rm.setOnPatch(*patch, electromag, ions); - auto layout = PHARE::amr::layoutFromPatch(*patch); + auto layout = PHARE::amr::layoutFromPatch(*patch, hierarchy); ionUpdater_.updatePopulations(ions, electromag, layout, dt, mode); // this needs to be done before calling the messenger @@ -582,7 +590,7 @@ void SolverPPC::moveIons_(level_t& level, Ions& ions, for (auto& patch : level) { auto _ = rm.setOnPatch(*patch, electromag, ions); - auto layout = PHARE::amr::layoutFromPatch(*patch); + auto layout = PHARE::amr::layoutFromPatch(*patch, hierarchy); ionUpdater_.updateIons(ions, layout); // no need to update time, since it has been done before diff --git a/src/amr/solvers/solver_ppc_threading.hpp b/src/amr/solvers/solver_ppc_threading.hpp new file mode 100644 index 000000000..2182badcf --- /dev/null +++ b/src/amr/solvers/solver_ppc_threading.hpp @@ -0,0 +1,104 @@ +#ifndef PHARE_SOLVER_PPC_THREADING_HPP +#define PHARE_SOLVER_PPC_THREADING_HPP + +#include + +#include "core/utilities/thread_pool.hpp" +#include "core/utilities/range/ranges.hpp" + + +namespace PHARE::solver +{ +template +class DefaultSolverPPCThreadingStrategy +{ + using This = DefaultSolverPPCThreadingStrategy; + + using Electromag = typename HybridModel::electromag_type; + using Ions = typename HybridModel::ions_type; + using ParticleArray = typename Ions::particle_array_type; + using VecFieldT = typename HybridModel::vecfield_type; + using GridLayout = typename HybridModel::gridlayout_type; + using ResourcesManager = typename HybridModel::resources_manager_type; + + using IonsView = core::IonsView; + using IonPopView = core::IonPopulationView; + using IonUpdater_t = core::IonUpdater; + + using PatchView = std::tuple; + + + struct IonView + { + IonPopView& pop; + + auto from(std::vector>& ion_pop_views) + { + return core::generate([](auto& pop_view) { return IonView(*pop_view); }, ion_pop_views); + } + }; + +public: + DefaultSolverPPCThreadingStrategy(std::size_t n_threads) + : n_threads{n_threads} + , pool{n_threads - 1} + { + } + + + template + void build_from(Level& level, HybridModel& hybridModel, ResourcesManager& resourcesManager) + { + ion_patch_views.clear(); + auto& hybridState = hybridModel.state; + for (auto& patch : *level) + { + auto _ = resourcesManager.setOnPatch(*patch, hybridState); + auto layout = PHARE::amr::layoutFromPatch(*patch); + ion_patch_views.emplace_back(layout, hybridState.electromag.view(), + IonsView::make(hybridState.ions)); + } + } + + void solve(initializer::PHAREDict updaterDict, double dt, core::UpdaterMode mode) + { + auto patch_views + = core::generate([](auto& patch_view) { return &patch_view; }, ion_patch_views); + + std::vector> domain_particles; + for (auto const& patch_view : patch_views) + for (auto const& pop : std::get<2>(*patch_view).pops) + domain_particles.push_back(std::make_tuple(patch_view, &pop.domainParticles())); + + auto ranges = make_balanced_ranges( + domain_particles, n_threads, [](auto& el) -> auto& { return *std::get<1>(el); }, + [](auto&& range, auto& el) { return std::make_tuple(range, el); }); + + auto thread_fn = [&](std::size_t thread_idx) { + IonUpdater_t ionUpdater{updaterDict}; + for (auto& range_tuple : ranges[thread_idx]) + { + auto& [range, patch_view_tuple] = range_tuple; + auto& [patch_view, _] = patch_view_tuple; + auto& [layout, electromag, __] = *patch_view; + // ionUpdater.updatePopulations(range, electromag, layout, dt, mode); // fix + } + }; + + for (std::size_t i = 1; i < n_threads; ++i) + pool.submit([&, i]() { thread_fn(i); }); + + thread_fn(0); + pool.wait_for_tasks(); + } + +private: + std::size_t n_threads = 1; + thread_pool pool; + std::vector ion_patch_views; +}; + +} // namespace PHARE::solver + + +#endif diff --git a/src/amr/solvers/solver_ppc_threading_sorted.hpp b/src/amr/solvers/solver_ppc_threading_sorted.hpp new file mode 100644 index 000000000..fe2797536 --- /dev/null +++ b/src/amr/solvers/solver_ppc_threading_sorted.hpp @@ -0,0 +1,112 @@ +#ifndef PHARE_SOLVER_PPC_THREADING_HPP +#define PHARE_SOLVER_PPC_THREADING_HPP + +#include + +#include "core/utilities/thread_pool.hpp" + + +namespace PHARE::solver +{ +template +class DefaultSolverPPCThreadingStrategy +{ + using This = DefaultSolverPPCThreadingStrategy; + + using Electromag = typename HybridModel::electromag_type; + using Ions = typename HybridModel::ions_type; + using ParticleArray = typename Ions::particle_array_type; + using VecFieldT = typename HybridModel::vecfield_type; + using GridLayout = typename HybridModel::gridlayout_type; + using ResourcesManager = typename HybridModel::resources_manager_type; + + using IonsView = core::IonsView; + using IonPopView = core::IonPopulationView; + using IonUpdater_t = core::IonUpdater; + + using PatchView = std::tuple; + + + struct IonView + { + IonPopView& pop; + + auto from(std::vector>& ion_pop_views) + { + return core::generate([](auto& pop_view) { return IonView(*pop_view); }, ion_pop_views); + } + }; + +public: + DefaultSolverPPCThreadingStrategy(std::size_t n_threads) + : n_threads{n_threads} + , pool{n_threads} + { + } + + + template + void build_from(Level& level, HybridModel& hybridModel, ResourcesManager& resourcesManager) + { + ion_patch_views.clear(); + auto& hybridState = hybridModel.state; + for (auto& patch : *level) + { + auto _ = resourcesManager.setOnPatch(*patch, hybridState); + auto layout = PHARE::amr::layoutFromPatch(*patch); + ion_patch_views.emplace_back(layout, hybridState.electromag.view(), + IonsView::make(hybridState.ions)); + } + } + + void solve(initializer::PHAREDict updaterDict, double dt, core::UpdaterMode mode) + { + auto patch_views + = core::generate([](auto& patch_view) { return &patch_view; }, ion_patch_views); + + std::sort(patch_views.begin(), patch_views.end(), [](auto const& a, auto const& b) { + auto p0 = core::sum_from(std::get<2>(*a), + [](auto const& pop) { return pop.domainParticles().size(); }); + auto p1 = core::sum_from(std::get<2>(*b), + [](auto const& pop) { return pop.domainParticles().size(); }); + return p0 < p1; + }); + + std::vector> patch_views_per_thread(n_threads); + { + std::size_t thread_idx = 0; + for (auto const& patch_view_ptr : patch_views) + { + patch_views_per_thread[thread_idx++].push_back(patch_view_ptr); + if (thread_idx == n_threads) + thread_idx = 0; + } + } + + auto thread_fn = [&](std::size_t thread_idx) { + IonUpdater_t ionUpdater{updaterDict}; + for (auto& patch_view : patch_views_per_thread[thread_idx]) + { + auto& [layout, electromag, ions] = *patch_view; + ionUpdater.updatePopulations(ions, electromag, layout, dt, mode); + } + }; + + std::vector threads; + for (std::size_t i = 1; i < n_threads; ++i) + pool.submit([&, i]() { thread_fn(i); }); + + thread_fn(0); + pool.wait_for_tasks(); + } + +private: + std::size_t n_threads = 1; + thread_pool pool; + std::vector ion_patch_views; +}; + +} // namespace PHARE::solver + + +#endif diff --git a/src/amr/solvers/threaded_solver_ppc.hpp b/src/amr/solvers/threaded_solver_ppc.hpp new file mode 100644 index 000000000..ac09573ae --- /dev/null +++ b/src/amr/solvers/threaded_solver_ppc.hpp @@ -0,0 +1,610 @@ + + +#ifndef PHARE_SOLVER_PPC_HPP +#define PHARE_SOLVER_PPC_HPP + +#include + +#include + +#include "initializer/data_provider.hpp" + +#include "amr/messengers/hybrid_messenger.hpp" +#include "amr/messengers/hybrid_messenger_info.hpp" +#include "amr/resources_manager/amr_utils.hpp" +#include "amr/resources_manager/resources_manager.hpp" +#include "amr/resources_manager/amr_utils.hpp" + +#include "amr/solvers/solver.hpp" + +#include "core/numerics/pusher/pusher.hpp" +#include "core/numerics/pusher/pusher_factory.hpp" +#include "core/numerics/interpolator/interpolator.hpp" +#include "core/numerics/boundary_condition/boundary_condition.hpp" + +#include "core/numerics/ion_updater/ion_updater.hpp" +#include "core/numerics/ampere/ampere.hpp" +#include "core/numerics/faraday/faraday.hpp" +#include "core/numerics/ohm/ohm.hpp" + +#include "core/data/particles/particle_array.hpp" +#include "core/data/vecfield/vecfield.hpp" +#include "core/data/grid/gridlayout_utils.hpp" + +#include "solver_ppc_threading.hpp" + +#include + +namespace PHARE::solver +{ +// ----------------------------------------------------------------------------- + +template +class SolverPPC : public ISolver +{ +private: + static constexpr auto dimension = HybridModel::dimension; + static constexpr auto interp_order = HybridModel::gridlayout_type::interp_order; + + using Electromag = typename HybridModel::electromag_type; + using Ions = typename HybridModel::ions_type; + using ParticleArray = typename Ions::particle_array_type; + using VecFieldT = typename HybridModel::vecfield_type; + using GridLayout = typename HybridModel::gridlayout_type; + using ResourcesManager = typename HybridModel::resources_manager_type; + using IPhysicalModel_t = IPhysicalModel; + using IMessenger = amr::IMessenger; + using HybridMessenger = amr::HybridMessenger; + + + using IonUpdater_t = PHARE::core::IonUpdater; + using ThreadingStrategy = DefaultSolverPPCThreadingStrategy; + + Electromag electromagPred_{"EMPred"}; + Electromag electromagAvg_{"EMAvg"}; + + + PHARE::core::Faraday faraday_; + PHARE::core::Ampere ampere_; + PHARE::core::Ohm ohm_; + + + std::size_t static operating_n_threads(PHARE::initializer::PHAREDict const& dict) + { + if (dict.contains("threads")) + return dict["threads"].template to(); + return 1; + } + + PHARE::initializer::PHAREDict updaterDict; + +public: + using patch_t = typename AMR_Types::patch_t; + using level_t = typename AMR_Types::level_t; + using hierarchy_t = typename AMR_Types::hierarchy_t; + + explicit SolverPPC(PHARE::initializer::PHAREDict const& dict) + : ISolver{"PPC"} + , ohm_{dict["ohm"]} + , updaterDict{dict["ion_updater"]} + , n_threads{operating_n_threads(dict)} + , threadingStrategy{n_threads} + { + } + + virtual ~SolverPPC() = default; + + + virtual std::string modelName() const override { return HybridModel::model_name; } + + virtual void fillMessengerInfo(std::unique_ptr const& info) const override; + + + virtual void registerResources(IPhysicalModel_t& model) override; + + + virtual void allocate(IPhysicalModel_t& model, SAMRAI::hier::Patch& patch, + double const allocateTime) const override; + + + + virtual void advanceLevel(std::shared_ptr const& hierarchy, int const levelNumber, + IPhysicalModel_t& model, IMessenger& fromCoarserMessenger, + double const currentTime, double const newTime) override; + + + +private: + using Messenger = amr::HybridMessenger; + + + void predictor1_(level_t& level, HybridModel& model, Messenger& fromCoarser, + double const currentTime, double const newTime); + + + void predictor2_(level_t& level, HybridModel& model, Messenger& fromCoarser, + double const currentTime, double const newTime); + + + void corrector_(level_t& level, HybridModel& model, Messenger& fromCoarser, + double const currentTime, double const newTime); + + + void average_(level_t& level, HybridModel& model); + + + void moveIons_(level_t& level, Ions& ions, Electromag& electromag, ResourcesManager& rm, + Messenger& fromCoarser, double const currentTime, double const newTime, + core::UpdaterMode mode); + + + void saveState_(level_t& level, Ions& ions, ResourcesManager& rm); + + void restoreState_(level_t& level, Ions& ions, ResourcesManager& rm); + + /* + template + void syncLevel(HybridMessenger& toCoarser) + { + toCoarser.syncMagnetic(model_.electromag.B); + toCoarser.syncElectric(model_.electromag.E); + }*/ + + std::size_t n_threads = 1; + + // extend lifespan + std::unordered_map tmpDomain; + std::unordered_map patchGhost; + + ThreadingStrategy threadingStrategy; +}; // end solverPPC + + + +// ----------------------------------------------------------------------------- + +template +void SolverPPC::registerResources(IPhysicalModel_t& model) +{ + auto& hmodel = dynamic_cast(model); + hmodel.resourcesManager->registerResources(electromagPred_); + hmodel.resourcesManager->registerResources(electromagAvg_); +} + + + + +template +void SolverPPC::allocate(IPhysicalModel_t& model, + SAMRAI::hier::Patch& patch, + double const allocateTime) const +{ + auto& hmodel = dynamic_cast(model); + hmodel.resourcesManager->allocate(electromagPred_, patch, allocateTime); + hmodel.resourcesManager->allocate(electromagAvg_, patch, allocateTime); +} + + + + +template +void SolverPPC::fillMessengerInfo( + std::unique_ptr const& info) const +{ + auto& modelInfo = dynamic_cast(*info); + + auto const& Epred = electromagPred_.E; + auto const& Bpred = electromagPred_.B; + + modelInfo.ghostElectric.emplace_back(Epred); + modelInfo.ghostMagnetic.emplace_back(Bpred); + modelInfo.initMagnetic.emplace_back(Bpred); +} + + +template +void SolverPPC::saveState_(level_t& level, Ions& ions, ResourcesManager& rm) +{ + for (auto& patch : level) + { + std::stringstream ss; + ss << patch->getGlobalId(); + + auto _ = rm.setOnPatch(*patch, ions); + for (auto& pop : ions) + { + auto key = ss.str() + "_" + pop.name(); + if (!tmpDomain.count(key)) + tmpDomain.emplace(key, pop.domainParticles()); + else + tmpDomain.at(key) = pop.domainParticles(); + if (!patchGhost.count(key)) + patchGhost.emplace(key, pop.patchGhostParticles()); + else + patchGhost.at(key) = pop.patchGhostParticles(); + } + } +} + +template +void SolverPPC::restoreState_(level_t& level, Ions& ions, + ResourcesManager& rm) +{ + for (auto& patch : level) + { + std::stringstream ss; + ss << patch->getGlobalId(); + + auto _ = rm.setOnPatch(*patch, ions); + for (auto& pop : ions) + { + pop.domainParticles() = std::move(tmpDomain.at(ss.str() + "_" + pop.name())); + pop.patchGhostParticles() = std::move(patchGhost.at(ss.str() + "_" + pop.name())); + } + } +} + + +template +void SolverPPC::advanceLevel(std::shared_ptr const& hierarchy, + int const levelNumber, IPhysicalModel_t& model, + IMessenger& fromCoarserMessenger, + double const currentTime, double const newTime) +{ + PHARE_LOG_SCOPE("SolverPPC::advanceLevel"); + + auto& hybridModel = dynamic_cast(model); + auto& hybridState = hybridModel.state; + auto& fromCoarser = dynamic_cast(fromCoarserMessenger); + auto& resourcesManager = *hybridModel.resourcesManager; + auto level = hierarchy->getPatchLevel(levelNumber); + + auto reset_moments = [&]() { + auto& ions = hybridState.ions; + for (auto& patch : *level) + { + auto _ = resourcesManager.setOnPatch(*patch, ions); + resetMoments(ions); + } + }; + + threadingStrategy.build_from(level, hybridModel, resourcesManager); + + predictor1_(*level, hybridModel, fromCoarser, currentTime, newTime); + average_(*level, hybridModel); + + saveState_(*level, hybridState.ions, resourcesManager); + reset_moments(); + moveIons_(*level, hybridState.ions, electromagAvg_, resourcesManager, fromCoarser, currentTime, + newTime, core::UpdaterMode::domain_only); + + predictor2_(*level, hybridModel, fromCoarser, currentTime, newTime); + average_(*level, hybridModel); + + restoreState_(*level, hybridState.ions, resourcesManager); + reset_moments(); + moveIons_(*level, hybridState.ions, electromagAvg_, resourcesManager, fromCoarser, currentTime, + newTime, core::UpdaterMode::all); + + corrector_(*level, hybridModel, fromCoarser, currentTime, newTime); +} + + + + +template +void SolverPPC::predictor1_(level_t& level, HybridModel& model, + Messenger& fromCoarser, + double const currentTime, double const newTime) +{ + PHARE_LOG_SCOPE("SolverPPC::predictor1_"); + + auto& hybridState = model.state; + auto& resourcesManager = model.resourcesManager; + auto dt = newTime - currentTime; + auto& electromag = hybridState.electromag; + auto levelNumber = level.getLevelNumber(); + + + { + PHARE_LOG_SCOPE("SolverPPC::predictor1_.faraday"); + + auto& Bpred = electromagPred_.B; + auto& B = electromag.B; + auto& E = electromag.E; + + for (auto& patch : level) + { + auto _ = resourcesManager->setOnPatch(*patch, Bpred, B, E); + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto __ = core::SetLayout(&layout, faraday_); + faraday_(B, E, Bpred, dt); + + + resourcesManager->setTime(Bpred, *patch, newTime); + } + + fromCoarser.fillMagneticGhosts(Bpred, levelNumber, newTime); + } + + + + { + PHARE_LOG_SCOPE("SolverPPC::predictor1_.ampere"); + + auto& Bpred = electromagPred_.B; + auto& J = hybridState.J; + + + for (auto& patch : level) + { + auto _ = resourcesManager->setOnPatch(*patch, Bpred, J); + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto __ = core::SetLayout(&layout, ampere_); + ampere_(Bpred, J); + + resourcesManager->setTime(J, *patch, newTime); + } + fromCoarser.fillCurrentGhosts(J, levelNumber, newTime); + } + + + + { + PHARE_LOG_SCOPE("SolverPPC::predictor1_.ohm"); + + auto& electrons = hybridState.electrons; + auto& Bpred = electromagPred_.B; + auto& Epred = electromagPred_.E; + auto& J = hybridState.J; + + for (auto& patch : level) + { + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto _ = resourcesManager->setOnPatch(*patch, Bpred, Epred, J, electrons); + electrons.update(layout); + auto& Ve = electrons.velocity(); + auto& Ne = electrons.density(); + auto& Pe = electrons.pressure(); + auto __ = core::SetLayout(&layout, ohm_); + ohm_(Ne, Ve, Pe, Bpred, J, Epred); + resourcesManager->setTime(Epred, *patch, newTime); + } + + fromCoarser.fillElectricGhosts(Epred, levelNumber, newTime); + } +} + + +template +void SolverPPC::predictor2_(level_t& level, HybridModel& model, + Messenger& fromCoarser, + double const currentTime, double const newTime) +{ + PHARE_LOG_SCOPE("SolverPPC::predictor2_"); + + auto& hybridState = model.state; + auto& resourcesManager = model.resourcesManager; + auto dt = newTime - currentTime; + auto levelNumber = level.getLevelNumber(); + + + + { + PHARE_LOG_SCOPE("SolverPPC::predictor2_.faraday"); + + auto& Bpred = electromagPred_.B; + auto& B = hybridState.electromag.B; + auto& Eavg = electromagAvg_.E; + + for (auto& patch : level) + { + auto _ = resourcesManager->setOnPatch(*patch, Bpred, B, Eavg); + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto __ = core::SetLayout(&layout, faraday_); + faraday_(B, Eavg, Bpred, dt); + + resourcesManager->setTime(Bpred, *patch, newTime); + } + + fromCoarser.fillMagneticGhosts(Bpred, levelNumber, newTime); + } + + + { + PHARE_LOG_SCOPE("SolverPPC::predictor2_.ampere"); + + auto& Bpred = electromagPred_.B; + auto& J = hybridState.J; + + + for (auto& patch : level) + { + auto _ = resourcesManager->setOnPatch(*patch, Bpred, J); + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto __ = core::SetLayout(&layout, ampere_); + ampere_(Bpred, J); + + resourcesManager->setTime(J, *patch, newTime); + } + fromCoarser.fillCurrentGhosts(J, levelNumber, newTime); + } + + + { + PHARE_LOG_SCOPE("SolverPPC::predictor2_.ohm"); + + auto& electrons = hybridState.electrons; + auto& Bpred = electromagPred_.B; + auto& Epred = electromagPred_.E; + auto& J = hybridState.J; + + for (auto& patch : level) + { + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto _ = resourcesManager->setOnPatch(*patch, Bpred, Epred, J, electrons); + electrons.update(layout); + auto& Ve = electrons.velocity(); + auto& Ne = electrons.density(); + auto& Pe = electrons.pressure(); + auto __ = core::SetLayout(&layout, ohm_); + ohm_(Ne, Ve, Pe, Bpred, J, Epred); + resourcesManager->setTime(Epred, *patch, newTime); + } + + fromCoarser.fillElectricGhosts(Epred, levelNumber, newTime); + } +} + + + + +template +void SolverPPC::corrector_(level_t& level, HybridModel& model, + Messenger& fromCoarser, double const currentTime, + double const newTime) +{ + PHARE_LOG_SCOPE("SolverPPC::corrector_"); + + auto& hybridState = model.state; + auto& resourcesManager = model.resourcesManager; + auto dt = newTime - currentTime; + auto levelNumber = level.getLevelNumber(); + + { + PHARE_LOG_SCOPE("SolverPPC::corrector_.faraday"); + + auto& B = hybridState.electromag.B; + auto& Eavg = electromagAvg_.E; + + for (auto& patch : level) + { + auto _ = resourcesManager->setOnPatch(*patch, B, Eavg); + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto __ = core::SetLayout(&layout, faraday_); + faraday_(B, Eavg, B, dt); + + resourcesManager->setTime(B, *patch, newTime); + } + + fromCoarser.fillMagneticGhosts(B, levelNumber, newTime); + } + + + + { + PHARE_LOG_SCOPE("SolverPPC::corrector_.ohm"); + + auto& electrons = hybridState.electrons; + auto& B = hybridState.electromag.B; + auto& E = hybridState.electromag.E; + auto& J = hybridState.J; + + for (auto& patch : level) + { + auto layout = PHARE::amr::layoutFromPatch(*patch); + auto _ = resourcesManager->setOnPatch(*patch, B, E, J, electrons); + electrons.update(layout); + auto& Ve = electrons.velocity(); + auto& Ne = electrons.density(); + auto& Pe = electrons.pressure(); + auto __ = core::SetLayout(&layout, ohm_); + ohm_(Ne, Ve, Pe, B, J, E); + resourcesManager->setTime(E, *patch, newTime); + } + + fromCoarser.fillElectricGhosts(E, levelNumber, newTime); + } +} + + + +template +void SolverPPC::average_(level_t& level, HybridModel& model) +{ + PHARE_LOG_SCOPE("SolverPPC::average_"); + + auto& hybridState = model.state; + auto& resourcesManager = model.resourcesManager; + + auto& Epred = electromagPred_.E; + auto& Bpred = electromagPred_.B; + auto& Bavg = electromagAvg_.B; + auto& Eavg = electromagAvg_.E; + auto& B = hybridState.electromag.B; + auto& E = hybridState.electromag.E; + + for (auto& patch : level) + { + auto _ = resourcesManager->setOnPatch(*patch, electromagAvg_, electromagPred_, + hybridState.electromag); + PHARE::core::average(B, Bpred, Bavg); + PHARE::core::average(E, Epred, Eavg); + } +} + + + +template +void SolverPPC::moveIons_(level_t& level, Ions& ions, + Electromag& electromag, ResourcesManager& rm, + Messenger& fromCoarser, double const currentTime, + double const newTime, core::UpdaterMode mode) +{ + PHARE_LOG_SCOPE("SolverPPC::moveIons_"); + + std::size_t nbrDomainParticles = 0; + std::size_t nbrPatchGhostParticles = 0; + std::size_t nbrLevelGhostNewParticles = 0; + std::size_t nbrLevelGhostOldParticles = 0; + std::size_t nbrLevelGhostParticles = 0; + for (auto& patch : level) + { + auto _ = rm.setOnPatch(*patch, ions); + + for (auto& pop : ions) + { + nbrDomainParticles += pop.domainParticles().size(); + nbrPatchGhostParticles += pop.patchGhostParticles().size(); + nbrLevelGhostNewParticles += pop.levelGhostParticlesNew().size(); + nbrLevelGhostOldParticles += pop.levelGhostParticlesOld().size(); + nbrLevelGhostParticles += pop.levelGhostParticles().size(); + nbrPatchGhostParticles += pop.patchGhostParticles().size(); + + if (nbrLevelGhostOldParticles < nbrLevelGhostParticles + and nbrLevelGhostOldParticles > 0) + throw std::runtime_error("Error - there are less old level ghost particles (" + + std::to_string(nbrLevelGhostOldParticles) + + ") than pushable (" + + std::to_string(nbrLevelGhostParticles) + ")"); + } + } + + + core::abort_if(n_threads == 0); + auto dt = newTime - currentTime; + threadingStrategy.solve(updaterDict, dt, mode); + + + for (auto& patch : level) + { + auto _ = rm.setOnPatch(*patch, electromag, ions); + // this needs to be done before calling the messenger + rm.setTime(ions, *patch, newTime); + } + + fromCoarser.fillIonGhostParticles(ions, level, newTime); + fromCoarser.fillIonMomentGhosts(ions, level, currentTime, newTime); + + for (auto& patch : level) + { + auto _ = rm.setOnPatch(*patch, electromag, ions); + auto layout = PHARE::amr::layoutFromPatch(*patch); + IonUpdater_t::updateIons(ions, layout); + // no need to update time, since it has been done before + } +} +} // namespace PHARE::solver + + +#endif diff --git a/src/amr/tagging/default_hybrid_tagger_strategy.hpp b/src/amr/tagging/default_hybrid_tagger_strategy.hpp index ede42a936..6ab30fb07 100644 --- a/src/amr/tagging/default_hybrid_tagger_strategy.hpp +++ b/src/amr/tagging/default_hybrid_tagger_strategy.hpp @@ -42,7 +42,7 @@ void DefaultHybridTaggerStrategy::tag(HybridModel& model, // SAMRAI tags int* buffer is FORTRAN ordering so we set false to the view bool constexpr c_ordering = false; - auto tagsv = core::NdArrayView(tags, layout.nbrCells()); + auto tagsv = core::NdArrayView(tags, layout.nbrCells()); if constexpr (dimension == 1 and false) { diff --git a/src/amr/tagging/hybrid_tagger.hpp b/src/amr/tagging/hybrid_tagger.hpp index 5ac86cdf7..eef98c7d1 100644 --- a/src/amr/tagging/hybrid_tagger.hpp +++ b/src/amr/tagging/hybrid_tagger.hpp @@ -87,7 +87,7 @@ void HybridTagger::tag(PHARE::solver::IPhysicalModel& model, auto tagsv = core::NdArrayView(hybridModel.tags[key]->data(), layout.nbrCells()); auto tagsvF - = core::NdArrayView(tags, layout.nbrCells()); + = core::NdArrayView(tags, layout.nbrCells()); if constexpr (HybridModel::dimension == 2) { for (auto iTag_x = 0u; iTag_x < nbrCells[0]; ++iTag_x) diff --git a/src/amr/tagging/hybrid_tagger_strategy.hpp b/src/amr/tagging/hybrid_tagger_strategy.hpp index 6dc5067f2..50a8fea8b 100644 --- a/src/amr/tagging/hybrid_tagger_strategy.hpp +++ b/src/amr/tagging/hybrid_tagger_strategy.hpp @@ -18,6 +18,6 @@ template HybridTaggerStrategy::~HybridTaggerStrategy() { } -} +} // namespace PHARE::amr #endif // HYBRID_TAGGER_STRATEGY_HPP diff --git a/src/amr/utilities/box/amr_box.hpp b/src/amr/utilities/box/amr_box.hpp index 677bb8998..f09108c9f 100644 --- a/src/amr/utilities/box/amr_box.hpp +++ b/src/amr/utilities/box/amr_box.hpp @@ -14,8 +14,8 @@ auto samrai_box_from(PHARE::core::Box const& box, int samrai_blockId { SAMRAI::tbox::Dimension dimension{dim}; SAMRAI::hier::BlockId blockId{samrai_blockId}; - return SAMRAI::hier::Box{SAMRAI::hier::Index{dimension, (*box.lower).data()}, - SAMRAI::hier::Index{dimension, (*box.upper).data()}, blockId}; + return SAMRAI::hier::Box{SAMRAI::hier::Index{dimension, &box.lower[0]}, + SAMRAI::hier::Index{dimension, &box.upper[0]}, blockId}; } template @@ -32,6 +32,9 @@ inline bool operator==(SAMRAI::hier::Box const& b1, SAMRAI::hier::Box const& b2) auto dim1 = b1.getDim().getValue(); auto dim2 = b2.getDim().getValue(); + if (dim1 != dim2) + return false; + bool boxesAreEqual = true; for (auto i = 0u; i < dim1; ++i) { @@ -82,6 +85,50 @@ struct Box : public PHARE::core::Box } }; + + + +template +inline bool isInBox(SAMRAI::hier::Box const& box, std::array const& iCell) +{ + auto const& lower = box.lower(); + auto const& upper = box.upper(); + + if (iCell[0] >= lower(0) && iCell[0] <= upper(0)) + { + if constexpr (dim > 1) + { + if (iCell[1] >= lower(1) && iCell[1] <= upper(1)) + { + if constexpr (dim > 2) + { + if (iCell[2] >= lower(2) && iCell[2] <= upper(2)) + { + return true; + } + } + else + { + return true; + } + } + } + else + { + return true; + } + } + return false; +} + + +template +inline bool isInBox(SAMRAI::hier::Box const& box, Particle const& particle) +{ + return isInBox(box, particle.iCell()); +} + + } // namespace PHARE::amr #endif diff --git a/src/core/data/electromag/electromag.hpp b/src/core/data/electromag/electromag.hpp index 1fd593f5f..ebf4f90b3 100644 --- a/src/core/data/electromag/electromag.hpp +++ b/src/core/data/electromag/electromag.hpp @@ -6,11 +6,24 @@ #include #include +#include "core/def.hpp" #include "core/hybrid/hybrid_quantities.hpp" #include "core/data/vecfield/vecfield_initializer.hpp" #include "initializer/data_provider.hpp" +namespace PHARE::core +{ +template +struct ElectromagView +{ + using view_t = ElectromagView; + using VecFieldView_t = typename VecField::view_t; + + VecFieldView_t E, B; +}; +} // namespace PHARE::core + namespace PHARE { namespace core @@ -19,6 +32,8 @@ namespace core class Electromag { public: + using view_t = ElectromagView; + static constexpr std::size_t dimension = VecFieldT::dimension; explicit Electromag(std::string name) @@ -72,6 +87,9 @@ namespace core B.copyData(source.B); } + auto view() { return view_t{E.view(), B.view()}; } + auto view() const { return view_t{E.view(), B.view()}; } + VecFieldT E; VecFieldT B; diff --git a/src/core/data/electrons/electrons.hpp b/src/core/data/electrons/electrons.hpp index 9eb5c9486..ede786e89 100644 --- a/src/core/data/electrons/electrons.hpp +++ b/src/core/data/electrons/electrons.hpp @@ -11,6 +11,7 @@ #include + namespace PHARE::core { template @@ -93,40 +94,39 @@ class StandardHybridElectronFluxComputer void computeBulkVelocity(GridLayout const& layout) { - auto const& Jx = J_(Component::X); - auto const& Jy = J_(Component::Y); - auto const& Jz = J_(Component::Z); - auto const& Vix = ions_.velocity()(Component::X); - auto const& Viy = ions_.velocity()(Component::Y); - auto const& Viz = ions_.velocity()(Component::Z); - auto const& Ni = ions_.density(); - - auto& Vex = Ve_(Component::X); - auto& Vey = Ve_(Component::Y); - auto& Vez = Ve_(Component::Z); + auto const J = J_.view(); + auto const Vi = ions_.velocity().view(); + auto const Ni = ions_.density().view(); + auto Ve = Ve_.view(); + + auto& Jx = J(Component::X); + auto& Jy = J(Component::Y); + auto& Jz = J(Component::Z); + auto& Vix = Vi(Component::X); + auto& Viy = Vi(Component::Y); + auto& Viz = Vi(Component::Z); + auto& Vex = Ve(Component::X); + auto& Vey = Ve(Component::Y); + auto& Vez = Ve(Component::Z); // from Ni because all components defined on primal - layout.evalOnBox(Ni, [&](auto const&... args) { - auto arr = std::array{args...}; - - auto const JxOnVx = GridLayout::project(Jx, arr, GridLayout::JxToMoments()); - auto const JyOnVy = GridLayout::project(Jy, arr, GridLayout::JyToMoments()); - auto const JzOnVz = GridLayout::project(Jz, arr, GridLayout::JzToMoments()); - - Vex(arr) = Vix(arr) - JxOnVx / Ni(arr); - Vey(arr) = Viy(arr) - JyOnVy / Ni(arr); - Vez(arr) = Viz(arr) - JzOnVz / Ni(arr); + layout.evalOnBox(Ni, [=] _PHARE_ALL_FN_(auto const& ijk) mutable { + auto const JxOnVx = GridLayout::project(Jx, ijk, GridLayout::JxToMoments()); + auto const JyOnVy = GridLayout::project(Jy, ijk, GridLayout::JyToMoments()); + auto const JzOnVz = GridLayout::project(Jz, ijk, GridLayout::JzToMoments()); + + Vex(ijk) = Vix(ijk) - JxOnVx / Ni(ijk); + Vey(ijk) = Viy(ijk) - JyOnVy / Ni(ijk); + Vez(ijk) = Viz(ijk) - JzOnVz / Ni(ijk); }); } - private: Ions& ions_; VecField& J_; VecField Ve_; - -}; // namespace PHARE::core +}; diff --git a/src/core/data/field/field.hpp b/src/core/data/field/field.hpp index 2b59bb915..2201f1132 100644 --- a/src/core/data/field/field.hpp +++ b/src/core/data/field/field.hpp @@ -9,77 +9,147 @@ #include #include +#include "core/def.hpp" +#include "core/vector.hpp" #include "core/data/ndarray/ndarray_vector.hpp" +namespace PHARE::core +{ +template +class FieldView : public NdArray +{ +public: + static constexpr bool is_contiguous = true; + static const std::size_t dimension = NdArray::dimension; + using type = typename NdArray::type; + using Super = NdArray; + using pointer_type = typename Super::pointer_type; + using data_view_t = typename NdArray::view_t; + using view_t = FieldView; + using Super::data; + using Super::shape; + using Super::size; + + // template>> + template + FieldView(std::array shape, PhysicalQuantity qty) + : Super{shape} + , qty_{qty} + { + } + + FieldView(pointer_type ptr, std::array shape, PhysicalQuantity qty) + : Super{ptr, shape} + , qty_{qty} + { + } + FieldView(NdArray&& ndArray, PhysicalQuantity qty) + : Super{std::move(ndArray)} + , qty_{qty} + { + } + FieldView(NdArray& ndArray, PhysicalQuantity qty) + : Super{ndArray} + , qty_{qty} + { + } -namespace PHARE -{ -namespace core + constexpr PhysicalQuantity physicalQuantity() const { return qty_; } + +private: + PhysicalQuantity qty_; +}; + + + +//! Class Field represents a multidimensional (1,2 or 3D) scalar field +/** Users of Field objects needing to know which physical quantity a specific + * Field instance represents can get this info by calling physicalQuantity(). + * Users may also give a string name to a field object and get a name by calling + * name(). + */ +template +class Field : public FieldView { - //! Class Field represents a multidimensional (1,2 or 3D) scalar field - /** Users of Field objects needing to know which physical quantity a specific - * Field instance represents can get this info by calling physicalQuantity(). - * Users may also give a string name to a field object and get a name by calling - * name(). - */ - template - class Field : public NdArrayImpl +public: + using Super = FieldView; + using impl_type = NdArrayImpl; + using type = typename NdArrayImpl::type; + using physical_quantity_type = PhysicalQuantity; + using data_view_t = typename NdArrayImpl::view_t; + using view_t = FieldView; + using Super::data; + using Super::physicalQuantity; + using Super::shape; + using Super::size; + + Field() = delete; + Field(Field const& source) = delete; + Field& operator=(Field&& source) = delete; + Field& operator=(Field const& source) = delete; + + template + Field(std::string const& name, PhysicalQuantity qty, Dims... dims) + : Super{std::array{dims...}, qty} + , name_{name} { - public: - using impl_type = NdArrayImpl; - using type = typename NdArrayImpl::type; - using physical_quantity_type = PhysicalQuantity; - using NdArrayImpl::dimension; + static_assert(sizeof...(Dims) == NdArrayImpl::dimension, "Invalid dimension"); + } + template + Field(std::string const& name, PhysicalQuantity qty, std::array const& dims) + : Super{dims, qty} + , name_{name} + { + } - Field() = delete; - Field(Field const& source) = delete; - Field(Field&& source) = default; - Field& operator=(Field&& source) = delete; - Field& operator=(Field const& source) = delete; + Field(Super ndArray, std::string const& name, PhysicalQuantity qty) + : Super{ndArray, qty} + , name_{name} + { + } - template - Field(std::string name, PhysicalQuantity qty, Dims... dims) - : NdArrayImpl{dims...} - , name_{std::move(name)} - , qty_{qty} - { - static_assert(sizeof...(Dims) == NdArrayImpl::dimension, "Invalid dimension"); - } + void copyData(Field const& source) + { + static_cast(*this) = static_cast(source); + } - template - Field(std::string name, PhysicalQuantity qty, std::array const& dims) - : NdArrayImpl{dims} - , name_{std::move(name)} - , qty_{qty} - { - } + std::string name() const { return name_; } - std::string name() const { return name_; } + auto view() const { return view_t{data_view_t{data(), shape()}, physicalQuantity()}; } + auto view() { return view_t{data_view_t{data(), shape()}, physicalQuantity()}; } - constexpr PhysicalQuantity physicalQuantity() const { return qty_; } +private: + std::string name_{"No Name"}; +}; - void copyData(Field const& source) - { - static_cast(*this) = static_cast(source); - } - private: - std::string name_{"No Name"}; - PhysicalQuantity qty_; - }; +template +void average(Field const& f1, + Field const& f2, + Field& avg) +{ + assert(f1.shape() == f2.shape() and f1.shape() == avg.shape()); + if constexpr (CompileOptions::WithUmpire && CompileOptions::WithRAJA) + { + auto d_f1 = f1.data(); + auto d_f2 = f2.data(); + auto d_avg = avg.data(); + for (std::size_t i = 0; i < f1.size(); ++i) + d_avg[i] = (d_f1[i] + d_f2[i]) * .5; // also bad - template - void average(Field const& f1, - Field const& f2, - Field& avg) + // probably change to + // PHARE_WITH_RAJA(PHARE::raja::exec( + // [=] RAJA_DEVICE(int i) mutable { d_avg[i] = (d_f1[i] + d_f2[i]) * .5; }, f1.size())); + } + else { std::transform(std::begin(f1), std::end(f1), std::begin(f2), std::begin(avg), std::plus()); @@ -87,8 +157,9 @@ namespace core std::transform(std::begin(avg), std::end(avg), std::begin(avg), [](double x) { return x * 0.5; }); } +} + +} // namespace PHARE::core -} // namespace core -} // namespace PHARE #endif diff --git a/src/core/data/grid/detail/mkn_gpu.hpp b/src/core/data/grid/detail/mkn_gpu.hpp new file mode 100644 index 000000000..9c10018c1 --- /dev/null +++ b/src/core/data/grid/detail/mkn_gpu.hpp @@ -0,0 +1,31 @@ + +#ifndef PHARE_CORE_GRID_DETAIL_MKN_GPU_H +#define PHARE_CORE_GRID_DETAIL_MKN_GPU_H + +namespace PHARE::core::mkn_gpu +{ +struct GridLayout +{ + template + static void evalOnBox(Fn& fn, std::vector& indexes, Args&... args) + { + // MKN_GPU::resources::Cuda res; + // auto tuple = std::make_tuple(args...); // copy for copy :( + // auto gpu_tuple = allocate_copy(res, tuple); + // auto d_indexes = allocate_copy(res, indexes); + // exec( + // [=] MKN_GPU_DEVICE(int i) mutable { + // std::apply( + // [=](auto&... targs) mutable { // + // fn(d_indexes[i], targs...); + // }, + // *gpu_tuple); + // }, + // res, indexes.size()); + // deallocate(res, gpu_tuple, d_indexes); + } +}; + + +} // namespace PHARE::core::mkn_gpu +#endif /* PHARE_CORE_GRID_DETAIL_MKN_GPU_H */ diff --git a/src/core/data/grid/detail/raja.hpp b/src/core/data/grid/detail/raja.hpp new file mode 100644 index 000000000..2c5422c3a --- /dev/null +++ b/src/core/data/grid/detail/raja.hpp @@ -0,0 +1,31 @@ + +#ifndef PHARE_CORE_GRID_DETAIL_RAJA_H +#define PHARE_CORE_GRID_DETAIL_RAJA_H + +namespace PHARE::core::grid_gpu_impl +{ +struct GridLayout +{ + template + static void evalOnBox(Fn& fn, std::vector& indexes, Args&... args) + { + RAJA::resources::Cuda res; + auto tuple = std::make_tuple(args...); // copy for copy :( + auto gpu_tuple = allocate_copy(res, tuple); + auto d_indexes = allocate_copy(res, indexes); + exec( + [=] RAJA_DEVICE(int i) mutable { + std::apply( + [=](auto&... targs) mutable { // + fn(d_indexes[i], targs...); + }, + *gpu_tuple); + }, + res, indexes.size()); + deallocate(res, gpu_tuple, d_indexes); + } +}; + + +} // namespace PHARE::core::grid_gpu_impl +#endif /* PHARE_CORE_GRID_DETAIL_RAJA_H */ diff --git a/src/core/data/grid/gridlayout.hpp b/src/core/data/grid/gridlayout.hpp index 431f92500..125d3eab9 100644 --- a/src/core/data/grid/gridlayout.hpp +++ b/src/core/data/grid/gridlayout.hpp @@ -1,23 +1,34 @@ #ifndef PHARE_CORE_GRID_GridLayout_HPP #define PHARE_CORE_GRID_GridLayout_HPP +#include +#include +#include +#include +#include +#include +#include "core/def.hpp" #include "core/hybrid/hybrid_quantities.hpp" #include "core/utilities/types.hpp" #include "core/data/field/field.hpp" #include "gridlayoutdefs.hpp" + #include "core/utilities/algorithm.hpp" #include "core/utilities/box/box.hpp" #include "core/utilities/constants.hpp" #include "core/utilities/index/index.hpp" #include "core/utilities/point/point.hpp" -#include -#include -#include -#include -#include -#include + +#if PHARE_HAVE_RAJA +#include "core/data/grid/detail/raja.hpp" +#endif + +#if PHARE_HAVE_MKN_GPU +#include "core/data/grid/detail/mkn_gpu.hpp" +#endif + namespace PHARE { @@ -34,14 +45,17 @@ namespace core { }; template - constexpr bool has_physicalQuantity_v = has_physicalQuantity::value; + auto constexpr has_physicalQuantity_v = has_physicalQuantity::value; - constexpr int centering2int(QtyCentering c) { return static_cast(c); } + auto constexpr static centering2int(QtyCentering c) + { + return static_cast(c); + } template - std::uint32_t constexpr ghostWidthForParticles() + std::uint32_t constexpr static ghostWidthForParticles() { return (interpOrder % 2 == 0 ? interpOrder / 2 + 1 : (interpOrder + 1) / 2); } @@ -108,7 +122,8 @@ namespace core GridLayout(std::array const& meshSize, std::array const& nbrCells, Point const& origin, - Box AMRBox = Box{}) + Box AMRBox = Box{}, + std::vector> neighbors = std::vector>{}) : meshSize_{meshSize} , origin_{origin} , nbrPhysicalCells_{nbrCells} @@ -116,6 +131,7 @@ namespace core , physicalEndIndexTable_{initPhysicalEnd_()} , ghostEndIndexTable_{initGhostEnd_()} , AMRBox_{AMRBox} + , neighbors_{neighbors} { if (AMRBox_.isEmpty()) { @@ -152,12 +168,15 @@ namespace core */ Point origin() const noexcept { return origin_; } - + auto& neighbors() const { return neighbors_; } /** * @brief returns the mesh size in the 'dim' dimensions */ - std::array const& meshSize() const noexcept { return meshSize_; } + std::array const& meshSize() const noexcept _PHARE_ALL_FN_ + { + return meshSize_; + } @@ -176,10 +195,12 @@ namespace core * @brief nbrCells returns the number of cells in the physical domain * described by the gridlayout */ - auto& nbrCells() const { return nbrPhysicalCells_; } + auto& nbrCells() const _PHARE_ALL_FN_ { return nbrPhysicalCells_; } - auto const& AMRBox() const { return AMRBox_; } + + + auto const& AMRBox() const _PHARE_ALL_FN_ { return AMRBox_; } @@ -289,7 +310,8 @@ namespace core * @brief physicalStartIndex returns the index of the first node of a given * centering and in a given direction that is in the physical domain, i.e. not a ghost node. */ - std::uint32_t physicalStartIndex(QtyCentering centering, Direction direction) const + std::uint32_t physicalStartIndex(QtyCentering centering, + Direction direction) const _PHARE_ALL_FN_ { std::uint32_t icentering = static_cast(centering); std::uint32_t iDir = static_cast(direction); @@ -542,6 +564,7 @@ namespace core * @brief the number of ghost nodes on each side of the mesh for a given centering */ auto static constexpr nbrGhosts(QtyCentering /*centering*/ = QtyCentering::primal) + _PHARE_ALL_FN_ { // Both dual and primal ghosts are the same! static_assert(nbrDualGhosts_() == nbrPrimalGhosts_()); @@ -593,7 +616,7 @@ namespace core * The next index is not just indexCenter+1 because this depends on the number * of ghost nodes for dual and primal nodes. */ - auto static nextIndex(QtyCentering centering, std::uint32_t indexCenter) + auto static nextIndex(QtyCentering centering, std::uint32_t indexCenter) _PHARE_ALL_FN_ { return indexCenter + nextIndexTable_[centering2int(centering)]; } @@ -603,7 +626,7 @@ namespace core * @brief prevIndex does the same thing as nextIndex but returns the index * of the node of a given centering just to the left of indexCenter. */ - auto static prevIndex(QtyCentering centering, std::uint32_t indexCenter) + auto static prevIndex(QtyCentering centering, std::uint32_t indexCenter) _PHARE_ALL_FN_ { return indexCenter + prevIndexTable_[centering2int(centering)]; } @@ -615,7 +638,7 @@ namespace core * on the dimensionality of the GridLayout. */ template - auto deriv(Field const& operand, MeshIndex index) + auto deriv(Field const& operand, MeshIndex index) const _PHARE_ALL_FN_ { auto fieldCentering = centering(operand.physicalQuantity()); using PHARE::core::dirX; @@ -682,7 +705,7 @@ namespace core * on the dimensionality of the GridLayout. */ template - auto laplacian(Field const& operand, MeshIndex index) + auto laplacian(Field const& operand, MeshIndex index) const _PHARE_ALL_FN_ { static_assert(Field::dimension == dimension, "field dimension must be equal to gridlayout dimension"); @@ -790,10 +813,10 @@ namespace core * This method only deals with **cell** indexes. */ template - auto AMRToLocal(Point AMRPoint) const + auto AMRToLocal(Point AMRPoint) const _PHARE_ALL_FN_ { static_assert(std::is_integral_v, "Error, must be MeshIndex (integral Point)"); - Point localPoint; + Point localPoint; // any direction, it's the same because we want cells auto localStart = physicalStartIndex(QtyCentering::dual, Direction::X); @@ -801,7 +824,9 @@ namespace core // for (auto i = 0u; i < dimension; ++i) { - localPoint[i] = AMRPoint[i] - (AMRBox_.lower[i] - localStart); + int local = AMRPoint[i] - (AMRBox_.lower[i] - localStart); + assert(local >= 0); + localPoint[i] = local; } return localPoint; } @@ -815,7 +840,7 @@ namespace core auto AMRToLocal(Box AMRBox) const { static_assert(std::is_integral_v, "Error, must be MeshIndex (integral Point)"); - auto localBox = Box{}; + auto localBox = Box{}; localBox.lower = AMRToLocal(AMRBox.lower); localBox.upper = AMRToLocal(AMRBox.upper); @@ -826,8 +851,9 @@ namespace core template - static typename Field::type project(Field const& field, MeshIndex index, - std::array, nbr_points> wps) + static typename Field::type + project(Field const& field, MeshIndex index, + std::array, nbr_points> wps) _PHARE_ALL_FN_ { typename Field::type result = 0.; for (auto const& wp : wps) @@ -889,7 +915,8 @@ namespace core * @return An std::array object, containing the size to which allocate * arrays of an HybridQuantity::Quantity 'qty' in every directions. */ - std::array allocSize(HybridQuantity::Scalar qty) const + std::array + allocSize(HybridQuantity::Scalar qty) const _PHARE_ALL_FN_ { std::uint32_t iQty = static_cast(qty); @@ -1130,28 +1157,79 @@ namespace core - template - void evalOnBox(Field& field, Fn&& fn) const + template + void evalOnBox(Field const& field, Fn&& fn, Args&&... args) const + { - auto indices = [&](auto const& centering, auto const direction) { + auto indices_fn = [&](auto const& centering, auto const direction) { return this->physicalStartToEnd(centering, direction); }; - evalOnBox_(field, fn, indices); + if constexpr (Field::is_host_mem) + { + evalOnBox_(field, fn, indices_fn, args...); + } + else + { + PHARE_WITH_RAJA( // try not to make indexes every time + auto indexes = make_indexes(field, indices_fn); + grid_gpu_impl::GridLayout::evalOnBox(fn, indexes, args...); // + ) + PHARE_WITH_MKN_GPU( // try not to make indexes every time + auto indexes = make_indexes(field, indices_fn); + mkn_gpu::GridLayout::evalOnBox(fn, indexes, args...); // + ) + } + } + + private: + template + static auto make_indexes(Field const& field, IndicesFn& startToEnd) + { + std::vector> indexes; + indexes.reserve(core::product(evalOnBox_shape(field, startToEnd))); + evalOnBox_( + field, [&](auto const&... args) { indexes.emplace_back(args...); }, startToEnd); + return indexes; } + template + static auto evalOnBox_shape(Field& field, IndicesFn& startToEnd) + { + std::array shape; - private: - template - static void evalOnBox_(Field& field, Fn& fn, IndicesFn& startToEnd) + auto const [ix0, ix1] = startToEnd(field, Direction::X); + shape[0] = ix1 - ix0 + 1; + + if constexpr (dimension > 1) + { + auto const [iy0, iy1] = startToEnd(field, Direction::Y); + shape[1] = iy1 - iy0 + 1; + } + + if constexpr (dimension > 2) + { + auto const [iz0, iz1] = startToEnd(field, Direction::Z); + shape[2] = iz1 - iz0 + 1; + } + return shape; + } + + + + + template + static void evalOnBox_(Field const& field, Fn& fn, IndicesFn& startToEnd, Args&... args) { + auto wrap_fn = [&](auto&& tuple) { fn(tuple, args...); }; + auto const [ix0, ix1] = startToEnd(field, Direction::X); for (auto ix = ix0; ix <= ix1; ++ix) { if constexpr (dimension == 1) { - fn(ix); + wrap_fn(std::make_tuple(ix)); } else { @@ -1161,24 +1239,29 @@ namespace core { if constexpr (dimension == 2) { - fn(ix, iy); + wrap_fn(std::make_tuple(ix, iy)); } else { auto const [iz0, iz1] = startToEnd(field, Direction::Z); for (auto iz = iz0; iz <= iz1; ++iz) - fn(ix, iy, iz); + wrap_fn(std::make_tuple(ix, iy, iz)); } } } } } + template + static void evalOnBox_(Field const& field, Fn&& fn, IndicesFn& startToEnd, Args&... args) + { + evalOnBox_(field, fn, startToEnd, args...); + } template - auto StartToEndIndices_(Centering const& centering, StartToEnd const&& startToEnd, - bool const includeEnd = false) const + static auto StartToEndIndices_(Centering const& centering, StartToEnd const&& startToEnd, + bool const includeEnd = false) { std::vector> indices; @@ -1318,7 +1401,7 @@ namespace core * directions depending on the multi-dimensional centering. */ std::array physicalNodeNbrFromCentering_( - std::array const& qtyCenterings) const + std::array const& qtyCenterings) const _PHARE_ALL_FN_ { std::array nodeNbr; @@ -1339,8 +1422,8 @@ namespace core * The calculation is easy : there are nbrPhysicalCells + 1 nodes in the domain * + 2 times the number of ghost nodes. */ - std::array - nodeNbrFromCentering_(std::array const& qtyCenterings) const + std::array nodeNbrFromCentering_( + std::array const& qtyCenterings) const _PHARE_ALL_FN_ { std::array nbrNodes = physicalNodeNbrFromCentering_(qtyCenterings); @@ -1478,6 +1561,8 @@ namespace core std::array, 2> ghostEndIndexTable_; Box AMRBox_; + std::vector> neighbors_; + // this constexpr initialization only works if primal==0 and dual==1 // this is defined in gridlayoutdefs.hpp don't change it because these @@ -1490,4 +1575,4 @@ namespace core } // namespace core } // namespace PHARE -#endif // GridLayout_HPP +#endif // PHARE_CORE_GRID_GridLayout_HPP diff --git a/src/core/data/grid/gridlayoutimplyee.hpp b/src/core/data/grid/gridlayoutimplyee.hpp index bf9f52df9..e1743812a 100644 --- a/src/core/data/grid/gridlayoutimplyee.hpp +++ b/src/core/data/grid/gridlayoutimplyee.hpp @@ -3,11 +3,13 @@ + #include "core/hybrid/hybrid_quantities.hpp" #include "core/utilities/types.hpp" #include "gridlayoutdefs.hpp" #include "core/utilities/constants.hpp" + #include #include @@ -114,7 +116,7 @@ namespace core // ------------------------------------------------------------------------ public: constexpr static std::array - centering(HybridQuantity::Scalar hybridQuantity) + safe_centering(HybridQuantity::Scalar& hybridQuantity) { constexpr gridDataT gridData_{}; if constexpr (dim == 1) @@ -149,7 +151,7 @@ namespace core return {{hybridQtyCentering_[gridData_.iVz][gridData_.idirX]}}; case HybridQuantity::Scalar::P: return {{hybridQtyCentering_[gridData_.iP][gridData_.idirX]}}; - default: throw std::runtime_error("Wrong hybridQuantity"); + default: hybridQuantity = HybridQuantity::Scalar::INVALID; } } @@ -199,7 +201,7 @@ namespace core case HybridQuantity::Scalar::P: return {{hybridQtyCentering_[gridData_.iP][gridData_.idirX], hybridQtyCentering_[gridData_.iP][gridData_.idirY]}}; - default: throw std::runtime_error("Wrong hybridQuantity"); + default: hybridQuantity = HybridQuantity::Scalar::INVALID; } } @@ -263,16 +265,25 @@ namespace core return {{hybridQtyCentering_[gridData_.iP][gridData_.idirX], hybridQtyCentering_[gridData_.iP][gridData_.idirY], hybridQtyCentering_[gridData_.iP][gridData_.idirZ]}}; - default: throw std::runtime_error("Wrong hybridQuantity"); + default: hybridQuantity = HybridQuantity::Scalar::INVALID; } } + return ConstArray(core::QtyCentering::primal); // ignored anyway. } + constexpr static std::array + centering(HybridQuantity::Scalar hybridQuantity) + { + std::array rval = safe_centering(hybridQuantity); + if (hybridQuantity == HybridQuantity::Scalar::INVALID) + throw_runtime_error("Wrong hybridQuantity"); + return rval; + } constexpr static std::array, 3> - centering(HybridQuantity::Vector hybridQuantity) + safe_centering(HybridQuantity::Vector& hybridQuantity) { switch (hybridQuantity) { @@ -296,13 +307,23 @@ namespace core centering(HybridQuantity::Scalar::Ey), centering(HybridQuantity::Scalar::Ez)}}; - - default: throw std::runtime_error("Wrong hybridQuantity"); + default: hybridQuantity = HybridQuantity::Vector::INVALID; } + return ConstArray, 3>( + ConstArray(core::QtyCentering::primal)); // ignored anyway. } + constexpr static std::array, 3> + centering(HybridQuantity::Vector hybridQuantity) + { + std::array, 3> rval = safe_centering(hybridQuantity); + if (hybridQuantity == HybridQuantity::Vector::INVALID) + throw_runtime_error("Wrong hybridQuantity"); + return rval; + } + auto static constexpr dualToPrimal() { diff --git a/src/core/data/ions/ion_population/ion_population.hpp b/src/core/data/ions/ion_population/ion_population.hpp index ce117350e..2464870eb 100644 --- a/src/core/data/ions/ion_population/ion_population.hpp +++ b/src/core/data/ions/ion_population/ion_population.hpp @@ -283,4 +283,74 @@ namespace core } // namespace core } // namespace PHARE + +namespace PHARE::core +{ +template +struct IonPopulationView +{ + using This = IonPopulationView; + + using particle_array_type = ParticleArray; + using VecFieldView_t = typename VecField::view_t; + using Field_t = typename VecField::field_type; + using FieldView_t = typename Field_t::view_t; + + FieldView_t density_; + VecFieldView_t flux_; + ParticleArray* domain; + ParticleArray* patch_ghost; + ParticleArray* level_ghost; + double const mass_; + + + auto& density() const { return density_; } + auto& density() { return density_; } + + auto& flux() const { return flux_; } + auto& flux() { return flux_; } + + double mass() const { return mass_; } + auto& domainParticles() { return *domain; } + auto& domainParticles() const { return *domain; } + auto& patchGhostParticles() { return *patch_ghost; } + auto& patchGhostParticles() const { return *patch_ghost; } + auto& levelGhostParticles() { return *level_ghost; } + auto& levelGhostParticles() const { return *level_ghost; } + + + auto static make(IonPopulation& pop) + { + return This{pop.density().view(), pop.flux().view(), &pop.domainParticles(), + &pop.patchGhostParticles(), &pop.levelGhostParticles(), pop.mass()}; + } +}; + +template +struct IonsView +{ + using IonPopView = IonPopulationView; + + using particle_array_type = ParticleArray; + using value_type = IonPopView; + + std::vector pops; + + + auto begin() { return std::begin(pops); } + auto end() { return std::end(pops); } + + auto begin() const { return std::begin(pops); } + auto end() const { return std::end(pops); } + + + template + auto static make(Ions& ions) + { + return IonsView{generate([&](auto& pop) { return IonPopView::make(pop); }, ions)}; + } +}; + +} // namespace PHARE::core + #endif diff --git a/src/core/data/ions/ions.hpp b/src/core/data/ions/ions.hpp index 0feb33e63..82d376760 100644 --- a/src/core/data/ions/ions.hpp +++ b/src/core/data/ions/ions.hpp @@ -25,6 +25,7 @@ namespace core class Ions { public: + using value_type = IonPopulation; using field_type = typename IonPopulation::field_type; using vecfield_type = typename IonPopulation::vecfield_type; using particle_array_type = typename IonPopulation::particle_array_type; @@ -226,6 +227,8 @@ namespace core return ss.str(); } + auto size() const { return nbrPopulations(); } + private: field_type* rho_{nullptr}; diff --git a/src/core/data/ions/particle_initializers/maxwellian_particle_initializer.hpp b/src/core/data/ions/particle_initializers/maxwellian_particle_initializer.hpp index 9c44ec623..a63ca73f4 100644 --- a/src/core/data/ions/particle_initializers/maxwellian_particle_initializer.hpp +++ b/src/core/data/ions/particle_initializers/maxwellian_particle_initializer.hpp @@ -78,7 +78,6 @@ class MaxwellianParticleInitializer : public ParticleInitializer bulkVelocity_; std::array thermalVelocity_; @@ -129,10 +128,12 @@ class MaxwellianInitFunctions -template -void MaxwellianParticleInitializer::loadParticles( - ParticleArray& particles, GridLayout const& layout) const +template +void MaxwellianParticleInitializer::loadParticles( + ParticleArray_t& particles, GridLayout const& layout) const { + // using Particle_t = typename ParticleArray_t::Particle_t; + auto point = [](std::size_t i, auto const& indices) -> core::Point { if constexpr (dimension == 1) return {std::get<0>(indices[i])}; @@ -142,17 +143,6 @@ void MaxwellianParticleInitializer::loadParticles( return {std::get<0>(indices[i]), std::get<1>(indices[i]), std::get<2>(indices[i])}; }; - - auto deltas = [](auto& pos, auto& gen) -> std::array { - if constexpr (dimension == 1) - return {pos(gen)}; - if constexpr (dimension == 2) - return {pos(gen), pos(gen)}; - if constexpr (dimension == 3) - return {pos(gen), pos(gen), pos(gen)}; - }; - - // in the following two calls, // primal indexes are given here because that's what cellCenteredCoordinates takes @@ -173,7 +163,10 @@ void MaxwellianParticleInitializer::loadParticles( auto randGen = getRNG(rngSeed_); ParticleDeltaDistribution deltaDistrib; - for (std::size_t flatCellIdx = 0; flatCellIdx < ndCellIndices.size(); flatCellIdx++) + // std::vector particles; + // particles.reserve(ndCellIndices.size() * nbrParticlePerCell_); + + for (std::size_t flatCellIdx = 0, i = 0; flatCellIdx < ndCellIndices.size(); ++flatCellIdx, ++i) { auto const cellWeight = n[flatCellIdx] / nbrParticlePerCell_; auto const AMRCellIndex = layout.localToAMR(point(flatCellIdx, ndCellIndices)); @@ -196,11 +189,14 @@ void MaxwellianParticleInitializer::loadParticles( if (basis_ == Basis::Magnetic) particleVelocity = basisTransform(basis, particleVelocity); - particles.emplace_back(Particle{cellWeight, particleCharge_, - AMRCellIndex.template toArray(), - deltas(deltaDistrib, randGen), particleVelocity}); + particles.emplace_back( + cellWeight, particleCharge_, AMRCellIndex.template toArray(), + core::ConstArrayFrom([&] { return deltaDistrib(randGen); }), + particleVelocity); } } + + // out_particles = std::move(particles); } } // namespace PHARE::core diff --git a/src/core/data/ndarray/ndarray_vector.hpp b/src/core/data/ndarray/ndarray_vector.hpp index c70d4f525..49dbecb6c 100644 --- a/src/core/data/ndarray/ndarray_vector.hpp +++ b/src/core/data/ndarray/ndarray_vector.hpp @@ -1,14 +1,18 @@ #ifndef PHARE_CORE_DATA_NDARRAY_NDARRAY_VECTOR_HPP #define PHARE_CORE_DATA_NDARRAY_NDARRAY_VECTOR_HPP -#include #include -#include -#include #include +#include +#include #include #include +#include + +#include "core/def.hpp" +#include "core/vector.hpp" +#include "core/utilities/types.hpp" namespace PHARE::core { @@ -16,12 +20,10 @@ template struct NdArrayViewer { template - static DataType const& at(DataType const* data, NCells const& nCells, Indexes const&... indexes) + static auto& at(DataType const* data, NCells const& nCells, + std::tuple const& params) _PHARE_ALL_FN_ { - auto params = std::forward_as_tuple(indexes...); - static_assert(sizeof...(Indexes) == dim); - // static_assert((... && std::is_unsigned_v)); TODO : manage later if - // this test should be included + static_assert(std::tuple_size_v> == dim); if constexpr (dim == 1) { @@ -54,9 +56,18 @@ struct NdArrayViewer } } + + template + static auto& at(DataType const* data, NCells const& nCells, + Indexes const&... indexes) _PHARE_ALL_FN_ + { + auto params = std::forward_as_tuple(indexes...); + return at(data, nCells, params); + } + template typename Indexes, typename Index> static DataType const& at(DataType const* data, NCells const& nCells, - Indexes const& indexes) + Indexes const& indexes) _PHARE_ALL_FN_ { if constexpr (dim == 1) @@ -126,137 +137,174 @@ class MaskedView -template -class NdArrayView : NdArrayViewer +template +class NdArrayView { + using viewer = NdArrayViewer; + public: - static constexpr bool is_contiguous = 1; + static constexpr auto is_contiguous = true; + static constexpr auto is_host_mem = is_host_mem_; static const std::size_t dimension = dim; using type = DataType; + using pointer_type = DataType*; + using This = NdArrayView; + using view_t = This; + + NdArrayView(pointer_type ptr, std::array const nCells) _PHARE_ALL_FN_ + : ptr_{ptr}, + size_{core::product(nCells)}, + nCells_{nCells} + { + } - explicit NdArrayView(Pointer ptr, std::array const& nCells) - : ptr_{ptr} - , nCells_{nCells} + + template + DataType const& operator()(std::array const& indexes) const _PHARE_ALL_FN_ { + return viewer::at(ptr_, nCells_, indexes); } - explicit NdArrayView(std::vector const& v, - std::array const& nbCell) - : NdArrayView{v.data(), nbCell} + template + DataType& operator()(std::array const& indexes) _PHARE_ALL_FN_ { + return const_cast(static_cast(*this)(indexes)); } template - DataType const& operator()(Indexes... indexes) const + DataType const& operator()(Indexes const... indexes) const _PHARE_ALL_FN_ { - return NdArrayViewer::at(ptr_, nCells_, indexes...); + return viewer::at(ptr_, nCells_, indexes...); } template - DataType& operator()(Indexes... indexes) + DataType& operator()(Indexes const... indexes) _PHARE_ALL_FN_ { return const_cast(static_cast(*this)(indexes...)); } - template - DataType const& operator()(std::array const& indexes) const - { - return NdArrayViewer::at(ptr_, nCells_, indexes); - } + // template + // DataType const& operator()(std::array const& indexes) const + // { + // return NdArrayViewer::at(ptr_, nCells_, indexes); + // } - template - DataType& operator()(std::array const& indexes) - { - return const_cast(static_cast(*this)(indexes)); - } + auto& data() const _PHARE_ALL_FN_ { return ptr_; } + auto& data() _PHARE_ALL_FN_ { return ptr_; } - auto data() const { return ptr_; } - std::size_t size() const + auto& size() const _PHARE_ALL_FN_ { return size_; } + auto& shape() const _PHARE_ALL_FN_ { return nCells_; } + + auto begin() const _PHARE_ALL_FN_ { return ptr_; } + auto begin() _PHARE_ALL_FN_ { return ptr_; } + + auto end() const _PHARE_ALL_FN_ { return ptr_ + size_; } + auto end() _PHARE_ALL_FN_ { return ptr_ + size_; } + +protected: + template + void reset(Vec& vec, std::array const& nCells) { - return std::accumulate(nCells_.begin(), nCells_.end(), 1, std::multiplies()); + this->ptr_ = vec.data(); + this->size_ = vec.size(); + this->nCells_ = nCells; } - auto shape() const { return nCells_; } private: - Pointer ptr_ = nullptr; + pointer_type ptr_ = nullptr; + std::size_t size_; std::array nCells_; }; +template +auto make_array_view(std::vector& vec, std::array shape) +{ + return NdArrayView{vec.data(), shape}; +} +template +auto make_array_view(std::vector const& vec, std::array shape) +{ + return NdArrayView{vec.data(), shape}; +} -template -class NdArrayVector +template> +class NdArrayVector : public StackVar>, + public NdArrayView()> { public: + static const auto dimension = dim; static constexpr bool is_contiguous = 1; - static const std::size_t dimension = dim; - using type = DataType; + static constexpr bool is_host_mem = PHARE::Allocator::is_host_mem(); - NdArrayVector() = delete; + using Allocator = Allocator_; + using vector_impl = std::vector; + using Vector = StackVar>; + using Super = NdArrayView; + using type = DataType; + using view_t = Super; + + using Super::data; + using Super::shape; + using Super::size; + using Vector::var; + + explicit NdArrayVector(std::array const& ncells) + : Vector{PHARE::Vector::template make(core::product(ncells))} + , Super{Vector::var.data(), ncells} + { + } template explicit NdArrayVector(Nodes... nodes) - : nCells_{nodes...} - , data_((... * nodes)) + : NdArrayVector{std::array{nodes...}} { static_assert(sizeof...(Nodes) == dim); } - explicit NdArrayVector(std::array const& ncells) - : nCells_{ncells} - , data_(std::accumulate(ncells.begin(), ncells.end(), 1, std::multiplies())) + + NdArrayVector(NdArrayVector const& that) + : Vector{PHARE::Vector::from(that.var)} + , Super{Vector::var.data(), that.shape()} { } - NdArrayVector(NdArrayVector const& source) = default; - NdArrayVector(NdArrayVector&& source) = default; - NdArrayVector& operator=(NdArrayVector const& source) = default; - NdArrayVector& operator=(NdArrayVector&& source) = default; - - auto data() const { return data_.data(); } - auto data() { return data_.data(); } - - auto size() const { return data_.size(); } - auto begin() const { return std::begin(data_); } - auto begin() { return std::begin(data_); } - - auto end() const { return std::end(data_); } - auto end() { return std::end(data_); } - - void zero() { std::fill(data_.begin(), data_.end(), 0); } + NdArrayVector(NdArrayVector&& that) + : Vector{PHARE::Vector::from(std::move(that.var))} + , Super{Vector::var.data(), that.shape()} + { + } + auto& operator=(NdArrayVector const& that) + { + if constexpr (is_host_mem) + this->var = that.var; + else + PHARE::Vector::copy(this->var, that.var); + Super::reset(this->var, that.shape()); - template - DataType const& operator()(Indexes... indexes) const - { - return NdArrayViewer::at(data_.data(), nCells_, indexes...); + return *this; } - template - DataType& operator()(Indexes... indexes) + auto& operator=(NdArrayVector&& that) { - return const_cast(static_cast(*this)(indexes...)); - } + if constexpr (is_host_mem) + this->var = std::move(that.var); + else + PHARE::Vector::copy(this->var, that.var); - template - DataType const& operator()(std::array const& indexes) const - { - return NdArrayViewer::at(data_.data(), nCells_, indexes); - } + Super::reset(this->var, that.shape()); - template - DataType& operator()(std::array const& indexes) - { - return const_cast(static_cast(*this)(indexes)); + return *this; } - auto& shape() const { return nCells_; } template auto operator[](Mask&& mask) @@ -264,14 +312,21 @@ class NdArrayVector return MaskedView{*this, std::forward(mask)}; } + void zero() + { + if (size() == 0) + return; + PHARE::Vector::fill(this->var, 0); + } + - auto& vector() { return data_; } - auto& vector() const { return data_; } + auto& vector() { return Vector::var; } + auto& vector() const { return Vector::var; } + void reset() { Super::reset(this->var, shape()); } -private: - std::array nCells_; - std::vector data_; + Super const& view() const { return *this; } + Super& view() { return *this; } }; @@ -344,9 +399,9 @@ class NdArrayMask } template - void fill3D(Array& array, typename Array::type val) const + void fill3D(Array& /*array*/, typename Array::type /*val*/) const { - throw std::runtime_error("3d not implemented"); + throw_runtime_error("3d not implemented"); } template @@ -365,7 +420,7 @@ class NdArrayMask cells += (shape[0] - (i * 2) - 2) * 2 + (shape[1] - (i * 2) - 2) * 2 + 4; if constexpr (Array::dimension == 3) - throw std::runtime_error("Not implemented dimension"); + throw_runtime_error("Not implemented dimension"); return cells; } @@ -445,7 +500,7 @@ void operator>>(MaskedView&& inner, MaskedView&& outer if constexpr (MaskedView_t::dimension == 3) { - throw std::runtime_error("3d not implemented"); + throw_runtime_error("3d not implemented"); } } diff --git a/src/core/data/particles/mapper/bisection_range_mapper.hpp b/src/core/data/particles/mapper/bisection_range_mapper.hpp new file mode 100644 index 000000000..e9b59ec2c --- /dev/null +++ b/src/core/data/particles/mapper/bisection_range_mapper.hpp @@ -0,0 +1,150 @@ +#ifndef PHARE_CORE_DATA_PARTICLES_BISECTION_RANGE_MAPPER_HPP +#define PHARE_CORE_DATA_PARTICLES_BISECTION_RANGE_MAPPER_HPP + +#include + +#include "core/logger.hpp" +#include "core/vector.hpp" +#include "core/utilities/box/box.hpp" +#include "core/utilities/range/range.hpp" + +namespace PHARE::core +{ +template +struct CellFlattener +{ + template + std::size_t operator()(Icell const& icell) const + { + if constexpr (Box_t::dimension == 3) + return icell[2] + icell[1] * box.upper[2] + icell[0] * box.upper[1] * box.upper[0]; + + return 1; + } + + Box_t const& box; +}; + +template +class BisectionRangeMapper +{ + static constexpr auto dim = ParticleArray::dimension; + using Ranges = std::vector>; + +public: + BisectionRangeMapper(ParticleArray const& particles_, Box_t const& box_) + : particles{particles_} + , box{box_} + { + } + + auto map() const; + +private: + template + std::size_t flat_cell(Icell const& icell) const + { + return flattener(icell); + } + + std::size_t find_lowest_of(std::size_t const&, std::size_t const&, std::size_t const&, + std::size_t const&, std::size_t const&) const; + std::size_t find_highest_of(std::size_t const&, std::size_t const&, std::size_t const&, + std::size_t const&, std::size_t const&) const; + + ParticleArray const& particles; + Box_t const& box; + CellFlattener const flattener{box}; + + std::size_t const lo_flat{flat_cell(box.lower())}; + std::size_t const up_flat{flat_cell(box.upper())}; +}; + + + +template +std::size_t BisectionRangeMapper::find_highest_of(std::size_t const& flell, + std::size_t const& lo, // + std::size_t const& up, + std::size_t const& lidx, + std::size_t const& uidx) const +{ + std::size_t midx = (uidx - lidx) / 2; + auto const& fmid = flat_cell(particles.iCell(midx + lidx)); + + std::stringstream ss; + ss << " " << flell << " " << lidx << " " << uidx << " " << midx << " " << fmid; + PHARE_LOG_LINE_STR(ss.str()); + + if (flell == fmid) + { + PHARE_LOG_LINE_STR("=="); + if (up == up_flat and flat_cell(particles.iCell(particles.size() - 1)) == flell) + return uidx; + return find_highest_of(flell, lo, up, lidx, uidx); // bug + } + + if (flell < fmid) + { + PHARE_LOG_LINE_STR("<"); + return find_highest_of(flell, lo, up, lidx, midx); + } + PHARE_LOG_LINE_STR(">"); + return find_highest_of(flell, lo, up, lidx + midx, uidx); +} + +template +std::size_t BisectionRangeMapper::find_lowest_of(std::size_t const& flell, + std::size_t const& lo, // + std::size_t const& up, + std::size_t const& lidx, + std::size_t const& uidx) const +{ + std::size_t midx = (uidx - lidx) / 2; + auto const& fmid = flat_cell(particles.iCell(midx + lidx)); + + std::stringstream ss; + ss << " " << flell << " " << lidx << " " << uidx << " " << midx; + PHARE_LOG_LINE_STR(ss.str()); + + if (flell == fmid) + { + PHARE_LOG_LINE_STR("=="); + if (lo == lo_flat and flat_cell(particles.iCell(0)) == flell) + return lidx; + return find_lowest_of(flell, lo, up, lidx, uidx); // bug + } + + if (flell < fmid) + { + PHARE_LOG_LINE_STR("<"); + return find_lowest_of(flell, lo, up, lidx, midx); + } + PHARE_LOG_LINE_STR(">"); + return find_lowest_of(flell, lo, up, lidx + midx, uidx); +} + + + +template +auto BisectionRangeMapper::map() const +{ + static_assert(dim == 3); // for now + + auto lowest = find_lowest_of(flat_cell(box.lower()), lo_flat, up_flat, 0, particles.size() - 1); + PHARE_LOG_LINE_STR(lowest); + + auto highest + = find_highest_of(flat_cell(box.upper()), lo_flat, up_flat, lowest, particles.size() - 1); + PHARE_LOG_LINE_STR(highest); + + // ranges are between lowest and highest + Ranges ranges; + + return ranges; +} + +} // namespace PHARE::core + + +#endif /* PHARE_CORE_DATA_PARTICLES_BISECTION_RANGE_MAPPER_HPP */ diff --git a/src/core/data/particles/mapper/edge_bisection_inner_ghost_area_mapper.hpp b/src/core/data/particles/mapper/edge_bisection_inner_ghost_area_mapper.hpp new file mode 100644 index 000000000..7975e00e2 --- /dev/null +++ b/src/core/data/particles/mapper/edge_bisection_inner_ghost_area_mapper.hpp @@ -0,0 +1,123 @@ +#ifndef PHARE_CORE_DATA_PARTICLES_EDGE_BISECTION_INNER_GHOST_MAPPER_HPP +#define PHARE_CORE_DATA_PARTICLES_EDGE_BISECTION_INNER_GHOST_MAPPER_HPP + +#include + +#include "core/logger.hpp" +#include "core/vector.hpp" +#include "core/utilities/box/box.hpp" +#include "core/utilities/range/range.hpp" + +namespace PHARE::core +{ +template +class EdgeBisectionMapper +{ + static constexpr auto dim = ParticleArray::dimension; + static constexpr auto ghost_particle_width = ghost_particle_width_; + + using Ranges = std::vector>; + +public: + template + auto map(ParticleArray const& /*particles*/, Box_t const& /*box*/); +}; + +template +auto flat_cell(Box_t const& box, Icell const& icell) +{ + if constexpr (Box_t::dimension == 3) + return icell[2] + icell[1] * box.upper[2] + icell[0] * box.upper[1] * box.upper[0]; + + return 1; +} + + +template +auto find_left_of(Ps const& particles, Box_t const& box, Icell const& icell, std::size_t idx) +{ + // if(){} + + // if(particles) + PHARE_LOG_LINE_STR("find_left_of"); + + return 1; +} + +template +auto find_right_of(Ps const& particles, Box_t const& box, Icell const& icell, std::size_t idx) +{ + PHARE_LOG_LINE_STR("find_right_of"); + + auto find_cell = [&]() { + auto v = icell; + v[0] += 1; + return v; + }(); + + auto flat_cell0 = flat_cell(box, icell); + auto flat_cell1 = flat_cell(box, find_cell); + + while (true) + { + if (flat_cell(box, particles.iCell(idx)) < flat_cell1) + { + auto half = idx / 2; + idx = +() + } + } + + return idx; +} + +template +auto bisect_left(Ps const& ps, Box_t const& box, Icell const& icell, std::size_t lo, std::size_t up) +{ + auto const left_wall_icell = [&]() { + auto v = icell; + v[0] = box.lower[0]; + return v; + }(); + auto const left_ghost_max = [&]() { + auto v = left_wall_icell; + v[0] += ghost_particle_width_; + return v; + }(); + + auto left_of_lower = find_left_of(particles, box, left_wall_icell, idx); + auto right_of_upper = find_right_of(particles, box, left_ghost_max, idx); + + return 1; +} + +template +auto bisect_right(ParticleArray const& particles, Box_t const& box, Icell const& icell) +{ +} + + + +template +template +auto EdgeBisectionMapper::map(ParticleArray const& particles, + Box_t const& box) +{ + static_assert(dim == 3); // for now + + Ranges ranges; + + std::size_t mid_index = particles.size() / 2; + auto const& mid_particle = particles.iCell(mid_index); + + bisect_left(particles, box, mid_particle, mid_index); + bisect_right(particles, box, mid_particle); + + for (int z = 0; z <= box.upper[2]; ++z) + for (int y = 0; z <= box.upper[1]; ++y) + { + // + } +} + +} // namespace PHARE::core +#endif /* PHARE_CORE_DATA_PARTICLES_EDGE_BISECTION_INNER_GHOST_MAPPER_HPP */ diff --git a/src/core/data/particles/mapper/edge_bisection_mapper.hpp b/src/core/data/particles/mapper/edge_bisection_mapper.hpp new file mode 100644 index 000000000..95663cadf --- /dev/null +++ b/src/core/data/particles/mapper/edge_bisection_mapper.hpp @@ -0,0 +1,33 @@ +// #ifndef PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_HPP +// #define PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_HPP + +// #include + +// #include "core/vector.hpp" +// #include "core/utilities/box/box.hpp" +// #include "core/utilities/range/range.hpp" + +// namespace PHARE::core +// { + + + +// template +// class EdgeBisectionMapper +// { +// static constexpr auto dim = ParticleArray::dimension; + +// using Ranges = std::vector>; + +// public: +// void map(); +// }; + + + + +// } // namespace PHARE::core + + + +// #endif /* PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_HPP */ diff --git a/src/core/data/particles/particle.hpp b/src/core/data/particles/particle.hpp index 721659cd9..de2ebb597 100644 --- a/src/core/data/particles/particle.hpp +++ b/src/core/data/particles/particle.hpp @@ -9,11 +9,11 @@ #include #include +#include "core/def.hpp" #include "core/utilities/point/point.hpp" #include "core/utilities/span.hpp" #include "core/utilities/types.hpp" - namespace PHARE::core { template @@ -28,10 +28,18 @@ struct ParticleDeltaDistribution }; + +template +auto cellAsPoint(std::array const& iCell) _PHARE_ALL_FN_ +{ + return Point{iCell}; +} + + template -auto cellAsPoint(Particle const& particle) +auto cellAsPoint(Particle const& particle) _PHARE_ALL_FN_ { - return Point{particle.iCell}; + return cellAsPoint(particle.iCell()); } @@ -41,124 +49,121 @@ struct Particle static_assert(dim > 0 and dim < 4, "Only dimensions 1,2,3 are supported."); static const size_t dimension = dim; - Particle(double a_weight, double a_charge, std::array cell, - std::array a_delta, std::array a_v) - : weight{a_weight} - , charge{a_charge} - , iCell{cell} - , delta{a_delta} - , v{a_v} + Particle(double const& a_weight, double const& a_charge, std::array const& cell, + std::array const& a_delta, std::array const& a_v) + : weight_{a_weight} + , charge_{a_charge} + , iCell_{cell} + , delta_{a_delta} + , v_{a_v} { } Particle() = default; - double weight; - double charge; + double weight_; + double charge_; + + std::array iCell_ = ConstArray(); + std::array delta_ = ConstArray(); + std::array v_ = ConstArray(); - std::array iCell = ConstArray(); - std::array delta = ConstArray(); - std::array v = ConstArray(); + std::array E_ = ConstArray(); + std::array B_ = ConstArray(); - double Ex = 0, Ey = 0, Ez = 0; - double Bx = 0, By = 0, Bz = 0; bool operator==(Particle const& that) const { - return (this->weight == that.weight) && // - (this->charge == that.charge) && // - (this->iCell == that.iCell) && // - (this->delta == that.delta) && // - (this->v == that.v) && // - (this->Ex == that.Ex) && // - (this->Ey == that.Ey) && // - (this->Ez == that.Ez) && // - (this->Bx == that.Bx) && // - (this->By == that.By) && // - (this->Bz == that.Bz); + return (this->weight_ == that.weight_) && // + (this->charge_ == that.charge_) && // + (this->iCell_ == that.iCell_) && // + (this->delta_ == that.delta_) && // + (this->v_ == that.v_); } + auto& weight() _PHARE_ALL_FN_ { return weight_; } + auto& weight() const _PHARE_ALL_FN_ { return weight_; } + + auto& charge() _PHARE_ALL_FN_ { return charge_; } + auto& charge() const _PHARE_ALL_FN_ { return charge_; } + + auto& iCell() _PHARE_ALL_FN_ { return iCell_; } + auto& iCell() const _PHARE_ALL_FN_ { return iCell_; } + + auto& delta() _PHARE_ALL_FN_ { return delta_; } + auto& delta() const _PHARE_ALL_FN_ { return delta_; } + + auto& v() _PHARE_ALL_FN_ { return v_; } + auto& v() const _PHARE_ALL_FN_ { return v_; } + + auto& E() _PHARE_ALL_FN_ { return E_; } + auto& E() const _PHARE_ALL_FN_ { return E_; } + + auto& B() _PHARE_ALL_FN_ { return B_; } + auto& B() const _PHARE_ALL_FN_ { return B_; } + + template friend std::ostream& operator<<(std::ostream& out, const Particle& particle); + + // auto& operator=(Particle const& that) + // { + // this->weight = that.weight; + // this->charge = that.charge; + // this->iCell = that.iCell; + // this->delta = that.delta; + // this->v = that.v; + // this->Ex = that.Ex; + // this->Ey = that.Ey; + // this->Ez = that.Ez; + // this->Bx = that.Bx; + // this->By = that.By; + // this->Bz = that.Bz; + // return *this; + // } }; template std::ostream& operator<<(std::ostream& out, Particle const& particle) { out << "iCell("; - for (auto c : particle.iCell) - { + for (auto c : particle.iCell()) out << c << ","; - } out << "), delta("; - for (auto d : particle.delta) - { + for (auto d : particle.delta()) out << d << ","; - } out << "), v("; - for (auto v : particle.v) - { + for (auto v : particle.v()) out << v << ","; - } - out << "), charge : " << particle.charge << ", weight : " << particle.weight; - out << ", Exyz : " << particle.Ex << "," << particle.Ey << "," << particle.Ez; - out << ", Bxyz : " << particle.Bx << "," << particle.By << "," << particle.Bz; - out << '\n'; + out << "), charge : " << particle.charge() << ", weight : " << particle.weight() << '\n'; return out; } -template -struct ParticleView -{ - static_assert(dim > 0 and dim < 4, "Only dimensions 1,2,3 are supported."); - static constexpr std::size_t dimension = dim; - - double& weight; - double& charge; - std::array& iCell; - std::array& delta; - std::array& v; -}; - - - - -template -inline constexpr auto is_phare_particle_type - = std::is_same_v, T> or std::is_same_v, T>; - - -template typename ParticleA, - template typename ParticleB> -typename std::enable_if_t< - is_phare_particle_type> and is_phare_particle_type>, - bool> -operator==(ParticleA const& particleA, ParticleB const& particleB) -{ - return particleA.weight == particleB.weight and // - particleA.charge == particleB.charge and // - particleA.iCell == particleB.iCell and // - particleA.delta == particleB.delta and // - particleA.v == particleB.v; -} } // namespace PHARE::core namespace std { +// <<<<<<< HEAD +// using namespace PHARE::core; -template typename Particle_t> -typename std::enable_if_t>, - PHARE::core::Particle> -copy(Particle_t const& from) +template +PHARE::core::Particle copy(PHARE::core::Particle const& from) +// ======= +// template typename Particle_t> +// typename std::enable_if_t>, +// PHARE::core::Particle> +// copy(Particle_t const& from) +// >>>>>>> a6ba967c (...) { - return {from.weight, from.charge, from.iCell, from.delta, from.v}; + return {from.weight(), from.charge(), from.iCell(), from.delta(), from.v()}; } } // namespace std + #endif diff --git a/src/core/data/particles/particle_array.hpp b/src/core/data/particles/particle_array.hpp index 8f951456f..706b8d72c 100644 --- a/src/core/data/particles/particle_array.hpp +++ b/src/core/data/particles/particle_array.hpp @@ -1,361 +1,174 @@ #ifndef PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_HPP #define PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_HPP - #include -#include -#include - -#include "core/utilities/indexer.hpp" -#include "particle.hpp" -#include "core/utilities/point/point.hpp" -#include "core/utilities/cellmap.hpp" -#include "core/logger.hpp" + +#include "core/vector.hpp" #include "core/utilities/box/box.hpp" #include "core/utilities/range/range.hpp" +#include "core/data/particles/particle_array_soa.hpp" +#include "core/data/particles/particle_array_aos.hpp" + namespace PHARE::core { -template -class ParticleArray +template +class ParticleArrayInternals { -public: - static constexpr bool is_contiguous = false; - static constexpr auto dimension = dim; - using This = ParticleArray; - using Particle_t = Particle; - using Vector = std::vector; - -private: - using CellMap_t = CellMap; - using IndexRange_ = IndexRange; - - -public: - using value_type = Particle_t; - using box_t = Box; - using iterator = typename Vector::iterator; - using const_iterator = typename Vector::const_iterator; - - - -public: - ParticleArray(box_t box) - : box_{box} - , cellMap_{box_} - { - assert(box_.size() > 0); - } - - ParticleArray(box_t box, std::size_t size) - : particles_(size) - , box_{box} - , cellMap_{box_} - { - assert(box_.size() > 0); - } - - ParticleArray(ParticleArray const& from) = default; - ParticleArray(ParticleArray&& from) = default; - ParticleArray& operator=(ParticleArray&& from) = default; - ParticleArray& operator=(ParticleArray const& from) = default; + static_assert(std::is_same_v); - std::size_t size() const { return particles_.size(); } - std::size_t capacity() const { return particles_.capacity(); } - - void clear() + template + struct S { - particles_.clear(); - cellMap_.clear(); - } - void reserve(std::size_t newSize) { return particles_.reserve(newSize); } - void resize(std::size_t newSize) { return particles_.resize(newSize); } - - auto const& operator[](std::size_t i) const { return particles_[i]; } - auto& operator[](std::size_t i) { return particles_[i]; } + using value_type = T; + }; - bool operator==(ParticleArray const& that) const + auto static constexpr resolve_t() { - return (this->particles_ == that.particles_); + if constexpr (mapped) + { + static_assert(aos, "No mapped SOA version"); + return S>{}; + } + else + { + if constexpr (aos) + return S>{}; + else + return S>{}; + } } - auto begin() const { return particles_.begin(); } - auto begin() { return particles_.begin(); } +public: + static constexpr bool aos = aos_; + static constexpr auto mapped = mapped_; + static constexpr auto alloc_mode = alloc_mode_; - auto end() const { return particles_.end(); } - auto end() { return particles_.end(); } + using value_type = typename decltype(resolve_t())::value_type; +}; - template - void insert(iterator position, InputIterator first, InputIterator last) - { - particles_.insert(position, first, last); - } - auto back() { return particles_.back(); } - auto front() { return particles_.front(); } +template> +class ParticleArray : public internals::value_type +{ +public: + auto static constexpr is_mapped = internals::mapped; - auto erase(IndexRange_& range) { cellMap_.erase(particles_, range); } - auto erase(IndexRange_&& range) - { - // TODO move ctor for range? - cellMap_.erase(std::forward(range)); - } + using This = ParticleArray; + using Super = typename internals::value_type; + using Super::erase; - iterator erase(iterator first, iterator last) - { - // should we erase particles indexes associated with these iterators from the cellmap? - // probably it does not matter if not. The reason is that - // particles erased from the particlearray are so because they left - // the patch cells to an outside cell. - // But in principle that cell will never be accessed because it is outside the patch. - // The only thing "bad" if these indexes are not deleted is that the - // size of the cellmap becomes unequal to the size of the particleArray. - // but ¯\_(ツ)_/¯ - return particles_.erase(first, last); - } + static constexpr bool is_contiguous = Super::is_contiguous; + static constexpr auto is_host_mem = Super::is_host_mem; + static constexpr auto dimension = Super::dimension; + static constexpr auto alloc_mode = internals::alloc_mode; + using box_t = Box; + using IndexRange_ = IndexRange; - Particle_t& emplace_back() - { - auto& part = particles_.emplace_back(); - cellMap_.add(particles_, particles_.size() - 1); - return part; - } + template + using array_type = typename Super::template array_type; + using const_iterator = typename Super::const_iterator; + using iterator = typename Super::iterator; + using value_type = typename Super::value_type; - Particle_t& emplace_back(Particle_t&& p) + template = true> + ParticleArray() { - auto& part = particles_.emplace_back(std::forward(p)); - cellMap_.add(particles_, particles_.size() - 1); - return part; } - void push_back(Particle_t const& p) + template = true> + ParticleArray(std::size_t size) + : Super{size} { - particles_.push_back(p); - cellMap_.add(particles_, particles_.size() - 1); } - void push_back(Particle_t&& p) + template = true> + ParticleArray(std::size_t size, Particle_t&& particle) + : Super{size, std::forward(particle)} { - particles_.push_back(std::forward(p)); - cellMap_.add(particles_, particles_.size() - 1); } - void swap(ParticleArray& that) { std::swap(this->particles_, that.particles_); } - - void map_particles() const { cellMap_.add(particles_); } - void empty_map() { cellMap_.empty(); } - - - auto nbr_particles_in(box_t const& box) const { return cellMap_.size(box); } - - void export_particles(box_t const& box, ParticleArray& dest) const + template = true> + ParticleArray(std::size_t size, Particle_t const& particle) + : Super{size, particle} { - PHARE_LOG_SCOPE("ParticleArray::export_particles"); - cellMap_.export_to(box, particles_, dest); } - template - void export_particles(box_t const& box, ParticleArray& dest, Fn&& fn) const + template + ParticleArray(It start, It end) + : Super{start, end} { - PHARE_LOG_SCOPE("ParticleArray::export_particles (Fn)"); - cellMap_.export_to(box, particles_.data(), dest, std::forward(fn)); } - template - void export_particles(box_t const& box, std::vector& dest, Fn&& fn) const - { - PHARE_LOG_SCOPE("ParticleArray::export_particles (box, vector, Fn)"); - cellMap_.export_to(box, particles_.data(), dest, std::forward(fn)); - } - - template - void export_particles(This& dest, Predicate&& pred) const + template = true> + ParticleArray(box_t box) + : Super{box} { - PHARE_LOG_SCOPE("ParticleArray::export_particles (Fn,vector)"); - cellMap_.export_if(particles_.data(), dest, std::forward(pred)); + assert(box.size() > 0); } - - template - void change_icell(Cell const& newCell, std::size_t particleIndex) + template = true> + ParticleArray(box_t box, std::size_t size) + : Super{box, size} { - auto oldCell = particles_[particleIndex].iCell; - particles_[particleIndex].iCell = newCell; - if (!box_.isEmpty()) - { - cellMap_.update(particles_, particleIndex, oldCell); - } + assert(box.size() > 0); } + ParticleArray(This const& that) = default; + ParticleArray(This&& that) = default; + This& operator=(This&& that) = default; + This& operator=(This const& that) = default; template auto partition(Predicate&& pred) { - return cellMap_.partition(makeIndexRange(*this), std::forward(pred)); - } - - template - void print(CellIndex const& cell) const - { - cellMap_.print(cell); + return Super::partition(makeIndexRange(*this), std::forward(pred)); } + auto erase(IndexRange_& range) { return Super::erase(range); } + auto erase(IndexRange_&& range) { return Super::erase(std::forward(range)); } - bool is_mapped() const + template + auto erase(Iterator first, Iterator last) { - bool ok = true; - if (particles_.size() != cellMap_.size()) - { - throw std::runtime_error("particle array not mapped, map.size() != array.size()"); - } - for (std::size_t pidx = 0; pidx < particles_.size(); ++pidx) - { - auto const& p = particles_[pidx]; - auto& icell = p.iCell; - auto l = cellMap_.list_at(icell); - if (!l) - throw std::runtime_error("particle cell not mapped"); - auto& ll = l->get(); - if (!ll.is_indexed(pidx)) - throw std::runtime_error("particle not indexed"); - } - return true; + return iterator{Super::erase(first, last), *this}; } - - void sortMapping() const { cellMap_.sort(); } - - auto& vector() { return particles_; } - auto& vector() const { return particles_; } - -private: - Vector particles_; - box_t box_; - mutable CellMap_t cellMap_; }; -} // namespace PHARE::core - - -namespace PHARE -{ -namespace core -{ - template - void empty(ParticleArray& array) - { - array.clear(); - } - - template - void swap(ParticleArray& array1, ParticleArray& array2) - { - array1.swap(array2); - } - +template +using AoSParticleArray + = ParticleArray>; - template - struct ContiguousParticles - { - static constexpr bool is_contiguous = true; - static constexpr std::size_t dimension = dim; - using ContiguousParticles_ = ContiguousParticles; - - template - using container_t = std::conditional_t, Span>; - - template> - ContiguousParticles(std::size_t s) - : iCell(s * dim) - , delta(s * dim) - , weight(s) - , charge(s) - , v(s * 3) - { - } +template +using AoSMappedParticleArray + = ParticleArray>; - template - ContiguousParticles(Container_int&& _iCell, Container_double&& _delta, - Container_double&& _weight, Container_double&& _charge, - Container_double&& _v) - : iCell{_iCell} - , delta{_delta} - , weight{_weight} - , charge{_charge} - , v{_v} - { - } +template +using SoAParticleArray + = ParticleArray>; - std::size_t size() const { return weight.size(); } - template - static std::array* _array_cast(T const* array) - { - return reinterpret_cast*>(const_cast(array)); - } +template +void empty(ParticleArray& array) +{ + array.clear(); +} - template - Return _to(std::size_t i) - { - return { - *const_cast(weight.data() + i), // - *const_cast(charge.data() + i), // - *_array_cast(iCell.data() + (dim * i)), // - *_array_cast(delta.data() + (dim * i)), // - *_array_cast<3>(v.data() + (3 * i)), - }; - } - auto copy(std::size_t i) { return _to>(i); } - auto view(std::size_t i) { return _to>(i); } +template +void swap(ParticleArray& array1, ParticleArray& array2) +{ + array1.swap(array2); +} - auto operator[](std::size_t i) const { return view(i); } - auto operator[](std::size_t i) { return view(i); } - struct iterator - { - iterator(ContiguousParticles_* particles) - { - for (std::size_t i = 0; i < particles->size(); i++) - views.emplace_back((*particles)[i]); - } - - iterator& operator++() - { - ++curr_pos; - return *this; - } - - bool operator!=(iterator const& other) const { return curr_pos != views.size(); } - auto& operator*() { return views[curr_pos]; } - auto& operator*() const { return views[curr_pos]; } - - std::size_t curr_pos = 0; - std::vector> views; - }; - - auto as_tuple() { return std::forward_as_tuple(weight, charge, iCell, delta, v); } - auto as_tuple() const { return std::forward_as_tuple(weight, charge, iCell, delta, v); } - - auto begin() { return iterator(this); } - auto cbegin() const { return iterator(this); } - - auto end() { return iterator(this); } - auto cend() const { return iterator(this); } - - container_t iCell; - container_t delta; - container_t weight, charge, v; - }; - template - using ContiguousParticlesView = ContiguousParticles; +} // namespace PHARE::core -} // namespace core -} // namespace PHARE #endif diff --git a/src/core/data/particles/particle_array_aos.hpp b/src/core/data/particles/particle_array_aos.hpp new file mode 100644 index 000000000..6d086ffbd --- /dev/null +++ b/src/core/data/particles/particle_array_aos.hpp @@ -0,0 +1,738 @@ +#ifndef PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_AOS_HPP +#define PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_AOS_HPP + + +#include +#include +#include + +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array_sorter.hpp" + +#include "core/vector.hpp" +#include "core/utilities/box/box.hpp" +#include "core/utilities/cellmap.hpp" +#include "core/utilities/iterators.hpp" +#include "core/utilities/point/point.hpp" +#include "core/utilities/range/range.hpp" + +namespace PHARE::core +{ +template +class AoSArray +{ +protected: + static constexpr auto is_vector = false; + +public: + static constexpr auto dimension = dim; + static constexpr auto is_host_mem = true; + + using Particle_t = Particle; + using container_type = std::array; + + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + + auto begin() const { return particles_.begin(); } + auto begin() { return particles_.begin(); } + + auto end() const { return particles_.end(); } + auto end() { return particles_.end(); } + + auto constexpr static size() { return size_; } + +protected: + container_type particles_; +}; + +template +class AoSSpan +{ +protected: + static constexpr auto is_vector = false; + +public: + static constexpr auto dimension = dim; + static constexpr auto is_host_mem = true; + + using Particle_t = Particle; + using container_type = Span; + + template + AoSSpan(Container& container) + : particles_{container.data(), container.size()} + { + } + + using iterator = Particle_t*; + using const_iterator = Particle_t const*; + + auto begin() const { return particles_.begin(); } + auto begin() _PHARE_ALL_FN_ { return particles_.begin(); } + + auto end() const { return particles_.end(); } + auto end() { return particles_.end(); } + + auto size() { return particles_.size(); } + +protected: + Span particles_; +}; + +template +class AoSVector +{ + using This = AoSVector; + + template + auto check_distance_size_t(Iterator const& start, Iterator const& end) + { + auto dist = std::distance(start, end); + if (dist < 0) + throw std::runtime_error("Error, number must be postive"); + return static_cast(dist); + } + +protected: + static constexpr auto is_vector = true; + +public: + static constexpr auto dimension = dim; + + using Particle_t = Particle; + using value_type = Particle_t; + + using allocator_type = PHARE::Allocator::allocator_type; + using container_type = std::vector; + + static constexpr auto is_host_mem = PHARE::Allocator::is_host_mem(); + + + AoSVector(std::size_t size = 0) + : particles_{PHARE::Vector::make(size)} + { + } + + template + AoSVector(std::size_t size, Particle_t const& particle) + : particles_{PHARE::Vector::make(size, particle)} + { + } + + template + AoSVector(Iterator start, Iterator end) + : AoSVector{check_distance_size_t(start, end)} + { + std::copy(start, end, particles_.begin()); + } + + AoSVector(AoSVector const& from) = default; + AoSVector(AoSVector&& from) = default; + AoSVector& operator=(AoSVector&& from) = default; + AoSVector& operator=(AoSVector const& from) = default; + + template + struct iterator_impl; + + using iterator = iterator_impl; + using const_iterator = iterator_impl; + + auto size() const { return particles_.size(); } + + void clear() { particles_.clear(); } + void reserve(std::size_t newSize) { return particles_.reserve(newSize); } + void resize(std::size_t newSize) { return particles_.resize(newSize); } + + auto& operator[](std::size_t i) const { return particles_.data()[i]; } + auto& operator[](std::size_t i) { return particles_.data()[i]; } + + bool operator==(This const& that) const { return (this->particles_ == that.particles_); } + + auto begin() const { return const_iterator{particles_.begin(), *this}; } + auto begin() { return iterator{particles_.begin(), *this}; } + + auto end() const { return const_iterator{particles_.end(), *this}; } + auto end() { return iterator{particles_.end(), *this}; } + + template + void insert(iterator position, InputIterator first, InputIterator last) + { + particles_.insert(position, first, last); + } + + auto back() { return particles_.back(); } + auto front() { return particles_.front(); } + + + + template + auto erase(Iterator first, Iterator last) + { + // should we erase particles indexes associated with these iterators from the cellmap? + // probably it does not matter if not. The reason is that + // particles erased from the particlearray are so because they left + // the patch cells to an outside cell. + // But in principle that cell will never be accessed because it is outside the patch. + // The only thing "bad" if these indexes are not deleted is that the + // size of the cellmap becomes unequal to the size of the particleArray. + // but ¯\_(ツ)_/¯ + return particles_.erase(particles_.begin() + first.idx(), particles_.begin() + last.idx()); + } + + Particle_t& emplace_back() { return particles_.emplace_back(); } + + + + Particle_t& emplace_back(Particle_t&& p) + { + return particles_.emplace_back(std::forward(p)); + } + + Particle_t& emplace_back(Particle_t const& p) + { + return particles_.emplace_back(std::forward(p)); + } + + template + Particle_t& emplace_back(Args const&... args) + { + return particles_.emplace_back(args...); + } + template + Particle_t& emplace_back(Args&&... args) + { + return particles_.emplace_back(Particle_t{args...}); + } + + void push_back(Particle_t const& p) { particles_.push_back(p); } + void push_back(Particle_t&& p) { particles_.push_back(std::forward(p)); } + +protected: + container_type particles_; +}; + + + +template +struct AoSParticles : public Super_ +{ + using Super = Super_; + using This = AoSParticles; + + using container_type = typename Super::container_type; + using Particle_t = typename Super::Particle_t; + + static constexpr bool is_contiguous = false; + static constexpr auto dimension = Super::dimension; + static constexpr auto size_of_particle() { return sizeof(Particle_t); } + + template + static constexpr bool is_vector() + { + return S::is_vector; + } + template + auto static constexpr is_span() + { + return std::is_same_v>; + } + + using Super::particles_; + + template + using array_type = AoSParticles>; + using vector_type = AoSParticles>; + + + + AoSParticles() {} + + template()>> + AoSParticles(std::size_t size) + : Super(size) + { + } + + template()>> + AoSParticles(std::size_t size, Particle_t&& particle) + : Super(size, std::forward(particle)) + { + } + + template()>> + AoSParticles(std::size_t size, Particle_t const& particle) + : Super(size, particle) + { + } + + + template()>> + AoSParticles(Iterator start, Iterator end) + : Super{start, end} + { + } + + template()>> + AoSParticles(Args&&... args) + : Super{std::forward(args)...} + { + } + template()>> + AoSParticles(Args const&... args) + : Super{args...} + { + } + + AoSParticles(AoSParticles const& from) = default; + AoSParticles(AoSParticles&& from) = default; + AoSParticles& operator=(AoSParticles&& from) = default; + AoSParticles& operator=(AoSParticles const& from) = default; + + + template // :( + auto static constexpr iterator_type() + { + throw std::runtime_error("never to be called in non constexpr context"); + if constexpr (is_vector<>()) + return std::conditional_t*, + typename Super::template iterator_impl*>{nullptr}; + else + return std::conditional_t{nullptr}; + } + + using iterator = std::decay_t())>; + using const_iterator = std::decay_t())>; + + template()>> + auto erase(It a, It b) + { + return Super::erase(a, b); + } + + auto begin() const _PHARE_ALL_FN_ + { + if constexpr (is_vector<>()) + return const_iterator{particles_.begin(), *this}; + else + return Super::begin(); + } + auto begin() _PHARE_ALL_FN_ + { + if constexpr (is_vector<>()) + return iterator{particles_.begin(), *this}; + else + return Super::begin(); + } + + auto end() const + { + if constexpr (is_vector<>()) + return const_iterator{particles_.end(), *this}; + else + return Super::end(); + } + auto end() + { + if constexpr (is_vector<>()) + return iterator{particles_.end(), *this}; + else + return Super::end(); + } + + auto& weight(std::size_t i) const { return particles_[i].weight(); } + auto& weight(std::size_t i) { return particles_[i].weight(); } + + auto& charge(std::size_t i) const { return particles_[i].charge(); } + auto& charge(std::size_t i) { return particles_[i].charge(); } + + auto& iCell(std::size_t i) const { return particles_[i].iCell(); } + auto& iCell(std::size_t i) { return particles_[i].iCell(); } + + auto& delta(std::size_t i) const { return particles_[i].delta(); } + auto& delta(std::size_t i) { return particles_[i].delta(); } + + auto& v(std::size_t i) const { return particles_[i].v(); } + auto& v(std::size_t i) { return particles_[i].v(); } + + auto& E(std::size_t i) const { return particles_[i].E_; } + auto& E(std::size_t i) { return particles_[i].E_; } + + auto& B(std::size_t i) const { return particles_[i].B_; } + auto& B(std::size_t i) { return particles_[i].B_; } + + auto data() const _PHARE_ALL_FN_ { return particles_.data(); } + auto data() _PHARE_ALL_FN_ { return particles_.data(); } + + + template(), bool> = 0> + auto capacity() const + { + return particles_.capacity(); + } + auto size() const { return particles_.size(); } + + template + auto partition(IndexRange&& range, Predicate&& pred) + { + return std::partition(range.begin(), range.end(), pred); + } + + auto& vector() { return particles_; } + auto& vector() const { return particles_; } + + + + void check() const {} + + + void sort() { Sorter{*this}(); } + void sort(std::int64_t s, std::int64_t e) { Sorter{*this}(s, e); } + + // does not swap cellmap (never did) + void swap(This& that) { std::swap(this->particles_, that.particles_); } + void swap(std::size_t const& a, std::size_t const& b) + { + if (a == b) + return; + + std::swap(particles_[a], particles_[b]); + } + + auto view() { return AoSParticles>{vector()}; } + // auto view() const { return vector().data(); } +}; + + +template +using AoSArrayParticles = AoSParticles>; + +template +using AoSVectorParticles = AoSParticles>; + +template +class AoSMappedParticles : public Super_ +{ + using Super = Super_; + using This = AoSMappedParticles; + using Super::particles_; + + static_assert(Super::template is_vector<>(), "not supported otherwise"); + +public: + static constexpr auto dimension = Super::dimension; + + using box_t = Box; + using CellMap_t = CellMap; + using Particle_t = typename Super::Particle_t; + + template + using array_type = typename Super::template array_type; + using vector_type = typename Super::vector_type; + + + AoSMappedParticles(box_t box) + : Super_{} + , box_{box} + , cellMap_{box_} + { + assert(box_.size() > 0); + } + + AoSMappedParticles(box_t box, std::size_t size) + : Super_(size) + , box_{box} + , cellMap_{box_} + { + assert(box_.size() > 0); + } + + + template // :( + auto static constexpr iterator_type() + { + throw std::runtime_error("never to be called in non constexpr context"); + return std::conditional_t*, + typename Super::template iterator_impl*>{nullptr}; + } + + using iterator = std::decay_t())>; + using const_iterator = std::decay_t())>; + + void clear() + { + Super::clear(); + cellMap_.clear(); + } + + template + auto erase(IndexRange& range) + { + cellMap_.erase(particles_, range); + } + template + auto erase(IndexRange&& range) + { + // TODO move ctor for range? + cellMap_.erase(std::forward(range)); + } + template + auto erase(It a, It b) + { + return Super::erase(a, b); + } + + + auto& emplace_back() + { + auto& part = Super::emplace_back(); + cellMap_.add(particles_, particles_.size() - 1); + return part; + } + + + auto& emplace_back(Particle_t&& p) + { + auto& part = Super::emplace_back(std::forward(p)); + cellMap_.add(particles_, particles_.size() - 1); + return part; + } + + auto& emplace_back(Particle_t const& p) + { + auto& part = Super::emplace_back(std::forward(p)); + cellMap_.add(particles_, particles_.size() - 1); + return part; + } + + template + auto& emplace_back(Args const&... args) + { + auto& part = Super::emplace_back(args...); + cellMap_.add(particles_, particles_.size() - 1); + return part; + } + template + auto& emplace_back(Args&&... args) + { + auto& part = Super::emplace_back(args...); + cellMap_.add(particles_, particles_.size() - 1); + return part; + } + + void push_back(Particle_t const& p) + { + Super::push_back(p); + cellMap_.add(particles_, particles_.size() - 1); + } + + void push_back(Particle_t&& p) + { + Super::push_back(std::forward(p)); + cellMap_.add(particles_, particles_.size() - 1); + } + + + void map_particles() const { cellMap_.add(particles_); } + void empty_map() { cellMap_.empty(); } + + + auto nbr_particles_in(box_t const& box) const { return cellMap_.size(box); } + + void export_particles(box_t const& box, This& dest) const + { + PHARE_LOG_SCOPE("ParticleArray::export_particles"); + cellMap_.export_to(box, *this, dest); + } + + template + void export_particles(box_t const& box, Dest& dest, Fn&& fn) const + { + PHARE_LOG_SCOPE("ParticleArray::export_particles (Fn)"); + cellMap_.export_to(box, *this, dest, std::forward(fn)); + } + + template + void export_particles(box_t const& box, std::vector& dest, Fn&& fn) const + { + PHARE_LOG_SCOPE("ParticleArray::export_particles (box, vector, Fn)"); + cellMap_.export_to(box, *this, dest, std::forward(fn)); + } + + template + void export_particles(This& dest, Predicate&& pred) const + { + PHARE_LOG_SCOPE("ParticleArray::export_particles (Fn,vector)"); + cellMap_.export_if(*this, dest, std::forward(pred)); + } + + + template + void change_icell(Cell const& newCell, std::size_t particleIndex) + { + auto oldCell = particles_[particleIndex].iCell(); + particles_[particleIndex].iCell() = newCell; + if (!box_.isEmpty()) + { + cellMap_.update(particles_, particleIndex, oldCell); + } + } + + + template + auto partition(IndexRange&& range, Predicate&& pred) + { + return cellMap_.partition(range, std::forward(pred)); + } + + template + void print(CellIndex const& cell) const + { + cellMap_.print(cell); + } + + + void sortMapping() const { cellMap_.sort(); } + + bool is_mapped() const + { + if (particles_.size() != cellMap_.size()) + { + throw std::runtime_error("particle array not mapped, map.size() != array.size()"); + } + for (std::size_t pidx = 0; pidx < particles_.size(); ++pidx) + { + auto const& p = particles_[pidx]; + auto& icell = p.iCell; + auto l = cellMap_.list_at(icell); + if (!l) + throw std::runtime_error("particle cell not mapped"); + auto& ll = l->get(); + if (!ll.is_indexed(pidx)) + throw std::runtime_error("particle not indexed"); + } + return true; + } + + auto begin() const { return const_iterator{particles_.begin(), *this}; } + auto begin() { return iterator{particles_.begin(), *this}; } + + auto end() const { return const_iterator{particles_.end(), *this}; } + auto end() { return iterator{particles_.end(), *this}; } + + void check() + { + if (particles_.size() != cellMap_.size()) + { + PHARE_LOG_LINE_STR(particles_.size()); + PHARE_LOG_LINE_STR(cellMap_.size()); + } + + core::abort_if(particles_.size() != cellMap_.size()); + // throw std::runtime_error("particle array not mapped, map.size() != array.size()"); + } + +protected: + box_t box_{}; + mutable CellMap_t cellMap_; +}; + +template +using AoSMappedVectorParticles = AoSMappedParticles>>; + + +template +template +struct AoSVector::iterator_impl + : public wrapped_iterator::container_type> +{ + static constexpr auto is_contiguous = false; + static constexpr auto dimension = dim; + + using Super = wrapped_iterator::container_type>; + + // using Super::difference_type; + // using Super::iterator_category; + + iterator_impl() {} + + template + iterator_impl(Iterator iter, T& container) + : Super{{iter}, &container} + { + } + iterator_impl(iterator_impl const& that) = default; + iterator_impl& operator=(iterator_impl const& other) = default; + + bool operator==(iterator_impl const& other) const + { + return (static_cast(*this) == static_cast(other)); + } + + auto operator+(std::size_t i) + { + iterator_impl copy = *this; + static_cast(copy) += i; + return copy; + } + + auto& weight() { return (*this)->weight(); } + auto& weight() const { return (*this)->weight(); } + + auto& charge() { return (*this)->charge(); } + auto& charge() const { return (*this)->charge(); } + + auto& iCell() { return (*this)->iCell(); } + auto& iCell() const { return (*this)->iCell(); } + + auto& delta() { return (*this)->delta(); } + auto& delta() const { return (*this)->delta(); } + + auto& v() { return (*this)->v(); } + auto& v() const { return (*this)->v(); } + + template + void weight(Weight const& weight) + { + (*this)->weight_ = weight; + } + + template + void charge(Charge const& charge) + { + (*this)->charge_ = charge; + } + + template + void iCell(ICell const& iCell) + { + (*this)->iCell_ = iCell; + } + + template + void delta(Delta const& delta) + { + (*this)->delta_ = delta; + } + + template + void v(V const& v) + { + (*this)->v_ = v; + } +}; + +} // namespace PHARE::core + +namespace std +{ +template +void sort(AoSParticles& particles) +{ + particles.sort(); +} +} // namespace std + + +#endif /* PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_AOS_HPP */ diff --git a/src/core/data/particles/particle_array_partitionner.hpp b/src/core/data/particles/particle_array_partitionner.hpp new file mode 100644 index 000000000..45bb2ca83 --- /dev/null +++ b/src/core/data/particles/particle_array_partitionner.hpp @@ -0,0 +1,28 @@ +#ifndef PHARE_CORE_DATA_PARTICLES_PARTICLE_PARTITIONNER_HPP +#define PHARE_CORE_DATA_PARTICLES_PARTICLE_PARTITIONNER_HPP + +#include "core/utilities/box/box.hpp" +#include "core/utilities/partitionner/partitionner.hpp" + +namespace PHARE::core +{ +template +auto static partition(ParticleArray& particles, Box_t const& box, std::vector neighbor_boxes) +{ + static_assert(!ParticleArray::is_mapped); // not for cell mapped particle arrays + PHARE_LOG_LINE_STR(particle_ghost_width); + + auto neighbor_ghost_boxes + = generate([](auto box) { return box.grow(particle_ghost_width); }, neighbor_boxes); + assert(all_overlaps(neighbor_ghost_boxes, box)); + auto overlaps = distinct_overlaps(neighbor_ghost_boxes, box); + + std::vector partition_boxes{shrink(box, particle_ghost_width)}; + std::copy(overlaps.begin(), overlaps.end(), std::back_inserter(partition_boxes)); + + return partitionner(particles.begin(), particles.end(), partition_boxes); +} + +} // namespace PHARE::core + +#endif /* PHARE_CORE_DATA_PARTICLES_PARTICLE_PARTITIONNER_HPP */ diff --git a/src/core/data/particles/particle_array_soa.hpp b/src/core/data/particles/particle_array_soa.hpp new file mode 100644 index 000000000..0be5c67a2 --- /dev/null +++ b/src/core/data/particles/particle_array_soa.hpp @@ -0,0 +1,794 @@ +#ifndef PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_SOA_HPP +#define PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_SOA_HPP + +#include "core/logger.hpp" +#include "core/vector.hpp" +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array_sorter.hpp" + +#include "core/utilities/span.hpp" // for flatten + +namespace PHARE::core +{ +template +struct SoAArray +{ + static constexpr auto dimension = dim; + static constexpr auto is_host_mem = true; + static constexpr auto is_vector = false; + + std::array weight_, charge_; + std::array, size_> iCell_; + std::array, size_> delta_; + std::array, size_> v_; + + + std::array, size_> E_; + std::array, size_> B_; + + + auto constexpr static size() { return size_; } +}; + + + + +// used in SOA_ParticelArray::operator[](std::size_t) for std algorithm interactions +// allows copying between vector and array impls without iterators +// could be a better way maybe but it's not so simple to change +template +using SoAParticle_crt = std::tuple const&, // iCell + std::array const&, // delta + std::array const&, // v + std::array const&, // E + std::array const& // B + >; +// template +// using SoAParticle_rt = std::tuple&, // iCell +// std::array&, // delta +// std::array&, // v +// std::array&, // E +// std::array& // B +// >; + + +template +struct SoAVector +{ + static constexpr auto dimension = dim; + static constexpr auto is_vector = true; + using container_type = std::vector>; + using value_type = typename container_type::value_type; + + + // template + // using allocator_t = PHARE::Allocator::allocator_type; + + template + using allocator_t = decltype(PHARE::allocator()); + + template + using container_t = std::vector>; + + + static constexpr auto is_host_mem // type doesn't matter here + = PHARE::Allocator::is_host_mem>(); + + + SoAVector() {} + + SoAVector(std::size_t size) + : weight_(size) + , charge_(size) + , iCell_(size) + , delta_(size) + , v_(size) + , E_(size) + , B_(size) + { + } + + template + SoAVector(std::size_t size, Particle_t&& from) + : weight_(size, from.weight()) + , charge_(size, from.charge()) + , iCell_(size, from.iCell()) + , delta_(size, from.delta()) + , v_(size, from.v()) + , E_(size, from.E()) + , B_(size, from.B()) + { + } + + auto size() const { return weight_.size(); } + + container_t weight_, charge_; + container_t> iCell_; + container_t> delta_; + container_t> v_; + + container_t> E_; + container_t> B_; +}; + + +// used when the memory is owned elsewhere, e.g. numpy arrays +template +struct SoASpan +{ + static constexpr auto dimension = dim; + static constexpr auto is_host_mem = true; + static constexpr auto is_vector = false; + + + template> + SoASpan(Container_int const&& _iCell, Container_double const&& _delta, + Container_double const&& _weight, Container_double const&& _charge, + Container_double const&& _v) + : size_{_weight.size()} + , weight_{reinterpret_cast(_weight.data())} + , charge_{reinterpret_cast(_charge.data())} + , iCell_{reinterpret_cast const*>(_iCell.data())} + , delta_{reinterpret_cast const*>(_delta.data())} + , v_{reinterpret_cast const*>(_v.data())} + { + } + + + template> + SoASpan(Container_int&& _iCell, Container_double&& _delta, Container_double&& _weight, + Container_double&& _charge, Container_double&& _v) + : size_{_weight.size()} + , weight_{reinterpret_cast(_weight.data())} + , charge_{reinterpret_cast(_charge.data())} + , iCell_{reinterpret_cast* const>(_iCell.data())} + , delta_{reinterpret_cast* const>(_delta.data())} + , v_{reinterpret_cast* const>(_v.data())} + { + } + + auto& size() const { return size_; } + + std::size_t size_; + + template + using container_t = std::conditional_t<_const_, T const*, T* const>; + + container_t weight_, charge_; + container_t> iCell_; + container_t> delta_; + container_t> v_; + + container_t> E_{}; + container_t> B_{}; +}; + + + +template +class SoAParticles : public Super_ +{ + template + struct iterator_impl; + +public: + static constexpr bool is_contiguous = true; + static constexpr auto dimension = Super_::dimension; + static constexpr auto is_host_mem = Super_::is_host_mem; + + using Super = Super_; + using This = SoAParticles; + using Super::size; + using Particle_t = SoAParticle_crt; + using value_type = Particle_t; + + template + using array_type = SoAParticles>; + using vector_type = SoAParticles>; + + using iterator = iterator_impl; + using const_iterator = iterator_impl; + + // public for pybind but avoid otherwise + using Super::B_; + using Super::charge_; + using Super::delta_; + using Super::E_; + using Super::iCell_; + using Super::v_; + using Super::weight_; + +protected: + template + auto static constexpr is_vector() + { + return Super::is_vector; + } + template + auto static constexpr is_span() + { + return std::is_same_v< + T, SoASpan> or std::is_same_v>; + } + + +public: + SoAParticles() {} + + template()>> + SoAParticles(std::size_t size) + : Super{size} + { + } + + template> + SoAParticles(std::size_t size, Particle_t&& particle) + : Super{size, std::forward(particle)} + { + } + + template()>> + SoAParticles(Args&&... args) + : Super{std::forward(args)...} + { + } + template()>> + SoAParticles(Args const&... args) + : Super{args...} + { + } + + SoAParticles(iterator start, iterator end); + + auto constexpr static size_of_particle() + { + return sizeof(typename decltype(iCell_)::value_type) + + sizeof(typename decltype(delta_)::value_type) + + sizeof(typename decltype(weight_)::value_type) + + sizeof(typename decltype(charge_)::value_type) + + sizeof(typename decltype(v_)::value_type) + + sizeof(typename decltype(E_)::value_type) + + sizeof(typename decltype(B_)::value_type); + } + + + + auto& weight(std::size_t i) const { return weight_[i]; } + auto& weight(std::size_t i) { return weight_[i]; } + + auto& charge(std::size_t i) const { return charge_[i]; } + auto& charge(std::size_t i) { return charge_[i]; } + + auto& iCell(std::size_t i) const { return iCell_[i]; } + auto& iCell(std::size_t i) { return iCell_[i]; } + + auto& delta(std::size_t i) const { return delta_[i]; } + auto& delta(std::size_t i) { return delta_[i]; } + + auto& v(std::size_t i) const { return v_[i]; } + auto& v(std::size_t i) { return v_[i]; } + + + auto& E(std::size_t i) const { return E_[i]; } + auto& E(std::size_t i) { return E_[i]; } + + auto& B(std::size_t i) const { return B_[i]; } + auto& B(std::size_t i) { return B_[i]; } + + auto& weight() const { return weight_; } + auto& charge() const { return charge_; } + auto& iCell() const { return iCell_; } + auto& delta() const { return delta_; } + auto& v() const { return v_; } + + + // for performing the same operation across all vectors e.g. with std apply + template + auto as_tuple(std::size_t i) + { + if constexpr (full) + std::forward_as_tuple(this->weight_[i], this->charge_[i], this->iCell_[i], + this->delta_[i], this->v_[i], this->E_[i], this->B_[i]); + else + return std::forward_as_tuple(this->weight_[i], this->charge_[i], this->iCell_[i], + this->delta_[i], this->v_[i]); + } + template + auto as_tuple(std::size_t i) const + { + if constexpr (full) + std::forward_as_tuple(this->weight_[i], this->charge_[i], this->iCell_[i], + this->delta_[i], this->v_[i], this->E_[i], this->B_[i]); + else + return std::forward_as_tuple(this->weight_[i], this->charge_[i], this->iCell_[i], + this->delta_[i], this->v_[i]); + } + + + template + auto as_tuple() + { + if constexpr (full) + return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_, E_, B_); + else + return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_); + } + + template + auto as_tuple() const + { + if constexpr (full) + return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_, E_, B_); + else + return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_); + } + + // to be avoided, but is convenient + auto operator[](std::size_t i) { return as_tuple(i); } + auto operator[](std::size_t i) const { return as_tuple(i); } + + auto begin() { return iterator(*this); } + auto begin() const { return const_iterator(*this); } + auto end() { return iterator(*this) + size(); } + auto end() const { return const_iterator(*this) + size(); } + + template(), int> = 0> + void push_back(iterator_impl const& particle) + { + this->weight_.push_back(particle.weight()); + this->charge_.push_back(particle.charge()); + this->iCell_.push_back(particle.iCell()); + this->delta_.push_back(particle.delta()); + this->v_.push_back(particle.v()); + this->E_.push_back(particle.E()); + this->B_.push_back(particle.B()); + } + + + template(), int> = 0> + void push_back(Particle_t const& particle) + { + auto const& [w, c, i, d, v, E, B] = particle; + this->weight_.push_back(w); + this->charge_.push_back(c); + this->iCell_.push_back(i); + this->delta_.push_back(d); + this->v_.push_back(v); + + this->E_.push_back(E); + this->B_.push_back(B); + } + + + + template + auto emplace_back(Args const&... args) + { + auto arg_tuple = std::forward_as_tuple(args...); + auto this_tuple = as_tuple(); + for_N>([&](auto ic) { + auto constexpr i = ic(); + std::get(this_tuple).emplace_back(std::get(arg_tuple)); + }); + this->E_.push_back(ConstArray()); + this->B_.push_back(ConstArray()); + + return (*this)[this->size() - 1]; + } + // template + // auto emplace_back(Args&&... args) + // { + // return (*this).emplace_back(args...); + // } + + + + // template + // // full adds E/B to the tuple, flat turns any vector to span + // auto as_tuple() + // { + // if constexpr (flat) + // { + // if constexpr (full) + // return std::forward_as_tuple(weight_, charge_, flatten(iCell_), flatten(delta_), + // flatten(v_), flatten(E_), flatten(B_)); + // else + // return std::forward_as_tuple(weight_, charge_, flatten(iCell_), flatten(delta_), + // flatten(v_)); + // } + // else + // { + // if constexpr (full) + // return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_, E_, B_); + // else + // return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_); + // } + // } + + // template + // // full adds E/B to the tuple, flat turns any vector to span + // auto as_tuple() const + // { + // if constexpr (flat) + // { + // if constexpr (full) + // return std::forward_as_tuple(weight_, charge_, flatten(iCell_), flatten(delta_), + // flatten(v_), flatten(E_), flatten(B_)); + // else + // return std::forward_as_tuple(weight_, charge_, flatten(iCell_), flatten(delta_), + // flatten(v_)); + // } + // else + // { + // if constexpr (full) + // return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_, E_, B_); + // else + // return std::forward_as_tuple(weight_, charge_, iCell_, delta_, v_); + // } + // } + + + void clear() + { + std::apply([](auto&... container) { ((container.clear()), ...); }, as_tuple()); + } + + void resize(std::size_t size) + { + std::apply([&](auto&... container) { ((container.resize(size)), ...); }, as_tuple()); + } + + void reserve(std::size_t size) + { + std::apply([&](auto&... container) { ((container.reserve(size)), ...); }, as_tuple()); + } + + void swap(This& that) + { + auto this_tuple = as_tuple(); + auto that_tuple = that.as_tuple(); + for_N>([&](auto ic) { + auto constexpr i = ic(); + std::get(this_tuple).swap(std::get(that_tuple)); + }); + } + + void swap(std::size_t const& a, std::size_t const& b) + { + if (a == b) + return; + + std::swap(weight_[a], weight_[b]); + std::swap(charge_[a], charge_[b]); + std::swap(iCell_[a], iCell_[b]); + std::swap(delta_[a], delta_[b]); + std::swap(v_[a], v_[b]); + std::swap(E_[a], E_[b]); + std::swap(B_[a], B_[b]); + } + + void erase(iterator first, iterator last) + { + std::apply( + [&](auto&... container) { + ((container.erase(container.begin() + first.curr_pos, + container.begin() + last.curr_pos)), + ...); + }, + as_tuple()); + } + + template(), bool> = 0> + std::size_t capacity() const + { + return weight_.capacity(); // they're all the same + } + + void sort() { Sorter{*this}(); } + void sort(std::int64_t s, std::int64_t e) { Sorter{*this}(s, e); } + + auto copy(std::size_t i) const + { + return Particle{ + weight_[i], charge_[i], iCell_[i], delta_[i], v_[i], + }; + } + + + auto back() { return (*this)[size() - 1]; } + auto front() { return (*this)[0]; } + + + + auto view() + { + return SoAParticles>{ + Span{iCell_[0].data(), iCell_.size() * dimension}, // iCell + Span{delta_[0].data(), delta_.size() * dimension}, // delta + Span{weight_.data(), weight_.size()}, // weight + Span{charge_.data(), charge_.size()}, // charge + Span{v_[0].data(), v_.size() * 3} // v; + }; + } + auto view() const { return SoAParticles>{}; } +}; + + +template +using SoAVectorParticles = SoAParticles>; + +template +using SoAArrayParticles = SoAParticles>; + + +template +template +struct SoAParticles::iterator_impl +{ + static constexpr auto dimension = OuterSuper::dimension; + auto static constexpr is_const = std::is_const_v; + auto static constexpr is_contiguous = true; + + using outer_type = std::decay_t; + using difference_type = std::size_t; + using iterator_category = std::forward_iterator_tag; + using value_type = std::decay_t; + using pointer = std::decay_t*; + using reference = std::decay_t&; + + + iterator_impl(T& particles_) + : particles{particles_} + { + } + + iterator_impl(iterator_impl&& that) = default; + iterator_impl(iterator_impl const& that) = default; + + auto& operator++() + { + ++curr_pos; + return *this; + } + auto& operator+=(std::int64_t i) + { + curr_pos += i; + return *this; + } + + auto& operator--() + { + --curr_pos; + return *this; + } + auto operator+(std::int64_t i) const + { + auto copy = *this; + copy.curr_pos += i; + return copy; + } + auto operator-(std::int64_t i) const + { + auto copy = *this; + copy.curr_pos -= i; + return copy; + } + + auto& operator=(iterator_impl const& that) + { + curr_pos = that.curr_pos; + return *this; + } + auto& operator=(iterator_impl&& that) + { + curr_pos = that.curr_pos; + return *this; + } + + + auto operator==(iterator_impl const& that) const { return curr_pos == that.curr_pos; } + auto operator!=(iterator_impl const& that) const { return curr_pos != that.curr_pos; } + auto operator<(iterator_impl const& that) const { return curr_pos < that.curr_pos; } + + + auto operator-(iterator_impl const& that) { return curr_pos - that.curr_pos; } + + auto& operator()() { return particles; } + auto& operator()() const { return particles; } + + auto& operator*() { return *this; } + auto& operator*() const { return *this; } + + auto idx() const { return curr_pos; } + + + auto& weight() { return particles.weight_[curr_pos]; } + auto& weight() const { return particles.weight_[curr_pos]; } + auto& charge() { return particles.charge_[curr_pos]; } + auto& charge() const { return particles.charge_[curr_pos]; } + auto& iCell() { return particles.iCell_[curr_pos]; } + auto& iCell() const { return particles.iCell_[curr_pos]; } + auto& delta() { return particles.delta_[curr_pos]; } + auto& delta() const { return particles.delta_[curr_pos]; } + auto& v() { return particles.v_[curr_pos]; } + auto& v() const { return particles.v_[curr_pos]; } + + auto& E() { return particles.E_[curr_pos]; } + auto& E() const { return particles.E_[curr_pos]; } + auto& B() { return particles.B_[curr_pos]; } + auto& B() const { return particles.B_[curr_pos]; } + + T& particles; + std::size_t curr_pos = 0; +}; + + + + +template +using ParticleArray_SOAView = SoAParticles>; + + +} // namespace PHARE::core + + +namespace std +{ +using namespace PHARE::core; + + +template typename Iterator, typename O> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, Particle::dimension>> +copy(Iterator src) +{ + return {src.weight(), src.charge(), src.iCell(), src.delta(), src.v()}; +} + + +template typename Iterator, typename O> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, void> copy(Iterator src_begin, // + Iterator src_end, + Iterator dst_begin) +{ + auto src_tuple = src_begin.particles.as_tuple(); + auto dst_tuple = dst_begin.particles.as_tuple(); + for_N>([&](auto ic) { + auto constexpr i = ic(); + auto& src = std::get(src_tuple); + auto& dst = std::get(dst_tuple); + std::copy(&src[src_begin.curr_pos], &src[src_end.curr_pos], &dst[dst_begin.curr_pos]); + }); +} + + + +template typename Iterator, typename O, typename Inserter> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, void> copy(Iterator src_begin, // + Iterator src_end, Inserter i) +{ + auto const& particles = src_begin.particles; + + for (; src_begin != src_end; ++src_begin, ++i) + *i = particles[src_begin.curr_pos]; +} + +template typename Iterator, typename O, typename Inserter, typename Fn> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, void> +copy_if(Iterator src_begin, Iterator src_end, Inserter i, Fn check) +{ + auto const& particles = src_begin.particles; + + for (; src_begin != src_end; ++src_begin) + if (check(src_begin)) + { + *i = particles[src_begin.curr_pos]; + ++i; + } +} + + +template typename Iterator, typename O> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, void> iter_swap(Iterator& a, + Iterator& b) +{ + assert(&a.particles == &b.particles); + + a.particles.swap(a.curr_pos, b.curr_pos); +} + +template typename Iterator, typename O, typename Predicate> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, Iterator> +find_if_not(Iterator first, Iterator last, Predicate q) +{ // https://en.cppreference.com/w/cpp/algorithm/find + for (; first != last; ++first) + if (!q(first)) // pass iterator! + return first; + return last; +} + + +template typename Iterator, typename O, typename Predicate> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, Iterator> +partition(Iterator first, Iterator last, Predicate p) +{ // https://en.cppreference.com/w/cpp/algorithm/partition + first = std::find_if_not(first, last, p); + if (first == last) + return first; + + for (auto i = first + 1; i != last; ++i) + { + if (p(i)) // pass iterator! + { + std::iter_swap(i, first); + ++first; + } + } + return first; +} + + +template typename Iterator, typename O, typename Fn> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, void> +transform(Iterator src_begin, Iterator src_end, Iterator dst_begin, Fn transform) +{ + if (&src_begin.particles != &dst_begin.particles) + throw std::runtime_error("transform cannot work"); + + for (; src_begin != src_end; ++src_begin, ++dst_begin) + /*dst_begin = */ transform(src_begin); // transform edits in place +} + + +template typename Iterator, typename O> +// SFINAE to support const iterators +typename std::enable_if_t::is_contiguous, std::size_t> distance(Iterator const& a, + Iterator const& b) +{ + assert(&a.particles == &b.particles); + assert(a.curr_pos <= b.curr_pos); + + return b.curr_pos - a.curr_pos; +} + + + +template +void sort(SoAParticles& particles) +{ + particles.sort(); +} + + +} // namespace std + + + +namespace PHARE::core +{ +template +SoAParticles::SoAParticles(SoAParticles::iterator start, + SoAParticles::iterator end) + : Super{std::distance(start, end)} +{ + std::copy(start, end, this->begin()); // impl above +} + + +} // namespace PHARE::core + + +#endif /* PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_SOA_HPP */ diff --git a/src/core/data/particles/particle_array_sorter.hpp b/src/core/data/particles/particle_array_sorter.hpp new file mode 100644 index 000000000..603fdfd56 --- /dev/null +++ b/src/core/data/particles/particle_array_sorter.hpp @@ -0,0 +1,191 @@ +#ifndef PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_SORTER_HPP +#define PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_SORTER_HPP + +// #include + +namespace PHARE::core +{ +template +struct Sorter +{ + // try flat index comparisons + // cell[2] + cell[1]*nz + cell[0]*ny*nz + + template + bool static constexpr el_wise_less(V const& v0, V const& v1) + { + for (std::int16_t i = v0.size() - 1; i >= 0; --i) + { + if (v0[i] < v1[i]) + return true; + if (v0[i] > v1[i]) + return false; + } + return false; + } + + template + bool static constexpr el_wise_gr8r(V const& v0, V const& v1) + { + for (std::int16_t i = v0.size() - 1; i >= 0; --i) + { + if (v0[i] > v1[i]) + return true; + if (v0[i] < v1[i]) + return false; + } + return false; + } + + void operator()(std::int64_t l, std::int64_t r) + { + auto i = l; + auto j = r; + + auto const half = particles.iCell((l + r) / 2); + + do + { + while (el_wise_less(particles.iCell(i), half)) + i++; + while (el_wise_gr8r(particles.iCell(j), half)) + j--; + + if (i <= j) + { + particles.swap(i, j); + + i++; + j--; + } + } while (i <= j); + + if (l < j) + (*this)(l, j); + if (i < r) + (*this)(i, r); + } + + void operator()() { (*this)(0, particles.size() - 1); } + + // void operator()() + // { + // std::size_t constexpr static HilbertOrder = 3; + // std::size_t constexpr static ndim = ParticleArray::dimension; + // using point_t = std::array; + + // std::sort(std::begin(particles), std::end(particles), [](auto const& a, auto const& b) { + // auto const h1 + // = hilbert::hilbert_distance_by_coords(a.iCell()); + // auto const h2 + // = hilbert::hilbert_distance_by_coords(b.iCell()); + // return h1 < h2; + // }); + // } + + ParticleArray& particles; +}; + + +} // namespace PHARE::core + + +#endif /* PHARE_CORE_DATA_PARTICLES_PARTICLE_ARRAY_SORTER_HPP */ + +#if 0 // review nicos madness + +/* __ sort part _____________________________________________________________ */ +void sort(struct sti *si, struct stx *sx, struct stp *sp[NS+1]) +{ +double xw, yw, zw; +int n0, n1, n2, nn2; +int *hist; +int sum, wh; +int ijk; +int i, j, k, m, s; +struct stp *sw; + + +/* __ print i'm sorting the part __ */ +if (sx->r == 0) printf("________________ i'm sorting the part ________________\n"); + +/* __ # of grid points __ */ +n0 = sx->n[0]+2; +n1 = sx->n[1]+2; +n2 = sx->n[2]+2; + +/* __ # of grid points on g2 __ */ +nn2 = n0*n1*n2; + +/* __ memory allocation for the histogram (that turns to be a cdf... __ */ +hist = (int *)malloc(nn2*sizeof(int)); + +/* __ memory allocation for the sorted part tab __ */ +sw = (struct stp *)malloc(si->nm*sizeof(struct stp)); +if (sw == NULL) +{ + printf("Error - allocation problem on proc %d at %s line %d\n", + sx->r, __FILE__, __LINE__); + fflush(stdout); + +} + +/* __ loop on the species __ */ +for (s = 1; s < NS+1; s++) + { + /* __ set hist to zero __ */ + for (ijk = 0; ijk < nn2; ijk++) + { + hist[ijk] = 0; + } + + /* __ loop on the part __ */ + for (m = 0; m < sx->ns[s]; m++) + { + /* __ interpolate on g2 __ */ + xw = sp[s][m].r[0]/si->dl[0]-sx->i0[0]+0.5; + yw = sp[s][m].r[1]/si->dl[1]-sx->i0[1]+0.5; + zw = sp[s][m].r[2]/si->dl[2]-sx->i0[2]+0.5; + + /* __ index for the part. "position" __ */ + i = (int)floor(xw); + j = (int)floor(yw); + k = (int)floor(zw); + + /* __ set the ijk index __ */ + sp[s][m].ijk = IDX(i , j , k , n0, n1, n2); + + /* __ fill the histogram __ */ + hist[sp[s][m].ijk]++; + } + + /* __ change the histogram in the associated cdf __ */ + for (sum = 0, ijk = 0; ijk < nn2; ijk++) + { + wh = hist[ijk]; + hist[ijk] = sum; + sum += wh; + } + + /* __ fill the new part tab __ */ + for (m = 0; m < sx->ns[s]; m++) + { + sw[hist[sp[s][m].ijk]++] = sp[s][m]; + } + + /* __ the outgoing part tab is now sorted __ */ + for (m = 0; m < sx->ns[s]; m++) + { + sp[s][m] = sw[m]; + } + + } + +/* __ free the pointers __ */ +free(sw); +free(hist); + +} + + +#endif diff --git a/src/core/data/particles/particle_packer.hpp b/src/core/data/particles/particle_packer.hpp index cf43f930c..025332a53 100644 --- a/src/core/data/particles/particle_packer.hpp +++ b/src/core/data/particles/particle_packer.hpp @@ -1,68 +1,78 @@ #ifndef PHARE_CORE_DATA_PARTICLE_PACKER_HPP #define PHARE_CORE_DATA_PARTICLE_PACKER_HPP - -#include #include +#include + +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array.hpp" -#include "particle.hpp" -#include "particle_array.hpp" namespace PHARE::core { +// PGI compiler (nvc++ 21.3-0) doesn't like static initializations of arrays, +// would result in empty strings +inline std::array packer_keys() +{ + // The order of this array must match the tuple order of ParticlePacker::get(particle) + return {"weight", "charge", "iCell", "delta", "v"}; +} + template class ParticlePacker { + using AoSParticles_t = AoSParticles>; + using Particle_t = typename AoSParticles_t::Particle_t; + public: - ParticlePacker(ParticleArray const& particles) + ParticlePacker(AoSParticles_t const& particles) : particles_{particles} { } - static auto get(Particle const& particle) + + static auto get(Particle_t const& particle) { - return std::forward_as_tuple(particle.weight, particle.charge, particle.iCell, - particle.delta, particle.v); + return std::forward_as_tuple(particle.weight_, particle.charge_, particle.iCell_, + particle.delta_, particle.v_); } static auto empty() { - Particle particle; + Particle_t particle; return get(particle); } + // sometimes we use this to infer the size of an ParticleArray // could be "charge" either static auto arbitrarySingleValueKey() { return "weight"; } - static auto& keys() { return keys_; } + static auto keys() { return packer_keys(); } auto get(std::size_t i) const { return get(particles_[i]); } bool hasNext() const { return it_ < particles_.size(); } auto next() { return get(it_++); } - void pack(ContiguousParticles& copy) + template + void pack(SoAParticles_t& copy) { - auto copyTo = [](auto& a, auto& idx, auto size, auto& v) { - std::copy(a.begin(), a.begin() + size, v.begin() + (idx * size)); - }; std::size_t idx = 0; while (this->hasNext()) { auto next = this->next(); - copy.weight[idx] = std::get<0>(next); - copy.charge[idx] = std::get<1>(next); - copyTo(std::get<2>(next), idx, dim, copy.iCell); - copyTo(std::get<3>(next), idx, dim, copy.delta); - copyTo(std::get<4>(next), idx, 3, copy.v); - idx++; + copy.weight(idx) = std::get<0>(next); + copy.charge(idx) = std::get<1>(next); + copy.iCell(idx) = std::get<2>(next); + copy.delta(idx) = std::get<3>(next); + copy.v(idx) = std::get<4>(next); + ++idx; } } private: - ParticleArray const& particles_; + AoSParticles_t const& particles_; std::size_t it_ = 0; - static inline std::array keys_{"weight", "charge", "iCell", "delta", "v"}; }; diff --git a/src/core/data/particles/particle_utilities.hpp b/src/core/data/particles/particle_utilities.hpp index d3a5aca48..d40b8233b 100644 --- a/src/core/data/particles/particle_utilities.hpp +++ b/src/core/data/particles/particle_utilities.hpp @@ -8,30 +8,44 @@ #include + namespace PHARE::core { -template /** * @brief positionAsPoint returns a point holding the physical position of the macroparticle. * The function assumes the iCell of the particle is in AMR index space. */ -auto positionAsPoint(Particle const& particle, GridLayout const& layout) +template +auto positionAsPoint(std::array const& iCell_, // + std::array const& delta_, // + GridLayout const& layout) { Point position; auto origin = layout.origin(); auto startIndexes = layout.physicalStartIndex(QtyCentering::primal); auto meshSize = layout.meshSize(); - auto iCell = layout.AMRToLocal(Point{particle.iCell}); + auto iCell = layout.AMRToLocal(cellAsPoint(iCell_)); for (auto iDim = 0u; iDim < GridLayout::dimension; ++iDim) { position[iDim] = origin[iDim]; - position[iDim] - += (iCell[iDim] - startIndexes[iDim] + particle.delta[iDim]) * meshSize[iDim]; + position[iDim] += (iCell[iDim] - startIndexes[iDim] + delta_[iDim]) * meshSize[iDim]; } return position; } +template +auto positionAsPoint(Particle const& particle, GridLayout const& layout) +{ + return positionAsPoint(particle.iCell(), particle.delta(), layout); +} + +template +auto positionAsPoint(ParticleArray_t const& particles, std::size_t idx, GridLayout const& layout) +{ + return positionAsPoint(particles.iCell(idx), particles.delta(idx), layout); +} + // this function is for debugging purposes // it looks for the given particle range, whether two particles @@ -46,7 +60,7 @@ void checkDeltas(ParticleRange const& prange) for (auto const& part : prange) { - deltas.push_back(part.iCell[0] + part.delta[0]); + deltas.push_back(part.iCell()[0] + part.delta()[0]); } std::sort(std::begin(deltas), std::end(deltas)); auto p = std::adjacent_find(std::begin(deltas), std::end(deltas)); @@ -55,11 +69,11 @@ void checkDeltas(ParticleRange const& prange) double delta = *p; std::cout << "Oops found duplicates \n"; auto part = std::find_if(std::begin(prange), std::end(prange), [delta](auto const& par) { - return par.iCell[0] + par.delta[0] == delta; + return par.iCell()[0] + par.delta()[0] == delta; }); auto part2 = std::find_if(part + 1, std::end(prange), [delta](auto const& par) { - return par.iCell[0] + par.delta[0] == delta; + return par.iCell()[0] + par.delta()[0] == delta; }); if (part == std::end(prange)) @@ -68,15 +82,15 @@ void checkDeltas(ParticleRange const& prange) std::cout << "part2 at the end of prange\n"; else { - std::cout << std::setprecision(12) << "part1 delta = " << part->delta[0] - << " , part2 delta = " << part2->delta[0] << "\n"; - std::cout << "part1 cell = " << part->iCell[0] << " , part2 Cell = " << part2->iCell[0] - << "\n"; + std::cout << std::setprecision(12) << "part1 delta = " << part->delta()[0] + << " , part2 delta = " << part2->delta()[0] << "\n"; + std::cout << "part1 cell = " << part->iCell()[0] + << " , part2 Cell = " << part2->iCell()[0] << "\n"; std::cout << "distance : " << std::distance(part, part2) << "\n"; std::cout << "size : " << prange.size() << "\n"; - std::cout << "p1.vx = " << part->v[0] << " p2.vx = " << part2->v[0] << "\n"; - std::cout << "p1.vy = " << part->v[1] << " p2.vy = " << part2->v[1] << "\n"; - std::cout << "p1.vz = " << part->v[2] << " p2.vz = " << part2->v[2] << "\n"; + std::cout << "p1.vx = " << part->v()[0] << " p2.vx = " << part2->v()[0] << "\n"; + std::cout << "p1.vy = " << part->v()[1] << " p2.vy = " << part2->v()[1] << "\n"; + std::cout << "p1.vz = " << part->v()[2] << " p2.vz = " << part2->v()[2] << "\n"; std::cout << &(*part) << " " << &(*part2) << "\n"; throw std::runtime_error("Oops duplicates duplicates throooow"); } diff --git a/src/core/data/vecfield/vecfield.hpp b/src/core/data/vecfield/vecfield.hpp index 35fe74353..52d9411fa 100644 --- a/src/core/data/vecfield/vecfield.hpp +++ b/src/core/data/vecfield/vecfield.hpp @@ -6,10 +6,108 @@ #include #include -#include "core/data/field/field.hpp" -#include "vecfield_component.hpp" +#include "core/def.hpp" +#include "core/utilities/types.hpp" #include "core/utilities/meta/meta_utilities.hpp" +#include "vecfield_component.hpp" +#include "core/data/field/field.hpp" + +namespace PHARE::core +{ +template +class VecFieldView +{ +public: + using This = VecFieldView; + using field_type = Field; + using Components = std::array; + static constexpr std::size_t dimension = Field::dimension; + + template + static constexpr Components make_components_from(VecField& vecfield) + { + return core::generate( + [&](auto& field_) { + auto& field = core::deref(field_); // might be a pointer + return field_type{field.data(), field.shape(), field.physicalQuantity()}; + }, + vecfield.components_); + } + + + VecFieldView(Components components) + : components_{components} + { + } + + + template + VecFieldView(VecField& vecfield) + : components_{make_components_from(vecfield)} + { + } + + VecFieldView() = default; + VecFieldView(VecFieldView const&) = default; + VecFieldView(VecFieldView&&) = default; + + auto& getComponent(Component component) const _PHARE_ALL_FN_ + { + switch (component) + { + case Component::X: return core::deref(components_[0]); + case Component::Y: return core::deref(components_[1]); + case Component::Z: return core::deref(components_[2]); + } + throw_runtime_error("Error - VecFieldView not usable"); + return core::deref(components_[0]); // hax + } + + + auto& getComponent(Component component) _PHARE_ALL_FN_ + { + switch (component) + { + case Component::X: return core::deref(components_[0]); + case Component::Y: return core::deref(components_[1]); + case Component::Z: return core::deref(components_[2]); + } + throw_runtime_error("Error - VecFieldView not usable"); + return core::deref(components_[0]); // hax + } + + auto& operator()(Component component) const _PHARE_ALL_FN_ { return getComponent(component); } + auto& operator()(Component component) _PHARE_ALL_FN_ { return getComponent(component); } + + + auto getComponents() const _PHARE_ALL_FN_ + { + return std::forward_as_tuple((*this)[0], (*this)[1], (*this)[2]); + } + auto getComponents() _PHARE_ALL_FN_ + { + return std::forward_as_tuple((*this)[0], (*this)[1], (*this)[2]); + } + + auto operator()() const _PHARE_ALL_FN_ { return getComponents(); } + auto operator()() _PHARE_ALL_FN_ { return getComponents(); } + + auto begin() const { return components_.begin(); } + auto begin() { return components_.begin(); } + + auto end() const { return components_.end(); } + auto end() { return components_.end(); } + + auto& operator[](std::size_t i) _PHARE_ALL_FN_ { return core::deref(components_[i]); } + auto& operator[](std::size_t i) const _PHARE_ALL_FN_ { return core::deref(components_[i]); } + +protected: + Components components_; +}; + +} // namespace PHARE::core + namespace PHARE { namespace core @@ -23,15 +121,26 @@ namespace core * VecField class is templated by the type of NdArray Field use and which * physical quantities they represent. */ + template - class VecField + class VecField : public VecFieldView*> { public: - VecField() = delete; - VecField& Vecfield(VecField const& source) = delete; - VecField(VecField&& source) = default; + using field_type = Field; + using Super = VecFieldView; + + using data_view_t = typename NdArrayImpl::view_t; + using view_t = VecFieldView>; + + using Super::components_; + using Super::getComponent; + using Super::getComponents; + + VecField() = delete; + VecField& Vecfield(VecField const& source) = delete; + VecField(VecField&& source) = default; VecField& operator=(VecField const& source) = delete; - VecField& operator=(VecField&& source) = default; + VecField& operator=(VecField&& source) = default; static constexpr std::size_t dimension = NdArrayImpl::dimension; @@ -43,13 +152,12 @@ namespace core * @param physQty is any of the vector physical quantities available in PHARE */ VecField(std::string const& name, typename PhysicalQuantity::Vector physQty) - : name_{name} + : Super{ConstArray(nullptr)} + , name_{name} , physQties_{PhysicalQuantity::componentsQuantities(physQty)} { } - - //------------------------------------------------------------------------- // start the ResourcesUser interface //------------------------------------------------------------------------- @@ -62,8 +170,6 @@ namespace core using resources_properties = std::vector; - using field_type = Field; - resources_properties getFieldNamesAndQuantities() const { return {{{componentNames_[0], physQties_[0]}, @@ -100,16 +206,12 @@ namespace core void zero() { - if (isUsable()) - { - for (auto& component : components_) - component->zero(); - } - else - { + if (!isUsable()) throw std::runtime_error( "Error, cannot zero the VecField because it is not usable"); - } + + for (auto& component : components_) + component->zero(); } @@ -120,41 +222,6 @@ namespace core std::string const& name() const { return name_; } - - Field& getComponent(Component component) - { - if (isUsable()) - { - switch (component) - { - case Component::X: return *components_[0]; - case Component::Y: return *components_[1]; - case Component::Z: return *components_[2]; - } - } - throw std::runtime_error("Error - VecField not usable"); - } - - - - - Field const& - getComponent(Component component) const - { - if (isUsable()) - { - switch (component) - { - case Component::X: return *components_[0]; - case Component::Y: return *components_[1]; - case Component::Z: return *components_[2]; - } - } - throw std::runtime_error("Error - VecField not usable"); - } - - - std::string getComponentName(Component component) const { switch (component) @@ -185,36 +252,22 @@ namespace core void copyData(VecField const& source) { - if (isUsable() && source.isUsable()) - { - for (std::size_t i = 0; i < 3; ++i) - { - components_[i]->copyData(*source.components_[i]); - } - } - else - { + if (!(isUsable() && source.isUsable())) throw std::runtime_error("Error, unusable VecField, cannot copyData"); - } - } - - auto begin() { return std::begin(components_); } - - auto cbegin() const { return std::cbegin(components_); } - - auto end() { return std::end(components_); } - - auto cend() const { return std::cend(components_); } + for (std::size_t i = 0; i < 3; ++i) + components_[i]->copyData(*source.components_[i]); + } + auto view() { return view_t{*this}; } + auto view() const { return view_t{*this}; } private: std::string name_ = "No Name"; std::array physQties_; const std::array componentNames_{name_ + "_x", name_ + "_y", name_ + "_z"}; - std::array components_{nullptr, nullptr, nullptr}; const std::unordered_map nameToIndex_{ {componentNames_[0], 0u}, {componentNames_[1], 1u}, {componentNames_[2], 2u}}; @@ -239,4 +292,5 @@ namespace core } // namespace core } // namespace PHARE + #endif diff --git a/src/core/data/vecfield/vecfield_initializer.hpp b/src/core/data/vecfield/vecfield_initializer.hpp index b73b37bb9..222dc9185 100644 --- a/src/core/data/vecfield/vecfield_initializer.hpp +++ b/src/core/data/vecfield/vecfield_initializer.hpp @@ -1,11 +1,47 @@ #ifndef VECFIELD_INITIALIZER_HPP #define VECFIELD_INITIALIZER_HPP +#include "core/vector.hpp" #include "core/data/grid/gridlayoutdefs.hpp" #include "core/data/vecfield/vecfield_component.hpp" #include "initializer/data_provider.hpp" -#include +#include +#include + + +namespace PHARE::core +{ +template +void initialize_field(Field& field, GridLayout const& layout, InitFunction const& init) +{ + auto const indices = layout.ghostStartToEndIndices(field, /*includeEnd=*/true); + auto const coords = layout.template indexesToCoordVectors( + indices, field, [](auto& gridLayout, auto& field_, auto const&... args) { + return gridLayout.fieldNodeCoordinates(field_, gridLayout.origin(), args...); + }); + + // keep grid data alive + auto grid = std::apply([&](auto const&... args) { return init(args...); }, coords); + assert(field.size() == grid->size()); + + if constexpr (Field::is_host_mem) // could this be a memcpy? + for (std::size_t cell_idx = 0; cell_idx < indices.size(); cell_idx++) + std::apply([&](auto&... args) { field(args...) = (*grid)[cell_idx]; }, + indices[cell_idx]); + else + { + if constexpr (CompileOptions::WithUmpire and CompileOptions::WithRAJA) + { + PHARE_WITH_RAJA(PHARE::core::raja::copy(field.data(), grid->data(), field.size())); + } + else + { + std::runtime_error("PHARE::core::initialize_field NO ALTERNATIVE"); + } + } +} +} // namespace PHARE::core namespace PHARE { @@ -31,33 +67,12 @@ namespace core static_assert(GridLayout::dimension == VecField::dimension, "dimension mismatch between vecfield and gridlayout"); - initializeComponent_(v.getComponent(Component::X), layout, x_); - initializeComponent_(v.getComponent(Component::Y), layout, y_); - initializeComponent_(v.getComponent(Component::Z), layout, z_); + initialize_field(v.getComponent(Component::X), layout, x_); + initialize_field(v.getComponent(Component::Y), layout, y_); + initialize_field(v.getComponent(Component::Z), layout, z_); } private: - template - void initializeComponent_(Field& field, GridLayout const& layout, - initializer::InitFunction const& init) - { - auto const indices = layout.ghostStartToEndIndices(field, /*includeEnd=*/true); - auto const coords = layout.template indexesToCoordVectors( - indices, field, [](auto& gridLayout, auto& field_, auto const&... args) { - return gridLayout.fieldNodeCoordinates(field_, gridLayout.origin(), args...); - }); - - std::shared_ptr> gridPtr // keep grid data alive - = std::apply([&](auto&... args) { return init(args...); }, coords); - Span& grid = *gridPtr; - - for (std::size_t cell_idx = 0; cell_idx < indices.size(); cell_idx++) - std::apply([&](auto&... args) { field(args...) = grid[cell_idx]; }, - indices[cell_idx]); - } - - - initializer::InitFunction x_; initializer::InitFunction y_; initializer::InitFunction z_; diff --git a/src/core/def.hpp b/src/core/def.hpp new file mode 100644 index 000000000..bd93b7ffe --- /dev/null +++ b/src/core/def.hpp @@ -0,0 +1,71 @@ +#ifndef PHARE_CORE_DEF_HPP +#define PHARE_CORE_DEF_HPP + +#include +#include +#include + +#if !defined(PHARE_WITH_GPU) or PHARE_HAVE_MKN_GPU == 1 +#if defined(PHARE_HAVE_MKN_GPU) and PHARE_HAVE_MKN_GPU == 1 +#define PHARE_WITH_GPU 1 +#include "mkn/gpu.hpp" +#else +#define PHARE_WITH_GPU 0 +#endif +#endif + +#if PHARE_WITH_GPU + +#define _PHARE_DEV_FN_ __device__ +#define _PHARE_HST_FN_ __host__ +#define _PHARE_ALL_FN_ _PHARE_HST_FN_ _PHARE_DEV_FN_ + +namespace PHARE +{ +// !WARNING! +// PGIC++/NVC++ CANNOT HANDLE separate device/host functions! +template +inline void throw_runtime_error(T const&) __device__ +{ + // gpu cannot throw + assert(false); +} + +template +inline void throw_runtime_error(T const& err) __host__ +{ + throw std::runtime_error(err); +} +} // namespace PHARE + +#else + +#define _PHARE_DEV_FN_ +#define _PHARE_HST_FN_ +#define _PHARE_ALL_FN_ + +namespace PHARE +{ +template +inline void throw_runtime_error(T const& err) +{ + throw std::runtime_error(err); +} +} // namespace PHARE + +#endif + + +#if !defined(NDEBUG) || defined(PHARE_FORCE_DEBUG_DO) +#define PHARE_DEBUG_DO(...) __VA_ARGS__ +#else +#define PHARE_DEBUG_DO(...) +#endif + + +#define _PHARE_TO_STR(x) #x // convert macro text to string +#define PHARE_TO_STR(x) _PHARE_TO_STR(x) + + + +#endif /* PHARE_CORE_DEF_HPP */ diff --git a/src/core/def/detail/mkn_gpu.hpp b/src/core/def/detail/mkn_gpu.hpp new file mode 100644 index 000000000..58de477c0 --- /dev/null +++ b/src/core/def/detail/mkn_gpu.hpp @@ -0,0 +1,15 @@ +#ifndef PHARE_CORE_DEF_DETAIL_MKN_GPU_HPP +#define PHARE_CORE_DEF_DETAIL_MKN_GPU_HPP + +#if !defined(PHARE_HAVE_MKN_GPU) +#define PHARE_HAVE_MKN_GPU 0 +#endif + +#if PHARE_HAVE_MKN_GPU +#include "mkn/gpu.hpp" +#define PHARE_WITH_MKN_GPU(...) __VA_ARGS__ +#else +#define PHARE_WITH_MKN_GPU(...) +#endif + +#endif /* PHARE_CORE_DEF_DETAIL_MKN_GPU_HPP */ diff --git a/src/core/def/detail/raja.hpp b/src/core/def/detail/raja.hpp new file mode 100644 index 000000000..5eb92c961 --- /dev/null +++ b/src/core/def/detail/raja.hpp @@ -0,0 +1,111 @@ +#ifndef PHARE_CORE_DEF_DETAIL_RAJA_HPP +#define PHARE_CORE_DEF_DETAIL_RAJA_HPP + +#if !defined(PHARE_HAVE_RAJA) +#define PHARE_HAVE_RAJA 0 +#endif + +#if PHARE_HAVE_RAJA + +#if defined(HAVE_SYS_TIMES_H) +#undef HAVE_SYS_TIMES_H // https://github.com/LLNL/SAMRAI/issues/93 +#undef HAVE_UNISTD_H +#endif + +#include "SAMRAI/SAMRAI_config.h" +#include "SAMRAI/tbox/Collectives.h" // tbox::parallel_synchronize(); +#include "RAJA/RAJA.hpp" + +#define PHARE_WITH_RAJA(...) __VA_ARGS__ +#else + +#define PHARE_WITH_RAJA(...) +#endif + + +#if PHARE_HAVE_RAJA + +namespace PHARE::core::raja +{ +inline void sync() +{ + SAMRAI::tbox::parallel_synchronize(); +} + +template +void set(T* const dst, T val, std::size_t size) +{ + std::vector src(size, val); // :( + // RAJA::resources::Cuda{}.memset(dst, val, sizeof(T) * size); // is int only + RAJA::resources::Cuda{}.memcpy(dst, src.data(), sizeof(T) * size); + sync(); +} + + +template +void exec(Fn& fn, RAJA::resources::Cuda& res, std::size_t size) +{ + using EXEC_POLICY = RAJA::cuda_exec_async; + RAJA::forall(res, RAJA::RangeSegment(0, size), + [=] RAJA_DEVICE(int i) mutable { fn(i); }); + sync(); +} + +template +void exec(Fn&& fn, RAJA::resources::Cuda& res, std::size_t size) +{ + exec(fn, res, size); +} + +template +void exec(Fn&& fn, std::size_t size) +{ + RAJA::resources::Cuda res; + exec(fn, res, size); +} + + +template +void copy(T* const dst, T const* const src, std::size_t size) +{ + RAJA::resources::Cuda{}.memcpy(dst, src, sizeof(T) * size); // is async + sync(); +} + + +template +auto allocate_copy(CudaRes& res, Type* src, std::size_t size = 1) +{ + assert(size); + Type* dst = res.template allocate(size); + res.memcpy(dst, src, sizeof(Type) * size); + return dst; +} + + +template +auto allocate_copy(CudaRes& res, std::vector& vec) +{ + return allocate_copy(res, vec.data(), vec.size()); +} + +template +auto allocate_copy(CudaRes& res, std::tuple& tup) +{ + return allocate_copy(res, &tup); +} + + + +template +auto deallocate(CudaRes& res, Ptrs&... ptrs) +{ + (res.deallocate(ptrs), ...); +} + + +} // namespace PHARE::core::raja + + +#endif /* PHARE_HAVE_RAJA */ +#endif /* PHARE_CORE_DEF_DETAIL_RAJA_HPP */ diff --git a/src/core/def/detail/umpire.hpp b/src/core/def/detail/umpire.hpp new file mode 100644 index 000000000..04dfaa9a6 --- /dev/null +++ b/src/core/def/detail/umpire.hpp @@ -0,0 +1,81 @@ +#ifndef PHARE_CORE_DEF_DETAIL_UMPIRE_HPP +#define PHARE_CORE_DEF_DETAIL_UMPIRE_HPP + +#if !defined(PHARE_HAVE_UMPIRE) +#define PHARE_HAVE_UMPIRE 0 +#endif + +#if PHARE_HAVE_UMPIRE + +#if defined(HAVE_SYS_TIMES_H) +#undef HAVE_SYS_TIMES_H // https://github.com/LLNL/SAMRAI/issues/93 +#undef HAVE_UNISTD_H +#endif + +#include "SAMRAI/SAMRAI_config.h" +#include "mkn/kul/log.hpp" // TORM +#define PHARE_WITH_UMPIRE(...) __VA_ARGS__ +#else +#define PHARE_WITH_UMPIRE(...) +#endif + + +#if PHARE_HAVE_UMPIRE +#include "SAMRAI/tbox/Collectives.h" // tbox::parallel_synchronize(); +#include "umpire/Allocator.hpp" +#include "umpire/ResourceManager.hpp" +#include "umpire/TypedAllocator.hpp" +#include "umpire/strategy/AllocationAdvisor.hpp" +#include "umpire/strategy/QuickPool.hpp" + +namespace PHARE::_umpire_ +{ +inline auto static_init() +{ + auto& rm = umpire::ResourceManager::getInstance(); + + if (rm.isAllocator("samrai::data_allocator") + and rm.getAllocator("samrai::data_allocator").getPlatform() != umpire::Platform::host) + throw std::runtime_error("Invalid samrai allocator state"); + + if (!rm.isAllocator("samrai::data_allocator")) + { // initialize samrai internals allocator to HOST + [[maybe_unused]] auto samrai_allocator = rm.makeAllocator( + "samrai::data_allocator", rm.getAllocator(umpire::resource::Host)); + assert(samrai_allocator.getPlatform() == umpire::Platform::host); + } + assert(rm.isAllocator("samrai::data_allocator")); + assert(rm.getAllocator("samrai::data_allocator").getPlatform() == umpire::Platform::host); + + + if (!rm.isAllocator("samrai::tag_allocator")) + { // initialize samrai internals allocator to HOST + [[maybe_unused]] auto samrai_allocator = rm.makeAllocator( + "samrai::tag_allocator", rm.getAllocator(umpire::resource::Unified)); + assert(samrai_allocator.getPlatform() != umpire::Platform::host); + } + assert(rm.isAllocator("samrai::tag_allocator")); + assert(rm.getAllocator("samrai::tag_allocator").getPlatform() != umpire::Platform::host); + + if (!rm.isAllocator("PHARE::data_allocator")) + { + auto allocator = rm.makeAllocator( + "internal::PHARE::um_allocation_advisor", rm.getAllocator(umpire::resource::Unified), + // Set preferred location to GPU + "PREFERRED_LOCATION"); + + [[maybe_unused]] auto phare_allocator + = rm.makeAllocator("PHARE::data_allocator", allocator); + assert(phare_allocator.getPlatform() != umpire::Platform::host); + assert(rm.isAllocator("PHARE::data_allocator")); + } + + return true; +} +inline static auto _static_sim_init = static_init(); +} // namespace PHARE::_umpire_ + + +#endif /* PHARE_HAVE_UMPIRE */ + +#endif /* PHARE_CORE_DEF_DETAIL_UMPIRE_HPP */ diff --git a/src/core/def/types/raja.hpp b/src/core/def/types/raja.hpp new file mode 100644 index 000000000..0aed8e1ee --- /dev/null +++ b/src/core/def/types/raja.hpp @@ -0,0 +1,7 @@ +#ifndef PHARE_CORE_DEF_TYPES_RAJA_HPP +#define PHARE_CORE_DEF_TYPES_RAJA_HPP + +#if PHARE_HAVE_RAJA +#endif /* PHARE_HAVE_RAJA */ + +#endif /* PHARE_CORE_DEF_TYPES_RAJA_HPP */ diff --git a/src/core/def/types/umpire.hpp b/src/core/def/types/umpire.hpp new file mode 100644 index 000000000..e956b53b7 --- /dev/null +++ b/src/core/def/types/umpire.hpp @@ -0,0 +1,7 @@ +#ifndef PHARE_CORE_DEF_TYPES_UMPIRE_HPP +#define PHARE_CORE_DEF_TYPES_UMPIRE_HPP + +#if PHARE_HAVE_UMPIRE +#endif /* PHARE_HAVE_UMPIRE */ + +#endif /* PHARE_CORE_DEF_TYPES_UMPIRE_HPP */ diff --git a/src/core/hybrid/hybrid_quantities.hpp b/src/core/hybrid/hybrid_quantities.hpp index dedde768b..70f3acd22 100644 --- a/src/core/hybrid/hybrid_quantities.hpp +++ b/src/core/hybrid/hybrid_quantities.hpp @@ -12,8 +12,9 @@ namespace PHARE::core class HybridQuantity { public: - enum class Scalar { Bx, By, Bz, Ex, Ey, Ez, Jx, Jy, Jz, rho, Vx, Vy, Vz, P, count }; - enum class Vector { B, E, J, V }; + enum class Scalar { Bx, By, Bz, Ex, Ey, Ez, Jx, Jy, Jz, rho, Vx, Vy, Vz, P, count, INVALID }; + + enum class Vector { B, E, J, V, INVALID }; static constexpr auto B() { return componentsQuantities(Vector::B); } static constexpr auto E() { return componentsQuantities(Vector::E); } diff --git a/src/core/logger.hpp b/src/core/logger.hpp index 6cf44d14e..d7cead9f6 100644 --- a/src/core/logger.hpp +++ b/src/core/logger.hpp @@ -3,6 +3,10 @@ #define PHARE_CORE_LOGGER_HPP +#include +#include + + #if !defined(NDEBUG) || defined(PHARE_FORCE_DEBUG_DO) #define PHARE_LOG_LINE_STR(str) \ std::cout << __FILE__ << ":" << __LINE__ << " - " << str << std::endl; @@ -12,16 +16,15 @@ #define PHARE_LOG_LINE PHARE_LOG_LINE_STR("") - - #if PHARE_WITH_CALIPER + #include "caliper/cali.h" #define PHARE_LOG_START(str) CALI_MARK_BEGIN(str) #define PHARE_LOG_STOP(str) CALI_MARK_END(str) #define PHARE_LOG_SCOPE(str) PHARE::scope_log __phare_scope##__line__(str) -#else +#else // !PHARE_WITH_CALIPER #define PHARE_LOG_START(str) #define PHARE_LOG_STOP(str) @@ -29,8 +32,7 @@ #endif -#include -#include + namespace PHARE { diff --git a/src/core/numerics/ampere/ampere.hpp b/src/core/numerics/ampere/ampere.hpp index c65e30ae5..1547610f1 100644 --- a/src/core/numerics/ampere/ampere.hpp +++ b/src/core/numerics/ampere/ampere.hpp @@ -4,6 +4,7 @@ #include #include +#include "core/def.hpp" #include "core/data/grid/gridlayoutdefs.hpp" #include "core/data/grid/gridlayout_utils.hpp" #include "core/data/vecfield/vecfield_component.hpp" @@ -15,66 +16,75 @@ namespace PHARE::core template class Ampere : public LayoutHolder { - constexpr static auto dimension = GridLayout::dimension; - using LayoutHolder::layout_; + auto static constexpr dimension = GridLayout::dimension; public: template - void operator()(VecField const& B, VecField& J) + void operator()(VecField const& B_, VecField& J_) { if (!this->hasLayout()) throw std::runtime_error( "Error - Ampere - GridLayout not set, cannot proceed to calculate ampere()"); + auto B = B_.view(); + auto J = J_.view(); + // can't use structured bindings because // "reference to local binding declared in enclosing function" - auto& Jx = J(Component::X); - auto& Jy = J(Component::Y); - auto& Jz = J(Component::Z); - - layout_->evalOnBox(Jx, [&](auto&... args) mutable { JxEq_(Jx, B, args...); }); - layout_->evalOnBox(Jy, [&](auto&... args) mutable { JyEq_(Jy, B, args...); }); - layout_->evalOnBox(Jz, [&](auto&... args) mutable { JzEq_(Jz, B, args...); }); + auto& Jx = J(Component::X); + auto& Jy = J(Component::Y); + auto& Jz = J(Component::Z); + auto& layout = *this->layout_; + + layout.evalOnBox( + Jx, [] _PHARE_ALL_FN_(auto&... args) { JxEq_(args...); }, Jx, B, layout); + layout.evalOnBox( + Jy, [] _PHARE_ALL_FN_(auto&... args) { JyEq_(args...); }, Jy, B, layout); + layout.evalOnBox( + Jz, [] _PHARE_ALL_FN_(auto&... args) { JzEq_(args...); }, Jz, B, layout); } private: - template - void JxEq_(Field& Jx, VecField const& B, Indexes const&... ijk) const + template + static void JxEq_(std::tuple const& ijk, Args&&... args) _PHARE_ALL_FN_ { - auto const& [_, By, Bz] = B(); + auto const& [Jx, B, layout] = std::forward_as_tuple(args...); + auto const& [_, By, Bz] = B(); if constexpr (dimension == 2) - Jx(ijk...) = layout_->template deriv(Bz, {ijk...}); + Jx(ijk) = layout.template deriv(Bz, {ijk}); if constexpr (dimension == 3) - Jx(ijk...) = layout_->template deriv(Bz, {ijk...}) - - layout_->template deriv(By, {ijk...}); + Jx(ijk) = layout.template deriv(Bz, {ijk}) + - layout.template deriv(By, {ijk}); } - template - void JyEq_(Field& Jy, VecField const& B, Indexes const&... ijk) const + template + static void JyEq_(std::tuple const& ijk, Args&&... args) _PHARE_ALL_FN_ { - auto const& [Bx, By, Bz] = B(); + auto const& [Jy, B, layout] = std::forward_as_tuple(args...); + auto const& [Bx, By, Bz] = B(); if constexpr (dimension == 1 || dimension == 2) - Jy(ijk...) = -layout_->template deriv(Bz, {ijk...}); + Jy(ijk) = -layout.template deriv(Bz, {ijk}); if constexpr (dimension == 3) - Jy(ijk...) = layout_->template deriv(Bx, {ijk...}) - - layout_->template deriv(Bz, {ijk...}); + Jy(ijk) = layout.template deriv(Bx, {ijk}) + - layout.template deriv(Bz, {ijk}); } - template - void JzEq_(Field& Jz, VecField const& B, Indexes const&... ijk) const + template + static void JzEq_(std::tuple const& ijk, Args&&... args) _PHARE_ALL_FN_ { - auto const& [Bx, By, Bz] = B(); + auto const& [Jz, B, layout] = std::forward_as_tuple(args...); + auto const& [Bx, By, Bz] = B(); if constexpr (dimension == 1) - Jz(ijk...) = layout_->template deriv(By, {ijk...}); + Jz(ijk) = layout.template deriv(By, {ijk}); else - Jz(ijk...) = layout_->template deriv(By, {ijk...}) - - layout_->template deriv(Bx, {ijk...}); + Jz(ijk) = layout.template deriv(By, {ijk}) + - layout.template deriv(Bx, {ijk}); } }; diff --git a/src/core/numerics/faraday/faraday.hpp b/src/core/numerics/faraday/faraday.hpp index 0fe28cc77..df3c3dba0 100644 --- a/src/core/numerics/faraday/faraday.hpp +++ b/src/core/numerics/faraday/faraday.hpp @@ -3,6 +3,7 @@ #include +#include "core/def.hpp" #include "core/data/grid/gridlayoutdefs.hpp" #include "core/data/grid/gridlayout_utils.hpp" #include "core/data/vecfield/vecfield_component.hpp" @@ -10,25 +11,50 @@ namespace PHARE::core { +template +class Faraday_ref; + template class Faraday : public LayoutHolder { constexpr static auto dimension = GridLayout::dimension; - using LayoutHolder::layout_; public: template - void operator()(VecField const& B, VecField const& E, VecField& Bnew, double dt) + void operator()(VecField const& B_, VecField const& E_, VecField& Bnew_, + double dt) _PHARE_ALL_FN_ { if (!this->hasLayout()) throw std::runtime_error( "Error - Faraday - GridLayout not set, cannot proceed to calculate faraday()"); - if (!(B.isUsable() && E.isUsable() && Bnew.isUsable())) + if (!(B_.isUsable() && E_.isUsable() && Bnew_.isUsable())) throw std::runtime_error("Error - Faraday - not all VecField parameters are usable"); - this->dt_ = dt; + auto E = E_.view(); + auto B = B_.view(); + auto Bnew = Bnew_.view(); + + Faraday_ref{*this->layout_, dt}(B, E, Bnew); + } +}; + +template +class Faraday_ref +{ + constexpr static auto dimension = GridLayout::dimension; + using This = Faraday_ref; +public: + Faraday_ref(GridLayout& layout_, double dt_) + : layout{layout_} + , dt{dt_} + { + } + + template + void operator()(VecField const& B, VecField const& E, VecField& Bnew) _PHARE_ALL_FN_ + { // can't use structured bindings because // "reference to local binding declared in enclosing function" auto const& Bx = B(Component::X); @@ -39,56 +65,64 @@ class Faraday : public LayoutHolder auto& Bynew = Bnew(Component::Y); auto& Bznew = Bnew(Component::Z); - layout_->evalOnBox(Bxnew, [&](auto&... args) mutable { BxEq_(Bx, E, Bxnew, args...); }); - layout_->evalOnBox(Bynew, [&](auto&... args) mutable { ByEq_(By, E, Bynew, args...); }); - layout_->evalOnBox(Bznew, [&](auto&... args) mutable { BzEq_(Bz, E, Bznew, args...); }); + layout.evalOnBox( + Bxnew, [] _PHARE_ALL_FN_(auto&... args) { BxEq_(args...); }, Bx, E, Bxnew, *this); + layout.evalOnBox( + Bynew, [] _PHARE_ALL_FN_(auto&... args) { ByEq_(args...); }, By, E, Bynew, *this); + layout.evalOnBox( + Bznew, [] _PHARE_ALL_FN_(auto&... args) { BzEq_(args...); }, Bz, E, Bznew, *this); } - private: - double dt_; - + GridLayout layout; + double dt; - template - void BxEq_(Field const& Bx, VecField const& E, Field& Bxnew, Indexes const&... ijk) const + template + static void BxEq_(std::tuple const& ijk, Args&&... args) _PHARE_ALL_FN_ { - auto const& [_, Ey, Ez] = E(); + auto const& [Bx, E, Bxnew, self] = std::forward_as_tuple(args...); + auto const& [layout, dt] = self; + auto const& [_, Ey, Ez] = E(); if constexpr (dimension == 1) - Bxnew(ijk...) = Bx(ijk...); + Bxnew(ijk) = Bx(ijk); if constexpr (dimension == 2) - Bxnew(ijk...) = Bx(ijk...) - dt_ * layout_->template deriv(Ez, {ijk...}); + Bxnew(ijk) = Bx(ijk) - dt * layout.template deriv(Ez, {ijk}); if constexpr (dimension == 3) - Bxnew(ijk...) = Bx(ijk...) - dt_ * layout_->template deriv(Ez, {ijk...}) - + dt_ * layout_->template deriv(Ey, {ijk...}); + Bxnew(ijk) = Bx(ijk) - dt * layout.template deriv(Ez, {ijk}) + + dt * layout.template deriv(Ey, {ijk}); } - template - void ByEq_(Field const& By, VecField const& E, Field& Bynew, Indexes const&... ijk) const + template + static void ByEq_(std::tuple const& ijk, Args&&... args) _PHARE_ALL_FN_ { - auto const& [Ex, _, Ez] = E(); + auto const& [By, E, Bynew, self] = std::forward_as_tuple(args...); + auto const& [layout, dt] = self; + auto const& [Ex, _, Ez] = E(); if constexpr (dimension == 1 || dimension == 2) - Bynew(ijk...) = By(ijk...) + dt_ * layout_->template deriv(Ez, {ijk...}); + Bynew(ijk) = By(ijk) + dt * layout.template deriv(Ez, {ijk}); if constexpr (dimension == 3) - Bynew(ijk...) = By(ijk...) - dt_ * layout_->template deriv(Ex, {ijk...}) - + dt_ * layout_->template deriv(Ez, {ijk...}); + Bynew(ijk) = By(ijk) - dt * layout.template deriv(Ex, {ijk}) + + dt * layout.template deriv(Ez, {ijk}); } - template - void BzEq_(Field const& Bz, VecField const& E, Field& Bznew, Indexes const&... ijk) const + template + static void BzEq_(std::tuple const& ijk, Args&&... args) _PHARE_ALL_FN_ { - auto const& [Ex, Ey, _] = E(); + auto const& [Bz, E, Bznew, self] = std::forward_as_tuple(args...); + auto const& [layout, dt] = self; + auto const& [Ex, Ey, _] = E(); if constexpr (dimension == 1) - Bznew(ijk...) = Bz(ijk...) - dt_ * layout_->template deriv(Ey, {ijk...}); + Bznew(ijk) = Bz(ijk) - dt * layout.template deriv(Ey, {ijk}); else - Bznew(ijk...) = Bz(ijk...) - dt_ * layout_->template deriv(Ey, {ijk...}) - + dt_ * layout_->template deriv(Ex, {ijk...}); + Bznew(ijk) = Bz(ijk) - dt * layout.template deriv(Ey, {ijk}) + + dt * layout.template deriv(Ex, {ijk}); } }; diff --git a/src/core/numerics/interpolator/interpolator.hpp b/src/core/numerics/interpolator/interpolator.hpp index dcc31d9f5..328b29a81 100644 --- a/src/core/numerics/interpolator/interpolator.hpp +++ b/src/core/numerics/interpolator/interpolator.hpp @@ -5,12 +5,18 @@ #include #include +#include + + +#include "core/def.hpp" +#include "core/logger.hpp" +#include "core/operators.hpp" #include "core/data/grid/gridlayout.hpp" #include "core/data/vecfield/vecfield_component.hpp" #include "core/utilities/point/point.hpp" -#include "core/logger.hpp" + namespace PHARE::core { @@ -54,7 +60,7 @@ class Weighter<1> { public: inline void computeWeight(double normalizedPos, int startIndex, - std::array& weights) + std::array& weights) _PHARE_ALL_FN_ { weights[1] = normalizedPos - static_cast(startIndex); weights[0] = 1. - weights[1]; @@ -71,7 +77,7 @@ class Weighter<2> { public: inline void computeWeight(double normalizedPos, int startIndex, - std::array& weights) + std::array& weights) _PHARE_ALL_FN_ { auto index = startIndex + 1; auto delta = static_cast(index) - normalizedPos; @@ -97,7 +103,7 @@ class Weighter<3> { public: inline void computeWeight(double normalizedPos, int startIndex, - std::array& weights) + std::array& weights) _PHARE_ALL_FN_ { constexpr double _4_over_3 = 4. / 3.; constexpr double _2_over_3 = 2. / 3.; @@ -134,7 +140,7 @@ class MeshToParticle template -auto static start_index_and_weights_for_qty(IndexWeights const& indexWeights) +auto static resolve_start_index_and_weights(IndexWeights const& indexWeights) _PHARE_ALL_FN_ { auto constexpr centerings = GridLayout::centering(quantity); auto const& [d_starts, d_weights, p_starts, p_weights] = indexWeights; @@ -158,17 +164,17 @@ class MeshToParticle<1> * the field \param[in] weights are the nbrPointsSupport weights used for the interpolation */ template - inline auto operator()(Field const& field, IndexWeights const& indexWeights) + inline auto op(Field const& field, IndexWeights const& indexWeights) _PHARE_ALL_FN_ { auto const& [xStartIndex, xWeights] - = start_index_and_weights_for_qty<0, GridLayout, quantity>(indexWeights); + = resolve_start_index_and_weights<0, GridLayout, quantity>(indexWeights); auto const& order_size = xWeights.size(); auto fieldAtParticle = 0.; - for (auto ik = 0u; ik < order_size; ++ik) + for (auto ix = 0u; ix < order_size; ++ix) { - fieldAtParticle += field(xStartIndex + ik) * xWeights[ik]; + fieldAtParticle += field(xStartIndex + ix) * xWeights[ix]; } return fieldAtParticle; } @@ -189,12 +195,12 @@ class MeshToParticle<2> * weights used for the interpolation in both directions */ template - inline auto operator()(Field const& field, IndexWeights const& indexWeights) + inline auto op(Field const& field, IndexWeights const& indexWeights) _PHARE_ALL_FN_ { auto const& [xStartIndex, xWeights] - = start_index_and_weights_for_qty<0, GridLayout, quantity>(indexWeights); + = resolve_start_index_and_weights<0, GridLayout, quantity>(indexWeights); auto const& [yStartIndex, yWeights] - = start_index_and_weights_for_qty<1, GridLayout, quantity>(indexWeights); + = resolve_start_index_and_weights<1, GridLayout, quantity>(indexWeights); auto const& order_size = xWeights.size(); @@ -230,14 +236,14 @@ class MeshToParticle<3> * weights used for the interpolation in the 3 directions */ template - inline auto operator()(Field const& field, IndexWeights const& indexWeights) + inline auto op(Field const& field, IndexWeights const& indexWeights) _PHARE_ALL_FN_ { auto const& [xStartIndex, xWeights] - = start_index_and_weights_for_qty<0, GridLayout, quantity>(indexWeights); + = resolve_start_index_and_weights<0, GridLayout, quantity>(indexWeights); auto const& [yStartIndex, yWeights] - = start_index_and_weights_for_qty<1, GridLayout, quantity>(indexWeights); + = resolve_start_index_and_weights<1, GridLayout, quantity>(indexWeights); auto const& [zStartIndex, zWeights] - = start_index_and_weights_for_qty<2, GridLayout, quantity>(indexWeights); + = resolve_start_index_and_weights<2, GridLayout, quantity>(indexWeights); auto const& order_size = xWeights.size(); @@ -265,16 +271,16 @@ class MeshToParticle<3> //! ParticleToMesh projects a particle density and flux to given grids -template +template> class ParticleToMesh { }; - -/** \brief specialization of ParticleToMesh for 1D interpolation */ -template<> -class ParticleToMesh<1> +/** \brief specialization of ParticleToMesh for 1D interpolation + */ +template +class ParticleToMesh<1, Op> { public: /** Performs the 1D interpolation * \param[in] density is the field that will be interpolated from the particle Particle @@ -296,25 +302,56 @@ class ParticleToMesh<1> auto const& [xWeights] = weights; auto const& order_size = xWeights.size(); - auto const partRho = particle.weight * coef; - auto const xPartFlux = particle.v[0] * particle.weight * coef; - auto const yPartFlux = particle.v[1] * particle.weight * coef; - auto const zPartFlux = particle.v[2] * particle.weight * coef; + auto const partRho = particle.weight() * coef; + auto const xPartFlux = particle.v()[0] * particle.weight() * coef; + auto const yPartFlux = particle.v()[1] * particle.weight() * coef; + auto const zPartFlux = particle.v()[2] * particle.weight() * coef; - for (auto ik = 0u; ik < order_size; ++ik) + for (auto ix = 0u; ix < order_size; ++ix) { - density(xStartIndex + ik) += partRho * xWeights[ik]; - xFlux(xStartIndex + ik) += xPartFlux * xWeights[ik]; - yFlux(xStartIndex + ik) += yPartFlux * xWeights[ik]; - zFlux(xStartIndex + ik) += zPartFlux * xWeights[ik]; + auto x = xStartIndex + ix; + density(x) += partRho * xWeights[ix]; + xFlux(x) += xPartFlux * xWeights[ix]; + yFlux(x) += yPartFlux * xWeights[ix]; + zFlux(x) += zPartFlux * xWeights[ix]; } } + + + + + // template + // inline void operator()(Particles const& particles, std::size_t pi, Field& density, + // VecField& flux, Indexes const& startIndex, Weights const& weights, + // double coef = 1.) + // { + // auto const& [xFlux, yFlux, zFlux] = flux(); + // auto const& [xStartIndex] = startIndex; + // auto const& [xWeights] = weights; + // auto const& order_size = xWeights.size(); + + // auto const partRho = particles.weight(pi) * coef; + // auto const xPartFlux = particles.v(pi)[0] * particles.weight(pi) * coef; + // auto const yPartFlux = particles.v(pi)[1] * particles.weight(pi) * coef; + // auto const zPartFlux = particles.v(pi)[2] * particles.weight(pi) * coef; + + // for (auto ix = 0u; ix < order_size; ++ix) + // { + // auto x = xStartIndex + ix; + // density(x) += partRho * xWeights[ix]; + // xFlux(x) += xPartFlux * xWeights[ix]; + // yFlux(x) += yPartFlux * xWeights[ix]; + // zFlux(x) += zPartFlux * xWeights[ix]; + // } + // } }; -/** \brief specialization of ParticleToMesh for 2D interpolation */ +/** \brief specialization of ParticleToMesh for 2D interpolation + */ template<> class ParticleToMesh<2> { @@ -338,17 +375,16 @@ class ParticleToMesh<2> auto const& [xWeights, yWeights] = weights; auto const& order_size = xWeights.size(); - auto const partRho = particle.weight * coef; - auto const xPartFlux = particle.v[0] * particle.weight * coef; - auto const yPartFlux = particle.v[1] * particle.weight * coef; - auto const zPartFlux = particle.v[2] * particle.weight * coef; + auto const partRho = particle.weight() * coef; + auto const xPartFlux = particle.v()[0] * particle.weight() * coef; + auto const yPartFlux = particle.v()[1] * particle.weight() * coef; + auto const zPartFlux = particle.v()[2] * particle.weight() * coef; for (auto ix = 0u; ix < order_size; ++ix) { for (auto iy = 0u; iy < order_size; ++iy) { - auto x = xStartIndex + ix; - auto y = yStartIndex + iy; + auto x = xStartIndex + ix, y = yStartIndex + iy; density(x, y) += partRho * xWeights[ix] * yWeights[iy]; xFlux(x, y) += xPartFlux * xWeights[ix] * yWeights[iy]; @@ -357,12 +393,46 @@ class ParticleToMesh<2> } } } + + + + // template + // inline void op_0(Particles const& particles, std::size_t pi, Field& density, VecField& flux, + // Indexes const& startIndex, Weights const& weights, double coef = 1.) + // { + // auto const& [xFlux, yFlux, zFlux] = flux(); + // auto const& [xStartIndex, yStartIndex] = startIndex; + // auto const& [xWeights, yWeights] = weights; + // auto const& order_size = xWeights.size(); + + // auto const& weight = particles.weight(pi); + // auto const& v = particles.v(pi); + // auto const partRho = weight * coef; + // auto const xPartFlux = v[0] * weight * coef; + // auto const yPartFlux = v[1] * weight * coef; + // auto const zPartFlux = v[2] * weight * coef; + + // for (auto ix = 0u; ix < order_size; ++ix) + // { + // for (auto iy = 0u; iy < order_size; ++iy) + // { + // auto x = xStartIndex + ix, y = yStartIndex + iy; + + // density(x, y) += partRho * xWeights[ix] * yWeights[iy]; + // xFlux(x, y) += xPartFlux * xWeights[ix] * yWeights[iy]; + // yFlux(x, y) += yPartFlux * xWeights[ix] * yWeights[iy]; + // zFlux(x, y) += zPartFlux * xWeights[ix] * yWeights[iy]; + // } + // } + // } }; -/** \brief specialization of ParticleToMesh for 3D interpolation */ +/** \brief specialization of ParticleToMesh for 3D interpolation + */ template<> class ParticleToMesh<3> { @@ -379,17 +449,18 @@ class ParticleToMesh<3> template inline void operator()(Field& density, VecField& flux, Particle const& particle, - Indexes const& startIndex, Weights const& weights, double coef = 1.) + Indexes const& startIndex, Weights const& weights, + double coef = 1.) _PHARE_ALL_FN_ { auto const& [xFlux, yFlux, zFlux] = flux(); auto const& [xStartIndex, yStartIndex, zStartIndex] = startIndex; auto const& [xWeights, yWeights, zWeights] = weights; auto const& order_size = xWeights.size(); - auto const partRho = particle.weight * coef; - auto const xPartFlux = particle.v[0] * particle.weight * coef; - auto const yPartFlux = particle.v[1] * particle.weight * coef; - auto const zPartFlux = particle.v[2] * particle.weight * coef; + auto const partRho = particle.weight() * coef; + auto const xPartFlux = particle.v()[0] * particle.weight() * coef; + auto const yPartFlux = particle.v()[1] * particle.weight() * coef; + auto const zPartFlux = particle.v()[2] * particle.weight() * coef; for (auto ix = 0u; ix < order_size; ++ix) { @@ -397,14 +468,17 @@ class ParticleToMesh<3> { for (auto iz = 0u; iz < order_size; ++iz) { - auto x = xStartIndex + ix; - auto y = yStartIndex + iy; - auto z = zStartIndex + iz; - - density(x, y, z) += partRho * xWeights[ix] * yWeights[iy] * zWeights[iz]; - xFlux(x, y, z) += xPartFlux * xWeights[ix] * yWeights[iy] * zWeights[iz]; - yFlux(x, y, z) += yPartFlux * xWeights[ix] * yWeights[iy] * zWeights[iz]; - zFlux(x, y, z) += zPartFlux * xWeights[ix] * yWeights[iy] * zWeights[iz]; + density(0, 0, 0) // xStartIndex + ix, yStartIndex + iy, zStartIndex + iz) + += 0; // partRho * xWeights[ix] * yWeights[iy] * zWeights[iz]; + + // xFlux(xStartIndex + ix, yStartIndex + iy, zStartIndex + iz) + // += xPartFlux * xWeights[ix] * yWeights[iy] * zWeights[iz]; + + // yFlux(xStartIndex + ix, yStartIndex + iy, zStartIndex + iz) + // += yPartFlux * xWeights[ix] * yWeights[iy] * zWeights[iz]; + + // zFlux(xStartIndex + ix, yStartIndex + iy, zStartIndex + iz) + // += zPartFlux * xWeights[ix] * yWeights[iy] * zWeights[iz]; } } } @@ -417,16 +491,18 @@ class ParticleToMesh<3> /** \brief Interpolator is used to perform particle-mesh interpolations using * 1st, 2nd or 3rd order interpolation in 1D, 2D or 3D, on a given layout. */ -template +template class Interpolator : private Weighter { + using This = Interpolator; + // this calculates the startIndex and the nbrPointsSupport() weights for // dual field interpolation and puts this at the corresponding location // in 'startIndex' and 'weights'. For dual fields, the normalizedPosition // is offseted compared to primal ones. - template - auto indexAndWeights_(GridLayout const& layout, ICell const& iCell_, Delta const& delta) + template + auto indexAndWeights_(GridLayout const& layout, ICell const& iCell_, + Delta const& delta) _PHARE_ALL_FN_ { // dual weights require -.5 to take the correct position weight auto constexpr dual_offset = .5; @@ -441,8 +517,7 @@ class Interpolator : private Weighter auto iCell = layout.AMRToLocal(Point{iCell_}); for (auto iDim = 0u; iDim < dimension; ++iDim) { - startIndex_[iDim] - = iCell[iDim] - computeStartLeftShift(delta[iDim]); + startIndex_[iDim] = iCell[iDim] - computeStartLeftShift(delta[iDim]); double normalizedPos = iCell[iDim] + delta[iDim]; @@ -453,9 +528,14 @@ class Interpolator : private Weighter } } + public: + using Operator = core::Operators; + auto static constexpr interp_order = interpOrder; auto static constexpr dimension = dim; + + /**\brief interpolate electromagnetic fields on all particles in the range * * For each particle : @@ -464,19 +544,76 @@ class Interpolator : private Weighter * - then it uses Interpol<> to calculate the interpolation of E and B components * onto the particle. */ - template - inline void operator()(ParticleRange&& particleRange, Electromag const& Em, - GridLayout const& layout) - { - PHARE_LOG_SCOPE("Interpolator::operator()"); - - auto begin = particleRange.begin(); - auto end = particleRange.end(); + /* PER PARTICLE FN - maybe dedupe */ + // template + // inline void operator()(std::size_t p_idx, Particles& particles, Electromag const& Em, + // GridLayout const& layout) + // { + // using Scalar = HybridQuantity::Scalar; + // auto const& [Ex, Ey, Ez] = Em.E(); + // auto const& [Bx, By, Bz] = Em.B(); + + // auto& [pEx, pEy, pEz] = particles.E(p_idx); + // auto& [pBx, pBy, pBz] = particles.B(p_idx); + + // auto& iCell = particles.iCell(p_idx); + // auto& delta = particles.delta(p_idx); + // indexAndWeights_(layout, iCell, delta); + // indexAndWeights_(layout, iCell, delta); + + // auto indexWeights = std::forward_as_tuple(dual_startIndex_, dual_weights_, + // primal_startIndex_, primal_weights_); + + // pEx = meshToParticle_.template op(Ex, indexWeights); + // pEy = meshToParticle_.template op(Ey, indexWeights); + // pEz = meshToParticle_.template op(Ez, indexWeights); + // pBx = meshToParticle_.template op(Bx, indexWeights); + // pBy = meshToParticle_.template op(By, indexWeights); + // pBz = meshToParticle_.template op(Bz, indexWeights); + // } + + template + inline void meshToParticle(ICell const iCell, Delta const& delta, Electromag const& Em, + Particle_EM particle_EM, GridLayout const& layout) _PHARE_ALL_FN_ + { using Scalar = HybridQuantity::Scalar; auto const& [Ex, Ey, Ez] = Em.E(); auto const& [Bx, By, Bz] = Em.B(); + indexAndWeights_(layout, iCell, delta); + indexAndWeights_(layout, iCell, delta); + + auto indexWeights = std::forward_as_tuple(dual_startIndex_, dual_weights_, + primal_startIndex_, primal_weights_); + + auto const& [E, B] = particle_EM; + auto& [pEx, pEy, pEz] = E; // particles.E(p_idx); + auto& [pBx, pBy, pBz] = B; // particles.B(p_idx); + + pEx = meshToParticle_.template op(Ex, indexWeights); + pEy = meshToParticle_.template op(Ey, indexWeights); + pEz = meshToParticle_.template op(Ez, indexWeights); + pBx = meshToParticle_.template op(Bx, indexWeights); + pBy = meshToParticle_.template op(By, indexWeights); + pBz = meshToParticle_.template op(Bz, indexWeights); + } + + template + inline void meshToParticle(Particle_t& particle, Electromag const& Em, + GridLayout const& layout) _PHARE_ALL_FN_ + { + meshToParticle(particle.iCell(), particle.delta(), Em, + std::forward_as_tuple(particle.E(), particle.B()), layout); + } + + template + inline void operator()(ParticleRange& particleRange, Electromag const& Em, + GridLayout const& layout) + { + PHARE_LOG_SCOPE("Interpolator::operator()"); + // for each particle, first calculate the startIndex and weights for dual and // primal quantities. then, knowing the centering (primal or dual) of each // electromagnetic component, we use Interpol to actually perform the @@ -484,32 +621,26 @@ class Interpolator : private Weighter // calculated twice, and not for each E,B component. PHARE_LOG_START("MeshToParticle::operator()"); - for (auto currPart = begin; currPart != end; ++currPart) - { - auto& iCell = currPart->iCell; - auto& delta = currPart->delta; - indexAndWeights_(layout, iCell, delta); - indexAndWeights_(layout, iCell, delta); - - auto indexWeights = std::forward_as_tuple(dual_startIndex_, dual_weights_, - primal_startIndex_, primal_weights_); - - currPart->Ex - = meshToParticle_.template operator()(Ex, indexWeights); - currPart->Ey - = meshToParticle_.template operator()(Ey, indexWeights); - currPart->Ez - = meshToParticle_.template operator()(Ez, indexWeights); - currPart->Bx - = meshToParticle_.template operator()(Bx, indexWeights); - currPart->By - = meshToParticle_.template operator()(By, indexWeights); - currPart->Bz - = meshToParticle_.template operator()(Bz, indexWeights); - } + + auto begin = particleRange.begin(); + auto end = particleRange.end(); + + auto& particles = begin(); + auto start = begin.idx(); + auto stop = end.idx(); + for (auto p_idx = start; p_idx < stop; ++p_idx) + meshToParticle(particles.iCell(p_idx), particles.delta(p_idx), Em, + std::forward_as_tuple(particles.E(p_idx), particles.B(p_idx)), layout); + PHARE_LOG_STOP("MeshToParticle::operator()"); } + template + inline void operator()(Particles&& particles, Electromag const& Em, GridLayout const& layout) + { + (*this)(particles, Em, layout); + } + /**\brief interpolate electromagnetic fields on all particles in the range * @@ -519,42 +650,53 @@ class Interpolator : private Weighter * - then it uses Interpol<> to calculate the interpolation of E and B components * onto the particle. */ - template - inline void operator()(ParticleRange&& particleRange, Field& density, VecField& flux, - GridLayout const& layout, double coef = 1.) + + template + void particleToMesh(Particle_t const& particle, Field& density, VecField& flux, + GridLayout const& layout, double coef = 1.) _PHARE_ALL_FN_ { - auto begin = particleRange.begin(); - auto end = particleRange.end(); auto& startIndex_ = primal_startIndex_; auto& weights_ = primal_weights_; + auto const& [xFlux, yFlux, zFlux] = flux.getComponents(); + + // TODO #3375 + indexAndWeights_(layout, particle.iCell(), particle.delta()); + + particleToMesh_(density, flux, particle, startIndex_, weights_, coef); + } + + + template + inline void operator()(Particles const& particles, Field& density, VecField& flux, + GridLayout const& layout, double coef = 1.) + { // for each particle, first calculate the startIndex and weights // for dual and primal quantities. // then, knowing the centering (primal or dual) of each electromagnetic // component, we use Interpol to actually perform the interpolation. - // the trick here is that the StartIndex and weights have only been calculated - // twice, and not for each E,B component. + // the trick here is that the StartIndex and weights have only been + // calculated twice, and not for each E,B component. PHARE_LOG_START("ParticleToMesh::operator()"); + auto begin = particles.begin(); + auto end = particles.end(); + for (auto currPart = begin; currPart != end; ++currPart) - { - // TODO #3375 - indexAndWeights_(layout, currPart->iCell, - currPart->delta); + particleToMesh(*currPart, density, flux, layout, coef); - particleToMesh_(density, flux, *currPart, startIndex_, weights_, coef); - } PHARE_LOG_STOP("ParticleToMesh::operator()"); } + /** * @brief Given a delta and an interpolation order, deduce which lower index to start * traversing from */ - template - static int computeStartLeftShift([[maybe_unused]] double delta) + template + static int computeStartLeftShift([[maybe_unused]] double delta) _PHARE_ALL_FN_ { static_assert(interpOrder > 0 and interpOrder < 4); @@ -562,7 +704,7 @@ class Interpolator : private Weighter if constexpr (interpOrder == 1) { - if constexpr (Centering == QtyCentering::primal) + if constexpr (centering == QtyCentering::primal) return 0; else return (delta < .5 ? 1 : 0); @@ -570,7 +712,7 @@ class Interpolator : private Weighter else if constexpr (interpOrder == 2) { - if constexpr (Centering == QtyCentering::primal) + if constexpr (centering == QtyCentering::primal) return (delta < .5 ? 1 : 0); else return 1; @@ -578,7 +720,7 @@ class Interpolator : private Weighter else if constexpr (interpOrder == 3) { - if constexpr (Centering == QtyCentering::primal) + if constexpr (centering == QtyCentering::primal) return 1; else return (delta < .5 ? 2 : 1); @@ -589,21 +731,63 @@ class Interpolator : private Weighter private: static_assert(dimension <= 3 && dimension > 0 && interpOrder >= 1 && interpOrder <= 3, "error"); - using Starts = std::array; - using Weights = std::array, dimension>; + template + using Array = std::array; + + // maybe could be std::uint8_t? + using Starts = Array; + using Weights = Array, dimension>; Weighter weightComputer_; MeshToParticle meshToParticle_; - ParticleToMesh particleToMesh_; + ParticleToMesh particleToMesh_; Starts dual_startIndex_; Weights dual_weights_; Starts primal_startIndex_; Weights primal_weights_; + + + template + auto static get_indexAndWeights(GridLayout const& layout, Particles const& particles, + std::size_t start) + { + // dual weights require -.5 to take the correct position weight + auto constexpr dual_offset = .5; + + Weighter weightComputer_; + + Array startIndexs; + Array weights; + + auto left = particles.size() - start; + auto end = S > left ? left : S; + for (std::size_t i = 0, pi = start; i < end; ++i, ++pi) + { + auto iCell = layout.AMRToLocal(Point{particles.iCell(pi)}); + auto const& delta = particles.delta(pi); + + for (auto iDim = 0u; iDim < dimension; ++iDim) + { + startIndexs[i][iDim] = iCell[iDim] - computeStartLeftShift(delta[iDim]); + + double normalizedPos = iCell[iDim] + delta[iDim]; + + if constexpr (centering == QtyCentering::dual) + normalizedPos -= dual_offset; + + weightComputer_.computeWeight(normalizedPos, startIndexs[i][iDim], + weights[i][iDim]); + } + } + + return std::make_tuple(startIndexs, weights); + } }; + } // namespace PHARE::core #endif diff --git a/src/core/numerics/ion_updater/ion_updater.hpp b/src/core/numerics/ion_updater/ion_updater.hpp index 92b4cc8df..ea9704438 100644 --- a/src/core/numerics/ion_updater/ion_updater.hpp +++ b/src/core/numerics/ion_updater/ion_updater.hpp @@ -15,8 +15,8 @@ #include "core/logger.hpp" -#include #include +#include namespace PHARE::core @@ -30,12 +30,11 @@ class IonUpdater static constexpr auto dimension = GridLayout::dimension; static constexpr auto interp_order = GridLayout::interp_order; - using Box = PHARE::core::Box; - using Interpolator = PHARE::core::Interpolator; - using VecField = typename Ions::vecfield_type; - using ParticleArray = typename Ions::particle_array_type; - using Particle_t = typename ParticleArray::Particle_t; - using PartIterator = typename ParticleArray::iterator; + using Box = PHARE::core::Box; + using Interpolator = PHARE::core::Interpolator; + using ParticleArray = typename Ions::particle_array_type; + // using Particle_t = typename ParticleArray::Particle_t; + // using PartIterator = typename ParticleArray::iterator; using ParticleRange = IndexRange; using BoundaryCondition = PHARE::core::BoundaryCondition; using Pusher = PHARE::core::Pusher::updateAndDepositDomain_(Ions& ion auto pushAndAccumulateGhosts = [&](auto& inputArray, bool copyInDomain = false) { auto outputArray{inputArray}; // TODO : dynamic allocation can we get rid of that - // eventually? + // eventually? inRange = makeIndexRange(inputArray); outRange = makeIndexRange(outputArray); + auto enteredInDomain = pusher_->move(inRange, outRange, em, pop.mass(), interpolator_, layout, inGhostBox, inDomainBox); diff --git a/src/core/numerics/ion_updater/ion_updater_per_particle.hpp b/src/core/numerics/ion_updater/ion_updater_per_particle.hpp new file mode 100644 index 000000000..2b2d289d1 --- /dev/null +++ b/src/core/numerics/ion_updater/ion_updater_per_particle.hpp @@ -0,0 +1,208 @@ +#ifndef PHARE_ION_UPDATER_PP_HPP +#define PHARE_ION_UPDATER_PP_HPP + + +#include "ion_updater.hpp" // for UpdaterMode/etc +#include "core/data/particles/particle_array_partitionner.hpp" + +namespace PHARE::core +{ +template +class IonUpdaterPP +{ +public: + static constexpr auto dimension = GridLayout::dimension; + static constexpr auto interp_order = GridLayout::interp_order; + static constexpr std::uint16_t n_samples = 256; // artibrary + + using Box = PHARE::core::Box; + using Interpolator = PHARE::core::Interpolator; + using ParticleArray = typename Ions::particle_array_type; + // using Particle_t = typename ParticleArray::Particle_t; + + using Pusher = PHARE::core::SimpleBorisPusher; + +private: + Interpolator interpolator_; + std::unique_ptr pusher_; + +public: + IonUpdaterPP(PHARE::initializer::PHAREDict const& dict) {} + + void updatePopulations(Ions& ions, Electromag const& em, GridLayout const& layout, + double const& dt, UpdaterMode = UpdaterMode::all); + + + void updateIons(Ions& ions, GridLayout const& layout); + + +private: + void updateAndDepositDomain_(Ions& ions, Electromag const& em, GridLayout const& layout); + void updateAndDepositAll_(Ions& ions, Electromag const& em, GridLayout const& layout); + + void move(); + + + auto& tmp_particles_from(ParticleArray const& particles) + { + bool resize_bigger = tmp_particles_.size() < tmp_particles_.size(); + if (resize_bigger) + tmp_particles_.resize(tmp_particles_.size() + + static_cast(tmp_particles_.size() * .05)); + else + tmp_particles_.resize(particles.size()); + std::copy(particles.begin(), particles.end(), tmp_particles_.begin()); + sample_sizes_[sample_iter_] = particles.size(); + ++sample_iter_; + if (sample_iter_ == n_samples) + { + sample_iter_ = 0; + for (std::uint16_t i = 0; i < n_samples; ++i) + if (sample_sizes_[i] > (tmp_particles_.size() + - static_cast(tmp_particles_.size() * .20))) + return tmp_particles_; // do not shrink + + tmp_particles_.shrink_to_fit(); + } + return tmp_particles_; + } + + ParticleArray tmp_particles_; + std::array sample_sizes_; // for freeing memory + std::uint16_t sample_iter_ = 0; // e.g. making tmp_particle_ smaller if we can + + double dt_ = 0; +}; + + + + +template +void IonUpdaterPP::updatePopulations(Ions& ions, Electromag const& em, + GridLayout const& layout, + double const& dt, + UpdaterMode mode) +{ + PHARE_LOG_SCOPE("IonUpdaterPP::updatePopulations"); + resetMoments(ions); + dt_ = dt; + if (mode == UpdaterMode::domain_only) + updateAndDepositDomain_(ions, em, layout); + else + updateAndDepositAll_(ions, em, layout); +} + + + +template +void IonUpdaterPP::updateIons(Ions& ions, GridLayout const& layout) +{ + fixMomentGhosts(ions, layout); + ions.computeDensity(); + ions.computeBulkVelocity(); +} + + +template +auto partition_particles(Particles& particles, GridLayout const& layout) +{ + auto constexpr partGhostWidth = GridLayout::nbrParticleGhosts(); + return partition(particles, layout.AMRBox(), layout.neighbors()); +} + +template +void IonUpdaterPP::updateAndDepositDomain_(Ions& ions, + Electromag const& em, + GridLayout const& layout) +{ + PHARE_LOG_SCOPE("IonUpdaterPP::updateAndDepositDomain_"); + + for (auto& pop : ions) + { + ParticleArray& domain_particles = pop.domainParticles(); + pusher_ = std::make_unique(layout, layout.meshSize(), dt_, pop.mass()); + for (std::size_t p_idx = 0; p_idx < domain_particles.size(); ++p_idx) + pusher_->move_particle(domain_particles[p_idx], em); + auto iterators = partition_particles(domain_particles, layout); + + // do later after partition/sorting + // interpolator_(inDomain, pop.density(), pop.flux(), layout); + // erase particles that left the box + // domain.erase(makeRange(domain, inDomain.iend(), domain.size())); + + auto pushAndAccumulateGhosts = [&](auto& inputArray) -> auto& + { + auto& ghost_particles = tmp_particles_from(inputArray); + for (std::size_t p_idx = 0; p_idx < ghost_particles.size(); ++p_idx) + pusher_->move_particle(ghost_particles[p_idx], em); + return ghost_particles; + }; + + auto& patch_ghost_particles = pushAndAccumulateGhosts(pop.patchGhostParticles()); + partition_particles(patch_ghost_particles, layout); + // interpoloate in domain + // copy inDomain into domain_particles + + auto& level_ghost_particles = pushAndAccumulateGhosts(pop.levelGhostParticles()); + partition_particles(level_ghost_particles, layout); + // interpoloate in domain + } +} + + +template +/** + * @brief IonUpdaterPP::updateAndDepositDomain_ + evolves moments and particles from time n to n+1 + */ +void IonUpdaterPP::updateAndDepositAll_(Ions& ions, + Electromag const& em, + GridLayout const& layout) +{ + PHARE_LOG_SCOPE("IonUpdaterPP::updateAndDepositAll_"); + + + // push domain particles, erase from array those leaving domain + // push patch and level ghost particles that are in ghost area (==ghost box without domain) + // copy patch and ghost particles out of ghost area that are in domain, in particle array + // finally all particles in domain are to be interpolated on mesh. + + + for (auto& pop : ions) + { + // auto& domainParticles = pop.domainParticles(); + // auto domainPartRange = makeIndexRange(domainParticles); + + // auto inDomain = pusher_->move( + // domainPartRange, domainPartRange, em, pop.mass(), interpolator_, layout, + // [](auto const& particleRange) { return particleRange; }, inDomainBox); + + // domainParticles.erase(makeRange(domainParticles, inDomain.iend(), + // domainParticles.size())); + + // auto pushAndCopyInDomain = [&](auto&& particleRange) { + // auto inGhostLayerRange = pusher_->move(particleRange, particleRange, em, pop.mass(), + // interpolator_, layout, inGhostBox, + // inGhostLayer); + + // auto& particleArray = particleRange.array(); + // particleArray.export_particles( + // domainParticles, [&](auto const& cell) { return isIn(Point{cell}, domainBox); }); + + // particleArray.erase( + // makeRange(particleArray, inGhostLayerRange.iend(), particleArray.size())); + // }; + + // pushAndCopyInDomain(makeIndexRange(pop.patchGhostParticles())); + // pushAndCopyInDomain(makeIndexRange(pop.levelGhostParticles())); + + // interpolator_(makeIndexRange(domainParticles), pop.density(), pop.flux(), layout); + } +} + + + +} // namespace PHARE::core + + +#endif // ION_UPDATER_HPP diff --git a/src/core/numerics/ohm/ohm.hpp b/src/core/numerics/ohm/ohm.hpp index 80ad56705..6fe6362c7 100644 --- a/src/core/numerics/ohm/ohm.hpp +++ b/src/core/numerics/ohm/ohm.hpp @@ -4,6 +4,7 @@ #include #include +#include "core/def.hpp" #include "core/data/grid/gridlayoutdefs.hpp" #include "core/data/grid/gridlayout.hpp" #include "core/data/grid/gridlayout_utils.hpp" @@ -14,6 +15,9 @@ namespace PHARE::core { +template +class Ohm_ref; // why does this need to exist? I forgot. + template class Ohm : public LayoutHolder { @@ -28,59 +32,88 @@ class Ohm : public LayoutHolder } template - void operator()(Field const& n, VecField const& Ve, Field const& Pe, VecField const& B, - VecField const& J, VecField& Enew) + void operator()(Field const& n_, VecField const& Ve_, Field const& Pe_, VecField const& B_, + VecField const& J_, VecField& Enew_) _PHARE_ALL_FN_ { - using Pack = OhmPack; - if (!this->hasLayout()) throw std::runtime_error( "Error - Ohm - GridLayout not set, cannot proceed to calculate ohm()"); - auto const& [Exnew, Eynew, Eznew] = Enew(); + auto n = n_.view(); + auto Ve = Ve_.view(); + auto Pe = Pe_.view(); + auto B = B_.view(); + auto J = J_.view(); + auto Enew = Enew_.view(); - layout_->evalOnBox(Exnew, [&](auto&... args) mutable { - this->template E_Eq_(Pack{Enew, n, Pe, Ve, B, J}, args...); - }); - layout_->evalOnBox(Eynew, [&](auto&... args) mutable { - this->template E_Eq_(Pack{Enew, n, Pe, Ve, B, J}, args...); - }); - layout_->evalOnBox(Eznew, [&](auto&... args) mutable { - this->template E_Eq_(Pack{Enew, n, Pe, Ve, B, J}, args...); - }); + using Ref = Ohm_ref; + Ref{*this->layout_, eta_, nu_}(n, Ve, Pe, B, J, Enew); } +private: double const eta_; double const nu_; +}; -private: - template - struct OhmPack +template +class Ohm_ref +{ + constexpr static auto dimension = GridLayout::dimension; + using This = Ohm_ref; + +public: + Ohm_ref(GridLayout& layout_, double eta, double nu) + : layout{layout_} + , eta_{eta} + , nu_{nu} { - VecField& Exyz; - Field const &n, &Pe; - VecField const &Ve, &B, &J; - }; + } + void operator()(Field const& n, VecField const& Ve, Field const& Pe, VecField const& B, + VecField const& J, VecField& Enew) const _PHARE_ALL_FN_ + { + auto const& [Exnew, Eynew, Eznew] = Enew(); + + layout.evalOnBox( + Exnew, + [] _PHARE_ALL_FN_(auto&... args) { This::template E_Eq_(args...); }, n, + Ve, Pe, B, J, Enew, *this); + layout.evalOnBox( + Eynew, + [] _PHARE_ALL_FN_(auto&... args) { This::template E_Eq_(args...); }, n, + Ve, Pe, B, J, Enew, *this); + layout.evalOnBox( + Eznew, + [] _PHARE_ALL_FN_(auto&... args) { This::template E_Eq_(args...); }, n, + Ve, Pe, B, J, Enew, *this); + } - template - void E_Eq_(OhmPack&& pack, IDXs const&... ijk) const +private: + GridLayout layout; + double const eta_; + double const nu_; + + + + template + static void E_Eq_(std::tuple const& ijk, Args&&... args) _PHARE_ALL_FN_ { - auto const& [E, n, Pe, Ve, B, J] = pack; - auto& Exyz = E(Tag); + auto const& [n, Ve, Pe, B, J, E, self] = std::forward_as_tuple(args...); + auto& Exyz = E(Tag); static_assert(Components::check()); - Exyz(ijk...) = ideal_(Ve, B, {ijk...}) // - + pressure_(n, Pe, {ijk...}) // - + resistive_(J, {ijk...}) // - + hyperresistive_(J, {ijk...}); + Exyz(ijk) = self.template ideal_(Ve, B, {ijk}) // + + self.template pressure_(n, Pe, {ijk}) // + + self.template resistive_(J, {ijk}) // + + self.template hyperresistive_(J, {ijk}); } - template - auto ideal1D_(VecField const& Ve, VecField const& B, MeshIndex<1> index) const + + template + auto ideal1D_(VecField const& Ve, VecField const& B, MeshIndex<1> index) const _PHARE_ALL_FN_ { if constexpr (component == Component::X) { @@ -133,8 +166,9 @@ class Ohm : public LayoutHolder } - template - auto ideal2D_(VecField const& Ve, VecField const& B, MeshIndex<2> index) const + + template + auto ideal2D_(VecField const& Ve, VecField const& B, MeshIndex<2> index) const _PHARE_ALL_FN_ { if constexpr (component == Component::X) { @@ -188,8 +222,8 @@ class Ohm : public LayoutHolder - template - auto ideal3D_(VecField const& Ve, VecField const& B, MeshIndex<3> index) const + template + auto ideal3D_(VecField const& Ve, VecField const& B, MeshIndex<3> index) const _PHARE_ALL_FN_ { if constexpr (component == Component::X) { @@ -242,8 +276,9 @@ class Ohm : public LayoutHolder - template - auto ideal_(VecField const& Ve, VecField const& B, MeshIndex index) const + template + auto ideal_(VecField const& Ve, VecField const& B, + MeshIndex index) const _PHARE_ALL_FN_ { if constexpr (dimension == 1) return ideal1D_(Ve, B, index); @@ -255,14 +290,16 @@ class Ohm : public LayoutHolder - template - auto pressure_(Field const& n, Field const& Pe, MeshIndex index) const + + template + auto pressure_(Field const& n, Field const& Pe, + MeshIndex index) const _PHARE_ALL_FN_ { if constexpr (component == Component::X) { auto const nOnEx = GridLayout::project(n, index, GridLayout::momentsToEx()); - auto gradPOnEx = layout_->template deriv(Pe, index); // TODO : issue 3391 + auto gradPOnEx = layout.template deriv(Pe, index); // TODO : issue 3391 return -gradPOnEx / nOnEx; } @@ -274,7 +311,7 @@ class Ohm : public LayoutHolder auto const nOnEy = GridLayout::project(n, index, GridLayout::momentsToEy()); auto gradPOnEy - = layout_->template deriv(Pe, index); // TODO : issue 3391 + = layout.template deriv(Pe, index); // TODO : issue 3391 return -gradPOnEy / nOnEy; } @@ -291,7 +328,7 @@ class Ohm : public LayoutHolder auto const nOnEz = GridLayout::project(n, index, GridLayout::momentsToEz()); auto gradPOnEz - = layout_->template deriv(Pe, index); // TODO : issue 3391 + = layout.template deriv(Pe, index); // TODO : issue 3391 return -gradPOnEz / nOnEz; } @@ -305,8 +342,8 @@ class Ohm : public LayoutHolder - template - auto resistive_(VecField const& J, MeshIndex index) const + template + auto resistive_(VecField const& J, MeshIndex index) const _PHARE_ALL_FN_ { auto const& Jxyx = J(component); @@ -332,10 +369,11 @@ class Ohm : public LayoutHolder - template - auto hyperresistive_(VecField const& J, MeshIndex index) const + template + auto hyperresistive_(VecField const& J, + MeshIndex index) const _PHARE_ALL_FN_ { - return -nu_ * layout_->laplacian(J(component), index); // TODO : issue 3391 + return -nu_ * layout.laplacian(J(component), index); // TODO : issue 3391 } }; diff --git a/src/core/numerics/pusher/boris.hpp b/src/core/numerics/pusher/boris.hpp index 7faf26194..b2aaf853c 100644 --- a/src/core/numerics/pusher/boris.hpp +++ b/src/core/numerics/pusher/boris.hpp @@ -90,7 +90,7 @@ class BorisPusher rangeOut = firstSelector(rangeOut); // get electromagnetic fields interpolated on the particles of rangeOut - // stop at newEnd. + // stop at newEnd. meshToParticle interpolator(rangeOut, emFields, layout); // get the particle velocity from t=n to t=n+1 @@ -107,10 +107,11 @@ class BorisPusher /** see Pusher::move() documentation*/ - virtual void setMeshAndTimeStep(std::array ms, double ts) override + void setMeshAndTimeStep(std::array const& ms, + double const ts) override _PHARE_ALL_FN_ { std::transform(std::begin(ms), std::end(ms), std::begin(halfDtOverDl_), - [ts](double& x) { return 0.5 * ts / x; }); + [ts](auto const& x) { return 0.5 * ts / x; }); dt_ = ts; } @@ -127,16 +128,16 @@ class BorisPusher std::array newCell; for (std::size_t iDim = 0; iDim < dim; ++iDim) { - double delta - = partIn.delta[iDim] + static_cast(halfDtOverDl_[iDim] * partIn.v[iDim]); + double delta = partIn.delta()[iDim] + + static_cast(halfDtOverDl_[iDim] * partIn.v()[iDim]); double iCell = std::floor(delta); if (std::abs(delta) > 2) { PHARE_LOG_ERROR("Error, particle moves more than 1 cell, delta >2"); } - partOut.delta[iDim] = delta - iCell; - newCell[iDim] = static_cast(iCell + partIn.iCell[iDim]); + partOut.delta()[iDim] = delta - iCell; + newCell[iDim] = static_cast(iCell + partIn.iCell()[iDim]); } return newCell; } @@ -150,6 +151,8 @@ class BorisPusher */ void pushStep_(ParticleRange const& rangeIn, ParticleRange& rangeOut, PushStep step) { + using ParticleArray = std::decay_t; + auto& inParticles = rangeIn.array(); auto& outParticles = rangeOut.array(); for (auto inIdx = rangeIn.ibegin(), outIdx = rangeOut.ibegin(); inIdx < rangeIn.iend(); @@ -166,13 +169,15 @@ class BorisPusher // over rangeIn particles. if (step == PushStep::PrePush) { - outParticles[outIdx].charge = inParticles[inIdx].charge; - outParticles[outIdx].weight = inParticles[inIdx].weight; - outParticles[outIdx].v = inParticles[inIdx].v; + outParticles[outIdx].charge() = inParticles[inIdx].charge(); + outParticles[outIdx].weight() = inParticles[inIdx].weight(); + outParticles[outIdx].v() = inParticles[inIdx].v(); } auto newCell = advancePosition_(inParticles[inIdx], outParticles[outIdx]); - if (newCell != inParticles[inIdx].iCell) - outParticles.change_icell(newCell, outIdx); + + if constexpr (ParticleArray::is_mapped) + if (newCell != inParticles[inIdx].iCell()) + outParticles.change_icell(newCell, outIdx); } } @@ -180,32 +185,41 @@ class BorisPusher /** Accelerate the particles in rangeIn and put the new velocity in rangeOut */ - void accelerate_(ParticleRange rangeIn, ParticleRange rangeOut, double mass) + template + void accelerate_(ParticleRangeIn const& inputParticles, ParticleRangeOut& outputParticles, + double const mass) { double dto2m = 0.5 * dt_ / mass; - auto& inParticles = rangeIn.array(); - auto& outParticles = rangeOut.array(); + auto out_idx = outputParticles.begin().idx(); + auto& out_particles = outputParticles.begin()(); - for (auto inIdx = rangeIn.ibegin(), outIdx = rangeOut.ibegin(); inIdx < rangeIn.iend(); - ++inIdx, ++outIdx) + auto in_start = inputParticles.begin().idx(); + auto in_end = inputParticles.end().idx(); + auto& in_particles = inputParticles.begin()(); + + for (auto in_idx = in_start; in_idx < in_end; ++in_idx) { - auto& inPart = inParticles[inIdx]; - auto& outPart = outParticles[inIdx]; - double coef1 = inPart.charge * dto2m; + auto const& E = in_particles.E(in_idx); + auto const& B = in_particles.B(in_idx); + + auto const& [Ex, Ey, Ez] = E; + auto const& [Bx, By, Bz] = B; + + double coef1 = in_particles.charge(in_idx) * dto2m; // We now apply the 3 steps of the BORIS PUSHER // 1st half push of the electric field - double velx1 = inPart.v[0] + coef1 * inPart.Ex; - double vely1 = inPart.v[1] + coef1 * inPart.Ey; - double velz1 = inPart.v[2] + coef1 * inPart.Ez; - + auto const& in_v = in_particles.v(in_idx); + std::array const vel = {in_v[0] + coef1 * Ex, // + in_v[1] + coef1 * Ey, // + in_v[2] + coef1 * Ez}; // preparing variables for magnetic rotation - double const rx = coef1 * inPart.Bx; - double const ry = coef1 * inPart.By; - double const rz = coef1 * inPart.Bz; + double const rx = coef1 * Bx; + double const ry = coef1 * By; + double const rz = coef1 * Bz; double const rx2 = rx * rx; double const ry2 = ry * ry; @@ -231,20 +245,16 @@ class BorisPusher double const mzz = 1. + rz2 - rx2 - ry2; // magnetic rotation - double const velx2 = (mxx * velx1 + mxy * vely1 + mxz * velz1) * invDet; - double const vely2 = (myx * velx1 + myy * vely1 + myz * velz1) * invDet; - double const velz2 = (mzx * velx1 + mzy * vely1 + mzz * velz1) * invDet; - + double const velx2 = (mxx * vel[0] + mxy * vel[1] + mxz * vel[2]) * invDet; + double const vely2 = (myx * vel[0] + myy * vel[1] + myz * vel[2]) * invDet; + double const velz2 = (mzx * vel[0] + mzy * vel[1] + mzz * vel[2]) * invDet; // 2nd half push of the electric field - velx1 = velx2 + coef1 * inPart.Ex; - vely1 = vely2 + coef1 * inPart.Ey; - velz1 = velz2 + coef1 * inPart.Ez; - // Update particle velocity - outPart.v[0] = velx1; - outPart.v[1] = vely1; - outPart.v[2] = velz1; + out_particles.v(out_idx) = {velx2 + coef1 * Ex, // + vely2 + coef1 * Ey, // + velz2 + coef1 * Ez}; + ++out_idx; } } diff --git a/src/core/numerics/pusher/boris_simpler.hpp b/src/core/numerics/pusher/boris_simpler.hpp new file mode 100644 index 000000000..2b91dce11 --- /dev/null +++ b/src/core/numerics/pusher/boris_simpler.hpp @@ -0,0 +1,167 @@ +#ifndef PHARE_CORE_PUSHER_BORIS_SIMPLER_HPP +#define PHARE_CORE_PUSHER_BORIS_SIMPLER_HPP + +#include +#include +#include +#include +#include +#include + +#include "core/errors.hpp" +#include "core/logger.hpp" + +#include "core/data/particles/particle.hpp" +#include "core/numerics/pusher/pusher.hpp" +#include "core/utilities/range/range.hpp" + +namespace PHARE::core +{ + +template +void c_boris_advancePosition(Float const* vIn, Float const* deltaIn, int const* iCellIn, + Float* deltaOut, int* iCellOut, Float const* halfDtOverDl) +{ + for (std::size_t iDim = 0; iDim < dim; ++iDim) + { + Float delta = deltaIn[iDim] + static_cast(halfDtOverDl[iDim] * vIn[iDim]); + Float iCell = std::floor(delta); + if (std::abs(delta) > 2) + throw std::runtime_error("Error, particle moves more than 1 cell, delta >2"); + deltaOut[iDim] = delta - iCell; + iCellOut[iDim] = static_cast(iCell + iCellIn[iDim]); + } +} + + +template +void c_boris_accelerate(Float const& chargeIn, Float const* vIn, Float const* EIn, + Float const* const BIn, Float* vOut, Float const& dto2m) +{ + static constexpr Float one = 1; + static constexpr Float two = 2; + Float coef1 = chargeIn * dto2m; + + // We now apply the 3 steps of the BORIS PUSHER + // 1st half push of the electric field + Float velx1 = vIn[0] + coef1 * EIn[0]; // see Fused Multiple/Add (FMA) + Float vely1 = vIn[1] + coef1 * EIn[1]; + Float velz1 = vIn[2] + coef1 * EIn[2]; + + // preparing variables for magnetic rotation + Float const rx = coef1 * BIn[0]; + Float const ry = coef1 * BIn[1]; + Float const rz = coef1 * BIn[2]; + + Float const rx2 = rx * rx; + Float const ry2 = ry * ry; + Float const rz2 = rz * rz; + Float const rxry = rx * ry; + Float const rxrz = rx * rz; + Float const ryrz = ry * rz; + + Float const invDet = one / (one + rx2 + ry2 + rz2); + + // preparing rotation matrix due to the magnetic field + // m = invDet*(I + r*r - r x I) - I where x denotes the cross product + Float const mxx = one + rx2 - ry2 - rz2; + Float const mxy = two * (rxry + rz); + Float const mxz = two * (rxrz - ry); + + Float const myx = two * (rxry - rz); + Float const myy = one + ry2 - rx2 - rz2; + Float const myz = two * (ryrz + rx); + + Float const mzx = two * (rxrz + ry); + Float const mzy = two * (ryrz - rx); + Float const mzz = one + rz2 - rx2 - ry2; + + // magnetic rotation + Float const velx2 = (mxx * velx1 + mxy * vely1 + mxz * velz1) * invDet; + Float const vely2 = (myx * velx1 + myy * vely1 + myz * velz1) * invDet; + Float const velz2 = (mzx * velx1 + mzy * vely1 + mzz * velz1) * invDet; + + // 2nd half push of the electric field / Update particle velocity + vOut[0] = velx2 + coef1 * EIn[0]; + vOut[1] = vely2 + coef1 * EIn[1]; + vOut[2] = velz2 + coef1 * EIn[2]; +} + + + + +template +class SimpleBorisPusher +{ + auto static mesh(std::array const& ms, double const& ts) + { + std::array const halfDtOverDl; + std::transform(std::begin(ms), std::end(ms), std::begin(halfDtOverDl), + [ts](auto const& x) { return 0.5 * ts / x; }); + return halfDtOverDl; + } + +public: + SimpleBorisPusher(GridLayout const& layout, std::array const& ms, double const& ts, + double const& mass) _PHARE_ALL_FN_ : layout_{layout}, + halfDtOverDl_{mesh(ms, ts)}, + mass_{mass}, + dt_{ts} + { + } + + + template + void move_particle(Particle_t const& particle_in, Particle_t& particle_out, + Electromag const& emFields) _PHARE_ALL_FN_ + { + advancePosition_(particle_in, particle_out); + interpolator_.meshToParticle(particle_out, emFields, layout_); + accelerate_(particle_in, particle_out, mass_); + advancePosition_(particle_in, particle_out); + } + + template + void move_particle(Particle_t& particle, Electromag const& emFields) + { + move_particle(particle, particle, emFields); + } + + +private: + template + void advancePosition_(Particle_t const& particle_in, Particle_t const& particle_out) const + { + c_boris_advancePosition( + /*vIn= */ &particle_in.v(), + /*deltaIn= */ &particle_in.delta(), + /*iCellIn= */ &particle_in.iCell(), + /*deltaOut= */ &particle_out.delta(), + /*iCellOut= */ &particle_out.iCell(), + /*halfDtOverDl=*/halfDtOverDl_); + } + + template + void accelerate_(Particle_t const& particle_in, Particle_t const& particle_out) const + { + c_boris_accelerate( + /*chargeIn= */ particle_in.charge(), + /*vIn= */ &particle_in.v(), + /*EIn= */ &particle_in.E(), + /*BIn= */ &particle_in.B(), + /*vOut= */ &particle_out.v(), + /*dto2m= */ dto2m); + } + + Interpolator interpolator_; + GridLayout const& layout_; + std::array const halfDtOverDl_; + double const mass_; + double const dt_; + double const dto2m = 0.5 * dt_ / mass_; +}; + +} // namespace PHARE::core + + +#endif /* PHARE_CORE_PUSHER_BORIS_SIMPLER_HPP */ diff --git a/src/core/numerics/pusher/pusher.hpp b/src/core/numerics/pusher/pusher.hpp index 9c1b48b9f..69485a44a 100644 --- a/src/core/numerics/pusher/pusher.hpp +++ b/src/core/numerics/pusher/pusher.hpp @@ -23,6 +23,14 @@ namespace core using ParticleSelector = std::function; public: + /** + * @brief setMeshAndTimeStep allows to let the pusher know what is the mesh + * size and time step in the domain where particles are to be pushed. + */ + virtual void setMeshAndTimeStep(std::array const& ms, + double const ts) _PHARE_ALL_FN_ = 0; + + // TODO : to really be independant on boris which has 2 push steps // we should have an arbitrary number of selectors, 1 per push step virtual ParticleRange move(ParticleRange const& rangeIn, ParticleRange& rangeOut, @@ -32,9 +40,9 @@ namespace core = 0; - virtual void setMeshAndTimeStep(std::array ms, double ts) = 0; - virtual ~Pusher() {} + Pusher() _PHARE_ALL_FN_ {} + virtual ~Pusher() _PHARE_ALL_FN_ {} }; } // namespace core diff --git a/src/core/numerics/pusher/pusher_factory.hpp b/src/core/numerics/pusher/pusher_factory.hpp index 28a85ecb2..cc222ff2a 100644 --- a/src/core/numerics/pusher/pusher_factory.hpp +++ b/src/core/numerics/pusher/pusher_factory.hpp @@ -6,6 +6,7 @@ #include #include "boris.hpp" +#include "boris_simpler.hpp" #include "pusher.hpp" namespace PHARE @@ -17,7 +18,9 @@ namespace core public: template - static auto makePusher(std::string pusherName) + static std::unique_ptr< + Pusher> + makePusher(std::string pusherName) { if (pusherName == "modified_boris") { diff --git a/src/core/operators.hpp b/src/core/operators.hpp new file mode 100644 index 000000000..12fc4aa21 --- /dev/null +++ b/src/core/operators.hpp @@ -0,0 +1,61 @@ +#ifndef PHARE_CORE_OPERATORS_HPP +#define PHARE_CORE_OPERATORS_HPP + +#include "core/def.hpp" + +#ifndef PHARE_WITH_GPU +#define PHARE_WITH_GPU 0 +#endif + +#if PHARE_WITH_GPU +// #include +#else +#include +#endif + +namespace PHARE::core +{ +template +struct Operators +{ + auto static constexpr GPU = PHARE_WITH_GPU; + + void operator+=(T const& v) + { + if constexpr (GPU and atomic) + { + atomicAdd(&t, v); + } + else if constexpr (atomic) + { + auto& atomic_t = *reinterpret_cast*>(&t); + T tmp = atomic_t.load(); + while (!atomic_t.compare_exchange_weak(tmp, tmp + v)) {} + } + else + t += v; + } + void operator+=(T const&& v) { (*this) += v; } + + void operator-=(T const& v) + { + if constexpr (GPU and atomic) + { + atomicSub(&t, v); + } + else if constexpr (atomic) + { + auto& atomic_t = *reinterpret_cast*>(&t); + T tmp = atomic_t.load(); + while (!atomic_t.compare_exchange_weak(tmp, tmp - v)) {} + } + else + t -= v; + } + void operator-=(T const&& v) { (*this) += v; } + + T& t; +}; +} // namespace PHARE::core + +#endif /* PHARE_CORE_OPERATORS_HPP */ diff --git a/src/core/utilities/box/box.hpp b/src/core/utilities/box/box.hpp index a43563764..15f14c4bf 100644 --- a/src/core/utilities/box/box.hpp +++ b/src/core/utilities/box/box.hpp @@ -2,21 +2,38 @@ #define PHARE_CORE_UTILITIES_BOX_BOX_HPP +#include "core/def.hpp" +#include "core/logger.hpp" #include "core/utilities/types.hpp" #include "core/utilities/point/point.hpp" #include "core/utilities/meta/meta_utilities.hpp" +#include #include -#include -#include #include +#include +#include +#include namespace PHARE::core { template class box_iterator; - +namespace +{ + template + void constexpr verify(Point const& lower, Point const& upper) + { + for (std::uint16_t i = 0; i < dim; ++i) + if (lower[i] > upper[i]) + { + PHARE_LOG_LINE_STR("Invalid box " + std::to_string(lower) + "-" + + std::to_string(upper)); + std::abort(); + } + } +} // namespace /** Represents a 1D, 2D or 3D box of integer or floating point * points. @@ -24,6 +41,10 @@ class box_iterator; template struct Box { + auto static constexpr dimension = dim; + using value_type = Type; + using This = Box; + Point lower; Point upper; @@ -33,6 +54,7 @@ struct Box : lower{_lower} , upper{_upper} { + // PHARE_DEBUG_DO(verify(lower, upper)); } template @@ -40,9 +62,11 @@ struct Box : lower{_lower} , upper{_upper} { + PHARE_DEBUG_DO(verify(lower, upper)); } bool operator==(Box const& box) const { return box.lower == lower && box.upper == upper; } + bool operator!=(Box const& other) const { return !(*this == other); } auto operator*(Box const& other) const { @@ -61,17 +85,24 @@ struct Box bool isEmpty() const { return (*this) == Box{}; } - void grow(Type const& size) + auto& grow(Type const& size) { assert(size >= 0); for (auto& c : lower) - { c -= size; - } for (auto& c : upper) - { c += size; - } + return *this; + } + + auto& shrink(Type const& size) + { + assert(size >= 0); + for (auto& c : lower) + c += size; + for (auto& c : upper) + c -= size; + return *this; } auto shape() const { return upper - lower + 1; } @@ -119,7 +150,6 @@ struct Box return iterator{this, {upper[0] + 1, upper[1] + 1, upper[2] + 1}}; } } - using value_type = Type; constexpr static std::size_t nbrRemainBoxes() @@ -135,8 +165,12 @@ struct Box else return 6; } + + std::vector remove(This const& that) const; }; + + template class box_iterator { @@ -198,12 +232,11 @@ Box(Point lower, Point upper) -> Box; * Returns occurs at the first box the point is in. */ template = dummy::value> -bool isIn(Point const& point, BoxContainer const& boxes) +bool isIn(Point const& point, BoxContainer const& boxes) _PHARE_ALL_FN_ { if (boxes.size() == 0) return false; - static_assert(std::is_same::value, "Box and Point should have the same data type"); @@ -231,7 +264,8 @@ bool isIn(Point const& point, BoxContainer const& boxes) * one box. */ template -bool isIn(Point const& point, Box const& box) +bool isIn(Point const& point, + Box const& box) _PHARE_ALL_FN_ { auto isIn1D = [](typename Point::value_type pos, typename Point::value_type lower, typename Point::value_type upper) { return pos >= lower && pos <= upper; }; @@ -257,6 +291,15 @@ Box grow(Box const& box, Type const& size) return copy; } +template +Box shrink(Box const& box, Type const& size) +{ + auto copy{box}; + copy.shrink(size); + return copy; +} + + template Box emptyBox() { @@ -277,7 +320,161 @@ auto& operator<<(std::ostream& os, Box const& box) } +} // namespace PHARE::core + + +namespace std +{ +template +auto to_string(PHARE::core::Box const& box) +{ + std::stringstream ss; + ss << box; + return ss.str(); +} + +} // namespace std + +namespace PHARE::core +{ +template +std::vector> Box::remove(Box const& to_remove) const +{ + using box_t = Box; + using _m = std::unordered_map; + + auto const box = *this; // needs to be copy or weird things happen, dunno tbh + + auto overlap = box * to_remove; + + if (not overlap) + return std::vector{*this}; + + auto copy = [](auto cpy, auto const& replace) { + for (auto const& [i, v] : replace) + cpy[i] = v; + return cpy; + }; + + auto intersection = *overlap; + + // maybe could be std::array>>? + std::unordered_map boxes; + + if (intersection.lower[0] > box.lower[0]) + boxes["left"] = Box(box.lower, copy(box.upper, _m{{0, intersection.lower[0] - 1}})); + if (intersection.upper[0] < box.upper[0]) + boxes["right"] = box_t{copy(box.lower, _m{{0, intersection.upper[0] + 1}}), box.upper}; + + Type minx = 0, maxx = 0; + if (dim > 1) + { + minx = boxes.count("left") > 0 ? intersection.lower[0] : box.lower[0]; + maxx = boxes.count("right") > 0 ? intersection.upper[0] : box.upper[0]; + + if (intersection.lower[1] > box.lower[1]) + boxes["down"] = box_t{copy(box.lower, _m{{0, minx}}), + copy(box.upper, _m{{0, maxx}, {1, intersection.lower[1] - 1}})}; + + if (intersection.upper[1] < box.upper[1]) + boxes["up"] = Box(copy(box.lower, _m{{0, minx}, {1, intersection.upper[1] + 1}}), + copy(box.upper, _m{{0, maxx}})); + } + + if (dim > 2) + { + Type miny = boxes.count("down") > 0 ? intersection.lower[1] : box.lower[1]; + Type maxy = boxes.count("up") > 0 ? intersection.upper[1] : box.upper[1]; + + if (intersection.lower[2] > box.lower[2]) + boxes["back"] = Box(copy(box.lower, _m{{0, minx}, {1, miny}}), + copy(intersection.lower - 1, _m{{0, maxx}, {1, maxy}})); + if (intersection.upper[2] < box.upper[2]) + boxes["front"] = Box(copy(intersection.upper + 1, _m{{0, minx}, {1, miny}}), + copy(box.upper, _m{{0, maxx}, {1, maxy}})); + } + + std::vector remaining; + for (auto const& [key, val] : boxes) + remaining.emplace_back(val); + return remaining; +} + +template +bool any_overlaps(Boxes const& boxes) +{ + for (std::size_t i = 0; i < boxes.size() - 1; ++i) + for (std::size_t j = i + 1; j < boxes.size(); ++j) + if (auto overlap = boxes[i] * boxes[j]) + return true; + return false; +} + +template +bool any_overlaps(Boxes const& boxes, Box const& box) +{ + for (std::size_t i = 0; i < boxes.size(); ++i) + if (auto overlap = boxes[i] * box) + return true; + return false; +} + +template +bool all_overlaps(Boxes const& boxes, Box const& box) +{ + std::uint32_t overlaps = 0; + for (std::size_t i = 0; i < boxes.size(); ++i) + if (auto overlap = boxes[i] * box) + ++overlaps; + return overlaps == boxes.size(); +} + +template +Boxes distinct_overlaps(Boxes& boxes) +{ + for (std::size_t i = 0; i < boxes.size() - 1; ++i) + { + auto const& a = boxes[i]; + + for (std::size_t j = i + 1; j < boxes.size(); ++j) + { + auto const& b = boxes[j]; + + if (auto overlap = a * b) + { + auto remaining0 = a.remove(b); + auto remaining1 = b.remove(a); + + boxes.insert(boxes.end(), remaining0.begin(), remaining0.end()); + boxes.insert(boxes.end(), remaining1.begin(), remaining1.end()); + + boxes[i] = *overlap; + boxes.erase(boxes.begin() + j); + + return distinct_overlaps(boxes); // dragons + } + } + } + + return boxes; +} + +template +auto distinct_overlaps(Boxes const& boxes, Box const& box) +{ + Boxes overlaps; + + for (std::size_t i = 0; i < boxes.size(); ++i) + if (auto overlap = boxes[i] * box) + overlaps.emplace_back(*overlap); + + return distinct_overlaps(overlaps); +} + + } // namespace PHARE::core + + #endif diff --git a/src/core/utilities/cellmap.hpp b/src/core/utilities/cellmap.hpp index 2a4e94a48..dce9fd40d 100644 --- a/src/core/utilities/cellmap.hpp +++ b/src/core/utilities/cellmap.hpp @@ -37,10 +37,10 @@ class CellMap { } - CellMap(CellMap const& from) = default; - CellMap(CellMap&& from) = default; + CellMap(CellMap const& from) = default; + CellMap(CellMap&& from) = default; CellMap& operator=(CellMap const& from) = default; - CellMap& operator=(CellMap&& from) = default; + CellMap& operator=(CellMap&& from) = default; auto nbr_cells() const { return cellIndexes_.size(); } @@ -78,8 +78,11 @@ class CellMap template void addToCell(CellIndex const& cell, std::size_t itemIndex); - static auto constexpr default_extractor = [](auto const& item) -> auto& { return item.iCell; }; - using DefaultExtractor = decltype(default_extractor); + static auto constexpr default_extractor = [](auto const& item) -> auto& + { + return item.iCell(); + }; + using DefaultExtractor = decltype(default_extractor); // same as above but cell is found with the CellExtractor @@ -260,7 +263,7 @@ template inline void CellMap::add(Array const& items, std::size_t itemIndex, CellExtractor extract) { - PHARE_LOG_SCOPE("CellMap::add (add 1 index)"); + // PHARE_LOG_SCOPE("CellMap::add (add 1 index)"); addToCell(extract(items[itemIndex]), itemIndex); } @@ -394,7 +397,7 @@ inline void CellMap::empty() template template inline void CellMap::update(Array& items, std::size_t itemIndex, - CellIndex const& oldCell, CellExtractor extract) + CellIndex const& oldCell, CellExtractor /*extract*/) { // we want to check first if the particle is in the map // already. if is, needs to remove it before inserting it again diff --git a/src/core/utilities/index/index.cpp b/src/core/utilities/index/index.cpp index b36629b9f..044d42916 100644 --- a/src/core/utilities/index/index.cpp +++ b/src/core/utilities/index/index.cpp @@ -7,9 +7,15 @@ namespace PHARE { namespace core { - MeshIndex<1> make_index(std::uint32_t i) { return MeshIndex<1>(i); } + MeshIndex<1> make_index(std::uint32_t i) + { + return MeshIndex<1>(i); + } - MeshIndex<2> make_index(std::uint32_t i, std::uint32_t j) { return MeshIndex<2>(i, j); } + MeshIndex<2> make_index(std::uint32_t i, std::uint32_t j) + { + return MeshIndex<2>(i, j); + } MeshIndex<3> make_index(std::uint32_t i, std::uint32_t j, std::uint32_t k) { diff --git a/src/core/utilities/indexer.hpp b/src/core/utilities/indexer.hpp index 802d048c8..1af1ca489 100644 --- a/src/core/utilities/indexer.hpp +++ b/src/core/utilities/indexer.hpp @@ -23,11 +23,11 @@ namespace PHARE::core class Indexer { public: - Indexer() = default; - Indexer(Indexer const& other) = default; - Indexer(Indexer&& other) = default; + Indexer() = default; + Indexer(Indexer const& other) = default; + Indexer(Indexer&& other) = default; Indexer& operator=(Indexer const& other) = default; - Indexer& operator=(Indexer&& other) = default; + Indexer& operator=(Indexer&& other) = default; void add(std::size_t itemIndex) { indexes_.push_back(itemIndex); } void remove(std::size_t itemIndex) diff --git a/src/core/utilities/iterators.hpp b/src/core/utilities/iterators.hpp new file mode 100644 index 000000000..f2faca53c --- /dev/null +++ b/src/core/utilities/iterators.hpp @@ -0,0 +1,51 @@ + +#ifndef PHARE_CORE_UTILITIES_ITERATORS_HPP +#define PHARE_CORE_UTILITIES_ITERATORS_HPP + +namespace PHARE::core +{ +template +using wrapped_iterator_base + = std::conditional_t; + + +template> +struct wrapped_iterator : public wrapped_iterator_base +{ + using outer_type = std::decay_t; + using value_type = typename Vector::value_type; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::size_t; + using Super = wrapped_iterator_base; + + + + wrapped_iterator operator+(std::int64_t i) + { + wrapped_iterator copy = *this; + static_cast(copy) += i; + return copy; + } + + auto& operator()() { return *container; } + auto& operator()() const { return *container; } + + + auto idx() const + { + auto val = std::distance(static_cast(container->begin()), + static_cast(*this)); + if (val < 0) + throw std::runtime_error("Invalid iterator distance"); + return static_cast(val); + } + + + T* container = nullptr; // might be const +}; + +} // namespace PHARE::core + + + +#endif /* PHARE_CORE_UTILITIES_ITERATORS_HPP */ diff --git a/src/core/utilities/partitionner/partitionner.hpp b/src/core/utilities/partitionner/partitionner.hpp index 989c63cef..261a1badd 100644 --- a/src/core/utilities/partitionner/partitionner.hpp +++ b/src/core/utilities/partitionner/partitionner.hpp @@ -5,8 +5,35 @@ #include "core/utilities/meta/meta_utilities.hpp" #include "core/utilities/box/box.hpp" +#include "core/utilities/range/range.hpp" #include "core/data/particles/particle.hpp" +namespace PHARE::core +{ +template +struct BoxRange +{ + using iterator = Iterator; + + BoxRange(Box box, Iterator begin, Iterator end) + : box_{box} + , first_{begin} + , end_{end} + { + } + + auto& begin() const { return first_; } + auto& end() const { return end_; } + auto& box() const { return box_; } + auto size() const { return std::distance(first_, end_); } + +private: + Box box_; + Iterator first_; + Iterator end_; +}; +} // namespace PHARE::core + namespace PHARE { namespace core @@ -31,19 +58,20 @@ namespace core * are leaving the patch but not through physical boundaries. * */ - template = dummy::value> - auto partitionner(ParticleIterator begin, ParticleIterator end, BoxContainer boxes) + template typename Vector, typename Box/*, + is_iterable = dummy::value*/> + auto partitionner(ParticleIterator begin, ParticleIterator end, Vector boxes) { - std::vector iterators; - iterators.push_back(begin); - auto pivot = begin; + std::vector> iterators; + auto prepivot = begin; + auto pivot = begin; for (auto const& box : boxes) { pivot = std::partition( pivot, end, [&box](auto const& part) { return isIn(cellAsPoint(part), box); }); - iterators.push_back(pivot); + iterators.emplace_back(box, prepivot, pivot); + prepivot = pivot; } return iterators; diff --git a/src/core/utilities/point/point.hpp b/src/core/utilities/point/point.hpp index b99f823c3..3929c5a8b 100644 --- a/src/core/utilities/point/point.hpp +++ b/src/core/utilities/point/point.hpp @@ -1,12 +1,14 @@ #ifndef PHARE_CORE_UTILITIES_POINT_POINT_HPP #define PHARE_CORE_UTILITIES_POINT_POINT_HPP -#include #include +#include +#include #include #include #include +#include "core/def.hpp" #include "core/utilities/meta/meta_utilities.hpp" namespace PHARE @@ -38,23 +40,30 @@ namespace core static constexpr std::size_t dimension = dim; using value_type = Type; + template - constexpr Point(Indexes... index) - : r{{index...}} + constexpr Point(std::tuple index) _PHARE_ALL_FN_ + : r{std::apply([](auto const&... args) { return std::array{args...}; }, + index)} { - allsame(index...); static_assert(sizeof...(Indexes) == dimension, "Error dimension does match number of arguments"); } - constexpr Point(std::array coords) - : r{std::move(coords)} + template + constexpr Point(Indexes... index) _PHARE_ALL_FN_ : r{{index...}} { + allsame(index...); + static_assert(sizeof...(Indexes) == dimension, + "Error dimension does match number of arguments"); } + + constexpr Point(std::array coords) _PHARE_ALL_FN_ : r{std::move(coords)} {} + template = dummy::value> - Point(Container c) + Point(Container c) _PHARE_ALL_FN_ { for (std::size_t i = 0; i < dim; ++i) { @@ -64,8 +73,8 @@ namespace core constexpr Point() { core::fill(Type{0}, r); } - auto& operator[](std::size_t i) { return r[i]; } - auto const& operator[](std::size_t i) const { return r[i]; } + auto& operator[](std::size_t i) _PHARE_ALL_FN_ { return r[i]; } + auto& operator[](std::size_t i) const _PHARE_ALL_FN_ { return r[i]; } bool operator==(Point const& p) const @@ -84,8 +93,8 @@ namespace core bool operator!=(Point const& other) const { return !(*this == other); } - template - auto toArray() const + template + auto toArray() const _PHARE_ALL_FN_ { std::array destArray; for (auto i = 0u; i < dimension; ++i) @@ -171,14 +180,20 @@ namespace core auto end() const { return r.end(); } + auto const& operator()() const { return r; } + + private: std::array r{}; }; - template + template), void>::type> Point(Indexes... indexes) -> Point>::type, sizeof...(indexes)>; + template auto& operator<<(std::ostream& os, Point const& p) { @@ -189,6 +204,7 @@ namespace core return os; } + } // namespace core } // namespace PHARE @@ -203,6 +219,14 @@ PHARE::core::Point abs(PHARE::core::Point const& point) return postive; } + +template +auto to_string(PHARE::core::Point const& point) +{ + return point.str(); +} + + } // namespace std diff --git a/src/core/utilities/range/range.hpp b/src/core/utilities/range/range.hpp index 3121eafff..eca23b945 100644 --- a/src/core/utilities/range/range.hpp +++ b/src/core/utilities/range/range.hpp @@ -11,12 +11,17 @@ namespace core template struct Range { + using iterator = Iterator; using iterator_category = typename Iterator::iterator_category; using value_type = typename Iterator::value_type; using difference_type = typename Iterator::difference_type; using reference = typename Iterator::reference; using pointer = typename Iterator::pointer; + Range() = default; + Range(Range&&) = default; + Range(Range const&) = default; + template explicit Range(Container const& c) : first_{std::begin(c)} @@ -60,9 +65,9 @@ namespace core } IndexRange& operator=(IndexRange const& from) = default; - IndexRange& operator=(IndexRange&& from) = default; - IndexRange(IndexRange&& from) = default; - IndexRange(IndexRange const& from) = default; + IndexRange& operator=(IndexRange&& from) = default; + IndexRange(IndexRange&& from) = default; + IndexRange(IndexRange const& from) = default; auto size() const { return end_ - first_; } auto ibegin() const { return first_; } @@ -82,6 +87,9 @@ namespace core // auto const& operator[](std::size_t idx) const { return (*array_)[first_ + idx]; } + auto& operator[](std::size_t i) { return *(first_ + i); } + auto& operator[](std::size_t i) const { return *(first_ + i); } + private: Index first_; Index end_; diff --git a/src/core/utilities/range/ranges.hpp b/src/core/utilities/range/ranges.hpp new file mode 100644 index 000000000..45efdebd5 --- /dev/null +++ b/src/core/utilities/range/ranges.hpp @@ -0,0 +1,105 @@ +#ifndef PHARE_CORE_UTILITITES_RANGES_HPP +#define PHARE_CORE_UTILITITES_RANGES_HPP + +#include +#include +#include +#include +#include + +#include "core/utilities/types.hpp" +#include "core/utilities/range/range.hpp" + +namespace PHARE::core +{ +template +auto ranges(Container& container, std::size_t batch_size, std::size_t offset = 0) +{ + std::size_t size = container.size() - offset; + std::size_t modulo = size % batch_size; + std::size_t n_ranges = size / batch_size; + std::vector> ranges; + ; + ranges.reserve(n_ranges + (modulo > 0 ? 1 : 0)); + auto begin = container.begin(); + for (std::size_t i = 0; i < n_ranges; ++i) + { + begin = container.begin() + offset + i * batch_size; + ranges.emplace_back(makeRange(begin, begin + batch_size)); + } + if (modulo > 0) + { + begin = container.begin() + offset + n_ranges * batch_size; + ranges.emplace_back(makeRange(begin, begin + modulo)); + } + return ranges; +} +} // namespace PHARE::core + +namespace PHARE::core::detail +{ +struct BalancedRangesDefaults +{ + auto static constexpr accessor = [](auto& el) -> auto& { return el; }; + using Accessor = decltype(accessor); + auto static constexpr builder = [](auto range, auto& el) { return range; }; + using Builder = decltype(builder); +}; +} // namespace PHARE::core::detail + + +namespace PHARE::core +{ +template +auto make_balanced_ranges(Container& container, std::size_t split = 10, + Accessor accessor = Defaults::accessor, + Builder builder = Defaults::builder) +{ + using Array = std::decay_t>; + using Range_t = Range; + using BuildRange_t + = std::decay_t>; + + auto arrays = core::generate([&](auto& el) { return &accessor(el); }, container); + + std::vector> ranges_vec(split); + std::size_t arr_idx = 0, offset = 0; + + auto load_ranges = [&](auto const t_idx, auto const size) { + auto rem = size; + while (rem > 0) + { + auto& arr = *arrays[arr_idx]; + auto range = makeRange(arr.begin() + offset, arr.end()); + auto op = rem >= range.size() ? range.size() : rem; + + ranges_vec[t_idx].emplace_back( + builder(makeRange(range.begin(), range.begin() + op), container[arr_idx])); + + offset += op; + if (rem >= range.size()) + { + ++arr_idx; + offset = 0; + } + rem -= op; + } + }; + + auto const total = sum_from(container, [&](auto const& el) { return accessor(el).size(); }); + std::size_t const each = total / split; + std::size_t const modulo = total % split; + + // give any remaining to index 0, assuming it's the main thread + load_ranges(0, each + modulo); + for (std::size_t i = 1; i < ranges_vec.size(); ++i) + load_ranges(i, each); + + return ranges_vec; +} + +} // namespace PHARE::core + +#endif // PHARE_CORE_UTILITITES_RANGES_HPP diff --git a/src/core/utilities/span.hpp b/src/core/utilities/span.hpp index c6b41cdb6..1ccc309c3 100644 --- a/src/core/utilities/span.hpp +++ b/src/core/utilities/span.hpp @@ -6,28 +6,33 @@ #include #include #include + +#include "core/logger.hpp" #include "core/utilities/types.hpp" namespace PHARE::core { -template +template struct Span { - using value_type = T; - - auto& operator[](SIZE i) { return ptr[i]; } - auto& operator[](SIZE i) const { return ptr[i]; } - T const* const& data() const { return ptr; } - T const* const& begin() const { return ptr; } - T* end() const { return ptr + s; } - SIZE const& size() const { return s; } - - T const* ptr = nullptr; + using value_type = std::decay_t; + + auto& operator[](SIZE i) _PHARE_ALL_FN_ { return ptr[i]; } + auto& operator[](SIZE i) const _PHARE_ALL_FN_ { return ptr[i]; } + auto data() const _PHARE_ALL_FN_ { return ptr; } + auto data() _PHARE_ALL_FN_ { return ptr; } + auto begin() _PHARE_ALL_FN_ { return ptr; } + auto begin() const _PHARE_ALL_FN_ { return ptr; } + auto end() _PHARE_ALL_FN_ { return ptr + s; } + auto end() const _PHARE_ALL_FN_ { return ptr + s; } + SIZE const& size() const _PHARE_ALL_FN_ { return s; } + + T* const ptr = nullptr; SIZE s = 0; }; -template +template class VectorSpan : private StackVar>, public core::Span { using Vector = StackVar>; @@ -53,7 +58,7 @@ class VectorSpan : private StackVar>, public core::Span -template +template struct SpanSet { using value_type = T; @@ -77,12 +82,14 @@ struct SpanSet { } + Span operator[](SIZE i) { return {this->vec.data() + displs[i], this->sizes[i]}; } Span operator[](SIZE i) const { return {this->vec.data() + displs[i], this->sizes[i]}; } T* data() const { return const_cast(vec.data()); } + T* data() { return const_cast(vec.data()); } struct iterator { @@ -95,7 +102,7 @@ struct SpanSet curr_pos += sv->sizes[curr_ptr++]; return *this; } - bool operator!=(iterator const& other) const { return curr_ptr != sv->sizes.size(); } + bool operator!=(iterator const& /*other*/) const { return curr_ptr != sv->sizes.size(); } Span operator*() const { return {sv->vec.data() + curr_pos, sv->sizes[curr_ptr]}; } SpanSet_* sv = nullptr; @@ -113,6 +120,29 @@ struct SpanSet std::vector displs; std::vector vec; }; + + + + +template +auto flatten(std::vector> const& data) +{ + assert(data.size() > 0); + + return Span{data.data()->data(), data.size() * size}; +} + + +template +auto flatten(std::vector>& data) +{ + assert(data.size() > 0); + + return Span{data.data()->data(), data.size() * size}; +} + + + } // namespace PHARE::core #endif // PHARE_CORE_UTILITIES_SPAN_HPP diff --git a/src/core/utilities/thread_pool.hpp b/src/core/utilities/thread_pool.hpp new file mode 100644 index 000000000..4460e54ef --- /dev/null +++ b/src/core/utilities/thread_pool.hpp @@ -0,0 +1,544 @@ +#pragma once + +/** + * @file thread_pool.hpp + * @author Barak Shoshany (baraksh@gmail.com) (http://baraksh.com) + * @version 2.0.0 + * @date 2021-08-14 + * @copyright Copyright (c) 2021 Barak Shoshany. Licensed under the MIT license. If you use this library in published research, please cite it as follows: + * - Barak Shoshany, "A C++17 Thread Pool for High-Performance Scientific Computing", doi:10.5281/zenodo.4742687, arXiv:2105.00613 (May 2021) + * + * @brief A C++17 thread pool for high-performance scientific computing. + * @details A modern C++17-compatible thread pool implementation, built from scratch with high-performance scientific computing in mind. The thread pool is implemented as a single lightweight and self-contained class, and does not have any dependencies other than the C++17 standard library, thus allowing a great degree of portability. In particular, this implementation does not utilize OpenMP or any other high-level multithreading APIs, and thus gives the programmer precise low-level control over the details of the parallelization, which permits more robust optimizations. The thread pool was extensively tested on both AMD and Intel CPUs with up to 40 cores and 80 threads. Other features include automatic generation of futures and easy parallelization of loops. Two helper classes enable synchronizing printing to an output stream by different threads and measuring execution time for benchmarking purposes. Please visit the GitHub repository at https://github.com/bshoshany/thread-pool for documentation and updates, or to submit feature requests and bug reports. + */ + +#define THREAD_POOL_VERSION "v2.0.0 (2021-08-14)" + +#include // std::atomic +#include // std::chrono +#include // std::int_fast64_t, std::uint_fast32_t +#include // std::function +#include // std::future, std::promise +#include // std::cout, std::ostream +#include // std::shared_ptr, std::unique_ptr +#include // std::mutex, std::scoped_lock +#include // std::queue +#include // std::this_thread, std::thread +#include // std::common_type_t, std::decay_t, std::enable_if_t, std::is_void_v, std::invoke_result_t +#include // std::move + +// ============================================================================================= // +// Begin class thread_pool // + +/** + * @brief A C++17 thread pool class. The user submits tasks to be executed into a queue. Whenever a thread becomes available, it pops a task from the queue and executes it. Each task is automatically assigned a future, which can be used to wait for the task to finish executing and/or obtain its eventual return value. + */ +class thread_pool +{ + typedef std::uint_fast32_t ui32; + typedef std::uint_fast64_t ui64; + +public: + // ============================ + // Constructors and destructors + // ============================ + + /** + * @brief Construct a new thread pool. + * + * @param _thread_count The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. With a hyperthreaded CPU, this will be twice the number of CPU cores. If the argument is zero, the default value will be used instead. + */ + thread_pool(const ui32 &_thread_count = std::thread::hardware_concurrency()) + : thread_count(_thread_count ? _thread_count : std::thread::hardware_concurrency()), threads(new std::thread[_thread_count ? _thread_count : std::thread::hardware_concurrency()]) + { + create_threads(); + } + + /** + * @brief Destruct the thread pool. Waits for all tasks to complete, then destroys all threads. Note that if the variable paused is set to true, then any tasks still in the queue will never be executed. + */ + ~thread_pool() + { + wait_for_tasks(); + running = false; + destroy_threads(); + } + + // ======================= + // Public member functions + // ======================= + + /** + * @brief Get the number of tasks currently waiting in the queue to be executed by the threads. + * + * @return The number of queued tasks. + */ + ui64 get_tasks_queued() const + { + const std::scoped_lock lock(queue_mutex); + return tasks.size(); + } + + /** + * @brief Get the number of tasks currently being executed by the threads. + * + * @return The number of running tasks. + */ + ui32 get_tasks_running() const + { + return tasks_total - (ui32)get_tasks_queued(); + } + + /** + * @brief Get the total number of unfinished tasks - either still in the queue, or running in a thread. + * + * @return The total number of tasks. + */ + ui32 get_tasks_total() const + { + return tasks_total; + } + + /** + * @brief Get the number of threads in the pool. + * + * @return The number of threads. + */ + ui32 get_thread_count() const + { + return thread_count; + } + + /** + * @brief Parallelize a loop by splitting it into blocks, submitting each block separately to the thread pool, and waiting for all blocks to finish executing. The user supplies a loop function, which will be called once per block and should iterate over the block's range. + * + * @tparam T1 The type of the first index in the loop. Should be a signed or unsigned integer. + * @tparam T2 The type of the index after the last index in the loop. Should be a signed or unsigned integer. If T1 is not the same as T2, a common type will be automatically inferred. + * @tparam F The type of the function to loop through. + * @param first_index The first index in the loop. + * @param index_after_last The index after the last index in the loop. The loop will iterate from first_index to (index_after_last - 1) inclusive. In other words, it will be equivalent to "for (T i = first_index; i < index_after_last; i++)". Note that if first_index == index_after_last, the function will terminate without doing anything. + * @param loop The function to loop through. Will be called once per block. Should take exactly two arguments: the first index in the block and the index after the last index in the block. loop(start, end) should typically involve a loop of the form "for (T i = start; i < end; i++)". + * @param num_blocks The maximum number of blocks to split the loop into. The default is to use the number of threads in the pool. + */ + template + void parallelize_loop(const T1 &first_index, const T2 &index_after_last, const F &loop, ui32 num_blocks = 0) + { + typedef std::common_type_t T; + T the_first_index = (T)first_index; + T last_index = (T)index_after_last; + if (the_first_index == last_index) + return; + if (last_index < the_first_index) + { + T temp = last_index; + last_index = the_first_index; + the_first_index = temp; + } + last_index--; + if (num_blocks == 0) + num_blocks = thread_count; + ui64 total_size = (ui64)(last_index - the_first_index + 1); + ui64 block_size = (ui64)(total_size / num_blocks); + if (block_size == 0) + { + block_size = 1; + num_blocks = (ui32)total_size > 1 ? (ui32)total_size : 1; + } + std::atomic blocks_running = 0; + for (ui32 t = 0; t < num_blocks; t++) + { + T start = ((T)(t * block_size) + the_first_index); + T end = (t == num_blocks - 1) ? last_index + 1 : ((T)((t + 1) * block_size) + the_first_index); + blocks_running++; + push_task([start, end, &loop, &blocks_running] + { + loop(start, end); + blocks_running--; + }); + } + while (blocks_running != 0) + { + sleep_or_yield(); + } + } + + /** + * @brief Push a function with no arguments or return value into the task queue. + * + * @tparam F The type of the function. + * @param task The function to push. + */ + template + void push_task(const F &task) + { + tasks_total++; + { + const std::scoped_lock lock(queue_mutex); + tasks.push(std::function(task)); + } + } + + /** + * @brief Push a function with arguments, but no return value, into the task queue. + * @details The function is wrapped inside a lambda in order to hide the arguments, as the tasks in the queue must be of type std::function, so they cannot have any arguments or return value. If no arguments are provided, the other overload will be used, in order to avoid the (slight) overhead of using a lambda. + * + * @tparam F The type of the function. + * @tparam A The types of the arguments. + * @param task The function to push. + * @param args The arguments to pass to the function. + */ + template + void push_task(const F &task, const A &...args) + { + push_task([task, args...] + { task(args...); }); + } + + /** + * @brief Reset the number of threads in the pool. Waits for all currently running tasks to be completed, then destroys all threads in the pool and creates a new thread pool with the new number of threads. Any tasks that were waiting in the queue before the pool was reset will then be executed by the new threads. If the pool was paused before resetting it, the new pool will be paused as well. + * + * @param _thread_count The number of threads to use. The default value is the total number of hardware threads available, as reported by the implementation. With a hyperthreaded CPU, this will be twice the number of CPU cores. If the argument is zero, the default value will be used instead. + */ + void reset(const ui32 &_thread_count = std::thread::hardware_concurrency()) + { + bool was_paused = paused; + paused = true; + wait_for_tasks(); + running = false; + destroy_threads(); + thread_count = _thread_count ? _thread_count : std::thread::hardware_concurrency(); + threads.reset(new std::thread[thread_count]); + paused = was_paused; + running = true; + create_threads(); + } + + /** + * @brief Submit a function with zero or more arguments and no return value into the task queue, and get an std::future that will be set to true upon completion of the task. + * + * @tparam F The type of the function. + * @tparam A The types of the zero or more arguments to pass to the function. + * @param task The function to submit. + * @param args The zero or more arguments to pass to the function. + * @return A future to be used later to check if the function has finished its execution. + */ + template , std::decay_t...>>>> + std::future submit(const F &task, const A &...args) + { + std::shared_ptr> task_promise(new std::promise); + std::future future = task_promise->get_future(); + push_task([task, args..., task_promise] + { + try + { + task(args...); + task_promise->set_value(true); + } + catch (...) + { + try + { + task_promise->set_exception(std::current_exception()); + } + catch (...) + { + } + } + }); + return future; + } + + /** + * @brief Submit a function with zero or more arguments and a return value into the task queue, and get a future for its eventual returned value. + * + * @tparam F The type of the function. + * @tparam A The types of the zero or more arguments to pass to the function. + * @tparam R The return type of the function. + * @param task The function to submit. + * @param args The zero or more arguments to pass to the function. + * @return A future to be used later to obtain the function's returned value, waiting for it to finish its execution if needed. + */ + template , std::decay_t...>, typename = std::enable_if_t>> + std::future submit(const F &task, const A &...args) + { + std::shared_ptr> task_promise(new std::promise); + std::future future = task_promise->get_future(); + push_task([task, args..., task_promise] + { + try + { + task_promise->set_value(task(args...)); + } + catch (...) + { + try + { + task_promise->set_exception(std::current_exception()); + } + catch (...) + { + } + } + }); + return future; + } + + /** + * @brief Wait for tasks to be completed. Normally, this function waits for all tasks, both those that are currently running in the threads and those that are still waiting in the queue. However, if the variable paused is set to true, this function only waits for the currently running tasks (otherwise it would wait forever). To wait for a specific task, use submit() instead, and call the wait() member function of the generated future. + */ + void wait_for_tasks() + { + while (true) + { + if (!paused) + { + if (tasks_total == 0) + break; + } + else + { + if (get_tasks_running() == 0) + break; + } + sleep_or_yield(); + } + } + + // =========== + // Public data + // =========== + + /** + * @brief An atomic variable indicating to the workers to pause. When set to true, the workers temporarily stop popping new tasks out of the queue, although any tasks already executed will keep running until they are done. Set to false again to resume popping tasks. + */ + std::atomic paused = false; + + /** + * @brief The duration, in microseconds, that the worker function should sleep for when it cannot find any tasks in the queue. If set to 0, then instead of sleeping, the worker function will execute std::this_thread::yield() if there are no tasks in the queue. The default value is 1000. + */ + ui32 sleep_duration = 1000; + +private: + // ======================== + // Private member functions + // ======================== + + /** + * @brief Create the threads in the pool and assign a worker to each thread. + */ + void create_threads() + { + for (ui32 i = 0; i < thread_count; i++) + { + threads[i] = std::thread(&thread_pool::worker, this); + } + } + + /** + * @brief Destroy the threads in the pool by joining them. + */ + void destroy_threads() + { + for (ui32 i = 0; i < thread_count; i++) + { + threads[i].join(); + } + } + + /** + * @brief Try to pop a new task out of the queue. + * + * @param task A reference to the task. Will be populated with a function if the queue is not empty. + * @return true if a task was found, false if the queue is empty. + */ + bool pop_task(std::function &task) + { + const std::scoped_lock lock(queue_mutex); + if (tasks.empty()) + return false; + else + { + task = std::move(tasks.front()); + tasks.pop(); + return true; + } + } + + /** + * @brief Sleep for sleep_duration microseconds. If that variable is set to zero, yield instead. + * + */ + void sleep_or_yield() + { + if (sleep_duration) + std::this_thread::sleep_for(std::chrono::microseconds(sleep_duration)); + else + std::this_thread::yield(); + } + + /** + * @brief A worker function to be assigned to each thread in the pool. Continuously pops tasks out of the queue and executes them, as long as the atomic variable running is set to true. + */ + void worker() + { + while (running) + { + std::function task; + if (!paused && pop_task(task)) + { + task(); + tasks_total--; + } + else + { + sleep_or_yield(); + } + } + } + + // ============ + // Private data + // ============ + + /** + * @brief A mutex to synchronize access to the task queue by different threads. + */ + mutable std::mutex queue_mutex = {}; + + /** + * @brief An atomic variable indicating to the workers to keep running. When set to false, the workers permanently stop working. + */ + std::atomic running = true; + + /** + * @brief A queue of tasks to be executed by the threads. + */ + std::queue> tasks = {}; + + /** + * @brief The number of threads in the pool. + */ + ui32 thread_count; + + /** + * @brief A smart pointer to manage the memory allocated for the threads. + */ + std::unique_ptr threads; + + /** + * @brief An atomic variable to keep track of the total number of unfinished tasks - either still in the queue, or running in a thread. + */ + std::atomic tasks_total = 0; +}; + +// End class thread_pool // +// ============================================================================================= // + +// ============================================================================================= // +// Begin class synced_stream // + +/** + * @brief A helper class to synchronize printing to an output stream by different threads. + */ +class synced_stream +{ +public: + /** + * @brief Construct a new synced stream. + * + * @param _out_stream The output stream to print to. The default value is std::cout. + */ + synced_stream(std::ostream &_out_stream = std::cout) + : out_stream(_out_stream){}; + + /** + * @brief Print any number of items into the output stream. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use this synced_stream object to print. + * + * @tparam T The types of the items + * @param items The items to print. + */ + template + void print(const T &...items) + { + const std::scoped_lock lock(stream_mutex); + (out_stream << ... << items); + } + + /** + * @brief Print any number of items into the output stream, followed by a newline character. Ensures that no other threads print to this stream simultaneously, as long as they all exclusively use this synced_stream object to print. + * + * @tparam T The types of the items + * @param items The items to print. + */ + template + void println(const T &...items) + { + print(items..., '\n'); + } + +private: + /** + * @brief A mutex to synchronize printing. + */ + mutable std::mutex stream_mutex = {}; + + /** + * @brief The output stream to print to. + */ + std::ostream &out_stream; +}; + +// End class synced_stream // +// ============================================================================================= // + +// ============================================================================================= // +// Begin class timer // + +/** + * @brief A helper class to measure execution time for benchmarking purposes. + */ +class timer +{ + typedef std::int_fast64_t i64; + +public: + /** + * @brief Start (or restart) measuring time. + */ + void start() + { + start_time = std::chrono::steady_clock::now(); + } + + /** + * @brief Stop measuring time and store the elapsed time since start(). + */ + void stop() + { + elapsed_time = std::chrono::steady_clock::now() - start_time; + } + + /** + * @brief Get the number of milliseconds that have elapsed between start() and stop(). + * + * @return The number of milliseconds. + */ + i64 ms() const + { + return (std::chrono::duration_cast(elapsed_time)).count(); + } + +private: + /** + * @brief The time point when measuring started. + */ + std::chrono::time_point start_time = std::chrono::steady_clock::now(); + + /** + * @brief The duration that has elapsed between start() and stop(). + */ + std::chrono::duration elapsed_time = std::chrono::duration::zero(); +}; + +// End class timer // +// ============================================================================================= // diff --git a/src/core/utilities/types.hpp b/src/core/utilities/types.hpp index cd623a4b7..1bcfd54fe 100644 --- a/src/core/utilities/types.hpp +++ b/src/core/utilities/types.hpp @@ -13,17 +13,10 @@ #include #include - #include "cppdict/include/dict.hpp" -#if !defined(NDEBUG) || defined(PHARE_FORCE_DEBUG_DO) -#define PHARE_DEBUG_DO(...) __VA_ARGS__ -#else -#define PHARE_DEBUG_DO(...) -#endif +#include "core/def.hpp" -#define _PHARE_TO_STR(x) #x // convert macro text to string -#define PHARE_TO_STR(x) _PHARE_TO_STR(x) namespace PHARE { @@ -169,6 +162,15 @@ namespace core return arr; } + template + constexpr auto ConstArrayFrom(FN fn) + { + std::array arr{}; + for (uint8_t i = 0; i < size; i++) + arr[i] = fn(); + return arr; + } + template std::vector displacementFrom(std::vector const& input) { @@ -187,6 +189,13 @@ namespace core { using value_type = T; + + template + StackVar(Args&&... args) + : var(std::forward(args...)) + { + } + T var; }; @@ -232,29 +241,46 @@ namespace core return std::string{val}; return std::nullopt; } - inline std::optional get_env(std::string&& key) { return get_env(key); } + inline std::optional get_env(std::string&& key) + { + return get_env(key); + } + + } // namespace core } // namespace PHARE - namespace PHARE::core { template -Multiplies product(Container const& container, Multiplies mul = 1) +Multiplies product(Container const& container, Multiplies mul = 1) _PHARE_ALL_FN_ { - return std::accumulate(container.begin(), container.end(), mul, std::multiplies()); + // std accumulate doesn't exist on GPU + for (auto const& v : container) + mul *= v; + return mul; +} + +template +auto sum_from(Container const& container, F fn) +{ + using value_type = typename Container::value_type; + using return_type = std::decay_t>; + return_type sum = 0; + for (auto const& el : container) + sum += fn(el); + return sum; } template -Return sum(Container const& container, Return r = 0) +Return sum(Container const& container, Return r = 0) _PHARE_HST_FN_ { return std::accumulate(container.begin(), container.end(), r); } - template auto generate(F&& f, std::size_t from, std::size_t to) { @@ -269,45 +295,59 @@ auto generate(F&& f, std::size_t from, std::size_t to) return v; } -template -auto generate(F&& f, std::size_t count) + +template +auto& deref(Type& type) _PHARE_ALL_FN_ { - return generate(std::forward(f), 0, count); + if constexpr (std::is_pointer_v) + return *type; + else + return type; } -template -auto generate(F&& f, Container& container) +template +auto generate(F&& f, std::size_t count) { - using T = typename Container::value_type; - using value_type = std::decay_t>; - std::vector v1; - if (container.size() > 0) - v1.reserve(container.size()); - for (auto& v : container) - v1.emplace_back(f(v)); - return v1; + return generate(std::forward(f), 0, count); } + template auto generate(F&& f, std::vector&& v) { return generate(std::forward(f), v); } + template auto constexpr generate_array__(F& f, std::array& arr) { return f(arr[Idx]); } - template auto constexpr generate_array_(F& f, std::array& arr, std::integer_sequence) { return std::array{generate_array__(f, arr)...}; } +template +auto constexpr generate(F&& f, std::array& arr) +{ + return generate_array_(f, arr, std::make_integer_sequence{}); +} +template +auto constexpr generate_array__(F& f, std::array const& arr) +{ + return f(arr[Idx]); +} +template +auto constexpr generate_array_(F& f, std::array const& arr, + std::integer_sequence) +{ + return std::array{generate_array__(f, arr)...}; +} template auto constexpr generate(F&& f, std::array const& arr) { @@ -315,6 +355,35 @@ auto constexpr generate(F&& f, std::array const& arr) } +// // template // general case +// // auto generate(F&& f, Container& container) +// template +// auto generate(F&& f, std::vector const& v0) +// { +// using T = typename Container::value_type; +// using value_type = std::decay_t>; +// std::vector v1; +// if (v0.size() > 0) +// v1.reserve(v0.size()); +// for (auto& v : v0) +// v1.emplace_back(f(v)); +// return v1; +// } + +template +auto generate(F&& f, Container& container) +{ + using T = typename Container::value_type; + using value_type = std::decay_t>; + std::vector vec; + vec.reserve(container.size()); + for (auto& v : container) + vec.emplace_back(f(v)); + return vec; +} + + + // calls operator bool() or copies bool auto constexpr static to_bool = [](auto const& v) { return bool{v}; }; @@ -339,6 +408,52 @@ auto none(Container const& container) +void inline abort_if(bool b) +{ + if (b) + std::abort(); +} + + + + +template +struct Apply +{ + template + constexpr auto operator()() + { + return std::integral_constant{}; + } +}; + +template +constexpr auto apply_N(Apply& f, std::integer_sequence const&) +{ + if constexpr (!std::is_same_v()), void>) + return std::make_tuple(f.template operator()()...); + (f.template operator()(), ...); +} +template +constexpr auto apply_N(Apply&& f) +{ + return apply_N(f, std::make_integer_sequence{}); +} + +template +constexpr void for_N(Fn&& fn) +{ + /* + for_N<2>([](auto ic) { + constexpr auto i = ic(); + // ... + }); + */ + + std::apply([&](auto... ics) { (fn(ics), ...); }, apply_N(Apply{})); +} + + } // namespace PHARE::core diff --git a/src/core/vector.hpp b/src/core/vector.hpp new file mode 100644 index 000000000..9b392d490 --- /dev/null +++ b/src/core/vector.hpp @@ -0,0 +1,227 @@ +#ifndef PHARE_CORE_VECTOR_HPP +#define PHARE_CORE_VECTOR_HPP + +#include + +#include "core/def.hpp" +#include "core/def/detail/umpire.hpp" +#include "core/def/detail/raja.hpp" // checks for umpire to know if gpu +#include "core/def/detail/mkn_gpu.hpp" + +namespace PHARE +{ +struct CompileOptions +{ + static constexpr bool WithUmpire = PHARE_HAVE_UMPIRE; + static constexpr bool WithMknGpu = PHARE_HAVE_MKN_GPU; + static constexpr bool WithRAJA = PHARE_HAVE_RAJA; +}; + +enum class AllocatorMode { + CPU, + GPU, +}; + +template +bool constexpr allocator_mode_supported() +{ + if constexpr (allocator_mode == AllocatorMode::CPU) + return true; + else if constexpr (allocator_mode == AllocatorMode::GPU) + return PHARE_HAVE_MKN_GPU or PHARE_HAVE_UMPIRE; + return false; +} + +template +auto constexpr allocator() +{ + static_assert(std::is_same_v); + static_assert(allocator_mode_supported()); + + if constexpr (allocator_mode == AllocatorMode::CPU) + return typename std::vector::allocator_type{}; + else if constexpr (allocator_mode == AllocatorMode::GPU) + { +#if PHARE_HAVE_MKN_GPU + return mkn::gpu::ManagedAllocator{}; +#elif PHARE_HAVE_UMPIRE + return umpire::TypedAllocator{}; +#endif + // or compile error + } + throw std::runtime_error("NOOO"); +} + +} // namespace PHARE + + +namespace PHARE +{ +class Allocator +{ +public: +#if PHARE_HAVE_MKN_GPU + template + using allocator_type = mkn::gpu::ManagedAllocator; +#elif PHARE_HAVE_UMPIRE + template + using allocator_type = umpire::TypedAllocator; +#else + template + using allocator_type = typename std::vector::allocator_type; +#endif + + template> + static constexpr bool is_host_mem() + { + return std::is_same_v::allocator_type>; + } + + template> + static auto make_allocator() + { + if constexpr (is_host_mem()) + { + return Allocator_t{}; + } + else + { + if constexpr (CompileOptions::WithMknGpu) + { + PHARE_WITH_MKN_GPU(return Allocator_t{}); + } + else + { + PHARE_WITH_UMPIRE(return Allocator_t{ + umpire::ResourceManager::getInstance().getAllocator("PHARE::data_allocator")}); + } + } + } +}; + +template +struct Vector +{ + using value_type = Type; + using allocator_type = typename Allocator::allocator_type; + + + template + static auto make(std::size_t size, bool reserve = false) + { + std::vector vec{Allocator::make_allocator()}; + if (size) + { + if (reserve) + vec.reserve(size); + else + vec.resize(size); + } + return vec; + } + + + static auto make(std::size_t size, Type const& val) + { + auto vec = make(size); + fill(vec, val); + return vec; + } + + + template + static void copy(std::vector& dst, std::vector const& src) + { + if (dst.size() != src.size()) + dst.resize(src.size()); + + if constexpr (Allocator::is_host_mem() + and Allocator::is_host_mem()) + { + dst = src; + } + else + { + if constexpr (CompileOptions::WithRAJA) + { + PHARE_WITH_RAJA(PHARE::core::raja::copy(dst.data(), src.data(), src.size())); + } + else if (CompileOptions::WithMknGpu) + { + PHARE_WITH_MKN_GPU(mkn::gpu::copy(dst.data(), src.data(), src.size())); + } + else + throw std::runtime_error("Vector::copy NO ALTERNATIVE"); + } + } + + + template + static void move(std::vector& dst, std::vector& src) + { + if constexpr (Allocator::is_host_mem() + and Allocator::is_host_mem()) + { + dst = std::move(src); + } + else + { + copy(dst, src); + } + } + + template + static auto from(std::vector const& that) + { + if constexpr (Allocator::is_host_mem()) + { + return that; + } + else + { + // allocations shouldn't happen on GPU, so assume CPU + std::vector dst{that.size()}; + copy(dst, that); + return dst; + } + } + + template + static auto from(std::vector&& that) + { + if constexpr (Allocator::is_host_mem()) + { + return std::move(that); + } + else + { + return from(that); + } + } + + + template + static auto fill(std::vector& vec, Type const& val) + { + if constexpr (Allocator::is_host_mem()) + { + std::fill(vec.begin(), vec.end(), val); + } + else + { + if constexpr (CompileOptions::WithRAJA) + { + PHARE_WITH_RAJA(PHARE::core::raja::set(vec.data(), val, vec.size())); + } + else if (CompileOptions::WithMknGpu) + { + PHARE_WITH_MKN_GPU(mkn::gpu::fill(vec, val)); + } + else + throw std::runtime_error("Vector::fill NO ALTERNATIVE"); + } + } +}; +} // namespace PHARE + +#endif /* PHARE_CORE_VECTOR_HPP */ diff --git a/src/diagnostic/detail/types/particle.hpp b/src/diagnostic/detail/types/particle.hpp index d6817ccef..08dadf4f4 100644 --- a/src/diagnostic/detail/types/particle.hpp +++ b/src/diagnostic/detail/types/particle.hpp @@ -13,6 +13,8 @@ #include #include + + namespace PHARE::diagnostic::h5 { /* @@ -39,8 +41,9 @@ class ParticlesDiagnosticWriter : public H5TypeWriter static constexpr auto dimension = H5Writer::dimension; static constexpr auto interpOrder = H5Writer::interpOrder; using Attributes = typename Super::Attributes; - using Packer = core::ParticlePacker; - using FloatType = typename H5Writer::FloatType; + // using Particle_t = typename core::PHARE_Types::Particle_t; + using Packer = core::ParticlePacker; + using FloatType = typename H5Writer::FloatType; ParticlesDiagnosticWriter(H5Writer& h5Writer) : Super{h5Writer} @@ -62,6 +65,10 @@ class ParticlesDiagnosticWriter : public H5TypeWriter DiagnosticProperties&, Attributes&, std::unordered_map>>&, std::size_t maxLevel) override; + +private: + // PGI compiler (nvc++ 21.3-0) doesn't like static initializations of arrays + std::array packer_keys_ = core::packer_keys(); }; @@ -137,8 +144,7 @@ void ParticlesDiagnosticWriter::initDataSets( std::string path{h5Writer_.getPatchPathAddTimestamp(lvl, patchID) + "/"}; std::size_t part_idx = 0; core::apply(Packer::empty(), [&](auto const& arg) { - createDataSet(path + Packer::keys()[part_idx], attr, Packer::keys()[part_idx], arg, - null); + createDataSet(path + packer_keys_[part_idx], attr, packer_keys_[part_idx], arg, null); ++part_idx; }); this->writeGhostsAttr_(h5file, path, core::ghostWidthForParticles(), null); @@ -168,6 +174,7 @@ void ParticlesDiagnosticWriter::write(DiagnosticProperties& diagnostic { auto& h5Writer = this->h5Writer_; + auto checkWrite = [&](auto& tree, auto pType, auto& ps) { std::string active{tree + pType}; if (diagnostic.quantity == active && ps.size() > 0) @@ -211,7 +218,7 @@ void ParticlesDiagnosticWriter::writeAttributes( writeAttributes_(diagnostic, h5file, fileAttributes, patchAttributes, maxLevel); } - } // namespace PHARE::diagnostic::h5 + #endif /* PHARE_DIAGNOSTIC_DETAIL_TYPES_PARTICLE_H */ diff --git a/src/diagnostic/diagnostic_manager.hpp b/src/diagnostic/diagnostic_manager.hpp index 17c1dfee4..b905eb591 100644 --- a/src/diagnostic/diagnostic_manager.hpp +++ b/src/diagnostic/diagnostic_manager.hpp @@ -91,10 +91,10 @@ class DiagnosticsManager : public IDiagnosticsManager Writer& writer() { return *writer_.get(); } - DiagnosticsManager(DiagnosticsManager const&) = delete; - DiagnosticsManager(DiagnosticsManager&&) = delete; + DiagnosticsManager(DiagnosticsManager const&) = delete; + DiagnosticsManager(DiagnosticsManager&&) = delete; DiagnosticsManager& operator=(DiagnosticsManager const&) = delete; - DiagnosticsManager& operator=(DiagnosticsManager&&) = delete; + DiagnosticsManager& operator=(DiagnosticsManager&&) = delete; private: bool needsAction_(double nextTime, double timeStamp, double timeStep) diff --git a/src/hdf5/detail/h5/h5_file.hpp b/src/hdf5/detail/h5/h5_file.hpp index 5c7304bcb..84ed79d7d 100644 --- a/src/hdf5/detail/h5/h5_file.hpp +++ b/src/hdf5/detail/h5/h5_file.hpp @@ -83,7 +83,10 @@ class HighFiveFile ~HighFiveFile() {} - HiFile& file() { return h5file_; } + HiFile& file() + { + return h5file_; + } template @@ -218,9 +221,9 @@ class HighFiveFile } - HighFiveFile(const HighFiveFile&) = delete; - HighFiveFile(const HighFiveFile&&) = delete; - HighFiveFile& operator=(const HighFiveFile&) = delete; + HighFiveFile(const HighFiveFile&) = delete; + HighFiveFile(const HighFiveFile&&) = delete; + HighFiveFile& operator=(const HighFiveFile&) = delete; HighFiveFile& operator=(const HighFiveFile&&) = delete; private: diff --git a/src/hdf5/writer/particle_writer.hpp b/src/hdf5/writer/particle_writer.hpp index fc49e1048..06240b8fb 100644 --- a/src/hdf5/writer/particle_writer.hpp +++ b/src/hdf5/writer/particle_writer.hpp @@ -18,18 +18,29 @@ class ParticleWriter template static void write(H5File& h5file, Particles const& particles, std::string const& path) { - auto constexpr dim = Particles::dimension; - using Packer = core::ParticlePacker; - - Packer packer(particles); - core::ContiguousParticles copy{particles.size()}; - packer.pack(copy); - - std::size_t part_idx = 0; - core::apply(copy.as_tuple(), [&](auto const& arg) { - auto data_path = path + packer.keys()[part_idx++]; - h5file.template write_data_set_flat<2>(data_path, arg.data()); - }); + auto constexpr dim = Particles::dimension; + using Packer = core::ParticlePacker; + auto static const packer_keys = Packer::keys(); + + auto write_ = [&](auto& soa) { + h5file.template write_data_set_flat<2>(path + packer_keys[0], soa.weight().data()); + h5file.template write_data_set_flat<2>(path + packer_keys[1], soa.charge().data()); + h5file.template write_data_set(path + packer_keys[2], soa.iCell().data()); + h5file.template write_data_set(path + packer_keys[3], soa.delta().data()); + h5file.template write_data_set(path + packer_keys[4], soa.v().data()); + }; + + if constexpr (Particles::is_contiguous) + { + write_(particles); + } + else + { + Packer packer(particles); + core::SoAParticleArray copy{particles.size()}; + packer.pack(copy); + write_(copy); + } } diff --git a/src/initializer/CMakeLists.txt b/src/initializer/CMakeLists.txt index 9e4a334f6..bcf70e21c 100644 --- a/src/initializer/CMakeLists.txt +++ b/src/initializer/CMakeLists.txt @@ -15,6 +15,7 @@ set(PYBIND11_CPP_STANDARD -std=c++17) add_library(${PROJECT_NAME} SHARED ${SOURCE_CPP} ${SOURCE_INC}) target_compile_options(${PROJECT_NAME} PRIVATE ${PHARE_WERROR_FLAGS}) +target_link_libraries(${PROJECT_NAME} PUBLIC ${PHARE_BASE_LIBS}) set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION ${PHARE_INTERPROCEDURAL_OPTIMIZATION}) @@ -24,7 +25,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC # this is the python module that wraps the add() method pybind11_add_module(dictator dictator.cpp) -target_link_libraries(dictator PUBLIC phare_initializer pybind11::embed) +target_link_libraries(dictator PUBLIC phare_initializer pybind11::embed ${PHARE_BASE_LIBS}) set_property(TARGET dictator PROPERTY INTERPROCEDURAL_OPTIMIZATION ${PHARE_INTERPROCEDURAL_OPTIMIZATION}) set_target_properties(dictator PROPERTIES diff --git a/src/initializer/pragma_disable.hpp b/src/initializer/pragma_disable.hpp index bd154ca06..27874f90f 100644 --- a/src/initializer/pragma_disable.hpp +++ b/src/initializer/pragma_disable.hpp @@ -12,28 +12,25 @@ #define DIAG_PRAGMA(compiler, x) DIAG_DO_PRAGMA(compiler diagnostic x) #endif #if defined(__clang__) -#define DISABLE_WARNING(gcc_unused, clang_option, msvc_unused) \ - DIAG_PRAGMA(clang, push) \ - DIAG_PRAGMA(clang, ignored DIAG_JOINSTR(-W, clang_option)) -#define ENABLE_WARNING(gcc_unused, clang_option, msvc_unused) \ - DIAG_PRAGMA(clang, pop) +#define DISABLE_WARNING(gcc_unused, clang_option, msvc_unused) \ + DIAG_PRAGMA(clang, push) \ + DIAG_PRAGMA(clang, ignored DIAG_JOINSTR(-W, clang_option)) +#define ENABLE_WARNING(gcc_unused, clang_option, msvc_unused) DIAG_PRAGMA(clang, pop) #elif defined(_MSC_VER) -#define DISABLE_WARNING(gcc_unused, clang_unused, msvc_errorcode) \ - DIAG_PRAGMA(msvc, push) DIAG_DO_PRAGMA(warning(disable :##msvc_errorcode)) -#define ENABLE_WARNING(gcc_unused, clang_unused, msvc_errorcode) \ - DIAG_PRAGMA(msvc, pop) +#define DISABLE_WARNING(gcc_unused, clang_unused, msvc_errorcode) \ + DIAG_PRAGMA(msvc, push) DIAG_DO_PRAGMA(warning(disable :##msvc_errorcode)) +#define ENABLE_WARNING(gcc_unused, clang_unused, msvc_errorcode) DIAG_PRAGMA(msvc, pop) #elif defined(__GNUC__) #if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406 -#define DISABLE_WARNING(gcc_option, clang_unused, msvc_unused) \ - DIAG_PRAGMA(GCC, push) DIAG_PRAGMA(GCC, ignored DIAG_JOINSTR(-W, gcc_option)) -#define ENABLE_WARNING(gcc_option, clang_unused, msvc_unused) \ - DIAG_PRAGMA(GCC, pop) +#define DISABLE_WARNING(gcc_option, clang_unused, msvc_unused) \ + DIAG_PRAGMA(GCC, push) DIAG_PRAGMA(GCC, ignored DIAG_JOINSTR(-W, gcc_option)) +#define ENABLE_WARNING(gcc_option, clang_unused, msvc_unused) DIAG_PRAGMA(GCC, pop) #else -#define DISABLE_WARNING(gcc_option, clang_unused, msvc_unused) \ - DIAG_PRAGMA(GCC, ignored DIAG_JOINSTR(-W, gcc_option)) -#define ENABLE_WARNING(gcc_option, clang_option, msvc_unused) \ - DIAG_PRAGMA(GCC, warning DIAG_JOINSTR(-W, gcc_option)) +#define DISABLE_WARNING(gcc_option, clang_unused, msvc_unused) \ + DIAG_PRAGMA(GCC, ignored DIAG_JOINSTR(-W, gcc_option)) +#define ENABLE_WARNING(gcc_option, clang_option, msvc_unused) \ + DIAG_PRAGMA(GCC, warning DIAG_JOINSTR(-W, gcc_option)) #endif #endif -#endif // PHARE_PRAGMA_DISABLE_HPP +#endif // PHARE_PRAGMA_DISABLE_HPP diff --git a/src/phare/phare.cpp b/src/phare/phare.cpp index fec4d4c06..fea15eda3 100644 --- a/src/phare/phare.cpp +++ b/src/phare/phare.cpp @@ -9,28 +9,22 @@ #include - -std::unique_ptr fromCommandLine(int argc, char** argv) +namespace PHARE { - using dataProvider [[maybe_unused]] = std::unique_ptr; - - switch (argc) - { - case 1: return nullptr; - case 2: - std::string arg = argv[1]; - auto moduleName = arg.substr(0, arg.find_last_of(".")); - if (arg.substr(arg.find_last_of(".") + 1) == "py") - { - std::replace(moduleName.begin(), moduleName.end(), '/', '.'); - std::cout << "python input detected, building with python provider...\n"; - return std::make_unique(moduleName); - } - - break; - } - return nullptr; +std::unique_ptr getSimulator(std::shared_ptr& hierarchy) +{ + PHARE::initializer::PHAREDict const& theDict + = PHARE::initializer::PHAREDictHandler::INSTANCE().dict(); + auto dim = theDict["simulation"]["dimension"].template to(); + auto interpOrder = theDict["simulation"]["interp_order"].template to(); + auto nbRefinedPart = theDict["simulation"]["refined_particle_nbr"].template to(); + + return core::makeAtRuntime(dim, interpOrder, nbRefinedPart, + SimulatorMaker{hierarchy}); } +} /* namespace PHARE */ + + int main(int argc, char** argv) { @@ -45,10 +39,11 @@ int main(int argc, char** argv) std::cout << "\n"; std::cout << "\n"; + PHARE::SamraiLifeCycle slc{argc, argv}; std::cerr << "creating python data provider\n"; - auto provider = fromCommandLine(argc, argv); + auto provider = PHARE::fromCommandLine(argc, argv); std::cerr << "reading user inputs..."; provider->read(); diff --git a/src/phare/phare.hpp b/src/phare/phare.hpp index 9ad14f48d..c53ca1f17 100644 --- a/src/phare/phare.hpp +++ b/src/phare/phare.hpp @@ -2,11 +2,14 @@ #ifndef PHARE_PHARE_INCLUDE_HPP #define PHARE_PHARE_INCLUDE_HPP +#include +#include +#include + #include "simulator/simulator.hpp" #include "core/utilities/algorithm.hpp" +#include "initializer/python_data_provider.hpp" -#include -#include namespace PHARE { @@ -42,6 +45,7 @@ class SamraiLifeCycle SAMRAI::tbox::SAMRAIManager::shutdown(); SAMRAI::tbox::SAMRAIManager::finalize(); SAMRAI::tbox::SAMRAI_MPI::finalize(); + PHARE::initializer::PHAREDictHandler::INSTANCE().stop(); } static void reset() @@ -53,6 +57,29 @@ class SamraiLifeCycle }; + +std::unique_ptr fromCommandLine(int argc, char** argv) +{ + using dataProvider [[maybe_unused]] = std::unique_ptr; + + switch (argc) + { + case 1: return nullptr; + case 2: + std::string arg = argv[1]; + auto moduleName = arg.substr(0, arg.find_last_of(".")); + if (arg.substr(arg.find_last_of(".") + 1) == "py") + { + std::replace(moduleName.begin(), moduleName.end(), '/', '.'); + std::cout << "python input detected, building with python provider...\n"; + return std::make_unique(moduleName); + } + + break; + } + return nullptr; +} + } // namespace PHARE diff --git a/src/phare_core.hpp b/src/phare_core.hpp index 3c77eb6ed..4c6b54cf8 100644 --- a/src/phare_core.hpp +++ b/src/phare_core.hpp @@ -2,6 +2,15 @@ #ifndef PHARE_CORE_INCLUDE_HPP #define PHARE_CORE_INCLUDE_HPP +#include +#include +#include +#include +#include + +#include "core/logger.hpp" +#include "core/vector.hpp" + #include "core/data/electromag/electromag.hpp" #include "core/data/electrons/electrons.hpp" #include "core/data/grid/gridlayout.hpp" @@ -16,43 +25,102 @@ #include "core/models/physical_state.hpp" #include "core/utilities/meta/meta_utilities.hpp" #include "core/utilities/algorithm.hpp" -#include "core/logger.hpp" - -#include -#include -#include -#include -#include #include "cppdict/include/dict.hpp" namespace PHARE::core { +template +struct CPU_Types; + + +template +struct CPU_Types +{ + using Array_t = PHARE::core::NdArrayVector; + using VecField_t = PHARE::core::VecField; + using Field_t = PHARE::core::Field; + using Electromag_t = PHARE::core::Electromag; + + using Particle_t = PHARE::core::Particle; + using ParticleAoS_t = PHARE::core::AoSMappedParticleArray; + using ParticleSoA_t = PHARE::core::SoAParticleArray; + using ParticleArray_t = ParticleAoS_t; +}; + +template +struct CPU_Types +{ + using Array_t = typename CPU_Types::Array_t; + using VecField_t = typename CPU_Types::VecField_t; + using Field_t = typename CPU_Types::Field_t; + using Electromag_t = typename CPU_Types::Electromag_t; + + using Particle_t = typename CPU_Types::Particle_t; + using ParticleArray_t = typename CPU_Types::ParticleArray_t; + + using YeeLayout_t = PHARE::core::GridLayoutImplYee; + using GridLayout_t = PHARE::core::GridLayout; + using IonPopulation_t = PHARE::core::IonPopulation; + using Ions_t = PHARE::core::Ions; + using Electrons_t = PHARE::core::Electrons; + + using ParticleInitializerFactory + = PHARE::core::ParticleInitializerFactory; + using MaxwellianParticleInitializer_t + = PHARE::core::MaxwellianParticleInitializer; +}; + + + +template +struct PHARE_Types; + + +template +struct PHARE_Types +{ + static auto constexpr dimension = dimension_; + static auto constexpr interp_order = 0; + + template + using Allocator_t = typename PHARE::Allocator::allocator_type; + + using ParticleAoS_t = PHARE::core::AoSMappedParticleArray; + using ParticleSoA_t = PHARE::core::SoAParticleArray; + using ParticleArray_t = ParticleAoS_t; + + using Array_t + = PHARE::core::NdArrayVector>; + + using VecField_t = PHARE::core::VecField; + using Field_t = PHARE::core::Field; + using Electromag_t = PHARE::core::Electromag; +}; + template struct PHARE_Types { static auto constexpr dimension = dimension_; static auto constexpr interp_order = interp_order_; - using Array_t = PHARE::core::NdArrayVector; - using VecField_t = PHARE::core::VecField; - using Field_t = PHARE::core::Field; + using Array_t = typename PHARE_Types::Array_t; + + using Field_t = PHARE::core::Field; + using VecField_t = PHARE::core::VecField; + using Electromag_t = PHARE::core::Electromag; + using YeeLayout_t = PHARE::core::GridLayoutImplYee; using GridLayout_t = PHARE::core::GridLayout; - using Particle_t = PHARE::core::Particle; - using ParticleAoS_t = PHARE::core::ParticleArray; - using ParticleArray_t = ParticleAoS_t; - using ParticleSoA_t = PHARE::core::ContiguousParticles; - + using ParticleArray_t = typename PHARE_Types::ParticleArray_t; using MaxwellianParticleInitializer_t = PHARE::core::MaxwellianParticleInitializer; using IonPopulation_t = PHARE::core::IonPopulation; using Ions_t = PHARE::core::Ions; using Electrons_t = PHARE::core::Electrons; - using ParticleInitializerFactory = PHARE::core::ParticleInitializerFactory; }; diff --git a/src/phare_solver.hpp b/src/phare_solver.hpp index 61eb081d2..dc67fd2a0 100644 --- a/src/phare_solver.hpp +++ b/src/phare_solver.hpp @@ -14,8 +14,47 @@ #include "amr/physical_models/mhd_model.hpp" #include "amr/physical_models/physical_model.hpp" + namespace PHARE::solver { +template +struct CPU_Types +{ + using core_types = PHARE::core::CPU_Types; + + using VecField_t = typename core_types::VecField_t; + using Electromag_t = typename core_types::Electromag_t; + using Ions_t = typename core_types::Ions_t; + using GridLayout_t = typename core_types::GridLayout_t; + using Electrons_t = typename core_types::Electrons_t; + + using IPhysicalModel = PHARE::solver::IPhysicalModel; + using MHDModel_t = PHARE::solver::MHDModel; + using HybridModel_t = PHARE::solver::HybridModel; + + + using SolverPPC_t = PHARE::solver::SolverPPC; + + + using LevelInitializerFactory_t = PHARE::solver::LevelInitializerFactory; + + + // amr deps + using amr_types = PHARE::amr::PHARE_Types; + using RefinementParams = typename amr_types::RefinementParams; + + using MessengerFactory // = amr/solver bidirectional dependency + = PHARE::amr::MessengerFactory; + // amr deps + + using MultiPhysicsIntegrator + = PHARE::solver::MultiPhysicsIntegrator; +}; + + + template struct PHARE_Types { @@ -35,10 +74,15 @@ struct PHARE_Types using IPhysicalModel = PHARE::solver::IPhysicalModel; using HybridModel_t = PHARE::solver::HybridModel; - using MHDModel_t = PHARE::solver::MHDModel; + using MHDModel_t = PHARE::solver::MHDModel; + using SolverPPC_t = PHARE::solver::SolverPPC; + + using SolverMHD_t = PHARE::solver::SolverMHD; - using LevelInitializerFactory_t = PHARE::solver::LevelInitializerFactory; + + using CPU_Types_t = CPU_Types; + using LevelInitializerFactory_t = typename CPU_Types_t::LevelInitializerFactory_t; // amr deps using amr_types = PHARE::amr::PHARE_Types; @@ -48,9 +92,9 @@ struct PHARE_Types = PHARE::amr::MessengerFactory; // amr deps - using MultiPhysicsIntegrator - = PHARE::solver::MultiPhysicsIntegrator; + using MultiPhysicsIntegrator = PHARE::solver::MultiPhysicsIntegrator< + MessengerFactory, PHARE::solver::LevelInitializerFactory, + PHARE::amr::SAMRAI_Types>; }; } // namespace PHARE::solver diff --git a/src/python3/CMakeLists.txt b/src/python3/CMakeLists.txt index c858b68de..8266b94cd 100644 --- a/src/python3/CMakeLists.txt +++ b/src/python3/CMakeLists.txt @@ -5,6 +5,7 @@ project(phare_python3) pybind11_add_module(cpp cpp_simulator.cpp) target_link_libraries(cpp PUBLIC phare_simulator) target_compile_options(cpp PRIVATE ${PHARE_FLAGS} -DPHARE_HAS_HIGHFIVE=${PHARE_HAS_HIGHFIVE}) # pybind fails with Werror +target_link_options(cpp PRIVATE ${PHARE_LIB_FLAGS}) set_target_properties(cpp PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/pybindlibs" @@ -16,6 +17,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug") pybind11_add_module(cpp_dbg cpp_simulator.cpp) target_link_libraries(cpp_dbg PUBLIC phare_simulator) target_compile_options(cpp_dbg PRIVATE ${PHARE_FLAGS} -DPHARE_HAS_HIGHFIVE=${PHARE_HAS_HIGHFIVE} -DPHARE_DIAG_DOUBLES=1 -DPHARE_CPP_MOD_NAME=cpp_dbg) + target_link_options(cpp_dbg PRIVATE ${PHARE_LIB_FLAGS}) set_target_properties(cpp_dbg PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/pybindlibs" diff --git a/src/python3/cpp_etc.cpp b/src/python3/cpp_etc.cpp index d3e356d26..2cec25290 100644 --- a/src/python3/cpp_etc.cpp +++ b/src/python3/cpp_etc.cpp @@ -1,4 +1,6 @@ +#include "core/def.hpp" + #include "python3/pybind_def.hpp" #include "simulator/simulator.hpp" diff --git a/src/python3/cpp_ext.cpp b/src/python3/cpp_ext.cpp new file mode 100644 index 000000000..d9fda180b --- /dev/null +++ b/src/python3/cpp_ext.cpp @@ -0,0 +1,28 @@ +/* + This file is for functions which are not coupled to the rest of the phare main simulator code +*/ + +#include "pybind11/stl.h" +#include "pybind11/numpy.h" +#include "pybind11/chrono.h" +#include "pybind11/complex.h" +#include "pybind11/functional.h" + +#include "core/utilities/types.hpp" +#include "python3/pybind_def.hpp" + +namespace py = pybind11; + +namespace PHARE::pydata +{ +PYBIND11_MODULE(cpp_ext, m) +{ + py::class_, std::shared_ptr>>(m, "Span"); + py::class_, std::shared_ptr>, core::Span>( + m, "PyWrapper"); + + m.def("makePyArrayWrapper", makePyArrayWrapper); +} + + +} // namespace PHARE::pydata diff --git a/src/python3/cpp_simulator.hpp b/src/python3/cpp_simulator.hpp index c0b04f7ab..f7d844f48 100644 --- a/src/python3/cpp_simulator.hpp +++ b/src/python3/cpp_simulator.hpp @@ -47,15 +47,15 @@ void declarePatchData(py::module& m, std::string key) template void declareDim(py::module& m) { - using CP = core::ContiguousParticles; - std::string name = "ContiguousParticles_" + std::to_string(dim); + using CP = core::SoAParticleArray; + std::string name = "ParticleArray_SOA_" + std::to_string(dim); py::class_>(m, name.c_str()) .def(py::init()) - .def_readwrite("iCell", &CP::iCell) - .def_readwrite("delta", &CP::delta) - .def_readwrite("weight", &CP::weight) - .def_readwrite("charge", &CP::charge) - .def_readwrite("v", &CP::v) + .def_readwrite("iCell", &CP::iCell_) + .def_readwrite("delta", &CP::delta_) + .def_readwrite("weight", &CP::weight_) + .def_readwrite("charge", &CP::charge_) + .def_readwrite("v", &CP::v_) .def("size", &CP::size); name = "PatchData" + name; @@ -156,7 +156,7 @@ void declare_sim(py::module& m) name = "make_simulator" + type_string; m.def(name.c_str(), [](std::shared_ptr const& hier) { - return std::shared_ptr{std::move(makeSimulator(hier))}; + return std::shared_ptr{std::move(makeSimulator(hier))}; }); } diff --git a/src/python3/data_wrangler.hpp b/src/python3/data_wrangler.hpp index 3682d1edc..794655ae7 100644 --- a/src/python3/data_wrangler.hpp +++ b/src/python3/data_wrangler.hpp @@ -22,7 +22,7 @@ namespace PHARE::pydata { template -class SimulatorCaster +class __attribute__((visibility("hidden"))) SimulatorCaster { public: using Simulator_t = Simulator; @@ -58,7 +58,7 @@ class SimulatorCaster template -class DataWrangler +class __attribute__((visibility("hidden"))) DataWrangler { public: static constexpr std::size_t dimension = _dimension; diff --git a/src/python3/particles.hpp b/src/python3/particles.hpp index 84d0e2411..93ce8640d 100644 --- a/src/python3/particles.hpp +++ b/src/python3/particles.hpp @@ -4,17 +4,20 @@ #include #include #include + #include "amr/data/particles/refine/particles_data_split.hpp" #include "core/data/particles/particle_packer.hpp" #include "core/data/particles/particle.hpp" + +#include "core/def.hpp" #include "core/utilities/types.hpp" #include "python3/pybind_def.hpp" namespace PHARE::pydata { -template -core::ContiguousParticlesView contiguousViewFrom(PyArrayTuple const& py_particles) +template +core::ParticleArray_SOAView contiguousViewFrom(PyArrayTuple& py_particles) { return {makeSpan(std::get<0>(py_particles)), // iCell makeSpan(std::get<1>(py_particles)), // delta @@ -62,20 +65,22 @@ template pyarray_particles_t splitPyArrayParticles(pyarray_particles_crt const& py_particles) { constexpr auto dim = Splitter::dimension; - constexpr auto interp_order = Splitter::interp_order; constexpr auto nbRefinedPart = Splitter::nbRefinedPart; PHARE_DEBUG_DO(assertParticlePyArraySizes(py_particles)); - auto particlesInView = contiguousViewFrom(py_particles); + auto particlesInView = contiguousViewFrom(py_particles); auto particlesOut = makePyArrayTuple(particlesInView.size() * nbRefinedPart); auto particlesOutView = contiguousViewFrom(particlesOut); Splitter splitter; + // for (auto it = particlesInView.begin(); it != particlesInView.end(); ++it) for (std::size_t i = 0; i < particlesInView.size(); i++) - splitter(amr::toFineGrid(std::copy(particlesInView[i])), particlesOutView, - i * nbRefinedPart); + { + auto it = particlesInView.begin() + i; + splitter(amr::toFineGrid(it), it, particlesOutView, i * nbRefinedPart); + } return particlesOut; } diff --git a/src/python3/patch_data.hpp b/src/python3/patch_data.hpp index 6222768a4..9eae3ad72 100644 --- a/src/python3/patch_data.hpp +++ b/src/python3/patch_data.hpp @@ -11,7 +11,7 @@ namespace PHARE::pydata { template -struct PatchData +struct __attribute__((visibility("hidden"))) PatchData { static auto constexpr dimension = dim; std::string patchID; diff --git a/src/python3/patch_level.hpp b/src/python3/patch_level.hpp index a1a784447..061c408ac 100644 --- a/src/python3/patch_level.hpp +++ b/src/python3/patch_level.hpp @@ -12,7 +12,7 @@ namespace PHARE::pydata { template -class PatchLevel +class __attribute__((visibility("hidden"))) PatchLevel { public: static constexpr std::size_t dimension = dim; @@ -21,6 +21,8 @@ class PatchLevel using PHARESolverTypes = solver::PHARE_Types; using HybridModel = typename PHARESolverTypes::HybridModel_t; + using ParticleArray_t = typename core::PHARE_Types::ParticleArray_t; + using Particle_t = typename ParticleArray_t::Particle_t; using GridLayout = typename HybridModel::gridlayout_type; @@ -255,45 +257,51 @@ class PatchLevel auto getParticles(std::string userPopName) { - using Nested = std::vector, dimension>>; + using Nested = std::vector, dimension>>; using Inner = std::unordered_map; std::unordered_map pop_particles; - auto getParticleData = [&](Inner& inner, GridLayout& grid, std::string patchID, - std::string key, auto& particles) { - if (particles.size() == 0) - return; + if constexpr (ParticleArray_t::is_host_mem) + { + auto getParticleData = [&](Inner& inner, GridLayout& grid, std::string patchID, + std::string key, auto& particles) { + if (particles.size() == 0) + return; - if (!inner.count(key)) - inner.emplace(key, Nested()); + if (!inner.count(key)) + inner.emplace(key, Nested()); - auto& patch_data = inner[key].emplace_back(particles.size()); - setPatchDataFromGrid(patch_data, grid, patchID); - core::ParticlePacker{particles}.pack(patch_data.data); - }; + auto& patch_data = inner[key].emplace_back(particles.size()); + setPatchDataFromGrid(patch_data, grid, patchID); + core::ParticlePacker{particles}.pack(patch_data.data); + }; - auto& ions = model_.state.ions; + auto& ions = model_.state.ions; - auto visit = [&](GridLayout& grid, std::string patchID, std::size_t /*iLevel*/) { - for (auto& pop : ions) - { - if ((userPopName != "" and userPopName == pop.name()) or userPopName == "all") + auto visit = [&](GridLayout& grid, std::string patchID, std::size_t /*iLevel*/) { + for (auto& pop : ions) { - if (!pop_particles.count(pop.name())) - pop_particles.emplace(pop.name(), Inner()); - - auto& inner = pop_particles.at(pop.name()); - - getParticleData(inner, grid, patchID, "domain", pop.domainParticles()); - getParticleData(inner, grid, patchID, "patchGhost", pop.patchGhostParticles()); - getParticleData(inner, grid, patchID, "levelGhost", pop.levelGhostParticles()); + if ((userPopName != "" and userPopName == pop.name()) or userPopName == "all") + { + if (!pop_particles.count(pop.name())) + pop_particles.emplace(pop.name(), Inner()); + + auto& inner = pop_particles.at(pop.name()); + + getParticleData(inner, grid, patchID, "domain", pop.domainParticles()); + getParticleData(inner, grid, patchID, "patchGhost", + pop.patchGhostParticles()); + getParticleData(inner, grid, patchID, "levelGhost", + pop.levelGhostParticles()); + } } - } - }; + }; - PHARE::amr::visitLevel(*hierarchy_.getPatchLevel(lvl_), - *model_.resourcesManager, visit, ions); + + PHARE::amr::visitLevel(*hierarchy_.getPatchLevel(lvl_), + *model_.resourcesManager, visit, ions); + } return pop_particles; } diff --git a/src/python3/pybind_def.hpp b/src/python3/pybind_def.hpp index 789a939d2..fa93b0d05 100644 --- a/src/python3/pybind_def.hpp +++ b/src/python3/pybind_def.hpp @@ -35,7 +35,7 @@ std::size_t ndSize(PyArrayInfo const& ar_info) template -class PyArrayWrapper : public core::Span +class __attribute__((visibility("hidden"))) PyArrayWrapper : public core::Span { public: PyArrayWrapper(PHARE::pydata::py_array_t const& array) @@ -64,6 +64,14 @@ core::Span makeSpan(py_array_t const& py_array) return {static_cast(ar_info.ptr), ndSize(ar_info)}; } +template +core::Span makeSpan(py_array_t& py_array) +{ + auto ar_info = py_array.request(); + assert(ar_info.ptr); + return {static_cast(ar_info.ptr), ndSize(ar_info)}; +} + } // namespace PHARE::pydata diff --git a/src/restarts/restarts_manager.hpp b/src/restarts/restarts_manager.hpp index 405b72bf3..12d83f3f6 100644 --- a/src/restarts/restarts_manager.hpp +++ b/src/restarts/restarts_manager.hpp @@ -63,10 +63,10 @@ class RestartsManager : public IRestartsManager Writer& writer() { return *writer_.get(); } - RestartsManager(RestartsManager const&) = delete; - RestartsManager(RestartsManager&&) = delete; + RestartsManager(RestartsManager const&) = delete; + RestartsManager(RestartsManager&&) = delete; RestartsManager& operator=(RestartsManager const&) = delete; - RestartsManager& operator=(RestartsManager&&) = delete; + RestartsManager& operator=(RestartsManager&&) = delete; private: bool needsAction_(double nextTime, double timeStamp, double timeStep) diff --git a/src/restarts/restarts_model_view.hpp b/src/restarts/restarts_model_view.hpp index 9e3b5d10b..7e7f1e66b 100644 --- a/src/restarts/restarts_model_view.hpp +++ b/src/restarts/restarts_model_view.hpp @@ -50,10 +50,10 @@ class ModelView : public IModelView auto patch_data_ids() const { return model_.patch_data_ids(); } - ModelView(ModelView const&) = delete; - ModelView(ModelView&&) = delete; + ModelView(ModelView const&) = delete; + ModelView(ModelView&&) = delete; ModelView& operator=(ModelView const&) = delete; - ModelView& operator=(ModelView&&) = delete; + ModelView& operator=(ModelView&&) = delete; protected: Model const& model_; diff --git a/src/simulator/CMakeLists.txt b/src/simulator/CMakeLists.txt index 4b0fcb75d..e4709326a 100644 --- a/src/simulator/CMakeLists.txt +++ b/src/simulator/CMakeLists.txt @@ -2,22 +2,18 @@ cmake_minimum_required(VERSION 3.9) project(phare_simulator) set( SOURCES_INC - simulator.hpp - simulator.cpp - phare_types.hpp + simulator.h + phare_types.h ) -add_library(${PROJECT_NAME} ${SOURCES_INC} ) -target_compile_options(${PROJECT_NAME} PRIVATE ${PHARE_WERROR_FLAGS} -DPHARE_HAS_HIGHFIVE=${PHARE_HAS_HIGHFIVE}) -set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION ${PHARE_INTERPROCEDURAL_OPTIMIZATION}) -target_include_directories(${PROJECT_NAME} PUBLIC +add_library(${PROJECT_NAME} INTERFACE) + +target_include_directories(${PROJECT_NAME} INTERFACE $ $) - -target_link_libraries(${PROJECT_NAME} PUBLIC +target_link_libraries(${PROJECT_NAME} INTERFACE phare_initializer phare_amr # for mpicc phare_diagnostic - ) - +) diff --git a/src/simulator/simulator.cpp b/src/simulator/simulator.cpp index b93a6005d..1f0404ebb 100644 --- a/src/simulator/simulator.cpp +++ b/src/simulator/simulator.cpp @@ -1,17 +1,18 @@ #include "simulator.hpp" -namespace PHARE -{ -std::unique_ptr getSimulator(std::shared_ptr& hierarchy) -{ - PHARE::initializer::PHAREDict const& theDict - = PHARE::initializer::PHAREDictHandler::INSTANCE().dict(); - auto dim = theDict["simulation"]["dimension"].template to(); - auto interpOrder = theDict["simulation"]["interp_order"].template to(); - auto nbRefinedPart = theDict["simulation"]["refined_particle_nbr"].template to(); +// namespace PHARE +// { +// std::unique_ptr getSimulator(std::shared_ptr& +// hierarchy) +// { +// PHARE::initializer::PHAREDict const& theDict +// = PHARE::initializer::PHAREDictHandler::INSTANCE().dict(); +// auto dim = theDict["simulation"]["dimension"].template to(); +// auto interpOrder = theDict["simulation"]["interp_order"].template to(); +// auto nbRefinedPart = theDict["simulation"]["refined_particle_nbr"].template to(); - return core::makeAtRuntime(dim, interpOrder, nbRefinedPart, - SimulatorMaker{hierarchy}); -} -} /* namespace PHARE */ +// return core::makeAtRuntime(dim, interpOrder, nbRefinedPart, +// SimulatorMaker{hierarchy}); +// } +// } /* namespace PHARE */ diff --git a/src/simulator/simulator.hpp b/src/simulator/simulator.hpp index 95d9242af..4780381a6 100644 --- a/src/simulator/simulator.hpp +++ b/src/simulator/simulator.hpp @@ -1,4 +1,5 @@ + #ifndef PHARE_SIMULATOR_SIMULATOR_HPP #define PHARE_SIMULATOR_SIMULATOR_HPP @@ -78,6 +79,8 @@ class Simulator : public ISimulator Simulator(PHARE::initializer::PHAREDict const& dict, std::shared_ptr const& hierarchy); + + ~Simulator() { if (coutbuf != nullptr) @@ -434,18 +437,560 @@ struct SimulatorMaker }; -std::unique_ptr getSimulator(std::shared_ptr& hierarchy); -template -std::unique_ptr> -makeSimulator(std::shared_ptr const& hierarchy) +template +std::unique_ptr makeSimulator(std::shared_ptr const& hierarchy) { - return std::make_unique>( - initializer::PHAREDictHandler::INSTANCE().dict(), hierarchy); + return std::make_unique(initializer::PHAREDictHandler::INSTANCE().dict(), hierarchy); } } // namespace PHARE #endif /*PHARE_SIMULATOR_SIMULATOR_H*/ + + + +// #ifndef PHARE_SIMULATOR_SIMULATOR_HPP +// #define PHARE_SIMULATOR_SIMULATOR_HPP + + + +// #include "phare_core.hpp" +// #include "phare_types.hpp" + +// #include "core/logger.hpp" +// #include "core/utilities/types.hpp" +// #include "core/utilities/mpi_utils.hpp" +// #include "core/utilities/timestamps.hpp" +// #include "amr/tagging/tagger_factory.hpp" + +// #include +// #include +// #include + + +// #if defined(HAVE_UMPIRE) +// #include "umpire/ResourceManager.hpp" +// #include "umpire/strategy/AllocationAdvisor.hpp" + +// #endif + + + +// namespace PHARE +// { +// class ISimulator +// { +// public: +// virtual double startTime() = 0; +// virtual double endTime() = 0; +// virtual double currentTime() = 0; +// virtual double timeStep() = 0; + +// virtual void initialize() = 0; +// virtual double advance(double dt) = 0; + +// virtual std::vector const& domainBox() const = 0; +// virtual std::vector const& cellWidth() const = 0; +// virtual std::size_t interporder() const = 0; + +// virtual std::string to_str() = 0; + +// virtual ~ISimulator() {} +// virtual bool dump(double timestamp, double timestep) { return false; } // overriding optional +// }; + +// template +// struct State +// { +// static constexpr std::size_t dimension = StateType::dimension; +// using HybridModel = typename StateType::HybridModel; +// using MultiPhysicsIntegrator = typename StateType::MultiPhysicsIntegrator; +// using SolverPPC = typename StateType::SolverPPC; +// using Integrator = PHARE::amr::Integrator; + +// template +// State(PHARE::initializer::PHAREDict const& dict, SimFunctors& functors) +// : multiphysInteg_{std::make_shared(dict["simulation"], functors)} +// { +// } + +// template +// State(State& that) +// : multiphysInteg_{that.multiphysInteg_} +// , hybridModel_{that.hybridModel_} +// , integrator_{that.integrator_} +// { +// } + +// std::shared_ptr multiphysInteg_; +// std::shared_ptr hybridModel_; +// std::shared_ptr integrator_; +// }; + + + + +// template +// struct WorkingState // might be CPU +// { +// static constexpr std::size_t dimension = _dimension; +// using PHARETypes = PHARE_Types; +// using HybridModel = typename PHARETypes::HybridModel_t; +// using MultiPhysicsIntegrator = typename PHARETypes::MultiPhysicsIntegrator; +// using SolverPPC = typename PHARETypes::SolverPPC_t; + +// // static constexpr bool is_cpu +// // = std::is_same_v::HybridModel>; +// }; + + + + +// template +// class Simulator : public ISimulator +// { +// public: +// double startTime() override { return startTime_; } +// double endTime() override { return finalTime_; } +// double timeStep() override { return dt_; } +// double currentTime() override { return currentTime_; } + +// void initialize() override; +// double advance(double dt) override; + +// std::vector const& domainBox() const override { return hierarchy_->domainBox(); } +// std::vector const& cellWidth() const override { return hierarchy_->cellWidth(); } +// std::size_t interporder() const override { return interp_order; } + +// auto& getHybridModel() { return state_->hybridModel_; } +// auto& getMHDModel() { return mhdModel_; } +// auto& getMultiPhysicsIntegrator() { return state_->multiphysInteg_; } + +// std::string to_str() override; + +// bool dump(double timestamp, double timestep) override +// { +// if (rMan) +// rMan->dump(timestamp, timestep); + +// if (dMan) +// return dMan->dump(timestamp, timestep); + +// return false; +// } + +// Simulator(PHARE::initializer::PHAREDict const& dict, +// std::shared_ptr const& hierarchy); +// ~Simulator() +// { +// if (coutbuf != nullptr) +// std::cout.rdbuf(coutbuf); +// } + +// static constexpr std::size_t dimension = _dimension; +// static constexpr std::size_t interp_order = _interp_order; +// static constexpr std::size_t nbRefinedPart = _nbRefinedPart; + +// using PHARETypes = PHARE_Types; + +// using IPhysicalModel = PHARE::solver::IPhysicalModel; +// using HybridModel = typename PHARETypes::HybridModel_t; +// using MHDModel = typename PHARETypes::MHDModel_t; + +// using SolverMHD = typename PHARETypes::SolverMHD_t; +// using SolverPPC = typename PHARETypes::SolverPPC_t; + +// using MessengerFactory = typename PHARETypes::MessengerFactory; +// using MultiPhysicsIntegrator = typename PHARETypes::MultiPhysicsIntegrator; + +// using SimFunctorParams = typename core::PHARE_Sim_Types::SimFunctorParams; +// using SimFunctors = typename core::PHARE_Sim_Types::SimulationFunctors; + +// // using InitState_t = InitState; +// using WorkingState_t = WorkingState; + + +// private: +// auto find_model(std::string name); + +// auto static log_file_name() +// { +// // ".log" directory is not created here, but in python if PHARE_LOG != "NONE" +// if (auto log = core::get_env("PHARE_LOG")) +// { +// if (log == "RANK_FILES") +// return ".log/" + std::to_string(core::mpi::rank()) + ".out"; + + +// if (log == "DATETIME_FILES") +// { +// auto date_time = core::mpi::date_time(); +// auto rank = std::to_string(core::mpi::rank()); +// auto size = std::to_string(core::mpi::size()); +// return ".log/" + date_time + "_" + rank + "_of_" + size + ".out"; +// } + +// if (log != "NONE") +// throw std::runtime_error( +// "PHARE_LOG invalid type, valid keys are RANK_FILES/DATETIME_FILES/NONE"); +// } + +// return std::string{""}; // unused +// } + +// std::ofstream log_out{log_file_name()}; +// std::streambuf* coutbuf = nullptr; +// std::shared_ptr hierarchy_; + +// std::vector modelNames_; +// std::vector descriptors_; +// MessengerFactory messengerFactory_; + +// float x_lo_[dimension]; +// float x_up_[dimension]; +// int maxLevelNumber_; +// double dt_; +// int timeStepNbr_ = 0; +// double startTime_ = 0; +// double finalTime_ = 0; +// double currentTime_ = 0; +// bool isInitialized = false; +// std::size_t fineDumpLvlMax = 0; + +// // physical models that can be used +// // std::shared_ptr hybridModel_; +// std::shared_ptr mhdModel_; + +// std::unique_ptr timeStamper; +// SimFunctors functors_; + +// std::shared_ptr> state_; + +// std::unique_ptr dMan; +// std::unique_ptr rMan; + +// SimFunctors functors_setup(PHARE::initializer::PHAREDict const& dict) +// { +// return {{"pre_advance", {/*empty vector*/}}}; +// } + + +// double restarts_init(initializer::PHAREDict const&); +// void diagnostics_init(initializer::PHAREDict const&); +// void hybrid_init(initializer::PHAREDict const&); +// }; + + + +// namespace +// { +// inline auto logging(std::ofstream& log_out) +// { +// std::streambuf* buf = nullptr; +// if (auto log = core::get_env("PHARE_LOG"); log != "NONE") +// { +// buf = std::cout.rdbuf(); +// std::cout.rdbuf(log_out.rdbuf()); +// } +// return buf; +// } +// } // namespace + + + +// //----------------------------------------------------------------------------- +// // Definitions +// //----------------------------------------------------------------------------- + + +// template +// double Simulator::restarts_init(initializer::PHAREDict const& dict) +// { +// rMan = restarts::RestartsManagerResolver::make_unique(*hierarchy_, *state_->hybridModel_, +// dict); + + +// PHARE_LOG_LINE_STR(dict.contains("restart_time")); + +// if (dict.contains("restart_time")) +// return (currentTime_ = dict["restart_time"].template to()); + +// return 0; +// } + + + +// template +// void Simulator::diagnostics_init(initializer::PHAREDict const& dict) +// { +// dMan = PHARE::diagnostic::DiagnosticsManagerResolver::make_unique(*hierarchy_, +// *state_->hybridModel_, +// dict); + +// if (dict.contains("fine_dump_lvl_max")) +// { +// auto fine_dump_lvl_max = dict["fine_dump_lvl_max"].template to(); + +// if (fine_dump_lvl_max > 0) +// { // copy for later +// this->fineDumpLvlMax = static_cast(fine_dump_lvl_max); +// functors_["pre_advance"]["fine_dump"] = [&](SimFunctorParams const& params) { +// std::size_t level_nbr = params["level_nbr"].template to(); +// auto timestamp = params["timestamp"].template to(); + +// if (this->fineDumpLvlMax >= level_nbr) +// this->dMan->dump_level(level_nbr, timestamp); +// }; +// } +// } +// } + + + +// template +// auto init_state(std::shared_ptr&& state, Hierarchy_t& hierarchy, +// MessengerFactory_t& messengerFactory, PHARE::initializer::PHAREDict const& dict, +// int maxLevelNumber, std::string prefix = "") +// { +// using HybridModel = typename State_t::HybridModel; +// using SolverPPC = typename State_t::SolverPPC; + +// auto& hybridModel_ = state->hybridModel_; +// hybridModel_ = std::make_shared( +// dict["simulation"], std::make_shared(prefix)); +// hybridModel_->resourcesManager->registerResources(hybridModel_->state); + +// // we register the hybrid model for all possible levels in the hierarchy +// // since for now it is the only model available same for the solver +// auto& multiphysInteg_ = state->multiphysInteg_; +// multiphysInteg_->registerModel(0, maxLevelNumber - 1, hybridModel_); +// multiphysInteg_->registerAndInitSolver(0, maxLevelNumber - 1, +// std::make_unique(dict["simulation"]["algo"])); +// multiphysInteg_->registerAndSetupMessengers(messengerFactory); + +// auto startTime = 0.; // TODO make it runtime +// auto endTime = 0.; // TODO make it runtime + +// state->integrator_ = std::make_shared( +// dict, hierarchy, multiphysInteg_, multiphysInteg_, startTime, endTime); + +// return std::move(state); +// } + + + + +// template +// void Simulator::hybrid_init(initializer::PHAREDict const& dict) +// { +// state_ = init_state(std::make_shared>(dict, functors_), hierarchy_, +// messengerFactory_, dict, maxLevelNumber_); + +// if (dict["simulation"].contains("restarts")) +// startTime_ = restarts_init(dict["simulation"]["restarts"]); + +// PHARE_LOG_LINE_STR(startTime_); +// PHARE_LOG_LINE_STR(currentTime_); + +// // hard coded for now, should get some params later from the dict +// auto hybridTagger_ = amr::TaggerFactory::make("HybridModel", "default"); +// state_->multiphysInteg_->registerTagger(0, maxLevelNumber_ - 1, std::move(hybridTagger_)); + +// timeStamper = core::TimeStamperFactory::create(dict["simulation"]); + +// if (dict["simulation"].contains("diagnostics")) +// diagnostics_init(dict["simulation"]["diagnostics"]); +// } + + + + +// template +// Simulator<_dimension, _interp_order, _nbRefinedPart>::Simulator( +// PHARE::initializer::PHAREDict const& dict, +// std::shared_ptr const& hierarchy) +// : coutbuf{logging(log_out)} +// , hierarchy_{hierarchy} +// , modelNames_{"HybridModel"} +// , descriptors_{PHARE::amr::makeDescriptors(modelNames_)} +// , messengerFactory_{descriptors_} +// , maxLevelNumber_{dict["simulation"]["AMR"]["max_nbr_levels"].template to()} +// , dt_{dict["simulation"]["time_step"].template to()} +// , timeStepNbr_{dict["simulation"]["time_step_nbr"].template to()} +// , finalTime_{dt_ * timeStepNbr_} +// , functors_{functors_setup(dict)} +// { +// if (find_model("HybridModel")) +// hybrid_init(dict); +// else +// throw std::runtime_error("unsupported model"); +// } + + + +// template +// std::string Simulator<_dimension, _interp_order, _nbRefinedPart>::to_str() +// { +// std::stringstream ss; +// ss << "PHARE SIMULATOR\n"; +// ss << "------------------------------------\n"; +// ss << "interpolation order : " << interp_order << "\n"; +// ss << "dimension : " << dimension << "\n"; +// ss << "time step : " << dt_ << "\n"; +// ss << "number of time steps : " << timeStepNbr_ << "\n"; +// ss << core::to_str(state_->hybridModel_->state); +// ss << "\n"; +// ss << "SolverPPC: " << typeid(SolverPPC).name() << "\n"; +// return ss.str(); +// } + +// template +// void Simulator<_dimension, _interp_order, _nbRefinedPart>::initialize() +// { +// try +// { +// if (isInitialized) +// std::runtime_error("cannot initialize - simulator already isInitialized"); + +// auto& integrator_ = state_->integrator_; + +// if (integrator_ == nullptr) +// throw std::runtime_error("Error - Simulator has no integrator"); + +// integrator_->initialize(); +// } +// catch (std::runtime_error const& e) +// { +// std::cerr << "runtime_error CAUGHT: " << e.what() << std::endl; +// std::rethrow_exception(std::current_exception()); +// } +// catch (std::exception const& e) +// { +// std::cerr << "EXCEPTION CAUGHT: " << e.what() << std::endl; +// std::rethrow_exception(std::current_exception()); +// } +// catch (...) +// { +// std::cerr << "UNKNOWN EXCEPTION CAUGHT" << std::endl; +// std::rethrow_exception(std::current_exception()); +// } + +// if (core::mpi::any(core::Errors::instance().any())) +// { +// this->dMan.release(); // closes/flushes hdf5 files +// throw std::runtime_error("forcing error"); +// } + +// isInitialized = true; + +// if (hierarchy_->isFromRestart()) +// hierarchy_->closeRestartFile(); +// } + + + + +// template +// double Simulator<_dimension, _interp_order, _nbRefinedPart>::advance(double dt) +// { +// double dt_new = 0; +// auto& integrator_ = state_->integrator_; + +// if (!integrator_) +// throw std::runtime_error("Error - no valid integrator in the simulator"); + +// try +// { +// PHARE_LOG_SCOPE("Simulator::advance"); +// dt_new = integrator_->advance(dt); +// currentTime_ = startTime_ + ((*timeStamper) += dt); +// } +// catch (std::runtime_error const& e) +// { +// std::cerr << "runtime_error CAUGHT: " << e.what() << std::endl; +// std::rethrow_exception(std::current_exception()); +// } +// catch (std::exception const& e) +// { +// std::cerr << "EXCEPTION CAUGHT: " << e.what() << std::endl; +// std::rethrow_exception(std::current_exception()); +// } +// catch (...) +// { +// std::cerr << "UNKNOWN EXCEPTION CAUGHT" << std::endl; +// std::rethrow_exception(std::current_exception()); +// } + +// if (core::mpi::any(core::Errors::instance().any())) +// { +// this->dMan.release(); // closes/flushes hdf5 files +// throw std::runtime_error("forcing error"); +// } + +// return dt_new; +// } + + + + +// template +// auto Simulator<_dimension, _interp_order, _nbRefinedPart>::find_model(std::string name) +// { +// return std::find(std::begin(modelNames_), std::end(modelNames_), name) != +// std::end(modelNames_); +// } + + + +// struct SimulatorMaker +// { +// SimulatorMaker(std::shared_ptr& hierarchy) +// : hierarchy_{hierarchy} +// { +// } + +// std::shared_ptr& hierarchy_; + +// template +// std::unique_ptr operator()(std::size_t userDim, std::size_t userInterpOrder, +// std::size_t userNbRefinedPart, Dimension dimension, +// InterpOrder interp_order, NbRefinedPart nbRefinedPart) +// { +// if (userDim == dimension() and userInterpOrder == interp_order() +// and userNbRefinedPart == nbRefinedPart()) +// { +// std::size_t constexpr d = dimension(); +// std::size_t constexpr io = interp_order(); +// std::size_t constexpr nb = nbRefinedPart(); + +// auto& theDict = PHARE::initializer::PHAREDictHandler::INSTANCE().dict(); + +// return std::make_unique>(theDict, hierarchy_); +// } + +// return nullptr; +// } +// }; + + +// std::unique_ptr getSimulator(std::shared_ptr& +// hierarchy); + + +// template +// std::unique_ptr makeSimulator(std::shared_ptr const& hierarchy) +// { +// return std::make_unique(initializer::PHAREDictHandler::INSTANCE().dict(), +// hierarchy); +// } + + +// } // namespace PHARE + +// #endif /*PHARE_SIMULATOR_SIMULATOR_H*/ diff --git a/tests/amr/amr.hpp b/tests/amr/amr.hpp new file mode 100644 index 000000000..d4981ad80 --- /dev/null +++ b/tests/amr/amr.hpp @@ -0,0 +1,34 @@ +#ifndef PHARE_TEST_AMR_AMR_HPP +#define PHARE_TEST_AMR_AMR_HPP + +#include +#include + +namespace PHARE::test::amr +{ +class SamraiLifeCycle +{ +public: + SamraiLifeCycle(int argc = 0, char** argv = nullptr) + { + SAMRAI::tbox::SAMRAI_MPI::init(&argc, &argv); + SAMRAI::tbox::SAMRAIManager::initialize(); + SAMRAI::tbox::SAMRAIManager::startup(); + } + ~SamraiLifeCycle() + { + SAMRAI::tbox::SAMRAIManager::shutdown(); + SAMRAI::tbox::SAMRAIManager::finalize(); + SAMRAI::tbox::SAMRAI_MPI::finalize(); + } + + static void reset() + { + SAMRAI::tbox::SAMRAIManager::shutdown(); + SAMRAI::tbox::SAMRAIManager::startup(); + } +}; + +} // namespace PHARE::test::amr + +#endif /* PHARE_TEST_AMR_AMR_HPP */ diff --git a/tests/amr/data/particles/copy/test_particledata_copyNd.cpp b/tests/amr/data/particles/copy/test_particledata_copyNd.cpp index f64bc980d..a55e50322 100644 --- a/tests/amr/data/particles/copy/test_particledata_copyNd.cpp +++ b/tests/amr/data/particles/copy/test_particledata_copyNd.cpp @@ -14,10 +14,15 @@ using namespace PHARE::core; using namespace PHARE::amr; -template -struct AParticlesDataND : public testing::Test + + +template +struct AParticlesData { - static constexpr auto dim = dimType{}(); + static constexpr auto dim = dim_; + + using ParticleArray_t = AoSMappedParticleArray; // permute with SoA + using Particle_t = Particle; SAMRAI::tbox::Dimension dimension{dim}; SAMRAI::hier::BlockId blockId{0}; @@ -37,55 +42,87 @@ struct AParticlesDataND : public testing::Test SAMRAI::hier::IntVector ghost{SAMRAI::hier::IntVector::getOne(dimension)}; - ParticlesData> destData{destDomain, ghost}; - ParticlesData> sourceData{sourceDomain, ghost}; - typename ParticleArray::Particle_t particle; - + ParticlesData destData{destDomain, ghost}; + ParticlesData sourceData{sourceDomain, ghost}; + Particle_t particle; - AParticlesDataND() + AParticlesData() { - particle.weight = 1.0; - particle.charge = 1.0; - particle.v = {1.0, 1.0, 1.0}; + particle.weight_ = 1.0; + particle.charge_ = 1.0; + particle.v_ = {1.0, 1.0, 1.0}; } }; +template +struct CopyTest : public ::testing::Test, public ParticlesData +{ +}; + + +using ParticlesDatas + = testing::Types, AParticlesData<2>, AParticlesData<3>/*, + AParticlesData<1, true>, AParticlesData<2, true>, AParticlesData<3, true>*/>; +TYPED_TEST_SUITE(CopyTest, ParticlesDatas); -using WithAllDim = testing::Types, DimConst<2>, DimConst<3>>; -TYPED_TEST_SUITE(AParticlesDataND, WithAllDim); +// TYPED_TEST(CopyTest, copiesSourceGhostParticleIntoDomainForGhostSrcOverDomainDest) +//{ +// static constexpr auto dim = TypeParam::dim; +// +// // particle is in the first ghost of the source patchdata +// // and in domain of the destination patchdata +// +// this->particle.iCell_ = ConstArray(2); +// +// this->sourceData.patchGhostParticles.push_back(this->particle); +// this->destData.copy(this->sourceData); +// +// ASSERT_THAT(this->destData.domainParticles.size(), Eq(1)); +// ASSERT_THAT(this->destData.patchGhostParticles.size(), Eq(0)); +//} -TYPED_TEST(AParticlesDataND, copiesSourceDomainParticleIntoGhostForDomainSrcOverGhostDest) +TYPED_TEST(CopyTest, copiesSourceDomainParticleIntoGhostForDomainSrcOverGhostDest) { - static constexpr auto dim = TypeParam{}(); + static constexpr auto dim = TypeParam::dim; // particle is in the domain of the source patchdata // and in first ghost of the destination patchdata - this->particle.iCell = ConstArray(6); + this->particle.iCell_ = ConstArray(6); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData); ASSERT_THAT(this->destData.patchGhostParticles.size(), Eq(1)); ASSERT_THAT(this->destData.domainParticles.size(), Eq(0)); + + // this->sourceData.domainParticles.clear(); + // this->sourceData.patchGhostParticles.clear(); + // this->destData.patchGhostParticles.clear(); + // this->destData.domainParticles.clear(); } -TYPED_TEST(AParticlesDataND, copiesSourceDomainParticleIntoDomainDestForDomainOverlapCells) +TYPED_TEST(CopyTest, copiesSourceDomainParticleIntoDomainDestForDomainOverlapCells) { - static constexpr auto dim = TypeParam{}(); + static constexpr auto dim = TypeParam::dim; // this set of particles is in the domain of the source patchdata // and in domain of the destination patchdata for (auto iCell = 3; iCell <= 5; ++iCell) { - this->particle.iCell = ConstArray(iCell); + this->particle.iCell_ = ConstArray(iCell); + ASSERT_THAT(this->sourceData.domainParticles.size(), Eq(0)); this->sourceData.domainParticles.push_back(this->particle); + ASSERT_THAT(this->sourceData.domainParticles.size(), Eq(1)); + + ASSERT_THAT(this->destData.domainParticles.size(), Eq(0)); + ASSERT_THAT(this->destData.patchGhostParticles.size(), Eq(0)); this->destData.copy(this->sourceData); ASSERT_THAT(this->destData.domainParticles.size(), Eq(1)); @@ -99,57 +136,45 @@ TYPED_TEST(AParticlesDataND, copiesSourceDomainParticleIntoDomainDestForDomainOv } -TYPED_TEST(AParticlesDataND, PreservesAllParticleAttributesAfterCopy) +TYPED_TEST(CopyTest, PreservesAllParticleAttributesAfterCopy) { - static constexpr auto dim = TypeParam{}(); + static constexpr auto dim = TypeParam::dim; // particle is in the domain of the source patchdata // and in domain of the destination patchdata - this->particle.iCell = ConstArray(3); - + this->particle.iCell_ = ConstArray(3); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData); - EXPECT_THAT(this->destData.domainParticles[0].v, Pointwise(DoubleEq(), this->particle.v)); - EXPECT_THAT(this->destData.domainParticles[0].iCell, Eq(this->particle.iCell)); - EXPECT_THAT(this->destData.domainParticles[0].delta, - Pointwise(DoubleEq(), this->particle.delta)); - EXPECT_THAT(this->destData.domainParticles[0].weight, DoubleEq(this->particle.weight)); - EXPECT_THAT(this->destData.domainParticles[0].charge, DoubleEq(this->particle.charge)); - EXPECT_DOUBLE_EQ(this->destData.domainParticles[0].Ex, this->particle.Ex); - EXPECT_DOUBLE_EQ(this->destData.domainParticles[0].Ey, this->particle.Ey); - EXPECT_DOUBLE_EQ(this->destData.domainParticles[0].Ez, this->particle.Ez); - EXPECT_DOUBLE_EQ(this->destData.domainParticles[0].Bx, this->particle.Bx); - EXPECT_DOUBLE_EQ(this->destData.domainParticles[0].By, this->particle.By); - EXPECT_DOUBLE_EQ(this->destData.domainParticles[0].Bz, this->particle.Bz); + auto& destDomainParticles = this->destData.domainParticles; + EXPECT_THAT(destDomainParticles.iCell(0), Eq(this->particle.iCell())); + EXPECT_THAT(destDomainParticles.delta(0), Pointwise(DoubleEq(), this->particle.delta())); + + EXPECT_THAT(destDomainParticles.v(0), Pointwise(DoubleEq(), this->particle.v())); + EXPECT_THAT(destDomainParticles.weight(0), DoubleEq(this->particle.weight())); + EXPECT_THAT(destDomainParticles.charge(0), DoubleEq(this->particle.charge())); // particle is in the domain of the source patchdata // and in last ghost of the destination patchdata - this->particle.iCell = ConstArray(6); - + this->particle.iCell_ = ConstArray(6); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData); - EXPECT_THAT(this->destData.patchGhostParticles[0].v, Pointwise(DoubleEq(), this->particle.v)); - EXPECT_THAT(this->destData.patchGhostParticles[0].iCell, Eq(this->particle.iCell)); - EXPECT_THAT(this->destData.patchGhostParticles[0].delta, - Pointwise(DoubleEq(), this->particle.delta)); - EXPECT_THAT(this->destData.patchGhostParticles[0].weight, DoubleEq(this->particle.weight)); - EXPECT_THAT(this->destData.patchGhostParticles[0].charge, DoubleEq(this->particle.charge)); - EXPECT_DOUBLE_EQ(this->destData.patchGhostParticles[0].Ex, this->particle.Ex); - EXPECT_DOUBLE_EQ(this->destData.patchGhostParticles[0].Ey, this->particle.Ey); - EXPECT_DOUBLE_EQ(this->destData.patchGhostParticles[0].Ez, this->particle.Ez); - EXPECT_DOUBLE_EQ(this->destData.patchGhostParticles[0].Bx, this->particle.Bx); - EXPECT_DOUBLE_EQ(this->destData.patchGhostParticles[0].By, this->particle.By); - EXPECT_DOUBLE_EQ(this->destData.patchGhostParticles[0].Bz, this->particle.Bz); + auto& destPatchGhostParticles = this->destData.patchGhostParticles; + EXPECT_THAT(destPatchGhostParticles.iCell(0), Eq(this->particle.iCell())); + EXPECT_THAT(destPatchGhostParticles.delta(0), Pointwise(DoubleEq(), this->particle.delta())); + + EXPECT_THAT(destPatchGhostParticles.v(0), Pointwise(DoubleEq(), this->particle.v())); + EXPECT_THAT(destPatchGhostParticles.weight(0), DoubleEq(this->particle.weight())); + EXPECT_THAT(destPatchGhostParticles.charge(0), DoubleEq(this->particle.charge())); } -TYPED_TEST(AParticlesDataND, copiesDataWithOverlapNoTransform) +TYPED_TEST(CopyTest, copiesDataWithOverlapNoTransform) { - static constexpr auto dim = TypeParam{}(); + static constexpr auto dim = TypeParam::dim; auto dimension = SAMRAI::tbox::Dimension{this->dim}; // now, with an overlap as union of 2 boxes @@ -175,11 +200,11 @@ TYPED_TEST(AParticlesDataND, copiesDataWithOverlapNoTransform) // and in domain of the destination patchdata // and also in the overlap - this->particle.iCell = ConstArray(3); + this->particle.iCell_ = ConstArray(3); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData, overlap); - this->particle.iCell = {{6}}; + this->particle.iCell_ = {{6}}; EXPECT_THAT(this->destData.domainParticles.size(), Eq(1)); EXPECT_THAT(this->destData.patchGhostParticles.size(), Eq(0)); @@ -192,7 +217,7 @@ TYPED_TEST(AParticlesDataND, copiesDataWithOverlapNoTransform) // and in last ghost of the destination patchdata // and also in the overlap - this->particle.iCell = ConstArray(6); + this->particle.iCell_ = ConstArray(6); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData, overlap); @@ -208,7 +233,7 @@ TYPED_TEST(AParticlesDataND, copiesDataWithOverlapNoTransform) // and in the domain of the destination patchdata // but not in the overlap... should not be copied in dest - this->particle.iCell = ConstArray(4); + this->particle.iCell_ = ConstArray(4); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData, overlap); @@ -218,9 +243,9 @@ TYPED_TEST(AParticlesDataND, copiesDataWithOverlapNoTransform) -TYPED_TEST(AParticlesDataND, copiesDataWithOverlapWithTransform) +TYPED_TEST(CopyTest, copiesDataWithOverlapWithTransform) { - static constexpr auto dim = TypeParam{}(); + static constexpr auto dim = TypeParam::dim; auto dimension = SAMRAI::tbox::Dimension{dim}; // using the same overlap as in previous test @@ -240,13 +265,13 @@ TYPED_TEST(AParticlesDataND, copiesDataWithOverlapWithTransform) // and in domain of the destination patchdata // and also in the overlap - this->particle.iCell = ConstArray(7); + this->particle.iCell_ = ConstArray(7); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData, overlap); EXPECT_THAT(this->destData.domainParticles.size(), Eq(1)); EXPECT_THAT(this->destData.patchGhostParticles.size(), Eq(0)); - EXPECT_EQ(5, this->destData.domainParticles[0].iCell[0]); + EXPECT_EQ(5, this->destData.domainParticles.iCell(0)[0]); this->sourceData.domainParticles.clear(); this->sourceData.patchGhostParticles.clear(); @@ -257,13 +282,13 @@ TYPED_TEST(AParticlesDataND, copiesDataWithOverlapWithTransform) // and in last ghost of the destination patchdata // and also in the overlap - this->particle.iCell = ConstArray(8); + this->particle.iCell_ = ConstArray(8); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData, overlap); EXPECT_THAT(this->destData.patchGhostParticles.size(), Eq(1)); EXPECT_THAT(this->destData.domainParticles.size(), Eq(0)); - EXPECT_EQ(6, this->destData.patchGhostParticles[0].iCell[0]); + EXPECT_EQ(6, this->destData.patchGhostParticles.iCell(0)[0]); this->sourceData.domainParticles.clear(); this->sourceData.patchGhostParticles.clear(); @@ -274,7 +299,7 @@ TYPED_TEST(AParticlesDataND, copiesDataWithOverlapWithTransform) // and in the domain of the destination patchdata // but not in the overlap... should not be copied in dest - this->particle.iCell = ConstArray(6); + this->particle.iCell_ = ConstArray(6); this->sourceData.domainParticles.push_back(this->particle); this->destData.copy(this->sourceData, overlap); diff --git a/tests/amr/data/particles/copy_overlap/test_particledata_copy_periodicNd.cpp b/tests/amr/data/particles/copy_overlap/test_particledata_copy_periodicNd.cpp index 871de56fe..19c9ade40 100644 --- a/tests/amr/data/particles/copy_overlap/test_particledata_copy_periodicNd.cpp +++ b/tests/amr/data/particles/copy_overlap/test_particledata_copy_periodicNd.cpp @@ -17,10 +17,12 @@ using namespace PHARE::core; using namespace PHARE::amr; -template -struct twoParticlesDataNDTouchingPeriodicBorders : public testing::Test +template +struct AParticlesData { - static constexpr auto dim = dimType{}(); + static constexpr auto dim = dim_; + using ParticleArray_t = AoSMappedParticleArray; // permute with SoA + using Particle_t = Particle; SAMRAI::tbox::Dimension dimension{dim}; SAMRAI::hier::BlockId blockId{0}; @@ -39,8 +41,8 @@ struct twoParticlesDataNDTouchingPeriodicBorders : public testing::Test SAMRAI::hier::Patch destPatch{destDomain, patchDescriptor}; SAMRAI::hier::Patch sourcePatch{sourceDomain, patchDescriptor}; - ParticlesData> destPdat{destDomain, ghost}; - ParticlesData> sourcePdat{sourceDomain, ghost}; + ParticlesData destPdat{destDomain, ghost}; + ParticlesData sourcePdat{sourceDomain, ghost}; std::shared_ptr destGeom{ std::make_shared(destPatch.getBox(), ghost)}; @@ -60,91 +62,64 @@ struct twoParticlesDataNDTouchingPeriodicBorders : public testing::Test std::dynamic_pointer_cast(destGeom->calculateOverlap( *sourceGeom, srcMask, fillBox, overwriteInterior, transformation))}; - typename ParticleArray::Particle_t particle; + Particle_t particle; - twoParticlesDataNDTouchingPeriodicBorders() + AParticlesData() { - particle.weight = 1.0; - particle.charge = 1.0; - particle.v = {{1.0, 1.0, 1.0}}; + particle.weight_ = 1.0; + particle.charge_ = 1.0; + particle.v_ = {{1.0, 1.0, 1.0}}; } }; +template +struct CopyOverlapTest : public ::testing::Test, public ParticlesData +{ +}; +using ParticlesDatas + = testing::Types, AParticlesData<2>, AParticlesData<3>/*, + AParticlesData<1, true>, AParticlesData<2, true>, AParticlesData<3, true>*/>; -using WithAllDim = testing::Types, DimConst<2>, DimConst<3>>; -TYPED_TEST_SUITE(twoParticlesDataNDTouchingPeriodicBorders, WithAllDim); +TYPED_TEST_SUITE(CopyOverlapTest, ParticlesDatas); -TYPED_TEST(twoParticlesDataNDTouchingPeriodicBorders, - haveATransformationThatPutsUpperSourceCellOnTopOfFirstGhostSourceCell) +TYPED_TEST(CopyOverlapTest, haveATransformationThatPutsUpperSourceCellOnTopOfFirstGhostSourceCell) { EXPECT_EQ(-16, this->transformation.getOffset()[0]); } -TYPED_TEST(twoParticlesDataNDTouchingPeriodicBorders, - canCopyUpperSourceParticlesInLowerDestGhostCell) +TYPED_TEST(CopyOverlapTest, canCopyUpperSourceParticlesInLowerDestGhostCell) { - static constexpr auto dim = TypeParam{}(); + static constexpr auto dim = TypeParam::dim; auto leftDestGhostCell = -1; auto rightSourceCell = 15; - if constexpr (dim == 1) - { - this->particle.iCell = {{rightSourceCell}}; - } - else if constexpr (dim == 2) - { - this->particle.iCell = {{rightSourceCell, rightSourceCell}}; - } - else if constexpr (dim == 3) - { - this->particle.iCell = {{rightSourceCell, rightSourceCell, rightSourceCell}}; - } + + this->particle.iCell_ = ConstArray(rightSourceCell); + this->sourcePdat.domainParticles.push_back(this->particle); this->destPdat.copy(this->sourcePdat, *(this->cellOverlap)); EXPECT_THAT(this->destPdat.patchGhostParticles.size(), Eq(1)); - EXPECT_EQ(leftDestGhostCell, this->destPdat.patchGhostParticles[0].iCell[0]); + EXPECT_EQ(leftDestGhostCell, this->destPdat.patchGhostParticles.iCell(0)[0]); } -TYPED_TEST(twoParticlesDataNDTouchingPeriodicBorders, preserveParticleAttributesInCopies) +TYPED_TEST(CopyOverlapTest, preserveParticleAttributesInCopies) { - static constexpr auto dim = TypeParam{}(); - + static constexpr auto dim = TypeParam::dim; - this->particle.iCell = ConstArray(15); + this->particle.iCell_ = ConstArray(15); this->sourcePdat.domainParticles.push_back(this->particle); this->destPdat.copy(this->sourcePdat, *(this->cellOverlap)); EXPECT_THAT(this->destPdat.patchGhostParticles.size(), Eq(1)); - EXPECT_THAT(this->destPdat.patchGhostParticles[0].v, Eq(this->particle.v)); - EXPECT_THAT(this->destPdat.patchGhostParticles[0].iCell[0], Eq(-1)); - if constexpr (dim > 1) - { - EXPECT_THAT(this->destPdat.patchGhostParticles[0].iCell[1], Eq(-1)); - } - if constexpr (dim > 2) - { - EXPECT_THAT(this->destPdat.patchGhostParticles[0].iCell[2], Eq(-1)); - } - EXPECT_THAT(this->destPdat.patchGhostParticles[0].delta, Eq(this->particle.delta)); - EXPECT_THAT(this->destPdat.patchGhostParticles[0].weight, Eq(this->particle.weight)); - EXPECT_THAT(this->destPdat.patchGhostParticles[0].charge, Eq(this->particle.charge)); - EXPECT_DOUBLE_EQ(this->destPdat.patchGhostParticles[0].Ex, this->particle.Ex); - EXPECT_DOUBLE_EQ(this->destPdat.patchGhostParticles[0].Ey, this->particle.Ey); - EXPECT_DOUBLE_EQ(this->destPdat.patchGhostParticles[0].Ez, this->particle.Ez); - EXPECT_DOUBLE_EQ(this->destPdat.patchGhostParticles[0].Bx, this->particle.Bx); - EXPECT_DOUBLE_EQ(this->destPdat.patchGhostParticles[0].By, this->particle.By); - EXPECT_DOUBLE_EQ(this->destPdat.patchGhostParticles[0].Bz, this->particle.Bz); } - - int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/tests/amr/data/particles/refine/test_particle_data_refine_tag_strategy.hpp b/tests/amr/data/particles/refine/test_particle_data_refine_tag_strategy.hpp index c9e840d13..c788db2a1 100644 --- a/tests/amr/data/particles/refine/test_particle_data_refine_tag_strategy.hpp +++ b/tests/amr/data/particles/refine/test_particle_data_refine_tag_strategy.hpp @@ -41,14 +41,16 @@ std::array boxBoundsUpper(Box const& box) template -ParticleArray loadCell(int iCellX, int iCellY, int iCellZ) +auto loadCell(int iCellX, int iCellY, int iCellZ) { + using Particle_t = Particle; + std::array _3diCell = {iCellX, iCellY, iCellZ}; float middle = 0.5; float delta = 0.30f; - Particle particle; + Particle_t particle; ParticleArray particles; particle.weight = 1.; @@ -87,6 +89,8 @@ ParticleArray loadCell(int iCellX, int iCellY, int iCellZ) template class TagStrategy : public SAMRAI::mesh::StandardTagAndInitStrategy { + using Particle_t = Particle; + public: TagStrategy(std::map const& dataToAllocate, std::shared_ptr& refineOperator, diff --git a/tests/amr/data/particles/stream_pack/test_main.cpp b/tests/amr/data/particles/stream_pack/test_main.cpp index 2531652d6..1a0fbe900 100644 --- a/tests/amr/data/particles/stream_pack/test_main.cpp +++ b/tests/amr/data/particles/stream_pack/test_main.cpp @@ -3,6 +3,12 @@ #include #include +#include "tests/amr/amr.hpp" + +#include "core/logger.hpp" +#include "core/data/grid/gridlayout.hpp" +#include "core/data/particles/particle_array_partitionner.hpp" + #include "amr/data/particles/particles_data.hpp" #include "amr/data/particles/particles_data_factory.hpp" #include @@ -25,10 +31,13 @@ using namespace PHARE::core; using namespace PHARE::amr; -template +template struct AParticlesData { static constexpr auto dimension = dim; + static constexpr auto ghosts = 1; + using ParticleArray_t = ParticleArray; + using Particle_t = typename ParticleArray_t::Particle_t; SAMRAI::tbox::Dimension amr_dimension{dim}; SAMRAI::hier::BlockId blockId{0}; @@ -47,8 +56,8 @@ struct AParticlesData SAMRAI::hier::Patch destPatch{destDomain, patchDescriptor}; SAMRAI::hier::Patch sourcePatch{sourceDomain, patchDescriptor}; - ParticlesData> destData{destDomain, ghost}; - ParticlesData> sourceData{sourceDomain, ghost}; + ParticlesData destData{destDomain, ghost}; + ParticlesData sourceData{sourceDomain, ghost}; std::shared_ptr destGeom{ std::make_shared(destPatch.getBox(), ghost)}; @@ -73,71 +82,91 @@ struct AParticlesData *sourceGeom, srcMask, fillBox, overwriteInterior, transformation))}; - typename ParticleArray::Particle_t particle; + Particle_t particle; - AParticlesData() + AParticlesData(std::array const& iCell) { - particle.weight = 1.0; - particle.charge = 1.0; - particle.v = {1.0, 1.0, 1.0}; + particle.weight_ = 1.0; + particle.charge_ = 1.0; + particle.v_ = {1.0, 1.0, 1.0}; + particle.iCell() = iCell; + sourceData.domainParticles.push_back(particle); } }; - -template -struct StreamPackTest : public ::testing::Test +template +struct ACellMappedParticlesData : AParticlesData> { -}; + using Super = AParticlesData>; -using ParticlesDatas = testing::Types, AParticlesData<2>, AParticlesData<3>>; -TYPED_TEST_SUITE(StreamPackTest, ParticlesDatas); + ACellMappedParticlesData(std::array const& iCell) + : Super{iCell} + { + } +}; -TYPED_TEST(StreamPackTest, PreserveVelocityWhenPackStreamWithPeriodics) +template +struct APartitionedParticlesData : AParticlesData> { - using ParticlesData = TypeParam; - constexpr auto dim = ParticlesData::dimension; + using Super = AParticlesData>; + using box_t = PHARE::core::Box; + using point_t = Point; + using iterator = typename std::vector>::iterator; + using Super::cellOverlap; + using Super::destDomain; + using Super::sourceData; + using Super::sourceDomain; + using Super::transformation; - ParticlesData param; - auto& particle = param.particle; - auto& sourceData = param.sourceData; - auto& cellOverlap = param.cellOverlap; - auto& destData = param.destData; - - particle.iCell = ConstArray(15); - sourceData.domainParticles.push_back(particle); - SAMRAI::tbox::MessageStream particlesWriteStream; - - sourceData.packStream(particlesWriteStream, *cellOverlap); + auto static constexpr particle_ghosts = ghostWidthForParticles(); - SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), - SAMRAI::tbox::MessageStream::Read, - particlesWriteStream.getBufferStart()}; + APartitionedParticlesData(std::array const& iCell) + : Super{iCell} + { + auto destBox = destDomain; + transformation.inverseTransform(destBox); + src_neighbor_boxes.emplace_back(phare_box_from(destBox)); - destData.unpackStream(particlesReadStream, *cellOverlap); + PHARE_LOG_LINE_STR(phare_box_from(sourceDomain)); + PHARE_LOG_LINE_STR(src_neighbor_boxes.back()); - ASSERT_THAT(destData.patchGhostParticles.size(), Eq(1)); - ASSERT_THAT(destData.patchGhostParticles[0].v, Eq(particle.v)); -} + sourceData.set_iterators(partition( + sourceData.domainParticles, phare_box_from(sourceDomain), src_neighbor_boxes)); + } + // std::vector partition_iterators; + std::vector src_neighbor_boxes; //{phare_box_from(destDomain)}; + // std::vector dst_neighbor_boxes{phare_box_from(sourceDomain)}; +}; +template +struct StreamPackTest : public ::testing::Test +{ +}; +using ParticlesDatas = testing::Types /*, // + // AParticlesData<2, AoSMappedParticleArray<2>>, + // AParticlesData<3, AoSMappedParticleArray<3>>, + APartitionedParticlesData<1> */ // + >; +TYPED_TEST_SUITE(StreamPackTest, ParticlesDatas, ); -TYPED_TEST(StreamPackTest, ShiftTheiCellWhenPackStreamWithPeriodics) +TYPED_TEST(StreamPackTest, PreserveVelocityWhenPackStreamWithPeriodics) { using ParticlesData = TypeParam; constexpr auto dim = ParticlesData::dimension; - ParticlesData param; + ParticlesData param{ConstArray(15)}; auto& particle = param.particle; auto& sourceData = param.sourceData; auto& cellOverlap = param.cellOverlap; auto& destData = param.destData; - particle.iCell = ConstArray(15); - - sourceData.domainParticles.push_back(particle); + // particle.iCell() = ConstArray(15); + // sourceData.domainParticles.push_back(particle); SAMRAI::tbox::MessageStream particlesWriteStream; @@ -149,159 +178,165 @@ TYPED_TEST(StreamPackTest, ShiftTheiCellWhenPackStreamWithPeriodics) destData.unpackStream(particlesReadStream, *cellOverlap); - // patch0 start at 0 , patch1 start at 10 - // with periodics condition, we have 0 equivalent to 15 - auto expectediCell = ConstArray(-1); - - ASSERT_THAT(destData.patchGhostParticles.size(), Eq(1)); - ASSERT_THAT(destData.patchGhostParticles[0].iCell, Eq(expectediCell)); + ASSERT_THAT(destData.patchGhostParticles[0].v_, Eq(particle.v_)); } -TYPED_TEST(StreamPackTest, PackInTheCorrectBufferWithPeriodics) -{ - using ParticlesData = TypeParam; - constexpr auto dim = ParticlesData::dimension; - - ParticlesData param; - auto& particle = param.particle; - auto& sourceData = param.sourceData; - auto& cellOverlap = param.cellOverlap; - auto& destData = param.destData; - - particle.iCell = ConstArray(15); - - sourceData.domainParticles.push_back(particle); - SAMRAI::tbox::MessageStream particlesWriteStream; +// TYPED_TEST(StreamPackTest, ShiftTheiCellWhenPackStreamWithPeriodics) +// { +// using ParticlesData = TypeParam; +// constexpr auto dim = ParticlesData::dimension; - sourceData.packStream(particlesWriteStream, *cellOverlap); +// ParticlesData param{ConstArray(15)}; +// // auto& particle = param.particle; +// auto& sourceData = param.sourceData; +// auto& cellOverlap = param.cellOverlap; +// auto& destData = param.destData; - SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), - SAMRAI::tbox::MessageStream::Read, - particlesWriteStream.getBufferStart()}; +// // particle.iCell() = ConstArray(15); +// // sourceData.domainParticles.push_back(particle); - destData.unpackStream(particlesReadStream, *cellOverlap); +// SAMRAI::tbox::MessageStream particlesWriteStream; - auto expectediCell = ConstArray(-1); +// sourceData.packStream(particlesWriteStream, *cellOverlap); - ASSERT_THAT(destData.patchGhostParticles.size(), Eq(1)); - ASSERT_THAT(destData.patchGhostParticles[0].iCell, Eq(expectediCell)); -} +// SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), +// SAMRAI::tbox::MessageStream::Read, +// particlesWriteStream.getBufferStart()}; +// destData.unpackStream(particlesReadStream, *cellOverlap); +// // patch0 start at 0 , patch1 start at 10 +// // with periodics condition, we have 0 equivalent to 15 +// auto expectediCell = ConstArray(-1); +// ASSERT_THAT(destData.patchGhostParticles.size(), Eq(1)); +// ASSERT_THAT(destData.patchGhostParticles[0].iCell(), Eq(expectediCell)); +// } -TYPED_TEST(StreamPackTest, - PreserveParticleAttributesWhenPackingWithPeriodicsFromGhostSrcToDomainDest) -{ - using ParticlesData = TypeParam; - constexpr auto dim = ParticlesData::dimension; - ParticlesData param; - auto& particle = param.particle; - auto& sourceData = param.sourceData; - auto& cellOverlap = param.cellOverlap; - auto& destData = param.destData; - particle.iCell = ConstArray(16); +// TYPED_TEST(StreamPackTest, PackInTheCorrectBufferWithPeriodics) +// { +// using ParticlesData = TypeParam; +// constexpr auto dim = ParticlesData::dimension; - sourceData.domainParticles.push_back(particle); +// ParticlesData param{ConstArray(15)}; +// // auto& particle = param.particle; +// auto& sourceData = param.sourceData; +// auto& cellOverlap = param.cellOverlap; +// auto& destData = param.destData; - SAMRAI::tbox::MessageStream particlesWriteStream; +// // particle.iCell() = ; +// // sourceData.domainParticles.push_back(particle); - sourceData.packStream(particlesWriteStream, *cellOverlap); +// SAMRAI::tbox::MessageStream particlesWriteStream; - SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), - SAMRAI::tbox::MessageStream::Read, - particlesWriteStream.getBufferStart()}; +// sourceData.packStream(particlesWriteStream, *cellOverlap); - destData.unpackStream(particlesReadStream, *cellOverlap); +// SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), +// SAMRAI::tbox::MessageStream::Read, +// particlesWriteStream.getBufferStart()}; - auto expectediCell = ConstArray(0); - - EXPECT_THAT(destData.domainParticles[0].v, Eq(particle.v)); - EXPECT_THAT(destData.domainParticles[0].iCell, Eq(expectediCell)); - EXPECT_THAT(destData.domainParticles[0].delta, Eq(particle.delta)); - EXPECT_THAT(destData.domainParticles[0].weight, Eq(particle.weight)); - EXPECT_THAT(destData.domainParticles[0].charge, Eq(particle.charge)); - EXPECT_DOUBLE_EQ(destData.domainParticles[0].Ex, particle.Ex); - EXPECT_DOUBLE_EQ(destData.domainParticles[0].Ey, particle.Ey); - EXPECT_DOUBLE_EQ(destData.domainParticles[0].Ez, particle.Ez); - EXPECT_DOUBLE_EQ(destData.domainParticles[0].Bx, particle.Bx); - EXPECT_DOUBLE_EQ(destData.domainParticles[0].By, particle.By); - EXPECT_DOUBLE_EQ(destData.domainParticles[0].Bz, particle.Bz); -} +// destData.unpackStream(particlesReadStream, *cellOverlap); +// auto expectediCell = ConstArray(-1); +// ASSERT_THAT(destData.patchGhostParticles.size(), Eq(1)); +// ASSERT_THAT(destData.patchGhostParticles[0].iCell(), Eq(expectediCell)); +// } -TYPED_TEST(StreamPackTest, - PreserveParticleAttributesWhenPackingWithPeriodicsFromDomainSrcToGhostDest) -{ - using ParticlesData = TypeParam; - constexpr auto dim = ParticlesData::dimension; - ParticlesData param; - auto& particle = param.particle; - auto& sourceData = param.sourceData; - auto& cellOverlap = param.cellOverlap; - auto& destData = param.destData; - particle.iCell = ConstArray(15); +// TYPED_TEST(StreamPackTest, +// PreserveParticleAttributesWhenPackingWithPeriodicsFromGhostSrcToDomainDest) +// { +// using ParticlesData = TypeParam; +// constexpr auto dim = ParticlesData::dimension; - sourceData.domainParticles.push_back(particle); +// ParticlesData param{ConstArray(16)}; +// auto& particle = param.particle; +// auto& sourceData = param.sourceData; +// auto& cellOverlap = param.cellOverlap; +// auto& destData = param.destData; - SAMRAI::tbox::MessageStream particlesWriteStream; +// // particle.iCell() = ; +// // sourceData.domainParticles.push_back(particle); - sourceData.packStream(particlesWriteStream, *cellOverlap); +// SAMRAI::tbox::MessageStream particlesWriteStream; - SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), - SAMRAI::tbox::MessageStream::Read, - particlesWriteStream.getBufferStart()}; +// sourceData.packStream(particlesWriteStream, *cellOverlap); - destData.unpackStream(particlesReadStream, *cellOverlap); +// SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), +// SAMRAI::tbox::MessageStream::Read, +// particlesWriteStream.getBufferStart()}; - auto expectediCell = ConstArray(-1); - - EXPECT_THAT(destData.patchGhostParticles[0].v, Eq(particle.v)); - EXPECT_THAT(destData.patchGhostParticles[0].iCell, Eq(expectediCell)); - EXPECT_THAT(destData.patchGhostParticles[0].delta, Eq(particle.delta)); - EXPECT_THAT(destData.patchGhostParticles[0].weight, Eq(particle.weight)); - EXPECT_THAT(destData.patchGhostParticles[0].charge, Eq(particle.charge)); - EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].Ex, particle.Ex); - EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].Ey, particle.Ey); - EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].Ez, particle.Ez); - EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].Bx, particle.Bx); - EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].By, particle.By); - EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].Bz, particle.Bz); -} +// destData.unpackStream(particlesReadStream, *cellOverlap); +// auto expectediCell = ConstArray(0); +// EXPECT_THAT(destData.domainParticles[0].v_, Eq(particle.v_)); +// EXPECT_THAT(destData.domainParticles[0].iCell_, Eq(expectediCell)); +// EXPECT_THAT(destData.domainParticles[0].delta_, Eq(particle.delta_)); +// EXPECT_THAT(destData.domainParticles[0].weight_, Eq(particle.weight_)); +// EXPECT_THAT(destData.domainParticles[0].charge_, Eq(particle.charge_)); +// EXPECT_DOUBLE_EQ(destData.domainParticles[0].E_[0], particle.E_[0]); +// EXPECT_DOUBLE_EQ(destData.domainParticles[0].E_[1], particle.E_[1]); +// EXPECT_DOUBLE_EQ(destData.domainParticles[0].E_[2], particle.E_[2]); +// EXPECT_DOUBLE_EQ(destData.domainParticles[0].B_[0], particle.B_[0]); +// EXPECT_DOUBLE_EQ(destData.domainParticles[0].B_[1], particle.B_[1]); +// EXPECT_DOUBLE_EQ(destData.domainParticles[0].B_[2], particle.B_[2]); +// } -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); +// TYPED_TEST(StreamPackTest, +// PreserveParticleAttributesWhenPackingWithPeriodicsFromDomainSrcToGhostDest) +// { +// using ParticlesData = TypeParam; +// constexpr auto dim = ParticlesData::dimension; - SAMRAI::tbox::SAMRAI_MPI::init(&argc, &argv); +// ParticlesData param{ConstArray(15)}; +// auto& particle = param.particle; +// auto& sourceData = param.sourceData; +// auto& cellOverlap = param.cellOverlap; +// auto& destData = param.destData; - SAMRAI::tbox::SAMRAIManager::initialize(); +// // particle.iCell() = ; +// // sourceData.domainParticles.push_back(particle); - SAMRAI::tbox::SAMRAIManager::startup(); +// SAMRAI::tbox::MessageStream particlesWriteStream; +// sourceData.packStream(particlesWriteStream, *cellOverlap); - int testResult = RUN_ALL_TESTS(); +// SAMRAI::tbox::MessageStream particlesReadStream{particlesWriteStream.getCurrentSize(), +// SAMRAI::tbox::MessageStream::Read, +// particlesWriteStream.getBufferStart()}; +// destData.unpackStream(particlesReadStream, *cellOverlap); - // Finalize +// auto expectediCell = ConstArray(-1); - SAMRAI::tbox::SAMRAIManager::shutdown(); +// EXPECT_THAT(destData.patchGhostParticles[0].v_, Eq(particle.v_)); +// EXPECT_THAT(destData.patchGhostParticles[0].iCell_, Eq(expectediCell)); +// EXPECT_THAT(destData.patchGhostParticles[0].delta_, Eq(particle.delta_)); +// EXPECT_THAT(destData.patchGhostParticles[0].weight_, Eq(particle.weight_)); +// EXPECT_THAT(destData.patchGhostParticles[0].charge_, Eq(particle.charge_)); +// EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].E_[0], particle.E_[0]); +// EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].E_[1], particle.E_[1]); +// EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].E_[2], particle.E_[2]); +// EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].B_[0], particle.B_[0]); +// EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].B_[1], particle.B_[1]); +// EXPECT_DOUBLE_EQ(destData.patchGhostParticles[0].B_[2], particle.B_[2]); +// } - SAMRAI::tbox::SAMRAIManager::finalize(); - SAMRAI::tbox::SAMRAI_MPI::finalize(); - return testResult; +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + PHARE::test::amr::SamraiLifeCycle life{argc, argv}; + return RUN_ALL_TESTS(); } diff --git a/tests/amr/messengers/test_messengers.cpp b/tests/amr/messengers/test_messengers.cpp index b432d6d7c..9207a83ef 100644 --- a/tests/amr/messengers/test_messengers.cpp +++ b/tests/amr/messengers/test_messengers.cpp @@ -1,8 +1,8 @@ -#include "src/simulator/simulator.hpp" -#include "src/simulator/phare_types.hpp" -#include "src/phare/phare.hpp" +#include "simulator/simulator.hpp" +#include "simulator/phare_types.hpp" +#include "phare/phare.hpp" #include "test_messenger_basichierarchy.hpp" #include "test_integrator_strat.hpp" diff --git a/tests/amr/resources_manager/test_resources_manager.cpp b/tests/amr/resources_manager/test_resources_manager.cpp index f0faf490e..143eb982d 100644 --- a/tests/amr/resources_manager/test_resources_manager.cpp +++ b/tests/amr/resources_manager/test_resources_manager.cpp @@ -29,13 +29,14 @@ static constexpr std::size_t dim = 1; static constexpr std::size_t interpOrder = 1; using GridImplYee1D = GridLayoutImplYee; using GridYee1D = GridLayout; - -using VecField1D = VecField, HybridQuantity>; -using IonPopulation1D = IonPopulation, VecField1D, GridYee1D>; -using Ions1D = Ions; -using Electromag1D = Electromag; -using Electrons1D = Electrons; -using MaxwellianParticleInitializer1D = MaxwellianParticleInitializer, GridYee1D>; +using ParticleArray_t = AoSMappedParticleArray<1>; +using Particle_t = Particle; +using VecField1D = VecField, HybridQuantity>; +using IonPopulation1D = IonPopulation; +using Ions1D = Ions; +using Electromag1D = Electromag; +using Electrons1D = Electrons; +using MaxwellianParticleInitializer1D = MaxwellianParticleInitializer; using HybridState1D = HybridState; diff --git a/tests/amr/tagging/CMakeLists.txt b/tests/amr/tagging/CMakeLists.txt index aa8fc2475..2409f3170 100644 --- a/tests/amr/tagging/CMakeLists.txt +++ b/tests/amr/tagging/CMakeLists.txt @@ -18,7 +18,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE ) target_link_libraries(${PROJECT_NAME} PRIVATE - phare_amr + phare_amr pybind11::embed ${GTEST_LIBS}) diff --git a/tests/core/data/electrons/test_electrons.cpp b/tests/core/data/electrons/test_electrons.cpp index 5c405b02c..2204255e8 100644 --- a/tests/core/data/electrons/test_electrons.cpp +++ b/tests/core/data/electrons/test_electrons.cpp @@ -449,16 +449,14 @@ TYPED_TEST(ElectronsTest, ThatElectronsVelocityEqualIonVelocityMinusJ) static constexpr auto dim = typename TypeParam::first_type{}(); static constexpr auto interp = typename TypeParam::second_type{}(); - auto& electrons = this->electrons; - auto& layout = this->layout; - using VecFieldND = VecField, HybridQuantity>; using FieldND = typename VecFieldND::field_type; using GridYee = GridLayout>; + auto& electrons = this->electrons; + auto& layout = this->layout; electrons.update(layout); - auto& Ne = electrons.density(); auto check = [&layout](FieldND const& Vecomp, FieldND const& Vicomp, FieldND const& Jcomp, diff --git a/tests/core/data/field/test_field.cpp b/tests/core/data/field/test_field.cpp index 8301ddc64..04d136392 100644 --- a/tests/core/data/field/test_field.cpp +++ b/tests/core/data/field/test_field.cpp @@ -2,6 +2,7 @@ #include #include +#include "phare_core.hpp" #include "core/data/field/field.hpp" #include "core/data/ndarray/ndarray_vector.hpp" #include "core/hybrid/hybrid_quantities.hpp" @@ -60,9 +61,12 @@ class GenericField3D : public ::testing::Test }; -using NdArrays1D = ::testing::Types>; -using NdArrays2D = ::testing::Types>; -using NdArrays3D = ::testing::Types>; +template +using NdArray_t = typename PHARE_Types::Array_t; + +using NdArrays1D = ::testing::Types>; +using NdArrays2D = ::testing::Types>; +using NdArrays3D = ::testing::Types>; TYPED_TEST_SUITE(GenericField1D, NdArrays1D); TYPED_TEST_SUITE(GenericField2D, NdArrays2D); @@ -173,8 +177,8 @@ TYPED_TEST(GenericField3D, physiscalQuantity) TEST(Field1D, canBeAssigned) { auto nx = 10u; - Field, HybridQuantity::Scalar> f{"test", HybridQuantity::Scalar::rho, nx}; - Field, HybridQuantity::Scalar> other{"other", HybridQuantity::Scalar::rho, nx}; + Field, HybridQuantity::Scalar> f{"test", HybridQuantity::Scalar::rho, nx}; + Field, HybridQuantity::Scalar> other{"other", HybridQuantity::Scalar::rho, nx}; for (auto& v : f) { @@ -196,9 +200,8 @@ TEST(Field2D, canBeAssigned) { auto nx = 10u; auto ny = 11u; - Field, HybridQuantity::Scalar> f{"test", HybridQuantity::Scalar::rho, nx, ny}; - Field, HybridQuantity::Scalar> other{"other", HybridQuantity::Scalar::rho, nx, - ny}; + Field, HybridQuantity::Scalar> f{"test", HybridQuantity::Scalar::rho, nx, ny}; + Field, HybridQuantity::Scalar> other{"other", HybridQuantity::Scalar::rho, nx, ny}; for (auto& v : f) { @@ -221,10 +224,9 @@ TEST(Field3D, canBeAssigned) auto nx = 10u; auto ny = 11u; auto nz = 12u; - Field, HybridQuantity::Scalar> f{"test", HybridQuantity::Scalar::rho, nx, ny, + Field, HybridQuantity::Scalar> f{"test", HybridQuantity::Scalar::rho, nx, ny, nz}; + Field, HybridQuantity::Scalar> other{"other", HybridQuantity::Scalar::rho, nx, ny, nz}; - Field, HybridQuantity::Scalar> other{"other", HybridQuantity::Scalar::rho, nx, - ny, nz}; for (auto& v : f) { @@ -245,11 +247,10 @@ TEST(Field3D, canBeAssigned) TEST(Field1D, canBeAveraged) { auto nx = 15u; - Field, HybridQuantity::Scalar> f1{"f1", HybridQuantity::Scalar::rho, nx}; - Field, HybridQuantity::Scalar> f2{"f2", HybridQuantity::Scalar::rho, nx}; - Field, HybridQuantity::Scalar> avg{"f2", HybridQuantity::Scalar::rho, nx}; + Field, HybridQuantity::Scalar> f1{"f1", HybridQuantity::Scalar::rho, nx}; + Field, HybridQuantity::Scalar> f2{"f2", HybridQuantity::Scalar::rho, nx}; + Field, HybridQuantity::Scalar> avg{"f2", HybridQuantity::Scalar::rho, nx}; - // for (auto& v : f1) { v = 10.; @@ -276,9 +277,9 @@ TEST(Field2D, canBeAveraged) { auto nx = 15u; auto ny = 25u; - Field, HybridQuantity::Scalar> f1{"f1", HybridQuantity::Scalar::rho, nx, ny}; - Field, HybridQuantity::Scalar> f2{"f2", HybridQuantity::Scalar::rho, nx, ny}; - Field, HybridQuantity::Scalar> avg{"f2", HybridQuantity::Scalar::rho, nx, ny}; + Field, HybridQuantity::Scalar> f1{"f1", HybridQuantity::Scalar::rho, nx, ny}; + Field, HybridQuantity::Scalar> f2{"f2", HybridQuantity::Scalar::rho, nx, ny}; + Field, HybridQuantity::Scalar> avg{"f2", HybridQuantity::Scalar::rho, nx, ny}; // for (auto& v : f1) @@ -308,12 +309,9 @@ TEST(Field3D, canBeAveraged) auto nx = 15u; auto ny = 25u; auto nz = 35u; - Field, HybridQuantity::Scalar> f1{"f1", HybridQuantity::Scalar::rho, nx, ny, - nz}; - Field, HybridQuantity::Scalar> f2{"f2", HybridQuantity::Scalar::rho, nx, ny, - nz}; - Field, HybridQuantity::Scalar> avg{"f2", HybridQuantity::Scalar::rho, nx, ny, - nz}; + Field, HybridQuantity::Scalar> f1{"f1", HybridQuantity::Scalar::rho, nx, ny, nz}; + Field, HybridQuantity::Scalar> f2{"f2", HybridQuantity::Scalar::rho, nx, ny, nz}; + Field, HybridQuantity::Scalar> avg{"f2", HybridQuantity::Scalar::rho, nx, ny, nz}; // for (auto& v : f1) diff --git a/tests/core/data/field/test_field.hpp b/tests/core/data/field/test_field.hpp index 38eff4218..8bce0d8f9 100644 --- a/tests/core/data/field/test_field.hpp +++ b/tests/core/data/field/test_field.hpp @@ -175,7 +175,7 @@ void test(GridLayout const& layout, template void test(GridLayout const& layout, - PHARE::core::Field const& field0, + PHARE::core::FieldView const& field0, PHARE::core::NdArrayView const& field1, FF const ff = FF{}) { static_assert(NdArrayImpl::dimension == dim); @@ -186,11 +186,12 @@ void test(GridLayout const& layout, template void test(GridLayout const& layout, - PHARE::core::Field const& field0, + PHARE::core::FieldView const& field0, std::vector const& fieldV, FF const ff = FF{}) { EXPECT_EQ(field0.size(), fieldV.size()); - core::NdArrayView const field1{fieldV.data(), field0.shape()}; + + auto field1 = core::make_array_view(fieldV, field0.shape()); test_fields(layout, field0, field1, ff); } @@ -200,20 +201,11 @@ void test(GridLayout const& layout, PHARE::core::Field field0, std::vector&& fieldV, FF const ff = FF{}) { EXPECT_EQ(field0.size(), fieldV.size()); - core::NdArrayView field1{fieldV, field0.shape()}; - test_fields(layout, field0, field1, ff); -} - -template -void test(GridLayout const& layout, PHARE::core::Field field0, - PHARE::core::Field field1, FF const ff = FF{}) -{ + auto field1 = core::make_array_view(fieldV, field0.shape()); test_fields(layout, field0, field1, ff); } - } // namespace PHARE::core diff --git a/tests/core/data/field/test_field_gpu.cpp b/tests/core/data/field/test_field_gpu.cpp new file mode 100644 index 000000000..0a1d06271 --- /dev/null +++ b/tests/core/data/field/test_field_gpu.cpp @@ -0,0 +1,60 @@ +#include "phare_core.hpp" +// #include "test_gridlayout.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace PHARE::core; + +template +using NdArray_t = typename PHARE_Types::Array_t; + +template +using ManagedVector = std::vector>; + +TEST(FieldGPUTest, copyWorksOnGPU) +{ + static_assert(std::is_same_v, + mkn::gpu::ManagedAllocator>); + + std::size_t constexpr static N = 1024; + std::size_t constexpr static dim = 3; + std::size_t constexpr static interp = 1; + std::uint32_t constexpr static cells = 30; + using Field_t = Field, HybridQuantity::Scalar>; + + Field_t rho{"test", HybridQuantity::Scalar::rho, ConstArray(cells)}; + auto v_rho = rho.view(); + assert(mkn::gpu::Pointer{rho.data()}.is_managed_ptr()); + assert(mkn::gpu::Pointer{v_rho.data()}.is_managed_ptr()); + KLOG(INF) << rho.size(); + for (std::size_t i = 0; i < N; ++i) + rho.data()[i] = i; + + { + mkn::gpu::Pointer a{rho.data()}; + KLOG(INF) << a.attributes.memoryType; + KLOG(INF) << a.attributes.isManaged; + KLOG(INF) << a.attributes.devicePointer; + KLOG(INF) << a.attributes.hostPointer; + KLOG(INF) << a.is_host_ptr(); + KLOG(INF) << a.is_managed_ptr(); + KLOG(INF) << a.is_device_ptr(); + } + + ManagedVector> icells(N, ConstArray(1)); + + auto rd = v_rho.data(); + // mkn::gpu::GDLauncher{N}([=] __device__(auto& r) { r.data()[mkn::gpu::idx()] += 1; }, v_rho); + mkn::gpu::GDLauncher{N}([=] __device__() { rd[mkn::gpu::idx()] += 1; }); + + auto expect = ConstArray(4); // 2 + 2 ghostnodes + for (std::size_t i = 0; i < N; ++i) + EXPECT_EQ(rho.data()[i], i + 1); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/core/data/gridlayout/gridlayout_amr.cpp b/tests/core/data/gridlayout/gridlayout_amr.cpp index e68e01421..d1c319254 100644 --- a/tests/core/data/gridlayout/gridlayout_amr.cpp +++ b/tests/core/data/gridlayout/gridlayout_amr.cpp @@ -84,7 +84,7 @@ TEST(GridLayout, canTransformAnAMRBoxIntoALocalBox) int nGhosts = layout.nbrGhosts(QtyCentering::dual); auto AMRBox = Box{Point{55}, Point{65}}; - auto expectedLocalBox = Box{Point{nGhosts + 5}, Point{nGhosts + 15}}; + auto expectedLocalBox = Box{Point{nGhosts + 5}, Point{nGhosts + 15}}; EXPECT_EQ(expectedLocalBox, layout.AMRToLocal(AMRBox)); } diff --git a/tests/core/data/gridlayout/gridlayout_test.hpp b/tests/core/data/gridlayout/gridlayout_test.hpp index 7f1897fdc..da7aecd6e 100644 --- a/tests/core/data/gridlayout/gridlayout_test.hpp +++ b/tests/core/data/gridlayout/gridlayout_test.hpp @@ -5,7 +5,7 @@ #include "core/data/grid/gridlayoutimplyee.hpp" #include "core/utilities/types.hpp" - +#include "test_gridlayout.hpp" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -23,22 +23,4 @@ class GridLayoutTest : public ::testing::TestWithParam> Param param; }; -template -class TestGridLayout : public GridLayout -{ // to expose a default constructor -public: - auto static constexpr dim = GridLayout::dimension; - - TestGridLayout() = default; - - TestGridLayout(std::uint32_t cells) - : GridLayout{PHARE::core::ConstArray(1.0 / cells), - PHARE::core::ConstArray(cells), - PHARE::core::Point{PHARE::core::ConstArray(0)}} - { - } - - auto static make(std::uint32_t cells) { return TestGridLayout{cells}; } -}; - #endif diff --git a/tests/core/data/gridlayout/test_gridlayout.hpp b/tests/core/data/gridlayout/test_gridlayout.hpp new file mode 100644 index 000000000..e4a9ed5f7 --- /dev/null +++ b/tests/core/data/gridlayout/test_gridlayout.hpp @@ -0,0 +1,35 @@ +#ifndef TESTS_CORE_DATA_GRIDLAYOUT_TEST_GRIDLAYOUT_HPP +#define TESTS_CORE_DATA_GRIDLAYOUT_TEST_GRIDLAYOUT_HPP + +#include "core/data/grid/gridlayout.hpp" +#include "core/data/grid/gridlayoutimplyee.hpp" +#include "core/utilities/types.hpp" + + +template +class TestGridLayout : public GridLayout +{ // to expose a default constructor +public: + auto static constexpr dim = GridLayout::dimension; + + TestGridLayout() = default; + + TestGridLayout(double dl, std::uint32_t cells) + : GridLayout{PHARE::core::ConstArray(dl), + PHARE::core::ConstArray(cells), + PHARE::core::Point{PHARE::core::ConstArray(0)}} + { + } + + TestGridLayout(std::uint32_t cells) + : GridLayout{PHARE::core::ConstArray(1.0 / cells), + PHARE::core::ConstArray(cells), + PHARE::core::Point{PHARE::core::ConstArray(0)}} + { + } + + auto static make(std::uint32_t cells) { return TestGridLayout{cells}; } + auto static make(double dl, std::uint32_t cells) { return TestGridLayout{dl, cells}; } +}; + +#endif /*TESTS_CORE_DATA_GRIDLAYOUT_TEST_GRIDLAYOUT_HPP*/ diff --git a/tests/core/data/gridlayout/test_gridlayout_gpu.cpp b/tests/core/data/gridlayout/test_gridlayout_gpu.cpp new file mode 100644 index 000000000..04bc4e1bd --- /dev/null +++ b/tests/core/data/gridlayout/test_gridlayout_gpu.cpp @@ -0,0 +1,41 @@ +#include "phare_core.hpp" +#include "test_gridlayout.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace PHARE::core; + +template +using ManagedVector = std::vector>; + +TEST(GridLayoutGPUTest, copyWorksOnGPU) +{ + std::size_t constexpr static N = 1024; + std::size_t constexpr static dim = 3; + std::size_t constexpr static interp = 1; + std::uint32_t constexpr static cells = 30; + + using PHARE_Types = PHARE::core::PHARE_Types; + using GridLayout_t = typename PHARE_Types::GridLayout_t; + + TestGridLayout layout{cells}; + + ManagedVector> icells(N, ConstArray(1)); + + mkn::gpu::GDLauncher{N}( + [=] __device__(auto ics, auto& l) { + ics[mkn::gpu::idx()] = l.AMRToLocal(Point{ConstArray(2)}).toArray(); + }, + icells, layout); + + auto expect = ConstArray(4); // 2 + 2 ghostnodes + for (std::size_t i = 0; i < N; ++i) + EXPECT_EQ(icells[i], expect); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/core/data/ion_population/test_ion_population.cpp b/tests/core/data/ion_population/test_ion_population.cpp index 4ad501da7..587c6bc60 100644 --- a/tests/core/data/ion_population/test_ion_population.cpp +++ b/tests/core/data/ion_population/test_ion_population.cpp @@ -2,13 +2,13 @@ #include - - #include "core/data/ions/ion_population/ion_population.hpp" +#include "core/data/particles/particle.hpp" #include "core/data/particles/particle_array.hpp" #include "initializer/data_provider.hpp" #include "core/hybrid/hybrid_quantities.hpp" + #include "gmock/gmock.h" #include "gtest/gtest.h" diff --git a/tests/core/data/ions/test_ions.cpp b/tests/core/data/ions/test_ions.cpp index c02afc4ab..3ee4e410a 100644 --- a/tests/core/data/ions/test_ions.cpp +++ b/tests/core/data/ions/test_ions.cpp @@ -2,6 +2,7 @@ #include +#include "phare_core.hpp" #include "core/data/ions/ion_population/ion_population.hpp" #include "core/data/ions/ions.hpp" @@ -26,19 +27,23 @@ using namespace PHARE::core; static constexpr std::size_t dim = 1; static constexpr std::size_t interpOrder = 1; -using GridImplYee1D = GridLayoutImplYee; -using GridYee1D = GridLayout; -using MaxwellianParticleInitializer1D = MaxwellianParticleInitializer, GridYee1D>; +using PHARE_TYPES = PHARE::core::PHARE_Types; +using NdArray_t = typename PHARE_TYPES::Array_t; +using ParticleArray_t = typename PHARE_TYPES::ParticleArray_t; + +using GridImplYee1D = GridLayoutImplYee; +using GridYee1D = GridLayout; +using MaxwellianParticleInitializer1D = MaxwellianParticleInitializer; class theIons : public ::testing::Test { protected: - using VecField1D = VecField, HybridQuantity>; + using VecField1D = VecField; using InitFunctionT = PHARE::initializer::InitFunction<1>; - using IonPopulation1D = IonPopulation, VecField1D, GridYee1D>; + using IonPopulation1D = IonPopulation; Ions ions; PHARE::initializer::PHAREDict createIonsDict() diff --git a/tests/core/data/maxwellian_particle_initializer/test_maxwellian_particle_initializer.cpp b/tests/core/data/maxwellian_particle_initializer/test_maxwellian_particle_initializer.cpp index 30d037226..af2db5fe5 100644 --- a/tests/core/data/maxwellian_particle_initializer/test_maxwellian_particle_initializer.cpp +++ b/tests/core/data/maxwellian_particle_initializer/test_maxwellian_particle_initializer.cpp @@ -1,6 +1,4 @@ - -#include - +// tests/core/data/maxwellian_particle_initializer/test_maxwellian_particle_initializer.cpp #include "core/data/grid/gridlayout.hpp" #include "core/data/grid/gridlayout_impl.hpp" @@ -20,38 +18,52 @@ using namespace PHARE::core; using namespace PHARE::initializer; +template class AMaxwellianParticleInitializer1D : public ::testing::Test { private: using GridLayoutT = GridLayout>; - using ParticleArrayT = ParticleArray<1>; using InitFunctionArray = std::array, 3>; + auto static init_particles(GridLayoutT const& layout) + { + if constexpr (ParticleArrayT::is_mapped) + return ParticleArrayT{layout.AMRBox()}; + else + return ParticleArrayT{}; + } + public: + static constexpr std::uint32_t nbrParticlesPerCell = 10000; + AMaxwellianParticleInitializer1D() : layout{{{0.1}}, {{50}}, Point{0.}, Box{Point{50}, Point{99}}} - , particles{layout.AMRBox()} + , particles{init_particles(layout)} , initializer{std::make_unique>( density, InitFunctionArray{vx, vy, vz}, InitFunctionArray{vthx, vthy, vthz}, 1., nbrParticlesPerCell)} { - // } GridLayoutT layout; ParticleArrayT particles; - std::uint32_t nbrParticlesPerCell{10000}; std::unique_ptr> initializer; + + auto operator()() { return std::forward_as_tuple(layout, particles, initializer); } }; +using Tests1d = testing::Types, AoSParticleArray<1>, SoAParticleArray<1>>; +TYPED_TEST_SUITE(AMaxwellianParticleInitializer1D, Tests1d); -TEST_F(AMaxwellianParticleInitializer1D, loadsTheCorrectNbrOfParticles) +TYPED_TEST(AMaxwellianParticleInitializer1D, loadsTheCorrectNbrOfParticles) { + auto const& [layout, particles, initializer] = (*this)(); + auto nbrCells = layout.nbrCells(); - auto expectedNbrParticles = nbrParticlesPerCell * nbrCells[0]; + auto expectedNbrParticles = TestFixture::nbrParticlesPerCell * nbrCells[0]; initializer->loadParticles(particles, layout); EXPECT_EQ(expectedNbrParticles, particles.size()); } @@ -59,26 +71,25 @@ TEST_F(AMaxwellianParticleInitializer1D, loadsTheCorrectNbrOfParticles) -TEST_F(AMaxwellianParticleInitializer1D, loadsParticlesInTheDomain) +TYPED_TEST(AMaxwellianParticleInitializer1D, loadsParticlesInTheDomain) { + auto const& [layout, particles, initializer] = (*this)(); + initializer->loadParticles(particles, layout); - auto i = 0u; - for (auto const& particle : particles) + + for (std::size_t i = 0; i < particles.size(); i++) { - EXPECT_TRUE(particle.iCell[0] >= 50 && particle.iCell[0] <= 99); - auto pos = positionAsPoint(particle, layout); + EXPECT_TRUE(particles.iCell(i)[0] >= 50 && particles.iCell(i)[0] <= 99); + auto pos = positionAsPoint(particles, i, layout); auto endDomain = layout.origin()[0] + layout.nbrCells()[0] * layout.meshSize()[0]; if (!((pos[0] > 0.) and (pos[0] < endDomain))) std::cout << "position : " << pos[0] << " not in domain (0," << endDomain << ")\n"; EXPECT_TRUE(pos[0] > 0. && pos[0] < endDomain); - i++; } } - - int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); diff --git a/tests/core/data/ndarray/raja_umpire_tests.h b/tests/core/data/ndarray/raja_umpire_tests.h new file mode 100644 index 000000000..1bbe29e00 --- /dev/null +++ b/tests/core/data/ndarray/raja_umpire_tests.h @@ -0,0 +1,50 @@ + +template +using NdArray_t = typename PHARE::core::PHARE_Types::Array_t; +using NdArrays = ::testing::Types, NdArray_t<2>, NdArray_t<3>>; + +template +struct NdArrayTest : public ::testing::Test +{ + static const auto dimension = NdArray::dimension; +}; +TYPED_TEST_SUITE(NdArrayTest, NdArrays); + +TYPED_TEST(NdArrayTest, is_raja_exec_settable) +{ + static constexpr auto dimension = TypeParam::dimension; + static constexpr auto shape = ConstArray(10); + + TypeParam src_arr{shape}, dst_arr{shape}; + for (auto& e : src_arr) + e = 12.; + auto dst = dst_arr.data(); + auto src = src_arr.data(); + PHARE::raja::exec([=] RAJA_DEVICE(int i) { dst[i] = src[i]; }, dst_arr.size()); + for (auto const& e : dst_arr) + EXPECT_DOUBLE_EQ(12., e); +} + +TYPED_TEST(NdArrayTest, is_raja_copyable) +{ + static constexpr auto dimension = TypeParam::dimension; + static constexpr auto shape = ConstArray(10); + + TypeParam src_arr{shape}, dst_arr{shape}; + for (auto& e : src_arr) + e = 12.; + PHARE::raja::copy(dst_arr.data(), src_arr.data(), dst_arr.size()); + for (auto const& e : dst_arr) + EXPECT_DOUBLE_EQ(12., e); +} + +TYPED_TEST(NdArrayTest, is_raja_settable) +{ + static constexpr auto dimension = TypeParam::dimension; + static constexpr auto shape = ConstArray(10); + + TypeParam dst_arr{shape}; + PHARE::raja::set(dst_arr.data(), 12., dst_arr.size()); + for (auto const& e : dst_arr) + EXPECT_DOUBLE_EQ(12., e); +} diff --git a/tests/core/data/ndarray/test_main.cpp b/tests/core/data/ndarray/test_main.cpp index be0bebd9f..52ee471da 100644 --- a/tests/core/data/ndarray/test_main.cpp +++ b/tests/core/data/ndarray/test_main.cpp @@ -1,14 +1,21 @@ -#include "gmock/gmock.h" -#include "gtest/gtest.h" #include #include +#include "phare_core.hpp" #include "core/data/ndarray/ndarray_vector.hpp" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + using namespace PHARE::core; +#if PHARE_HAVE_RAJA and PHARE_HAVE_UMPIRE +#include "raja_umpire_tests.h" +#endif + + template class GenericNdArray1D : public ::testing::Test @@ -58,9 +65,12 @@ class GenericNdArray3D : public ::testing::Test }; -using NdArray1D = ::testing::Types>; -using NdArray2D = ::testing::Types>; -using NdArray3D = ::testing::Types>; +template +using NdArray_t = typename PHARE_Types::Array_t; + +using NdArray1D = ::testing::Types>; +using NdArray2D = ::testing::Types>; +using NdArray3D = ::testing::Types>; TYPED_TEST_SUITE(GenericNdArray1D, NdArray1D); @@ -86,8 +96,8 @@ TYPED_TEST(GenericNdArray1D, IsModifiable) TYPED_TEST(GenericNdArray1D, CanBeReadOnly) { std::uint32_t i{2}; - this->a(i) = 12.; - NdArrayVector<1> const& ref = this->a; + this->a(i) = 12.; + NdArray_t<1> const& ref = this->a; EXPECT_EQ(12., ref(i)); } @@ -110,12 +120,15 @@ TYPED_TEST(GenericNdArray1D, AccessWholeArray) } -TYPED_TEST(GenericNdArray1D, HasCopyCtor) +TYPED_TEST(GenericNdArray1D, HasCopyCtor) // this is operator= test { for (auto& e : this->a) e = 12.; - NdArrayVector<1> other{this->nx}; + for (auto const& e : this->a) + EXPECT_DOUBLE_EQ(12., e); + + NdArray_t<1> other{this->nx}; other = this->a; for (auto const& e : other) @@ -128,7 +141,10 @@ TYPED_TEST(GenericNdArray1D, HasMoveCtor) for (auto& e : this->a) e = 12.; - NdArrayVector<1> other = std::move(this->a); + for (auto const& e : this->a) + EXPECT_DOUBLE_EQ(12., e); + + NdArray_t<1> other = std::move(this->a); for (auto const& e : other) EXPECT_DOUBLE_EQ(12., e); @@ -153,8 +169,8 @@ TYPED_TEST(GenericNdArray2D, IsModifiable) TYPED_TEST(GenericNdArray2D, CanBeReadOnly) { std::uint32_t i{2}, j{3}; - this->a(i, j) = 12.; - NdArrayVector<2> const& ref = this->a; + this->a(i, j) = 12.; + NdArray_t<2> const& ref = this->a; EXPECT_EQ(12., ref(i, j)); } @@ -186,7 +202,7 @@ TYPED_TEST(GenericNdArray2D, HasCopyCtor) for (auto& e : this->a) e = 12.; - NdArrayVector<2> other{this->nx, this->ny}; + NdArray_t<2> other{this->nx, this->ny}; other = this->a; for (auto const& e : other) @@ -199,7 +215,7 @@ TYPED_TEST(GenericNdArray2D, HasMoveCtor) for (auto& e : this->a) e = 12.; - NdArrayVector<2> other = std::move(this->a); + NdArray_t<2> other = std::move(this->a); for (auto const& e : other) EXPECT_DOUBLE_EQ(12., e); @@ -224,8 +240,8 @@ TYPED_TEST(GenericNdArray3D, IsModifiable) TYPED_TEST(GenericNdArray3D, CanBeReadOnly) { std::uint32_t i{2}, j{3}, k{4}; - this->a(i, j, k) = 12.; - NdArrayVector<3> const& ref = this->a; + this->a(i, j, k) = 12.; + NdArray_t<3> const& ref = this->a; EXPECT_EQ(12., ref(i, j, k)); } @@ -261,7 +277,7 @@ TYPED_TEST(GenericNdArray3D, HasCopyCtor) for (auto& e : this->a) e = 12.; - NdArrayVector<3> other{this->nx, this->ny, this->nz}; + NdArray_t<3> other{this->nx, this->ny, this->nz}; other = this->a; for (auto const& e : other) @@ -274,7 +290,7 @@ TYPED_TEST(GenericNdArray3D, HasMoveCtor) for (auto& e : this->a) e = 12.; - NdArrayVector<3> other = std::move(this->a); + NdArray_t<3> other = std::move(this->a); for (auto const& e : other) EXPECT_DOUBLE_EQ(12., e); @@ -282,12 +298,13 @@ TYPED_TEST(GenericNdArray3D, HasMoveCtor) + TEST(MaskedView1d, maskOps) { constexpr std::size_t dim = 1; constexpr std::uint32_t size = 20; using Mask = NdArrayMask; - NdArrayVector array{size}; + NdArray_t array{size}; EXPECT_EQ(std::accumulate(array.begin(), array.end(), 0), 0); @@ -320,7 +337,7 @@ TEST(MaskedView2d, maskOps) constexpr std::uint32_t size = 20; constexpr std::uint32_t sizeSq = 20 * 20; using Mask = NdArrayMask; - NdArrayVector array{size, size}; + NdArray_t array{size, size}; EXPECT_EQ(std::accumulate(array.begin(), array.end(), 0), 0); @@ -359,7 +376,7 @@ TEST(MaskedView2d, maskOps2) constexpr std::uint32_t size0 = 20, size1 = 22; constexpr std::uint32_t sizeSq = size0 * size1; using Mask = NdArrayMask; - NdArrayVector array{size0, size1}; + NdArray_t array{size0, size1}; EXPECT_EQ(std::accumulate(array.begin(), array.end(), 0), 0); diff --git a/tests/core/data/particle_initializer/test_main.cpp b/tests/core/data/particle_initializer/test_main.cpp index 405d42e5a..a7e99b90a 100644 --- a/tests/core/data/particle_initializer/test_main.cpp +++ b/tests/core/data/particle_initializer/test_main.cpp @@ -5,6 +5,7 @@ #include "core/data/grid/gridlayout.hpp" #include "core/data/grid/gridlayoutimplyee.hpp" #include "core/data/ions/particle_initializers/particle_initializer_factory.hpp" +#include "core/data/particles/particle.hpp" #include "core/data/particles/particle_array.hpp" #include "initializer/data_provider.hpp" @@ -12,15 +13,11 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include #include using namespace PHARE::core; using namespace PHARE::initializer; -using namespace PHARE::core; -using namespace PHARE::initializer; - #include "tests/initializer/init_functions.hpp" using namespace PHARE::initializer::test_fn::func_1d; // density/etc are here diff --git a/tests/core/data/particles/CMakeLists.txt b/tests/core/data/particles/CMakeLists.txt index 062dc496d..7869758b6 100644 --- a/tests/core/data/particles/CMakeLists.txt +++ b/tests/core/data/particles/CMakeLists.txt @@ -1,22 +1,16 @@ cmake_minimum_required (VERSION 3.9) -project(test-particles) +project(test_particles) -function(_particles_test src test_name) +add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) - add_executable(${test_name} ${src}) +target_include_directories(${PROJECT_NAME} PRIVATE + ${GTEST_INCLUDE_DIRS} +) - target_include_directories(${test_name} PRIVATE - ${GTEST_INCLUDE_DIRS} - ) +target_link_libraries(${PROJECT_NAME} PRIVATE + phare_core + ${GTEST_LIBS}) - target_link_libraries(${test_name} PRIVATE - phare_core - ${GTEST_LIBS}) +add_phare_test(${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}) - add_phare_test(${test_name} ${CMAKE_CURRENT_BINARY_DIR}) - -endfunction(_particles_test) - -_particles_test(test_main.cpp test-particles) -_particles_test(test_interop.cpp test-particles-interop) diff --git a/tests/core/data/particles/sorting/test_particle_sorting.cpp b/tests/core/data/particles/sorting/test_particle_sorting.cpp new file mode 100644 index 000000000..bf28fcfe4 --- /dev/null +++ b/tests/core/data/particles/sorting/test_particle_sorting.cpp @@ -0,0 +1,70 @@ + +// #include "core/data/grid/gridlayout.hpp" +// #include "core/data/grid/gridlayoutimplyee.hpp" +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array.hpp" +// #include "core/data/particles/particle_utilities.hpp" +// #include "core/utilities/point/point.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace PHARE::core; + +template +class ParticleArrayFixture3D +{ + static constexpr std::size_t ndim = 3; + using Particle_t = Particle; + +public: + void add(std::array const& iCell) + { + particles.push_back(Particle_t{0.01, 1., iCell, {0.002f, 0.2f, 0.8f}, {1.8, 1.83, 2.28}}); + } + + void print() + { + for (auto const& particle : particles) + std::cout << __LINE__ << " " << Point{particle.iCell()} << std::endl; + std::cout << std::endl; + } + + ParticleArray particles; +}; + +template +struct ParticleArraySortingTest : public ::testing::Test +{ +}; + +using ParticlesArrays = testing::Types>, + ParticleArrayFixture3D> // + >; + +TYPED_TEST_SUITE(ParticleArraySortingTest, ParticlesArrays, ); + +TYPED_TEST(ParticleArraySortingTest, _3d_sorting_test) +{ + TypeParam fixture{}; + auto& particles = fixture.particles; + std::vector> iCells{{2, 2, 2}, {2, 1, 0}, {1, 1, 1}, // + {1, 0, 1}, {0, 2, 0}, {0, 0, 0}, + {1, 1, 2}, {0, 1, 0}, {2, 0, 2}}; + for (auto const& iCell : iCells) + fixture.add(iCell); + std::sort(particles); + std::vector> expected{{0, 0, 0}, {0, 1, 0}, {2, 1, 0}, // + {0, 2, 0}, {1, 0, 1}, {1, 1, 1}, + {2, 0, 2}, {1, 1, 2}, {2, 2, 2}}; + ASSERT_EQ(particles.size(), expected.size()); + for (std::size_t i = 0; i < particles.size(); ++i) + ASSERT_EQ(particles.iCell(i), expected[i]); +} + + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/core/data/particles/test_bisection_range_mapper.cpp b/tests/core/data/particles/test_bisection_range_mapper.cpp new file mode 100644 index 000000000..cab7fca9a --- /dev/null +++ b/tests/core/data/particles/test_bisection_range_mapper.cpp @@ -0,0 +1,41 @@ + +#include "core/utilities/types.hpp" +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array.hpp" +#include "core/data/particles/mapper/bisection_range_mapper.hpp" + +#include "tests/core/data/particles/test_particles.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace PHARE::core; + + +template +struct ParticleArrayTest : public ::testing::Test +{ +}; + +using ParticleList = testing::Types, AoSParticleArray<3>>; +TYPED_TEST_SUITE(ParticleArrayTest, ParticleList, ); + +TYPED_TEST(ParticleArrayTest, testMapping) +{ + using ParticleArray = TypeParam; + using Box_t = Box; + using Mapper = BisectionRangeMapper; + + Box_t box{{0, 0, 0}, {9, 9, 9}}; + ParticleArray particles; + add_particles_in(particles, box, 100); + + Mapper mapper{particles, box}; + mapper.map(); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/core/data/particles/test_edge_bisection_ghost_mapper.cpp b/tests/core/data/particles/test_edge_bisection_ghost_mapper.cpp new file mode 100644 index 000000000..d4bee96d6 --- /dev/null +++ b/tests/core/data/particles/test_edge_bisection_ghost_mapper.cpp @@ -0,0 +1,40 @@ + +#include "core/utilities/types.hpp" +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array.hpp" +#include "core/data/particles/mapper/edge_bisection_inner_ghost_area_mapper.hpp" + +#include "tests/core/data/particles/test_particles.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace PHARE::core; + +template +struct ParticleArrayTest : public ::testing::Test +{ +}; + +using ParticleList = testing::Types, AoSParticleArray<3>>; +TYPED_TEST_SUITE(ParticleArrayTest, ParticleList, ); + +TYPED_TEST(ParticleArrayTest, testMapping) +{ + using ParticleArray = TypeParam; + std::size_t constexpr static extra_ghost_cells = 2; + + Box box{{0, 0, 0}, {9, 9, 9}}; + + ParticleArray particles; + add_particles_in(particles, box, 100); + + EdgeBisectionMapper mapper; + mapper.map(particles, box); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/core/data/particles/test_interop.cpp b/tests/core/data/particles/test_interop.cpp index c9eb98f12..5e1df549a 100644 --- a/tests/core/data/particles/test_interop.cpp +++ b/tests/core/data/particles/test_interop.cpp @@ -7,61 +7,61 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -using namespace PHARE::core; +// using namespace PHARE::core; -template -struct ParticleListTest : public ::testing::Test -{ -}; +// template +// struct ParticleListTest : public ::testing::Test +// { +// }; -using ParticleList = testing::Types, Particle<2>, Particle<3>>; +// using ParticleList = testing::Types, Particle<2>, Particle<3>>; -TYPED_TEST_SUITE(ParticleListTest, ParticleList); +// TYPED_TEST_SUITE(ParticleListTest, ParticleList); -TYPED_TEST(ParticleListTest, SoAandAoSInterop) -{ - using Particle = TypeParam; - constexpr auto dim = Particle::dimension; - constexpr std::size_t size = 10; - constexpr Box domain{ConstArray(0), ConstArray(size - 1)}; +// TYPED_TEST(ParticleListTest, SoAandAoSInterop) +// { +// using Particle = TypeParam; +// constexpr auto dim = Particle::dimension; +// constexpr std::size_t size = 10; +// constexpr Box domain{ConstArray(0), ConstArray(size - 1)}; - ContiguousParticles contiguous{size}; - for (std::size_t i = 0; i < size; i++) - { - auto view = contiguous[i]; - view.weight = 1 + i; - view.charge = 1 + i; - view.iCell = ConstArray(i); - view.delta = ConstArray(i + 1); - view.v = ConstArray(view.weight + 2); - EXPECT_EQ(std::copy(view), view); - } - EXPECT_EQ(contiguous.size(), size); +// ContiguousParticles contiguous{size}; +// for (std::size_t i = 0; i < size; i++) +// { +// auto view = contiguous[i]; +// view.weight = 1 + i; +// view.charge = 1 + i; +// view.iCell = ConstArray(i); +// view.delta = ConstArray(i + 1); +// view.v = ConstArray(view.weight + 2); +// EXPECT_EQ(std::copy(view), view); +// } +// EXPECT_EQ(contiguous.size(), size); - for (std::size_t i = 0; i < size; i++) - { - EXPECT_EQ(contiguous.weight[i], i + 1); // fastest - EXPECT_EQ(contiguous[i].weight, i + 1); - EXPECT_EQ(contiguous[i], std::copy(contiguous[i])); - } +// for (std::size_t i = 0; i < size; i++) +// { +// EXPECT_EQ(contiguous.weight[i], i + 1); // fastest +// EXPECT_EQ(contiguous[i].weight, i + 1); +// EXPECT_EQ(contiguous[i], std::copy(contiguous[i])); +// } - ParticleArray particleArray{domain}; - for (auto const& view : contiguous) - { - auto i = particleArray.size(); - particleArray.emplace_back(std::copy(view)); - EXPECT_EQ(contiguous[i], particleArray.back()); - } - EXPECT_EQ(particleArray.size(), size); - EXPECT_EQ(contiguous.size(), particleArray.size()); +// ParticleArray particleArray; +// for (auto const& view : contiguous) +// { +// auto i = particleArray.size(); +// particleArray.emplace_back(std::copy(view)); +// EXPECT_EQ(contiguous[i], particleArray.back()); +// } +// EXPECT_EQ(particleArray.size(), size); +// EXPECT_EQ(contiguous.size(), particleArray.size()); - ContiguousParticles AoSFromSoA{particleArray.size()}; - ParticlePacker{particleArray}.pack(AoSFromSoA); +// ContiguousParticles AoSFromSoA{particleArray.size()}; +// ParticlePacker{particleArray}.pack(AoSFromSoA); - std::size_t i = 0; - for (auto const& particle : AoSFromSoA) - EXPECT_EQ(particle, particleArray[i++]); -} +// std::size_t i = 0; +// for (auto const& particle : AoSFromSoA) +// EXPECT_EQ(particle, particleArray[i++]); +// } int main(int argc, char** argv) { diff --git a/tests/core/data/particles/test_interop.hpp b/tests/core/data/particles/test_interop.hpp new file mode 100644 index 000000000..a2c466e6b --- /dev/null +++ b/tests/core/data/particles/test_interop.hpp @@ -0,0 +1,75 @@ + +#include "core/utilities/types.hpp" +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array.hpp" +#include "core/data/particles/particle_packer.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using namespace PHARE::core; + +template +struct ParticleListTest : public ::testing::Test +{ +}; + +using ParticleList = testing::Types, Particle<2>, Particle<3>>; +TYPED_TEST_SUITE(ParticleListTest, ParticleList, ); + +TYPED_TEST(ParticleListTest, SoAandAoSInterop) +{ + using Particle = TypeParam; + constexpr auto dim = Particle::dimension; + constexpr std::size_t size = 10; + // constexpr Box domain{ConstArray(0), ConstArray(size - 1)}; + + SoAParticleArray contiguous{size}; + for (std::size_t i = 0; i < size; i++) + { + contiguous.weight(i) = 1 + i; + contiguous.charge(i) = 1 + i; + contiguous.iCell(i) = ConstArray(i); + contiguous.delta(i) = ConstArray(i + 1); + contiguous.v(i) = ConstArray(contiguous.weight(i) + 2); + // EXPECT_EQ(std::copy(contiguous[i]), contiguous[i]); + } + EXPECT_EQ(contiguous.size(), size); + + for (std::size_t i = 0; i < size; i++) + { + EXPECT_EQ(contiguous.weight(i), i + 1); // fastest + // EXPECT_EQ(contiguous[i], std::copy(contiguous[i])); + } + + ParticleArray particleArray; + for (auto const& it : contiguous) + { + auto i = particleArray.size(); + particleArray.emplace_back(std::copy(it)); + EXPECT_EQ(std::copy(contiguous.begin() + i), particleArray.back()); + } + EXPECT_EQ(particleArray.size(), size); + EXPECT_EQ(contiguous.size(), particleArray.size()); + + contiguous.reserve(particleArray.capacity()); + EXPECT_EQ(contiguous.capacity(), particleArray.capacity()); + + SoAParticleArray AoSFromSoA{particleArray.size()}; + ParticlePacker{particleArray}.pack(AoSFromSoA); + + std::size_t i = 0; + for (auto const& it : AoSFromSoA) + EXPECT_EQ(std::copy(it), particleArray[i++]); +} + +TYPED_TEST(ParticleListTest, SoAArrayTypeConstruct) +{ + typename SoAParticleArray<1>::template array_type<2> refinedParticles; +} + + +TYPED_TEST(ParticleListTest, AoSArrayTypeConstruct) +{ + typename AoSMappedParticleArray<1>::template array_type<2> refinedParticles; +} \ No newline at end of file diff --git a/tests/core/data/particles/test_particle_partitionner.hpp b/tests/core/data/particles/test_particle_partitionner.hpp new file mode 100644 index 000000000..340660794 --- /dev/null +++ b/tests/core/data/particles/test_particle_partitionner.hpp @@ -0,0 +1,365 @@ +// tests/core/data/particles/test_particle_partitionner.cpp + +#include "core/data/grid/gridlayout.hpp" +#include "core/data/grid/gridlayoutimplyee.hpp" +#include "core/data/particles/particle.hpp" +#include "core/data/particles/particle_array.hpp" +#include "core/data/particles/particle_utilities.hpp" +#include "core/data/particles/particle_array_partitionner.hpp" +#include "core/utilities/point/point.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + + +#include "tests/core/data/particles/test_particles.hpp" + + +using namespace PHARE::core; + +class ParticlePartitioner : public ::testing::Test +{ +protected: + Particle<3> part; + +public: + ParticlePartitioner() + : part{0.01, 1., {43, 75, 92}, {0.002f, 0.2f, 0.8f}, {1.8, 1.83, 2.28}} + { + } +}; + +TEST_F(ParticlePartitioner, box_remove_1) +{ + using box_t = Box; + box_t a{{0}, {2}}; + box_t b{{1}, {1}}; + auto const remaining = a.remove(b); + assert(not any_overlaps(remaining)); + EXPECT_EQ(2, sum_from(remaining, [](auto& r) { return r.size(); })); +} + +TEST_F(ParticlePartitioner, box_remove_2) +{ + using box_t = Box; + box_t a{{0, 0}, {2, 2}}; + box_t b{{1, 1}, {1, 1}}; + auto const remaining = a.remove(b); + assert(not any_overlaps(remaining)); + EXPECT_EQ(std::pow(3, 2) - 1, sum_from(remaining, [](auto& r) { return r.size(); })); +} + + +TEST_F(ParticlePartitioner, box_remove_3) +{ + auto constexpr static dim = 3; + using box_t = Box; + using point_t = Point; + + { + box_t a{{0, 0, 0}, {4, 4, 4}}; + box_t b{{0, 1, 0}, {4, 4, 4}}; + box_t r{{0, 0, 0}, {4, 0, 4}}; + auto const remaining = a.remove(b); + assert(not any_overlaps(remaining)); + assert(remaining[0] == r); + } + + auto expect_cells = [](auto const& boxes, auto const& skip_point) { + for (int x = 0; x < 3; ++x) + for (int y = 0; y < 3; ++y) + for (int z = 0; z < 3; ++z) + if (point_t p{x, y, z}; p != skip_point) + assert(isIn(p, boxes)); + }; + + { + point_t p{0, 0, 0}; + box_t a{{0, 0, 0}, {2, 2, 2}}; + auto const remaining = a.remove(box_t{p, p}); + assert(not any_overlaps(remaining)); + EXPECT_EQ(std::pow(3, 3) - 1, sum_from(remaining, [](auto& r) { return r.size(); })); + expect_cells(remaining, p); + } + + { + point_t p{0, 0, 2}; + box_t a{{0, 0, 0}, {2, 2, 2}}; + auto const remaining = a.remove(box_t{p, p}); + assert(not any_overlaps(remaining)); + EXPECT_EQ(std::pow(3, 3) - 1, sum_from(remaining, [](auto& r) { return r.size(); })); + expect_cells(remaining, p); + } + + { + point_t p{2, 2, 2}; + box_t a{{0, 0, 0}, {2, 2, 2}}; + auto const remaining = a.remove(box_t{p, p}); + assert(not any_overlaps(remaining)); + EXPECT_EQ(std::pow(3, 3) - 1, sum_from(remaining, [](auto& r) { return r.size(); })); + expect_cells(remaining, p); + } + + { + point_t p{1, 1, 1}; + box_t a{{0, 0, 0}, {2, 2, 2}}; + auto const remaining = a.remove(box_t{p, p}); + assert(not any_overlaps(remaining)); + EXPECT_EQ(std::pow(3, 3) - 1, sum_from(remaining, [](auto& r) { return r.size(); })); + expect_cells(remaining, p); + } +} + + + +TEST_F(ParticlePartitioner, simple_unique_overlaps_9) +{ + auto constexpr static dim = 3; + auto constexpr static extra_ghost_cells = 1; + using box_t = Box; + + box_t middle_box{{3, 3, 3}, {5, 5, 5}}; + + std::vector neighbor_boxes{ + // z == 0 + {{3, 3, 0}, {5, 5, 2}}, + // // z = 3 + {{3, 6, 3}, {5, 8, 5}}, + }; + assert(not any_overlaps(neighbor_boxes)); + assert(not any_overlaps(neighbor_boxes, middle_box)); + + auto neighbor_ghost_boxes + = generate([](auto box) { return box.grow(extra_ghost_cells); }, neighbor_boxes); + assert(all_overlaps(neighbor_ghost_boxes, middle_box)); + + auto overlaps = distinct_overlaps(neighbor_ghost_boxes, middle_box); + assert(not any_overlaps(overlaps)); + + EXPECT_EQ(15, sum_from(overlaps, [](auto& r) { return r.size(); })); +} + +TEST_F(ParticlePartitioner, simple_unique_overlaps) +{ + auto constexpr static dim = 3; + auto constexpr static extra_ghost_cells = 1; + using box_t = Box; + + box_t middle_box{{3, 3, 3}, {5, 5, 5}}; + + std::vector neighbor_boxes{ + // z == 0 + {{3, 3, 0}, {5, 5, 2}}, + // // z = 3 + {{3, 0, 3}, {5, 2, 5}}, + {{0, 3, 3}, {2, 5, 5}}, + {{6, 3, 3}, {8, 5, 5}}, + {{3, 6, 3}, {5, 8, 5}}, + // // z = 6 + {{3, 3, 6}, {5, 5, 8}}, + }; + assert(not any_overlaps(neighbor_boxes)); + assert(not any_overlaps(neighbor_boxes, middle_box)); + + auto neighbor_ghost_boxes + = generate([](auto box) { return box.grow(extra_ghost_cells); }, neighbor_boxes); + assert(all_overlaps(neighbor_ghost_boxes, middle_box)); + + auto overlaps = distinct_overlaps(neighbor_ghost_boxes, middle_box); + assert(not any_overlaps(overlaps)); + + EXPECT_EQ(std::pow(3, 3) - 1, sum_from(overlaps, [](auto& r) { return r.size(); })); +} + + +TEST_F(ParticlePartitioner, full_overlaps) +{ + auto constexpr static dim = 3; + auto constexpr static extra_ghost_cells = 1; + using box_t = Box; + + box_t middle_box{{3, 3, 3}, {5, 5, 5}}; + + std::vector neighbor_boxes{ + // z == 0 + {{0, 0, 0}, {2, 2, 2}}, + {{3, 0, 0}, {5, 2, 2}}, + {{6, 0, 0}, {8, 2, 2}}, + + {{0, 3, 0}, {2, 5, 2}}, + {{3, 3, 0}, {5, 5, 2}}, + {{6, 3, 0}, {8, 5, 2}}, + + {{0, 6, 0}, {2, 8, 2}}, + {{3, 6, 0}, {5, 8, 2}}, + {{6, 6, 0}, {8, 8, 2}}, + + // // z = 3 + {{0, 0, 3}, {2, 2, 5}}, + {{3, 0, 3}, {5, 2, 5}}, + {{6, 0, 3}, {8, 2, 5}}, + + {{0, 3, 3}, {2, 5, 5}}, + // {{3, 3, 3}, {5, 5, 5}}, // skip + {{6, 3, 3}, {8, 5, 5}}, + + {{0, 6, 3}, {2, 8, 5}}, + {{3, 6, 3}, {5, 8, 5}}, + {{6, 6, 3}, {8, 8, 5}}, + + // // z = 6 + {{0, 0, 6}, {2, 2, 8}}, + {{3, 0, 6}, {5, 2, 8}}, + {{6, 0, 6}, {8, 2, 8}}, + + {{0, 3, 6}, {2, 5, 8}}, + {{3, 3, 6}, {5, 5, 8}}, + {{6, 3, 6}, {8, 5, 8}}, + + {{0, 6, 6}, {2, 8, 8}}, + {{3, 6, 6}, {5, 8, 8}}, + {{6, 6, 6}, {8, 8, 8}}, + }; + + assert(not any_overlaps(neighbor_boxes)); + assert(not any_overlaps(neighbor_boxes, middle_box)); + + auto neighbor_ghost_boxes + = generate([](auto box) { return box.grow(extra_ghost_cells); }, neighbor_boxes); + + assert(all_overlaps(neighbor_ghost_boxes, middle_box)); + + auto overlaps = distinct_overlaps(neighbor_ghost_boxes, middle_box); + assert(not any_overlaps(overlaps)); + + EXPECT_EQ(std::pow(3, 3) - 1, sum_from(overlaps, [](auto& r) { return r.size(); })); +} + + +TEST_F(ParticlePartitioner, full_overlaps_5_x_5) +{ + auto constexpr static dim = 3; + auto constexpr static extra_ghost_cells = 2; + using box_t = Box; + using point_t = Point; + + box_t middle_box{{10, 10, 10}, {14, 14, 14}}; + + std::vector neighbor_boxes; + for (int x = 1; x < 4; ++x) + { + auto x0 = x * 5; + + for (int y = 1; y < 4; ++y) + { + auto y0 = y * 5; + + for (int z = 1; z < 4; ++z) + { + auto z0 = z * 5; + + point_t p{x0, y0, z0}; + if (p == middle_box.lower) + continue; + + neighbor_boxes.push_back(box_t{{x0, y0, z0}, {x0 + 4, y0 + 4, z0 + 4}}); + } + } + } + + assert(not any_overlaps(neighbor_boxes)); + assert(not any_overlaps(neighbor_boxes, middle_box)); + + auto neighbor_ghost_boxes + = generate([](auto box) { return box.grow(extra_ghost_cells); }, neighbor_boxes); + + assert(all_overlaps(neighbor_ghost_boxes, middle_box)); + + auto overlaps = distinct_overlaps(neighbor_ghost_boxes, middle_box); + assert(not any_overlaps(overlaps)); + + EXPECT_EQ(std::pow(5, 3) - 1, sum_from(overlaps, [](auto& r) { return r.size(); })); +} + +TEST_F(ParticlePartitioner, partition_overlaps) +{ + auto constexpr static dim = 3; + auto constexpr static extra_ghost_cells = 2; + using box_t = Box; + using point_t = Point; + + box_t middle_box{{10, 10, 10}, {14, 14, 14}}; + + std::vector neighbor_boxes; + for (int x = 1; x < 4; ++x) + { + auto x0 = x * 5; + + for (int y = 1; y < 4; ++y) + { + auto y0 = y * 5; + + for (int z = 1; z < 4; ++z) + { + auto z0 = z * 5; + + point_t p{x0, y0, z0}; + if (p == middle_box.lower) + continue; + + neighbor_boxes.push_back(box_t{{x0, y0, z0}, {x0 + 4, y0 + 4, z0 + 4}}); + } + } + } + + assert(not any_overlaps(neighbor_boxes)); + assert(not any_overlaps(neighbor_boxes, middle_box)); + auto neighbor_ghost_boxes + = generate([](auto box) { return box.grow(extra_ghost_cells); }, neighbor_boxes); + assert(all_overlaps(neighbor_ghost_boxes, middle_box)); + + auto overlaps = distinct_overlaps(neighbor_ghost_boxes, middle_box); + assert(not any_overlaps(overlaps)); + EXPECT_EQ(std::pow(5, 3) - 1, sum_from(overlaps, [](auto& r) { return r.size(); })); + + auto middle_ghost_box = grow(middle_box, extra_ghost_cells); + auto ghost_boxes = middle_ghost_box.remove(middle_box); + + auto L = [&](auto&& particles) { + std::size_t ppc = 10; + add_particles_in(particles, middle_ghost_box, ppc); + assert(particles.size() == std::pow(9, 3) * ppc); + + auto iterators = partition(particles, middle_box, neighbor_boxes); + assert(iterators.size() > 1); + + assert(particles.begin() == iterators[0].begin()); + assert(std::distance(particles.begin(), iterators[0].end()) == ppc); + + for (auto it = particles.begin(); it != iterators[0].end(); ++it) + assert((not isIn(point_t{(*it).iCell()}, ghost_boxes)) + and (not isIn(point_t{(*it).iCell()}, overlaps))); + + for (std::size_t i = 1; i < iterators.size(); ++i) + for (auto it = iterators[i].begin(); it != iterators[i].end(); ++it) + assert(isIn(point_t{(*it).iCell()}, overlaps)); + + for (auto it = iterators.back().end(); it != particles.end(); ++it) + assert(isIn(point_t{(*it).iCell()}, ghost_boxes)); + + auto n_ghost_particles = std::distance(iterators.back().end(), particles.end()); + assert(n_ghost_particles == (std::pow(9, 3) - std::pow(5, 3)) * ppc); + }; + + + L(AoSParticleArray{}); + L(SoAParticleArray{}); +} + + +// int main(int argc, char** argv) +//{ +// ::testing::InitGoogleTest(&argc, argv); +// +// return RUN_ALL_TESTS(); +// } diff --git a/tests/core/data/particles/test_main.cpp b/tests/core/data/particles/test_particles.cpp similarity index 69% rename from tests/core/data/particles/test_main.cpp rename to tests/core/data/particles/test_particles.cpp index 6c52d8f0a..1b0cac978 100644 --- a/tests/core/data/particles/test_main.cpp +++ b/tests/core/data/particles/test_particles.cpp @@ -4,7 +4,6 @@ #include "core/data/particles/particle.hpp" #include "core/data/particles/particle_array.hpp" #include "core/data/particles/particle_utilities.hpp" -#include "core/utilities/box/box.hpp" #include "core/utilities/point/point.hpp" #include "gmock/gmock.h" @@ -12,7 +11,8 @@ #include - +#include "test_interop.hpp" +#include "test_particle_partitionner.hpp" using namespace PHARE::core; @@ -31,46 +31,35 @@ class AParticle : public ::testing::Test TEST_F(AParticle, ParticleWeightIsWellInitialized) { - EXPECT_DOUBLE_EQ(0.01, part.weight); + EXPECT_DOUBLE_EQ(0.01, part.weight()); } TEST_F(AParticle, ParticleChargeIsInitiliazedOK) { - EXPECT_DOUBLE_EQ(1., part.charge); -} - -TEST_F(AParticle, ParticleFieldsAreInitializedToZero) -{ - EXPECT_DOUBLE_EQ(0.0, part.Ex); - EXPECT_DOUBLE_EQ(0.0, part.Ey); - EXPECT_DOUBLE_EQ(0.0, part.Ez); - - EXPECT_DOUBLE_EQ(0.0, part.Bx); - EXPECT_DOUBLE_EQ(0.0, part.By); - EXPECT_DOUBLE_EQ(0.0, part.Bz); + EXPECT_DOUBLE_EQ(1., part.charge()); } TEST_F(AParticle, ParticleVelocityIsInitializedOk) { - EXPECT_DOUBLE_EQ(1.8, part.v[0]); - EXPECT_DOUBLE_EQ(1.83, part.v[1]); - EXPECT_DOUBLE_EQ(2.28, part.v[2]); + EXPECT_DOUBLE_EQ(1.8, part.v()[0]); + EXPECT_DOUBLE_EQ(1.83, part.v()[1]); + EXPECT_DOUBLE_EQ(2.28, part.v()[2]); } TEST_F(AParticle, ParticleDeltaIsInitializedOk) { - EXPECT_FLOAT_EQ(0.002f, part.delta[0]); - EXPECT_FLOAT_EQ(0.2f, part.delta[1]); - EXPECT_FLOAT_EQ(0.8f, part.delta[2]); + EXPECT_FLOAT_EQ(0.002f, part.delta()[0]); + EXPECT_FLOAT_EQ(0.2f, part.delta()[1]); + EXPECT_FLOAT_EQ(0.8f, part.delta()[2]); } TEST_F(AParticle, ParticleCellIsInitializedOK) { - EXPECT_EQ(43, part.iCell[0]); - EXPECT_EQ(75, part.iCell[1]); - EXPECT_EQ(92, part.iCell[2]); + EXPECT_EQ(43, part.iCell()[0]); + EXPECT_EQ(75, part.iCell()[1]); + EXPECT_EQ(92, part.iCell()[2]); } @@ -92,14 +81,14 @@ TEST_F(AParticle, CanBeReducedToAnAbsolutePositionPoint) GridLayout> layout{meshSize, nbrCells, origin, Box{Point{40, 60, 80}, Point{59, 89, 119}}}; - auto iCell = layout.AMRToLocal(Point{part.iCell}); + auto iCell = layout.AMRToLocal(cellAsPoint(part)); auto p = positionAsPoint(part, layout); auto startIndexes = layout.physicalStartIndex(QtyCentering::primal); auto expectedPosition = Point{}; for (auto i = 0u; i < 3; ++i) { expectedPosition[i] - = origin[i] + meshSize[i] * (iCell[i] - startIndexes[i] + part.delta[i]), + = origin[i] + meshSize[i] * (iCell[i] - startIndexes[i] + part.delta()[i]), p[i]; EXPECT_DOUBLE_EQ(expectedPosition[i], p[i]); } diff --git a/tests/core/data/particles/test_particles.hpp b/tests/core/data/particles/test_particles.hpp new file mode 100644 index 000000000..c192c3ca9 --- /dev/null +++ b/tests/core/data/particles/test_particles.hpp @@ -0,0 +1,86 @@ +#ifndef PHARE_CORE_DATA_TEST_PARTICLES_HPP +#define PHARE_CORE_DATA_TEST_PARTICLES_HPP + + +#include "phare_core.hpp" +#include "core/utilities/types.hpp" + + + +namespace PHARE::core +{ +template +PHARE::core::Particle particle(int icell = 15) +{ + return {/*.weight = */ 0, + /*.charge = */ 1, + /*.iCell = */ PHARE::core::ConstArray(icell), + /*.delta = */ PHARE::core::ConstArray(.5), + /*.v = */ {{.00001, .00001, .00001}}}; +} + +template +auto make_particles(std::size_t n_particles) +{ + return PHARE::core::ParticleArray{n_particles, particle()}; +} + +template +void disperse(Particles& particles, Point lo, Point up, std::optional seed = std::nullopt) +{ + auto gen = [&]() { + if (!seed.has_value()) + { + std::random_device rd; + std::seed_seq seed_seq{rd(), rd(), rd(), rd(), rd(), rd(), rd()}; + return std::mt19937_64(seed_seq); + } + return std::mt19937_64(*seed); + }(); + for (std::size_t i = 0; i < Particles::dimension; i++) + { + std::uniform_int_distribution<> distrib(lo[i], up[i]); + for (auto& particle : particles) + particle.iCell()[i] = distrib(gen); + } +} +template +void disperse(Particles& particles, std::size_t lo, std::size_t up, + std::optional seed = std::nullopt) +{ + auto constexpr static dim = Particles::dimension; + + disperse(particles, core::ConstArray(lo), core::ConstArray(up), seed); +} + +template +auto add_particles_in(Particles& particles, Box disperse_in, std::size_t ppc) +{ + particles.resize(ppc * disperse_in.size()); + + std::size_t part_idx = 0; + for (auto const& iterator : disperse_in) + for (std::size_t i = 0; i < ppc; ++i) + particles.iCell(part_idx++) = iterator.template toArray(); + + return particles; +} + +template +auto make_particles_in(std::size_t ppc, Box disperse_in) +{ + auto particles = make_particles(ppc * disperse_in.size()); + add_particles_in(particles, disperse_in, ppc); + return particles; +} + +template +auto make_particles(std::size_t ppc, Box disperse_in, std::optional seed = std::nullopt) +{ + auto particles = make_particles(ppc * disperse_in.size()); + disperse(particles, disperse_in.lower, disperse_in.upper, seed); + return particles; +} +} // namespace PHARE::core + +#endif /* PHARE_CORE_DATA_TEST_PARTICLES_HPP */ diff --git a/tests/core/data/test_gpu_vector.hpp b/tests/core/data/test_gpu_vector.hpp new file mode 100644 index 000000000..721c1c514 --- /dev/null +++ b/tests/core/data/test_gpu_vector.hpp @@ -0,0 +1,13 @@ + +// +#if PHARE_HAVE_MKN_GPU +#include "test_gpu_vector_mkn.hpp" + +// +#elif PHARE_HAVE_UMPIRE + +// +#else + +// +#endif diff --git a/tests/core/data/test_gpu_vector_mkn.hpp b/tests/core/data/test_gpu_vector_mkn.hpp new file mode 100644 index 000000000..a8950255e --- /dev/null +++ b/tests/core/data/test_gpu_vector_mkn.hpp @@ -0,0 +1,63 @@ +// expects to be included +#include +#include + +// TYPED_TEST(DimConstTest, particle_array_is_host_to_device_copyable) +// { +// constexpr auto dim = TypeParam{}; +// using Particle_t = PHARE::core::Particle +// using host_t = PHARE::core::ParticleArray::allocator_type>; +// using dev_t = PHARE::Vector; + +// host_t host_particles( +// } + + +TYPED_TEST(VectorTest, is_host_to_device_copyable) +{ + using T = typename TypeParam::value_type; + + std::vector vec0(10, 1); + auto vec1 = TypeParam::make(10); + EXPECT_EQ(PHARE::core::sum(vec1), 0); + + TypeParam::copy(vec1, vec0); + EXPECT_EQ(PHARE::core::sum(vec1), 10); +} + +TYPED_TEST(VectorTest, is_device_to_host_copyable) +{ + using T = typename TypeParam::value_type; + + auto vec0 = TypeParam::make(10); + TypeParam::fill(vec0, 1); + EXPECT_EQ(PHARE::core::sum(vec0), 10); + + std::vector vec1(10, 0); + EXPECT_EQ(PHARE::core::sum(vec1), 0); + TypeParam::copy(vec1, vec0); + EXPECT_EQ(PHARE::core::sum(vec1), 10); +} + +struct is_even +{ + __device__ bool operator()(const int& x) { return (x % 2) == 0; } +}; + +TYPED_TEST(VectorTest, thrust_device_partition) +{ + auto constexpr N = 1024; + auto A = TypeParam::make(N); + + assert(mkn::gpu::Pointer{A.data()}.is_managed_ptr()); + for (std::size_t i = 0; i < N; ++i) + A[i] = i; + + // there might be other versions that work + thrust::stable_partition(A.data(), A.data() + N, is_even()); + + for (std::size_t i = 0; i < N / 2; ++i) + EXPECT_EQ(static_cast(A[i]) % 2, 0); + for (std::size_t i = N / 2; i < N; ++i) + EXPECT_EQ(static_cast(A[i]) % 2, 1); +} diff --git a/tests/core/data/test_vector.cpp b/tests/core/data/test_vector.cpp new file mode 100644 index 000000000..8904ca0ea --- /dev/null +++ b/tests/core/data/test_vector.cpp @@ -0,0 +1,72 @@ +#include "core/vector.hpp" +#include "core/utilities/types.hpp" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +template +using Vector = PHARE::Vector; +using Vectors = ::testing::Types, Vector>; + +template +struct VectorTest : public ::testing::Test{}; + +TYPED_TEST_SUITE(VectorTest, Vectors); + + +template +using DimConst = PHARE::core::DimConst; +using DimConsts = ::testing::Types, DimConst<2>>; + +template +struct DimConstTest : public ::testing::Test{}; + +TYPED_TEST_SUITE(DimConstTest, DimConsts); + +// +#if PHARE_WITH_GPU +#include "test_gpu_vector.hpp" +#endif + +TYPED_TEST(VectorTest, is_constructable) +{ + auto vec = TypeParam::make(10); + EXPECT_EQ(vec.size(), 10); + EXPECT_EQ(PHARE::core::sum(vec), 0); +} + +TYPED_TEST(VectorTest, is_fillable) +{ + auto vec = TypeParam::make(10); + EXPECT_EQ(PHARE::core::sum(vec), 0); + + TypeParam::fill(vec, 1); + EXPECT_EQ(PHARE::core::sum(vec), 10); +} + +TYPED_TEST(VectorTest, is_copyable) +{ + auto vec0 = TypeParam::make(10); + TypeParam::fill(vec0, 1); + auto vec1 = vec0; + EXPECT_EQ(vec0, vec1); +} + +TYPED_TEST(VectorTest, is_movable) +{ + auto vec0 = TypeParam::make(10); + TypeParam::fill(vec0, 1); + auto vec1 = std::move(vec0); + + EXPECT_EQ(vec0.size(), 0); + EXPECT_EQ(vec1.size(), 10); + EXPECT_EQ(PHARE::core::sum(vec1), 10); +} + + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/tests/core/data/vecfield/test_main.cpp b/tests/core/data/vecfield/test_main.cpp index 7405ad5da..6d137e178 100644 --- a/tests/core/data/vecfield/test_main.cpp +++ b/tests/core/data/vecfield/test_main.cpp @@ -1,8 +1,10 @@ + #include "gmock/gmock.h" #include "gtest/gtest.h" #include +#include "phare_core.hpp" #include "core/data/field/field.hpp" #include "core/data/ndarray/ndarray_vector.hpp" #include "core/data/vecfield/vecfield.hpp" @@ -29,7 +31,11 @@ class VecFieldGeneric : public ::testing::Test VecField vf2; }; -using NdArrays = ::testing::Types, NdArrayVector<2>, NdArrayVector<3>>; + +template +using NdArray_t = typename PHARE_Types::Array_t; + +using NdArrays = ::testing::Types, NdArray_t<2>, NdArray_t<3>>; TYPED_TEST_SUITE(VecFieldGeneric, NdArrays); @@ -88,21 +94,21 @@ class VecFieldTest : public ::testing::Test static const std::uint32_t nx; static const std::uint32_t ny; static const std::uint32_t nz; - Field, typename HybridQuantity::Scalar> bx1d_; - Field, typename HybridQuantity::Scalar> by1d_; - Field, typename HybridQuantity::Scalar> bz1d_; + Field, typename HybridQuantity::Scalar> bx1d_; + Field, typename HybridQuantity::Scalar> by1d_; + Field, typename HybridQuantity::Scalar> bz1d_; - Field, typename HybridQuantity::Scalar> bx2d_; - Field, typename HybridQuantity::Scalar> by2d_; - Field, typename HybridQuantity::Scalar> bz2d_; + Field, typename HybridQuantity::Scalar> bx2d_; + Field, typename HybridQuantity::Scalar> by2d_; + Field, typename HybridQuantity::Scalar> bz2d_; - Field, typename HybridQuantity::Scalar> bx3d_; - Field, typename HybridQuantity::Scalar> by3d_; - Field, typename HybridQuantity::Scalar> bz3d_; + Field, typename HybridQuantity::Scalar> bx3d_; + Field, typename HybridQuantity::Scalar> by3d_; + Field, typename HybridQuantity::Scalar> bz3d_; - VecField, HybridQuantity> B1D_; - VecField, HybridQuantity> B2D_; - VecField, HybridQuantity> B3D_; + VecField, HybridQuantity> B1D_; + VecField, HybridQuantity> B2D_; + VecField, HybridQuantity> B3D_; }; const std::uint32_t VecFieldTest::nx = 10; @@ -299,10 +305,10 @@ TEST(aVecField, dataCanBeCopiedIntoAnother) { using Scalar = typename HybridQuantity::Scalar; - Field, Scalar> bx1{"B1_bx1", Scalar::Bx, 2u, 3u, 4u}; - Field, Scalar> by1{"B1_by1", Scalar::By, 2u, 3u, 4u}; - Field, Scalar> bz1{"B1_bz1", Scalar::Bz, 2u, 3u, 4u}; - VecField, HybridQuantity> B1{"B1", HybridQuantity::Vector::B}; + Field, Scalar> bx1{"B1_bx1", Scalar::Bx, 2u, 3u, 4u}; + Field, Scalar> by1{"B1_by1", Scalar::By, 2u, 3u, 4u}; + Field, Scalar> bz1{"B1_bz1", Scalar::Bz, 2u, 3u, 4u}; + VecField, HybridQuantity> B1{"B1", HybridQuantity::Vector::B}; B1.setBuffer("B1_x", &bx1); B1.setBuffer("B1_y", &by1); B1.setBuffer("B1_z", &bz1); @@ -311,10 +317,10 @@ TEST(aVecField, dataCanBeCopiedIntoAnother) by1(1, 1, 1) = 13; bz1(1, 1, 1) = 14; - Field, Scalar> bx2{"B2_bx2", Scalar::Bx, 2u, 3u, 4u}; - Field, Scalar> by2{"B2_by2", Scalar::By, 2u, 3u, 4u}; - Field, Scalar> bz2{"B2_bz2", Scalar::Bz, 2u, 3u, 4u}; - VecField, HybridQuantity> B2{"B2", HybridQuantity::Vector::B}; + Field, Scalar> bx2{"B2_bx2", Scalar::Bx, 2u, 3u, 4u}; + Field, Scalar> by2{"B2_by2", Scalar::By, 2u, 3u, 4u}; + Field, Scalar> bz2{"B2_bz2", Scalar::Bz, 2u, 3u, 4u}; + VecField, HybridQuantity> B2{"B2", HybridQuantity::Vector::B}; B2.setBuffer("B2_x", &bx2); B2.setBuffer("B2_y", &by2); B2.setBuffer("B2_z", &bz2); diff --git a/tests/core/data/vecfield/test_vecfield.hpp b/tests/core/data/vecfield/test_vecfield.hpp index 7de393066..e98dba529 100644 --- a/tests/core/data/vecfield/test_vecfield.hpp +++ b/tests/core/data/vecfield/test_vecfield.hpp @@ -31,9 +31,8 @@ struct VecFieldMock bool isUsable() const { return true; } - + auto& view() const { return *this; } auto& view() { return *this; } - auto view() const { return *this; } }; diff --git a/tests/core/numerics/ampere/test_main.cpp b/tests/core/numerics/ampere/test_main.cpp index c24a88e63..c109b4a1f 100644 --- a/tests/core/numerics/ampere/test_main.cpp +++ b/tests/core/numerics/ampere/test_main.cpp @@ -1,9 +1,8 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include #include - +#include #include "core/data/field/field.hpp" #include "core/data/grid/gridlayout.hpp" @@ -135,19 +134,23 @@ std::vector read(std::string filename) class Ampere1DTest : public ::testing::Test { protected: - using GridLayoutImpl = GridLayoutImplYee<1, 1>; + static constexpr auto dimension = 1; + static constexpr auto interp_order = 1; + + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = NdArrayVector; + GridLayout layout; - static constexpr auto interp_order = GridLayoutImpl::interp_order; - Field, HybridQuantity::Scalar> Bx; - Field, HybridQuantity::Scalar> By; - Field, HybridQuantity::Scalar> Bz; - Field, HybridQuantity::Scalar> Jx; - Field, HybridQuantity::Scalar> Jy; - Field, HybridQuantity::Scalar> Jz; + Field Bx; + Field By; + Field Bz; + Field Jx; + Field Jy; + Field Jz; - VecField, HybridQuantity> B; - VecField, HybridQuantity> J; + VecField B; + VecField J; Ampere> ampere; @@ -176,19 +179,23 @@ class Ampere1DTest : public ::testing::Test class Ampere2DTest : public ::testing::Test { protected: - using GridLayoutImpl = GridLayoutImplYee<2, 1>; + static constexpr auto dimension = 2; + static constexpr auto interp_order = 1; + + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = NdArrayVector; + GridLayout layout; - static constexpr auto interp_order = GridLayoutImpl::interp_order; - Field, HybridQuantity::Scalar> Bx; - Field, HybridQuantity::Scalar> By; - Field, HybridQuantity::Scalar> Bz; - Field, HybridQuantity::Scalar> Jx; - Field, HybridQuantity::Scalar> Jy; - Field, HybridQuantity::Scalar> Jz; + Field Bx; + Field By; + Field Bz; + Field Jx; + Field Jy; + Field Jz; - VecField, HybridQuantity> B; - VecField, HybridQuantity> J; + VecField B; + VecField J; Ampere> ampere; @@ -217,19 +224,23 @@ class Ampere2DTest : public ::testing::Test class Ampere3DTest : public ::testing::Test { protected: - using GridLayoutImpl = GridLayoutImplYee<3, 1>; + static constexpr auto dimension = 3; + static constexpr auto interp_order = 1; + + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = NdArrayVector; + GridLayout layout; - static constexpr auto interp_order = GridLayoutImpl::interp_order; - Field, HybridQuantity::Scalar> Bx; - Field, HybridQuantity::Scalar> By; - Field, HybridQuantity::Scalar> Bz; - Field, HybridQuantity::Scalar> Jx; - Field, HybridQuantity::Scalar> Jy; - Field, HybridQuantity::Scalar> Jz; + Field Bx; + Field By; + Field Bz; + Field Jx; + Field Jy; + Field Jz; - VecField, HybridQuantity> B; - VecField, HybridQuantity> J; + VecField B; + VecField J; Ampere> ampere; @@ -287,6 +298,8 @@ TEST_F(Ampere1DTest, ampere1DCalculatedOk) } + + TEST_F(Ampere2DTest, ampere2DCalculatedOk) { auto filename_jx = std::string{"jx_yee_2D_order1.txt"}; diff --git a/tests/core/numerics/boundary_condition/test_main.cpp b/tests/core/numerics/boundary_condition/test_main.cpp index 364ff91cd..8681f12ac 100644 --- a/tests/core/numerics/boundary_condition/test_main.cpp +++ b/tests/core/numerics/boundary_condition/test_main.cpp @@ -19,10 +19,11 @@ class ABoundaryConditionWhereAllParticlesLeave : public ::testing::Test , leavingParticles_(10) { bc.setBoundaryBoxes(boundaryBoxes); - for (auto& part : leavingParticles_) + + for (std::size_t i = 0; i < leavingParticles_.size(); ++i) { - part.iCell[0] = 5; // these particles are out... - part.iCell[1] = -1; // and not through the boundarybox + leavingParticles_.iCell(i)[0] = 5; // these particles are out... + leavingParticles_.iCell(i)[1] = -1; // and not through the boundarybox } } diff --git a/tests/core/numerics/faraday/test_main.cpp b/tests/core/numerics/faraday/test_main.cpp index 915bb089d..bda659cf0 100644 --- a/tests/core/numerics/faraday/test_main.cpp +++ b/tests/core/numerics/faraday/test_main.cpp @@ -158,21 +158,25 @@ std::vector read(std::string filename) class Faraday1DTest : public ::testing::Test { protected: - using GridLayoutImpl = GridLayoutImplYee<1, 1>; + static constexpr auto dimension = 1; + static constexpr auto interp_order = 1; + + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = NdArrayVector; + GridLayout layout; - static constexpr auto interp_order = GridLayoutImpl::interp_order; - Field, HybridQuantity::Scalar> Bx; - Field, HybridQuantity::Scalar> By; - Field, HybridQuantity::Scalar> Bz; - Field, HybridQuantity::Scalar> Ex; - Field, HybridQuantity::Scalar> Ey; - Field, HybridQuantity::Scalar> Ez; - Field, HybridQuantity::Scalar> Bxnew; - Field, HybridQuantity::Scalar> Bynew; - Field, HybridQuantity::Scalar> Bznew; - VecField, HybridQuantity> B; - VecField, HybridQuantity> E; - VecField, HybridQuantity> Bnew; + Field Bx; + Field By; + Field Bz; + Field Ex; + Field Ey; + Field Ez; + Field Bxnew; + Field Bynew; + Field Bznew; + VecField B; + VecField E; + VecField Bnew; Faraday> faraday; public: @@ -209,21 +213,25 @@ class Faraday1DTest : public ::testing::Test class Faraday2DTest : public ::testing::Test { protected: - using GridLayoutImpl = GridLayoutImplYee<2, 1>; + static constexpr auto dimension = 2; + static constexpr auto interp_order = 1; + + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = NdArrayVector; + GridLayout layout; - static constexpr auto interp_order = GridLayoutImpl::interp_order; - Field, HybridQuantity::Scalar> Bx; - Field, HybridQuantity::Scalar> By; - Field, HybridQuantity::Scalar> Bz; - Field, HybridQuantity::Scalar> Ex; - Field, HybridQuantity::Scalar> Ey; - Field, HybridQuantity::Scalar> Ez; - Field, HybridQuantity::Scalar> Bxnew; - Field, HybridQuantity::Scalar> Bynew; - Field, HybridQuantity::Scalar> Bznew; - VecField, HybridQuantity> B; - VecField, HybridQuantity> E; - VecField, HybridQuantity> Bnew; + Field Bx; + Field By; + Field Bz; + Field Ex; + Field Ey; + Field Ez; + Field Bxnew; + Field Bynew; + Field Bznew; + VecField B; + VecField E; + VecField Bnew; Faraday> faraday; public: @@ -260,21 +268,25 @@ class Faraday2DTest : public ::testing::Test class Faraday3DTest : public ::testing::Test { protected: - using GridLayoutImpl = GridLayoutImplYee<3, 1>; + static constexpr auto dimension = 3; + static constexpr auto interp_order = 1; + + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = NdArrayVector; + GridLayout layout; - static constexpr auto interp_order = GridLayoutImpl::interp_order; - Field, HybridQuantity::Scalar> Bx; - Field, HybridQuantity::Scalar> By; - Field, HybridQuantity::Scalar> Bz; - Field, HybridQuantity::Scalar> Ex; - Field, HybridQuantity::Scalar> Ey; - Field, HybridQuantity::Scalar> Ez; - Field, HybridQuantity::Scalar> Bxnew; - Field, HybridQuantity::Scalar> Bynew; - Field, HybridQuantity::Scalar> Bznew; - VecField, HybridQuantity> B; - VecField, HybridQuantity> E; - VecField, HybridQuantity> Bnew; + Field Bx; + Field By; + Field Bz; + Field Ex; + Field Ey; + Field Ez; + Field Bxnew; + Field Bynew; + Field Bznew; + VecField B; + VecField E; + VecField Bnew; Faraday> faraday; public: diff --git a/tests/core/numerics/interpolator/test_main.cpp b/tests/core/numerics/interpolator/test_main.cpp index 59d9c8a50..fe812f131 100644 --- a/tests/core/numerics/interpolator/test_main.cpp +++ b/tests/core/numerics/interpolator/test_main.cpp @@ -3,15 +3,15 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -#include +#include #include #include +#include #include #include -#include -#include +#include + -#include "core/utilities/box/box.hpp" #include "core/utilities/range/range.hpp" #include "phare_core.hpp" #include "core/data/electromag/electromag.hpp" @@ -29,6 +29,30 @@ using namespace PHARE::core; +template +struct TestablesBase +{ + static constexpr auto interp_order = interpOrder; + static constexpr auto dimension = dim; + + using Interpolator_t = Interpolator; +}; + +template +struct Testables : public TestablesBase +{ + // using PHARE_TYPES = PHARE::core::PHARE_Types; + using ParticleArray_t = AoSParticleArray; +}; + +template +struct Testables_SOA : public TestablesBase +{ + // using PHARE_TYPES = PHARE::core::PHARE_Types; + using ParticleArray_t = SoAParticleArray; +}; + + template class AWeighter : public ::testing::Test @@ -62,8 +86,7 @@ class AWeighter : public ::testing::Test auto delta = normalizedPositions[i] - icell; auto startIndex = icell - - Interpolator_t::template computeStartLeftShift(delta); + - Interpolator_t::template computeStartLeftShift(delta); this->weighter.computeWeight(normalizedPositions[i], startIndex, weights_[i]); } @@ -151,7 +174,7 @@ BSpline readFromFile(std::string centering, std::size_t order) return bs; } -template +template void check_bspline(Weighter& weighter, std::string centering_id) { using Interpolator_t = typename AWeighter_t::Interpolator_t; @@ -171,8 +194,7 @@ void check_bspline(Weighter& weighter, std::string centering_id) { auto delta = static_cast(ipos) * dx; - auto startIndex - = icell - Interpolator_t::template computeStartLeftShift(delta); + auto startIndex = icell - Interpolator_t::template computeStartLeftShift(delta); double normalizedPosition = icell + delta; if constexpr (centering == QtyCentering::dual) @@ -199,7 +221,7 @@ TYPED_TEST(AWeighter, computesPrimalBSplineWeightsForAnyParticlePosition) static_assert(Interpolator_t::interp_order == GridLayout_t::interp_order); assert(GridLayout_t::nbrGhosts() == Interpolator_t::interp_order + 1); - check_bspline(this->weighter, "primal"); + check_bspline(this->weighter, "primal"); } TYPED_TEST(AWeighter, computesDualBSplineWeightsForAnyParticlePosition) { @@ -210,30 +232,33 @@ TYPED_TEST(AWeighter, computesDualBSplineWeightsForAnyParticlePosition) static_assert(Interpolator_t::interp_order == GridLayout_t::interp_order); assert(GridLayout_t::nbrGhosts() == Interpolator_t::interp_order + 1); - check_bspline(this->weighter, "dual"); + check_bspline(this->weighter, "dual"); } -template +template class A1DInterpolator : public ::testing::Test { public: + using InterpolatorT = typename Testables::Interpolator_t; + using ParticleArray_t = typename Testables::ParticleArray_t; + static constexpr auto dimension = InterpolatorT::dimension; static constexpr auto interp_order = InterpolatorT::interp_order; // arbitrary number of cells static constexpr std::uint32_t nx = 50; - using PHARE_TYPES = PHARE::core::PHARE_Types; - using GridLayout_t = typename PHARE_TYPES::GridLayout_t; - using NdArray_t = typename PHARE_TYPES::Array_t; - using ParticleArray_t = typename PHARE_TYPES::ParticleArray_t; - using VF = VecField; + using PHARE_TYPES = PHARE::core::PHARE_Types; + using GridLayout_t = typename PHARE_TYPES::GridLayout_t; + using NdArray_t = typename PHARE_TYPES::Array_t; + using VF = VecField; Electromag em; GridLayout_t layout{{0.1}, {nx}, {0.}}; ParticleArray_t particles; InterpolatorT interp; - constexpr static auto safeLayer = static_cast(1 + ghostWidthForParticles()); + + Field bx1d_; @@ -252,7 +277,7 @@ class A1DInterpolator : public ::testing::Test A1DInterpolator() : em{"EM"} - , particles{grow(layout.AMRBox(), safeLayer), 1} + , particles{1} , bx1d_{"field", HybridQuantity::Scalar::Bx, nx} , by1d_{"field", HybridQuantity::Scalar::By, nx} , bz1d_{"field", HybridQuantity::Scalar::Bz, nx} @@ -271,10 +296,10 @@ class A1DInterpolator : public ::testing::Test ez1d_(ix) = ez0; } - for (auto& part : particles) + for (std::size_t i = 0; i < particles.size(); ++i) { - part.iCell[0] = 5; - part.delta[0] = 0.32f; + particles.iCell(i)[0] = 5; + particles.delta(i)[0] = 0.32f; } } }; @@ -283,7 +308,8 @@ class A1DInterpolator : public ::testing::Test using Interpolators1D - = ::testing::Types, Interpolator<1, 2>, Interpolator<1, 3>>; + = ::testing::Types, Testables<1, 2>, Testables<1, 3>, // + Testables_SOA<1, 1>, Testables_SOA<1, 2>, Testables_SOA<1, 3>>; TYPED_TEST_SUITE(A1DInterpolator, Interpolators1D); @@ -298,31 +324,31 @@ TYPED_TEST(A1DInterpolator, canComputeAllEMfieldsAtParticle) this->em.B.setBuffer("EM_B_y", &this->by1d_); this->em.B.setBuffer("EM_B_z", &this->bz1d_); - this->interp(makeIndexRange(this->particles), this->em, this->layout); + this->interp(this->particles, this->em, this->layout); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ex - this->ex0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[0] - this->ex0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ey - this->ey0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[1] - this->ey0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ez - this->ez0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[2] - this->ez0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Bx - this->bx0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[0] - this->bx0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.By - this->by0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[1] - this->by0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Bz - this->bz0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[2] - this->bz0) < 1e-8; })); this->em.E.setBuffer("EM_E_x", nullptr); @@ -335,27 +361,29 @@ TYPED_TEST(A1DInterpolator, canComputeAllEMfieldsAtParticle) -template +template class A2DInterpolator : public ::testing::Test { public: + using InterpolatorT = typename Testables::Interpolator_t; + using ParticleArray_t = typename Testables::ParticleArray_t; + static constexpr auto dimension = InterpolatorT::dimension; static constexpr auto interp_order = InterpolatorT::interp_order; // arbitrary number of cells static constexpr std::uint32_t nx = 50; static constexpr std::uint32_t ny = 50; - using PHARE_TYPES = PHARE::core::PHARE_Types; - using GridLayoutImpl = GridLayoutImplYee; - using NdArray_t = typename PHARE_TYPES::Array_t; - using ParticleArray_t = typename PHARE_TYPES::ParticleArray_t; - using VF = VecField; + using PHARE_TYPES = PHARE::core::PHARE_Types; + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = typename PHARE_TYPES::Array_t; + using VF = VecField; Electromag em; GridLayout layout{{0.1, 0.1}, {nx, ny}, {0., 0.}}; ParticleArray_t particles; InterpolatorT interp; - constexpr static auto safeLayer = static_cast(1 + ghostWidthForParticles()); + Field bx_; Field by_; @@ -373,7 +401,7 @@ class A2DInterpolator : public ::testing::Test A2DInterpolator() : em{"EM"} - , particles{grow(layout.AMRBox(), safeLayer), 1} + , particles{1} , bx_{"field", HybridQuantity::Scalar::Bx, nx, ny} , by_{"field", HybridQuantity::Scalar::By, nx, ny} , bz_{"field", HybridQuantity::Scalar::Bz, nx, ny} @@ -394,10 +422,10 @@ class A2DInterpolator : public ::testing::Test } } - for (auto& part : particles) + for (std::size_t i = 0; i < particles.size(); ++i) { - part.iCell[0] = 5; - part.delta[0] = 0.32f; + particles.iCell(i)[0] = 5; + particles.delta(i)[0] = 0.32f; } } }; @@ -406,7 +434,8 @@ class A2DInterpolator : public ::testing::Test using Interpolators2D - = ::testing::Types, Interpolator<2, 2>, Interpolator<2, 3>>; + = ::testing::Types, Testables<2, 2>, Testables<2, 3>, // + Testables_SOA<2, 1>, Testables_SOA<2, 2>, Testables_SOA<2, 3>>; TYPED_TEST_SUITE(A2DInterpolator, Interpolators2D); @@ -421,31 +450,31 @@ TYPED_TEST(A2DInterpolator, canComputeAllEMfieldsAtParticle) this->em.B.setBuffer("EM_B_y", &this->by_); this->em.B.setBuffer("EM_B_z", &this->bz_); - this->interp(makeIndexRange(this->particles), this->em, this->layout); + this->interp(this->particles, this->em, this->layout); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ex - this->ex0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[0] - this->ex0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ey - this->ey0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[1] - this->ey0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ez - this->ez0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[2] - this->ez0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Bx - this->bx0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[0] - this->bx0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.By - this->by0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[1] - this->by0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Bz - this->bz0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[2] - this->bz0) < 1e-8; })); this->em.E.setBuffer("EM_E_x", nullptr); @@ -459,10 +488,13 @@ TYPED_TEST(A2DInterpolator, canComputeAllEMfieldsAtParticle) -template +template class A3DInterpolator : public ::testing::Test { public: + using InterpolatorT = typename Testables::Interpolator_t; + using ParticleArray_t = typename Testables::ParticleArray_t; + static constexpr auto dimension = InterpolatorT::dimension; static constexpr auto interp_order = InterpolatorT::interp_order; // arbitrary number of cells @@ -470,14 +502,13 @@ class A3DInterpolator : public ::testing::Test static constexpr std::uint32_t ny = 50; static constexpr std::uint32_t nz = 50; - using PHARE_TYPES = PHARE::core::PHARE_Types; - using GridLayoutImpl = GridLayoutImplYee; - using NdArray_t = typename PHARE_TYPES::Array_t; - using ParticleArray_t = typename PHARE_TYPES::ParticleArray_t; - using VF = VecField; + using PHARE_TYPES = PHARE::core::PHARE_Types; + using GridLayoutImpl = GridLayoutImplYee; + using NdArray_t = typename PHARE_TYPES::Array_t; + using VF = VecField; GridLayout layout{{0.1, 0.1, 0.1}, {nx, ny, nz}, {0., 0., 0.}}; - constexpr static auto safeLayer = static_cast(1 + ghostWidthForParticles()); + Electromag em; ParticleArray_t particles; InterpolatorT interp; @@ -498,7 +529,7 @@ class A3DInterpolator : public ::testing::Test A3DInterpolator() : em{"EM"} - , particles{grow(layout.AMRBox(), safeLayer), 1} + , particles{1} , bx_{"field", HybridQuantity::Scalar::Bx, nx, ny, nz} , by_{"field", HybridQuantity::Scalar::By, nx, ny, nz} , bz_{"field", HybridQuantity::Scalar::Bz, nx, ny, nz} @@ -522,10 +553,10 @@ class A3DInterpolator : public ::testing::Test } } - for (auto& part : particles) + for (std::size_t i = 0; i < particles.size(); ++i) { - part.iCell[0] = 5; - part.delta[0] = 0.32f; + particles.iCell(i)[0] = 5; + particles.delta(i)[0] = 0.32f; } } }; @@ -534,7 +565,8 @@ class A3DInterpolator : public ::testing::Test using Interpolators3D - = ::testing::Types, Interpolator<3, 2>, Interpolator<3, 3>>; + = ::testing::Types, Testables<3, 2>, Testables<3, 3>, // + Testables_SOA<3, 1>, Testables_SOA<3, 2>, Testables_SOA<3, 3>>; TYPED_TEST_SUITE(A3DInterpolator, Interpolators3D); @@ -549,31 +581,31 @@ TYPED_TEST(A3DInterpolator, canComputeAllEMfieldsAtParticle) this->em.B.setBuffer("EM_B_y", &this->by_); this->em.B.setBuffer("EM_B_z", &this->bz_); - this->interp(makeIndexRange(this->particles), this->em, this->layout); + this->interp(this->particles, this->em, this->layout); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ex - this->ex0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[0] - this->ex0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ey - this->ey0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[1] - this->ey0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Ez - this->ez0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.E()[2] - this->ez0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Bx - this->bx0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[0] - this->bx0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.By - this->by0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[1] - this->by0) < 1e-8; })); EXPECT_TRUE( std::all_of(std::begin(this->particles), std::end(this->particles), - [this](auto const& part) { return std::abs(part.Bz - this->bz0) < 1e-8; })); + [this](auto const& part) { return std::abs(part.B()[2] - this->bz0) < 1e-8; })); this->em.E.setBuffer("EM_E_x", nullptr); @@ -593,17 +625,19 @@ TYPED_TEST(A3DInterpolator, canComputeAllEMfieldsAtParticle) -template +template class ACollectionOfParticles_1d : public ::testing::Test { + using Interpolator = typename Testables::Interpolator_t; + using ParticleArray_t = typename Testables::ParticleArray_t; + static constexpr auto dimension = Interpolator::dimension; static constexpr auto interp_order = Interpolator::interp_order; - using PHARE_TYPES = PHARE::core::PHARE_Types; - using NdArray_t = typename PHARE_TYPES::Array_t; - using ParticleArray_t = typename PHARE_TYPES::ParticleArray_t; - using GridLayout_t = typename PHARE_TYPES::GridLayout_t; - using Particle_t = typename ParticleArray_t::Particle_t; + using PHARE_TYPES = PHARE::core::PHARE_Types; + using NdArray_t = typename PHARE_TYPES::Array_t; + using GridLayout_t = typename PHARE_TYPES::GridLayout_t; + using Particle_t = Particle<1>; public: static constexpr std::uint32_t nx = 30; @@ -611,7 +645,7 @@ class ACollectionOfParticles_1d : public ::testing::Test static constexpr std::uint32_t numOfPart = Interpolator::interp_order + 2; GridLayout> layout{{0.1}, {nx}, {0.}}; - constexpr static auto safeLayer = static_cast(1 + ghostWidthForParticles()); + Particle_t part; ParticleArray_t particles; @@ -627,7 +661,7 @@ class ACollectionOfParticles_1d : public ::testing::Test ACollectionOfParticles_1d() : part{} - , particles{grow(layout.AMRBox(), safeLayer)} + , particles{} , rho{"field", HybridQuantity::Scalar::rho, nx} , vx{"v_x", HybridQuantity::Scalar::Vx, nx} , vy{"v_y", HybridQuantity::Scalar::Vy, nx} @@ -640,106 +674,106 @@ class ACollectionOfParticles_1d : public ::testing::Test if constexpr (Interpolator::interp_order == 1) { - part.iCell[0] = 19; // AMR index - part.delta[0] = 0.5f; - part.weight = 1.0; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 19; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 1.0; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 20; // AMR index - part.delta[0] = 0.5f; - part.weight = 0.4; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 20; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 0.4; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 20; // AMR index - part.delta[0] = 0.5f; - part.weight = 0.6; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 20; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 0.6; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); } if constexpr (Interpolator::interp_order == 2) { - part.iCell[0] = 19; // AMR index - part.delta[0] = 0.0f; - part.weight = 1.0; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 19; // AMR index + part.delta_[0] = 0.0f; + part.weight_ = 1.0; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 20; // AMR index - part.delta[0] = 0.0f; - part.weight = 0.2; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 20; // AMR index + part.delta_[0] = 0.0f; + part.weight_ = 0.2; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 20; // AMR index - part.delta[0] = 0.0f; - part.weight = 0.8; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 20; // AMR index + part.delta_[0] = 0.0f; + part.weight_ = 0.8; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 21; // AMR index - part.delta[0] = 0.0f; - part.weight = 1.0; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 21; // AMR index + part.delta_[0] = 0.0f; + part.weight_ = 1.0; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); } if constexpr (Interpolator::interp_order == 3) { - part.iCell[0] = 18; // AMR index - part.delta[0] = 0.5f; - part.weight = 1.0; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 18; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 1.0; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 19; // AMR index - part.delta[0] = 0.5f; - part.weight = 1.0; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 19; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 1.0; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 20; // AMR index - part.delta[0] = 0.5f; - part.weight = 1.0; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 20; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 1.0; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 21; // AMR index - part.delta[0] = 0.5f; - part.weight = 0.1; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 21; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 0.1; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); - part.iCell[0] = 21; // AMR index - part.delta[0] = 0.5f; - part.weight = 0.9; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; + part.iCell_[0] = 21; // AMR index + part.delta_[0] = 0.5f; + part.weight_ = 0.9; + part.v_[0] = +2.; + part.v_[1] = -1.; + part.v_[2] = +1.; particles.push_back(part); } interpolator(makeIndexRange(particles), rho, v, layout); @@ -765,26 +799,40 @@ TYPED_TEST_P(ACollectionOfParticles_1d, DepositCorrectlyTheirWeight_1d) } REGISTER_TYPED_TEST_SUITE_P(ACollectionOfParticles_1d, DepositCorrectlyTheirWeight_1d); -using MyTypes = ::testing::Types, Interpolator<1, 2>, Interpolator<1, 3>>; +using MyTypes = ::testing::Types, Testables<1, 2>, Testables<1, 3>, // + Testables_SOA<1, 1>, Testables_SOA<1, 2>, Testables_SOA<1, 3>>; INSTANTIATE_TYPED_TEST_SUITE_P(testInterpolator, ACollectionOfParticles_1d, MyTypes); -template +template struct ACollectionOfParticles_2d : public ::testing::Test { + using Interpolator = typename Testables::Interpolator_t; + using ParticleArray_t = typename Testables::ParticleArray_t; + + static constexpr std::size_t dim = Interpolator::dimension; static constexpr auto interp_order = Interpolator::interp_order; - static constexpr std::size_t dim = 2; + static constexpr std::uint32_t nx = 15, ny = 15; static constexpr int start = 0, end = 5; - using PHARE_TYPES = PHARE::core::PHARE_Types; - using NdArray_t = typename PHARE_TYPES::Array_t; - using ParticleArray_t = typename PHARE_TYPES::ParticleArray_t; - using GridLayout_t = typename PHARE_TYPES::GridLayout_t; - constexpr static auto safeLayer = static_cast(1 + ghostWidthForParticles()); + using PHARE_TYPES = PHARE::core::PHARE_Types; + using NdArray_t = typename PHARE_TYPES::Array_t; + using GridLayout_t = typename PHARE_TYPES::GridLayout_t; + + static PHARE::core::Particle particle() + { + return { + /*.weight = */ 1, + /*.charge = */ 1, + /*.iCell = */ ConstArray(), + /*.delta = */ ConstArray(.5), + /*.v = */ {{2, -1, 1}}, + }; + } ACollectionOfParticles_2d() - : particles{grow(layout.AMRBox(), safeLayer)} + : particles{end * end, particle()} , rho{"field", HybridQuantity::Scalar::rho, nx, ny} , vx{"v_x", HybridQuantity::Scalar::Vx, nx, ny} , vy{"v_y", HybridQuantity::Scalar::Vy, nx, ny} @@ -797,20 +845,12 @@ struct ACollectionOfParticles_2d : public ::testing::Test for (int i = start; i < end; i++) for (int j = start; j < end; j++) - { - auto& part = particles.emplace_back(); - part.iCell = {i, j}; - part.delta = ConstArray(.5); - part.weight = 1.; - part.v[0] = +2.; - part.v[1] = -1.; - part.v[2] = +1.; - } - interpolator(makeIndexRange(particles), rho, v, layout); + particles.iCell((i * end) + j) = {i, j}; + + interpolator(particles, rho, v, layout); } GridLayout_t layout{ConstArray(.1), {nx, ny}, ConstArray(0)}; - ParticleArray_t particles; Field rho, vx, vy, vz; VecField v; @@ -832,7 +872,9 @@ TYPED_TEST_P(ACollectionOfParticles_2d, DepositCorrectlyTheirWeight_2d) REGISTER_TYPED_TEST_SUITE_P(ACollectionOfParticles_2d, DepositCorrectlyTheirWeight_2d); -using My2dTypes = ::testing::Types, Interpolator<2, 2>, Interpolator<2, 3>>; +using My2dTypes = ::testing::Types, Testables<2, 2>, Testables<2, 3>, // + Testables_SOA<2, 1>, Testables_SOA<2, 2>, Testables_SOA<2, 3>>; + INSTANTIATE_TYPED_TEST_SUITE_P(testInterpolator, ACollectionOfParticles_2d, My2dTypes); diff --git a/tests/core/numerics/ion_updater/CMakeLists.txt b/tests/core/numerics/ion_updater/CMakeLists.txt index 50f732c1b..60e8da9bd 100644 --- a/tests/core/numerics/ion_updater/CMakeLists.txt +++ b/tests/core/numerics/ion_updater/CMakeLists.txt @@ -2,19 +2,27 @@ cmake_minimum_required (VERSION 3.9) project(test-updater) -set(SOURCES test_updater.cpp) -add_executable(${PROJECT_NAME} ${SOURCES}) +function(_add_ion_updater_test src_name) -target_include_directories(${PROJECT_NAME} PRIVATE - ${GTEST_INCLUDE_DIRS} + add_executable(${src_name} ${src_name}.cpp) + + target_include_directories(${src_name} PRIVATE + ${GTEST_INCLUDE_DIRS} + ) + + target_link_directories(${src_name} PRIVATE + ${HDF5_LIBRARY_PATH} ) -target_link_libraries(${PROJECT_NAME} PRIVATE - phare_core - phare_simulator - ${GTEST_LIBS}) + target_link_libraries(${src_name} PRIVATE + phare_core + phare_simulator + ${GTEST_LIBS}) -add_no_mpi_phare_test(${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}) + add_no_mpi_phare_test(${src_name} ${CMAKE_CURRENT_BINARY_DIR}) +endfunction(_add_ion_updater_test) +_add_ion_updater_test(test_updater) +#_add_ion_updater_test(test_multithreaded_updater) diff --git a/tests/core/numerics/ion_updater/test_updater.cpp b/tests/core/numerics/ion_updater/test_updater.cpp index f7d177830..8df55e5d7 100644 --- a/tests/core/numerics/ion_updater/test_updater.cpp +++ b/tests/core/numerics/ion_updater/test_updater.cpp @@ -406,8 +406,10 @@ struct IonUpdaterTest : public ::testing::Test using ParticleArray = typename PHARETypes::ParticleArray_t; using ParticleInitializerFactory = typename PHARETypes::ParticleInitializerFactory; - using IonUpdater = typename PHARE::core::IonUpdater; + using Field = typename PHARETypes::Field_t; + using VecField = typename PHARETypes::VecField_t; + using IonUpdater = typename PHARE::core::IonUpdater; double dt{0.01}; @@ -417,8 +419,6 @@ struct IonUpdaterTest : public ::testing::Test // data for electromagnetic fields - using Field = typename PHARETypes::Field_t; - using VecField = typename PHARETypes::VecField_t; ElectromagBuffers emBuffers; IonsBuffers ionsBuffers; @@ -427,7 +427,6 @@ struct IonUpdaterTest : public ::testing::Test Ions ions{init_dict["ions"]}; - IonUpdaterTest() : ncells{100} , layout{{0.1}, {100u}, {{0.}}} @@ -486,6 +485,7 @@ struct IonUpdaterTest : public ::testing::Test auto& patchGhostPart = pop.patchGhostParticles(); + // copies need to be put in the ghost cell // we have copied particles be now their iCell needs to be udpated // our choice is : @@ -506,24 +506,25 @@ struct IonUpdaterTest : public ::testing::Test // | | | | // -------|-------| | // --------------- + for (auto const& part : domainPart) { if constexpr (interp_order == 2 or interp_order == 3) { - if (part.iCell[0] == firstAMRCell[0] - or part.iCell[0] == firstAMRCell[0] + 1) + if (part.iCell()[0] == firstAMRCell[0] + or part.iCell()[0] == firstAMRCell[0] + 1) { auto p{part}; - p.iCell[0] -= 2; + p.iCell()[0] -= 2; levelGhostPartOld.push_back(p); } } else if constexpr (interp_order == 1) { - if (part.iCell[0] == firstAMRCell[0]) + if (part.iCell()[0] == firstAMRCell[0]) { auto p{part}; - p.iCell[0] -= 1; + p.iCell()[0] -= 1; levelGhostPartOld.push_back(p); } } @@ -547,19 +548,20 @@ struct IonUpdaterTest : public ::testing::Test { if constexpr (interp_order == 2 or interp_order == 3) { - if (part.iCell[0] == lastAMRCell[0] or part.iCell[0] == lastAMRCell[0] - 1) + if (part.iCell()[0] == lastAMRCell[0] + or part.iCell()[0] == lastAMRCell[0] - 1) { auto p{part}; - p.iCell[0] += 2; + p.iCell()[0] += 2; patchGhostPart.push_back(p); } } else if constexpr (interp_order == 1) { - if (part.iCell[0] == lastAMRCell[0]) + if (part.iCell()[0] == lastAMRCell[0]) { auto p{part}; - p.iCell[0] += 1; + p.iCell()[0] += 1; patchGhostPart.push_back(p); } } @@ -695,8 +697,9 @@ struct IonUpdaterTest : public ::testing::Test EXPECT_GE(0.07, diff); if (diff >= 0.07) - std::cout << "actual : " << density(ix) << " prescribed : " << functionX[i] - << " diff : " << diff << " ix : " << ix << "\n"; + std::cout << i << " actual : " << density(ix) + << " prescribed : " << functionX[i] << " diff : " << diff + << " ix : " << ix << " max: " << functionX.size() << "\n"; } }; @@ -770,17 +773,18 @@ TYPED_TEST(IonUpdaterTest, loadsPatchGhostParticlesOnRightGhostArea) { if constexpr (TypeParam::interp_order == 1) { - for (auto const& part : pop.patchGhostParticles()) + auto& ghost = pop.patchGhostParticles(); + for (auto it = ghost.begin(); it != ghost.end(); ++it) { - EXPECT_EQ(lastAMRCell[0] + 1, part.iCell[0]); + EXPECT_EQ(lastAMRCell[0] + 1, it.iCell()[0]); } } else if constexpr (TypeParam::interp_order == 2 or TypeParam::interp_order == 3) { - typename IonUpdaterTest::ParticleArray copy{pop.patchGhostParticles()}; + auto copy{pop.patchGhostParticles()}; auto firstInOuterMostCell = std::partition( std::begin(copy), std::end(copy), [&lastAMRCell](auto const& particle) { - return particle.iCell[0] == lastAMRCell[0] + 1; + return particle.iCell()[0] == lastAMRCell[0] + 1; }); EXPECT_EQ(nbrPartPerCell, std::distance(std::begin(copy), firstInOuterMostCell)); EXPECT_EQ(nbrPartPerCell, std::distance(firstInOuterMostCell, std::end(copy))); @@ -803,9 +807,10 @@ TYPED_TEST(IonUpdaterTest, loadsLevelGhostParticlesOnLeftGhostArea) { if constexpr (TypeParam::interp_order == 1) { - for (auto const& part : pop.levelGhostParticles()) + auto& ghost = pop.levelGhostParticles(); + for (auto it = ghost.begin(); it != ghost.end(); ++it) { - EXPECT_EQ(firstAMRCell[0] - 1, part.iCell[0]); + EXPECT_EQ(firstAMRCell[0] - 1, it.iCell()[0]); } } else if constexpr (TypeParam::interp_order == 2 or TypeParam::interp_order == 3) @@ -813,7 +818,7 @@ TYPED_TEST(IonUpdaterTest, loadsLevelGhostParticlesOnLeftGhostArea) typename IonUpdaterTest::ParticleArray copy{pop.levelGhostParticles()}; auto firstInOuterMostCell = std::partition( std::begin(copy), std::end(copy), [&firstAMRCell](auto const& particle) { - return particle.iCell[0] == firstAMRCell[0] - 1; + return particle.iCell()[0] == firstAMRCell[0] - 1; }); EXPECT_EQ(nbrPartPerCell, std::distance(std::begin(copy), firstInOuterMostCell)); EXPECT_EQ(nbrPartPerCell, std::distance(firstInOuterMostCell, std::end(copy))); @@ -851,12 +856,12 @@ TYPED_TEST(IonUpdaterTest, particlesUntouchedInMomentOnlyMode) EXPECT_EQ(cpy.size(), original.size()); for (std::size_t iPart = 0; iPart < original.size(); ++iPart) { - EXPECT_EQ(cpy[iPart].iCell[0], original[iPart].iCell[0]); - EXPECT_DOUBLE_EQ(cpy[iPart].delta[0], original[iPart].delta[0]); + EXPECT_EQ(cpy.iCell(iPart)[0], original.iCell(iPart)[0]); + EXPECT_DOUBLE_EQ(cpy.delta(iPart)[0], original.delta(iPart)[0]); for (std::size_t iDir = 0; iDir < 3; ++iDir) { - EXPECT_DOUBLE_EQ(cpy[iPart].v[iDir], original[iPart].v[iDir]); + EXPECT_DOUBLE_EQ(cpy.v(iPart)[iDir], original.v(iPart)[iDir]); } } }; diff --git a/tests/core/numerics/ohm/test_main.cpp b/tests/core/numerics/ohm/test_main.cpp index e54c2e5b8..aa914b1d9 100644 --- a/tests/core/numerics/ohm/test_main.cpp +++ b/tests/core/numerics/ohm/test_main.cpp @@ -1,9 +1,12 @@ -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include #include +#include +#if defined(HAVE_RAJA) and defined(HAVE_UMPIRE) +#include "SAMRAI/tbox/Collectives.h" // tbox::parallel_synchronize(); +#include "core/def.h" +#include "simulator/simulator.h" // static allocator init - probably should be isolated +#endif #include "core/data/field/field.hpp" #include "core/data/grid/gridlayout.hpp" @@ -13,7 +16,8 @@ #include "core/numerics/ohm/ohm.hpp" #include "core/utilities/index/index.hpp" -#include "phare_core.hpp" +#include "gmock/gmock.h" +#include "gtest/gtest.h" using namespace PHARE::core; @@ -65,27 +69,29 @@ struct OhmTest : public ::testing::Test static constexpr auto dim = typename TypeInfo::first_type{}(); static constexpr auto interp = typename TypeInfo::second_type{}(); - using GridYee = GridLayout>; + using GridYee = GridLayout>; + using NdArray_t = NdArrayVector; + GridYee layout = NDlayout::create(); - Field, HybridQuantity::Scalar> n; - Field, HybridQuantity::Scalar> Vx; - Field, HybridQuantity::Scalar> Vy; - Field, HybridQuantity::Scalar> Vz; - Field, HybridQuantity::Scalar> P; - Field, HybridQuantity::Scalar> Bx; - Field, HybridQuantity::Scalar> By; - Field, HybridQuantity::Scalar> Bz; - Field, HybridQuantity::Scalar> Jx; - Field, HybridQuantity::Scalar> Jy; - Field, HybridQuantity::Scalar> Jz; - Field, HybridQuantity::Scalar> Exnew; - Field, HybridQuantity::Scalar> Eynew; - Field, HybridQuantity::Scalar> Eznew; - VecField, HybridQuantity> V; - VecField, HybridQuantity> B; - VecField, HybridQuantity> J; - VecField, HybridQuantity> Enew; + Field n; + Field Vx; + Field Vy; + Field Vz; + Field P; + Field Bx; + Field By; + Field Bz; + Field Jx; + Field Jy; + Field Jz; + Field Exnew; + Field Eynew; + Field Eznew; + VecField V; + VecField B; + VecField J; + VecField Enew; Ohm ohm; OhmTest() diff --git a/tests/core/numerics/pusher/test_pusher.cpp b/tests/core/numerics/pusher/test_pusher.cpp index 154b598c3..e65ce4b1f 100644 --- a/tests/core/numerics/pusher/test_pusher.cpp +++ b/tests/core/numerics/pusher/test_pusher.cpp @@ -13,6 +13,7 @@ #include "core/data/particles/particle_array.hpp" #include "core/numerics/boundary_condition/boundary_condition.hpp" #include "core/numerics/pusher/boris.hpp" +#include "core/numerics/pusher/boris_simpler.hpp" #include "core/numerics/pusher/pusher_factory.hpp" #include "core/utilities/range/range.hpp" #include "core/utilities/box/box.hpp" @@ -71,17 +72,24 @@ Trajectory readExpectedTrajectory() class Interpolator { public: - template - void operator()(ParticleRange particles, Electromag const&, GridLayout&) + template + inline void operator()(Particles& range, Electromag const& Em, GridLayout const& layout) { - for (auto currPart = std::begin(particles); currPart != std::end(particles); ++currPart) + auto& particles = range.begin()(); + std::size_t start = particles.begin().idx(); + std::size_t end = particles.end().idx(); + + for (auto p_idx = start; p_idx < end; ++p_idx) { - currPart->Ex = 0.01; - currPart->Ey = -0.05; - currPart->Ez = 0.05; - currPart->Bx = 1.; - currPart->By = 1.; - currPart->Bz = 1.; + auto& [Ex, Ey, Ez] = particles.E(p_idx); + auto& [Bx, By, Bz] = particles.B(p_idx); + + Ex = 0.01; + Ey = -0.05; + Ez = 0.05; + Bx = 1.; + By = 1.; + Bz = 1.; } } }; @@ -98,8 +106,8 @@ class Electromag class DummySelector { public: - template - Range operator()(Range& particles) const + template + auto operator()(Particle const& particles) const { return particles; } @@ -118,8 +126,11 @@ struct DummyLayout template class APusher : public ::testing::Test { + using ParticleArray_t = AoSMappedParticleArray; + using Particle_t = typename ParticleArray_t::Particle_t; + public: - using Pusher_ = BorisPusher>, Electromag, Interpolator, + using Pusher_ = BorisPusher, Electromag, Interpolator, BoundaryCondition, DummyLayout>; APusher() @@ -132,23 +143,27 @@ class APusher : public ::testing::Test , tstart{0} , tend{10} , nt{static_cast((tend - tstart) / dt + 1)} + { - particlesIn.emplace_back( - Particle{1., 1., ConstArray(5), ConstArray(0.), {0., 10., 0.}}); - particlesOut.emplace_back( - Particle{1., 1., ConstArray(5), ConstArray(0.), {0., 10., 0.}}); + particlesIn.emplace_back(Particle_t{ + 1., 1., ConstArray(5), ConstArray(0.), {0., 10., 0.}}); + particlesOut.emplace_back(Particle_t{ + 1., 1., ConstArray(5), ConstArray(0.), {0., 10., 0.}}); dxyz.fill(0.05); for (std::size_t i = 0; i < dim; i++) actual[i].resize(nt, 0.05); pusher->setMeshAndTimeStep(dxyz, dt); } + + protected: - using Particle = typename ParticleArray::Particle_t; Trajectory expectedTrajectory; + DummyLayout layout; - ParticleArray particlesIn; - ParticleArray particlesOut; + ParticleArray_t particlesIn; + ParticleArray_t particlesOut; + std::unique_ptr pusher; double mass; double dt; @@ -176,12 +191,12 @@ TEST_F(APusher3D, trajectoryIsOk) for (decltype(nt) i = 0; i < nt; ++i) { - actual[0][i] - = (particlesOut[0].iCell[0] + particlesOut[0].delta[0]) * static_cast(dxyz[0]); - actual[1][i] - = (particlesOut[0].iCell[1] + particlesOut[0].delta[1]) * static_cast(dxyz[1]); - actual[2][i] - = (particlesOut[0].iCell[2] + particlesOut[0].delta[2]) * static_cast(dxyz[2]); + actual[0][i] = (particlesOut[0].iCell()[0] + particlesOut[0].delta()[0]) + * static_cast(dxyz[0]); + actual[1][i] = (particlesOut[0].iCell()[1] + particlesOut[0].delta()[1]) + * static_cast(dxyz[1]); + actual[2][i] = (particlesOut[0].iCell()[2] + particlesOut[0].delta()[2]) + * static_cast(dxyz[2]); pusher->move( rangeIn, rangeOut, em, mass, interpolator, layout, @@ -206,10 +221,10 @@ TEST_F(APusher2D, trajectoryIsOk) for (decltype(nt) i = 0; i < nt; ++i) { - actual[0][i] - = (particlesOut[0].iCell[0] + particlesOut[0].delta[0]) * static_cast(dxyz[0]); - actual[1][i] - = (particlesOut[0].iCell[1] + particlesOut[0].delta[1]) * static_cast(dxyz[1]); + actual[0][i] = (particlesOut[0].iCell()[0] + particlesOut[0].delta()[0]) + * static_cast(dxyz[0]); + actual[1][i] = (particlesOut[0].iCell()[1] + particlesOut[0].delta()[1]) + * static_cast(dxyz[1]); pusher->move( rangeIn, rangeOut, em, mass, interpolator, layout, [](auto& rge) { return rge; }, @@ -232,8 +247,8 @@ TEST_F(APusher1D, trajectoryIsOk) for (decltype(nt) i = 0; i < nt; ++i) { - actual[0][i] - = (particlesOut[0].iCell[0] + particlesOut[0].delta[0]) * static_cast(dxyz[0]); + actual[0][i] = (particlesOut[0].iCell()[0] + particlesOut[0].delta()[0]) + * static_cast(dxyz[0]); pusher->move(rangeIn, rangeOut, em, mass, interpolator, layout, selector, selector); @@ -251,10 +266,12 @@ TEST_F(APusher1D, trajectoryIsOk) // and those that stay. class APusherWithLeavingParticles : public ::testing::Test { + using ParticleArray_t = AoSMappedParticleArray<1>; + public: APusherWithLeavingParticles() : pusher{std::make_unique< - BorisPusher<1, IndexRange>, Electromag, Interpolator, + BorisPusher<1, IndexRange, Electromag, Interpolator, BoundaryCondition<1, 1>, DummyLayout<1>>>()} , mass{1} , dt{0.001} @@ -281,8 +298,9 @@ class APusherWithLeavingParticles : public ::testing::Test } + protected: - std::unique_ptr>, Electromag, Interpolator, + std::unique_ptr, Electromag, Interpolator, BoundaryCondition<1, 1>, DummyLayout<1>>> pusher; double mass; @@ -296,9 +314,9 @@ class APusherWithLeavingParticles : public ::testing::Test Box domain; Box cells; BoundaryCondition<1, 1> bc; - ParticleArray<1> particlesIn; - ParticleArray<1> particlesOut1; - ParticleArray<1> particlesOut2; + ParticleArray_t particlesIn; + ParticleArray_t particlesOut1; + ParticleArray_t particlesOut2; }; @@ -306,11 +324,11 @@ class APusherWithLeavingParticles : public ::testing::Test TEST_F(APusherWithLeavingParticles, splitLeavingFromNonLeavingParticles) { - auto rangeIn = makeIndexRange(particlesIn); - auto inDomain = rangeIn; + auto rangeIn = makeIndexRange(particlesIn); + using IndexRange = std::decay_t; + auto inDomain = rangeIn; - auto selector = [this](auto& particleRange) // - { + auto selector = [this](IndexRange& particleRange) -> IndexRange { auto& box = this->cells; return particleRange.array().partition( [&](auto const& cell) { return PHARE::core::isIn(Point{cell}, box); }); @@ -329,10 +347,10 @@ TEST_F(APusherWithLeavingParticles, splitLeavingFromNonLeavingParticles) } } EXPECT_TRUE(std::none_of(inDomain.end(), std::end(particlesIn), [this](auto const& particle) { - return PHARE::core::isIn(Point{particle.iCell}, cells); + return PHARE::core::isIn(Point{particle.iCell()}, cells); })); EXPECT_TRUE(std::all_of(std::begin(inDomain), std::end(inDomain), [this](auto const& particle) { - return PHARE::core::isIn(Point{particle.iCell}, cells); + return PHARE::core::isIn(Point{particle.iCell()}, cells); })); } @@ -357,11 +375,10 @@ TEST_F(APusherWithLeavingParticles, pusherWithOrWithoutBCReturnsSameNbrOfStaying for (decltype(nt) i = 0; i < nt; ++i) { - auto layout = DummyLayout<1>{}; - newEndWithBC - = pusher->move(rangeIn, rangeOut1, em, mass, interpolator, selector, bc, layout); - newEndWithoutBC - = pusher->move(rangeIn, rangeOut2, em, mass, interpolator, selector, layout); + auto layout = DummyLayout<1>{}; + newEndWithBC = pusher->move(rangeIn, rangeOut1, em, mass, interpolator, selector, bc, + layout); + newEndWithoutBC = pusher->move(rangeIn, rangeOut2, em, mass, interpolator, selector, layout); if (newEndWithBC != std::end(particlesOut1) || newEndWithoutBC != std::end(particlesOut2)) { @@ -381,11 +398,30 @@ TEST_F(APusherWithLeavingParticles, pusherWithOrWithoutBCReturnsSameNbrOfStaying TEST(APusherFactory, canReturnABorisPusher) { - auto pusher - = PusherFactory::makePusher<1, IndexRange>, Electromag, Interpolator, - BoundaryCondition<1, 1>, DummyLayout<1>>("modified_boris"); + { + auto pusher + = PusherFactory::makePusher<1, IndexRange>, Electromag, + Interpolator, BoundaryCondition<1, 1>, DummyLayout<1>>( + "modified_boris"); + + EXPECT_NE(nullptr, pusher); + } + { + auto pusher + = PusherFactory::makePusher<1, IndexRange>, Electromag, + Interpolator, BoundaryCondition<1, 1>, DummyLayout<1>>( + "modified_boris"); + + EXPECT_NE(nullptr, pusher); + } - EXPECT_NE(nullptr, pusher); + // { + // pusher = PusherFactory::makePusher<1, IndexRange>, Electromag, + // Interpolator, + // BoundaryCondition<1, 1>, + // DummyLayout<1>>("modified_boris"); + // EXPECT_NE(nullptr, pusher); + // } } diff --git a/tests/core/utilities/cellmap/test_cellmap.cpp b/tests/core/utilities/cellmap/test_cellmap.cpp index 3b6e4650e..23beed49c 100644 --- a/tests/core/utilities/cellmap/test_cellmap.cpp +++ b/tests/core/utilities/cellmap/test_cellmap.cpp @@ -24,18 +24,21 @@ struct Obj template struct Particle { - std::array iCell; + std::array iCell_; double delta; bool operator==(Particle const& other) const { bool equal = true; for (auto i = 0u; i < dim; ++i) { - equal &= (iCell[i] == other.iCell[i]); + equal &= (iCell_[i] == other.iCell_[i]); } equal &= (std::abs(delta - other.delta) < 1e-12); return delta; } + + auto& iCell() { return iCell_; } + auto& iCell() const { return iCell_; } }; @@ -99,11 +102,11 @@ TEST(CellMap, canSortCells) TEST(CellMap, itemCanBeRemoved) { std::array, 2> particles; - particles[0].iCell[0] = 14; - particles[0].iCell[1] = 27; - particles[1].iCell[0] = 14; - particles[1].iCell[1] = 26; - auto constexpr dim = 2u; + particles[0].iCell_[0] = 14; + particles[0].iCell_[1] = 27; + particles[1].iCell_[0] = 14; + particles[1].iCell_[1] = 26; + auto constexpr dim = 2u; Box b{{10, 12}, {30, 32}}; CellMap cm{b}; cm.addToCell(std::array{14, 27}, 0); @@ -144,7 +147,7 @@ auto make_particles_in(PHARE::core::Box box, std::size_t nppc) Particle p; for (auto idim = 0u; idim < dim; ++idim) { - p.iCell[idim] = cell[idim]; + p.iCell_[idim] = cell[idim]; } p.delta = dis(gen); particles.push_back(p); @@ -204,7 +207,7 @@ TEST(CellMap, givesAccessToAllParticlesInACell) EXPECT_EQ(blist.size(), nppc); for (auto particleIndex : blist) { - EXPECT_EQ(Point{particles[particleIndex].iCell}, cell); + EXPECT_EQ(Point{particles[particleIndex].iCell()}, cell); } } } @@ -314,7 +317,7 @@ TEST_F(CellMapExportFix, exportItems) EXPECT_EQ(selected.size(), selected.capacity()); for (auto const& p : selected) { - EXPECT_TRUE(isIn(Point{p.iCell}, selectionBox)); + EXPECT_TRUE(isIn(Point{p.iCell()}, selectionBox)); } } @@ -328,7 +331,7 @@ TEST_F(CellMapExportFix, exportWithTransform) EXPECT_EQ(selected.capacity(), capa); cm.export_to(selectionBox, particles, selected, [&](auto const& part) { auto copy{part}; - copy.iCell[0] += 100; + copy.iCell()[0] += 100; return copy; }); EXPECT_EQ(selected.size(), selected.capacity()); @@ -337,7 +340,7 @@ TEST_F(CellMapExportFix, exportWithTransform) offsetedSelectionBox.upper[0] += 100; for (auto const& p : selected) { - EXPECT_TRUE(isIn(Point{p.iCell}, offsetedSelectionBox)); + EXPECT_TRUE(isIn(Point{p.iCell()}, offsetedSelectionBox)); } } @@ -349,7 +352,7 @@ TEST_F(CellMapExportFix, exportWithPredicate) cm.export_if(particles, selected, [&](auto const& cell) { return isIn(cell, selectionBox); }); for (auto const& p : selected) { - EXPECT_TRUE(isIn(Point{p.iCell}, selectionBox)); + EXPECT_TRUE(isIn(Point{p.iCell()}, selectionBox)); } } @@ -401,14 +404,14 @@ TEST_F(CellMappedParticleBox, trackParticle) { EXPECT_EQ(cm.size(), particles.size()); // pretends the particle change cell in x - auto oldcell = particles[200].iCell; - particles[200].iCell[0] += 1; + auto oldcell = particles[200].iCell(); + particles[200].iCell()[0] += 1; cm.update(particles, 200, oldcell); EXPECT_EQ(cm.size(), particles.size()); - auto& blist = cm(particles[200].iCell); + auto& blist = cm(particles[200].iCell()); auto found = false; for (auto particleIndex : blist) { @@ -434,11 +437,11 @@ TEST_F(CellMappedParticleBox, partitionsParticlesInPatchBox) for (std::size_t idx = inPatchRange.ibegin(); idx < inPatchRange.iend(); ++idx) { - EXPECT_TRUE(isIn(Point{particles[idx].iCell}, patchBox)); + EXPECT_TRUE(isIn(Point{particles[idx].iCell()}, patchBox)); } for (std::size_t idx = inPatchRange.iend(); idx < particles.size(); ++idx) { - EXPECT_FALSE(isIn(Point{particles[idx].iCell}, patchBox)); + EXPECT_FALSE(isIn(Point{particles[idx].iCell()}, patchBox)); } } @@ -456,7 +459,7 @@ TEST_F(CellMappedParticleBox, allButOneParticleSatisfyPredicate) for (std::size_t idx = singleParticleRange.ibegin(); idx < singleParticleRange.iend(); ++idx) { - EXPECT_NE(Point{particles[idx].iCell}, patchBox.lower); + EXPECT_NE(Point{particles[idx].iCell()}, patchBox.lower); } } @@ -464,7 +467,7 @@ TEST_F(CellMappedParticleBox, allButLastAlreadySatisfyPredicate) { EXPECT_EQ(cm.size(), particles.size()); - auto lastParticleCell = particles[particles.size() - 1].iCell; + auto lastParticleCell = particles[particles.size() - 1].iCell(); auto doNotTakeThatParticle = [&](auto const& cell) { return cell != lastParticleCell; }; auto allParts = makeIndexRange(particles); auto singleParticleRange = cm.partition(allParts, doNotTakeThatParticle); @@ -475,7 +478,7 @@ TEST_F(CellMappedParticleBox, allButLastAlreadySatisfyPredicate) for (std::size_t idx = singleParticleRange.ibegin(); idx < singleParticleRange.iend(); ++idx) { - EXPECT_NE(Point{particles[idx].iCell}, lastParticleCell); + EXPECT_NE(Point{particles[idx].iCell()}, lastParticleCell); } } @@ -515,11 +518,11 @@ TEST_F(CellMappedParticleBox, rangeBasedPartition) for (std::size_t idx = inpatch.ibegin(); idx < inpatch.iend(); ++idx) { - EXPECT_TRUE(isIn(Point{partRange.array()[idx].iCell}, patchBox)); + EXPECT_TRUE(isIn(Point{partRange.array()[idx].iCell()}, patchBox)); } for (std::size_t idx = inpatch.iend(); idx < partRange.iend(); ++idx) { - EXPECT_FALSE(isIn(Point{partRange.array()[idx].iCell}, patchBox)); + EXPECT_FALSE(isIn(Point{partRange.array()[idx].iCell()}, patchBox)); } } @@ -537,18 +540,18 @@ TEST_F(CellMappedParticleBox, getPatchParticlesFromNonLeavingPartition) EXPECT_EQ(inGhostBoxRange.size(), nppc * ghostBox.size()); for (auto idx = inGhostBoxRange.ibegin(); idx < inGhostBoxRange.iend(); ++idx) { - EXPECT_TRUE(isIn(Point{particles[idx].iCell}, ghostBox)); + EXPECT_TRUE(isIn(Point{particles[idx].iCell()}, ghostBox)); } for (auto idx = inGhostBoxRange.iend(); idx < particles.size(); ++idx) { - EXPECT_TRUE(isIn(Point{particles[idx].iCell}, outBox) - and !isIn(Point{particles[idx].iCell}, ghostBox)); + EXPECT_TRUE(isIn(Point{particles[idx].iCell()}, outBox) + and !isIn(Point{particles[idx].iCell()}, ghostBox)); } for (auto idx = inPatchRange.ibegin(); idx < inPatchRange.iend(); ++idx) { - EXPECT_TRUE(isIn(Point{particles[idx].iCell}, patchBox)); + EXPECT_TRUE(isIn(Point{particles[idx].iCell()}, patchBox)); } EXPECT_EQ(0, inPatchRange.ibegin()); @@ -564,7 +567,7 @@ TEST_F(CellMappedParticleBox, eraseOutOfPatchRange) cm.erase(toErase); for (auto const& part : particles) - EXPECT_TRUE(isIn(Point{part.iCell}, patchBox)); + EXPECT_TRUE(isIn(Point{part.iCell()}, patchBox)); EXPECT_EQ(particles.size(), patchBox.size() * nppc); } diff --git a/tests/core/utilities/partitionner/test_main.cpp b/tests/core/utilities/partitionner/test_main.cpp index 1350ba516..ae53c3a70 100644 --- a/tests/core/utilities/partitionner/test_main.cpp +++ b/tests/core/utilities/partitionner/test_main.cpp @@ -57,37 +57,37 @@ class APartitionner : public ::testing::Test for (int i = 0; i < 850; ++i) { - particles[i].iCell[0] = disInX(gen); - particles[i].iCell[1] = disInY(gen); + particles.iCell(i)[0] = disInX(gen); + particles.iCell(i)[1] = disInY(gen); } for (int i = 850; i < 900; ++i) { - particles[i].iCell[0] = 21; - particles[i].iCell[1] = disInBox0Y(gen); + particles.iCell(i)[0] = 21; + particles.iCell(i)[1] = disInBox0Y(gen); } for (int i = 900; i < 950; ++i) { - particles[i].iCell[0] = disInBox1X(gen); - particles[i].iCell[1] = 11; + particles.iCell(i)[0] = disInBox1X(gen); + particles.iCell(i)[1] = 11; } for (int i = 950; i < 1000; ++i) { - particles[i].iCell[0] = 21; - particles[i].iCell[1] = 11; + particles.iCell(i)[0] = 21; + particles.iCell(i)[1] = 11; } for (int i = 1000; i < 1025; ++i) { - particles[i].iCell[0] = -1; - particles[i].iCell[1] = disInBox0Y(gen); + particles.iCell(i)[0] = -1; + particles.iCell(i)[1] = disInBox0Y(gen); } for (int i = 1025; i < 1050; ++i) { - particles[i].iCell[0] = disInBox1X(gen); - particles[i].iCell[1] = -1; + particles.iCell(i)[0] = disInBox1X(gen); + particles.iCell(i)[1] = -1; } } @@ -111,10 +111,11 @@ class APartitionner : public ::testing::Test protected: - ParticleArray<2> particles; + using ParticleArray_t = ParticleArray<2>; + ParticleArray_t particles; Box patchBox; std::vector> boundaryBoxes; - ParticleArray<2>::iterator firstLeaving; + ParticleArray_t::iterator firstLeaving; }; diff --git a/tests/core/utilities/point/test_point.cpp b/tests/core/utilities/point/test_point.cpp index 34ebcd525..1d962ec19 100644 --- a/tests/core/utilities/point/test_point.cpp +++ b/tests/core/utilities/point/test_point.cpp @@ -5,7 +5,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" -using namespace PHARE; +using namespace PHARE::core; TEST(Point, canBeBuiltByTemplateDeduction) diff --git a/tests/core/utilities/range/CMakeLists.txt b/tests/core/utilities/range/CMakeLists.txt index 8b4c78a79..266423e09 100644 --- a/tests/core/utilities/range/CMakeLists.txt +++ b/tests/core/utilities/range/CMakeLists.txt @@ -1,19 +1,8 @@ cmake_minimum_required (VERSION 3.9) -project(test-range) - -set(SOURCES test_main.cpp) - -add_executable(${PROJECT_NAME} ${SOURCES}) - -target_include_directories(${PROJECT_NAME} PRIVATE - ${GTEST_INCLUDE_DIRS} - ) - -target_link_libraries(${PROJECT_NAME} PRIVATE - phare_core - ${GTEST_LIBS}) +project(test_range) +add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp) +target_include_directories(${PROJECT_NAME} PRIVATE ${GTEST_INCLUDE_DIRS} ) +target_link_libraries(${PROJECT_NAME} PRIVATE phare_core ${GTEST_LIBS}) add_no_mpi_phare_test(${PROJECT_NAME} ${CMAKE_CURRENT_BINARY_DIR}) - - diff --git a/tests/core/utilities/range/test_range.cpp b/tests/core/utilities/range/test_range.cpp new file mode 100644 index 000000000..87027e8b3 --- /dev/null +++ b/tests/core/utilities/range/test_range.cpp @@ -0,0 +1,16 @@ + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "test_range.hpp" +#include "test_ranges.hpp" +// #include "test_range_replacer.hpp" + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/tests/core/utilities/range/test_main.cpp b/tests/core/utilities/range/test_range.hpp similarity index 84% rename from tests/core/utilities/range/test_main.cpp rename to tests/core/utilities/range/test_range.hpp index e7685ed16..196bd0fbd 100644 --- a/tests/core/utilities/range/test_main.cpp +++ b/tests/core/utilities/range/test_range.hpp @@ -1,3 +1,5 @@ +#ifndef PHARE_TEST_UTILITY_TEST_RANGE_HPP +#define PHARE_TEST_UTILITY_TEST_RANGE_HPP #include @@ -37,11 +39,4 @@ TEST(ARange, returnBeginAndEnd) } - - -int main(int argc, char** argv) -{ - ::testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -} +#endif /* PHARE_TEST_UTILITY_TEST_RANGE_HPP */ diff --git a/tests/core/utilities/range/test_ranges.hpp b/tests/core/utilities/range/test_ranges.hpp new file mode 100644 index 000000000..5391af880 --- /dev/null +++ b/tests/core/utilities/range/test_ranges.hpp @@ -0,0 +1,211 @@ +#ifndef PHARE_TEST_UTILITY_TEST_RANGES_HPP +#define PHARE_TEST_UTILITY_TEST_RANGES_HPP + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "core/utilities/range/ranges.hpp" + +namespace PHARE::core +{ +template +auto _check_ranges(RangesList const& ranges_list, std::size_t val = 0) +{ + for (auto const& ranges : ranges_list) + { + EXPECT_GE(ranges.size(), 1); + for (auto const& range : ranges) + { + EXPECT_GE(range.size(), 1); + for (auto const& s : range) + EXPECT_EQ(s, val++); + } + } + return val; +} + + +TEST(Ranges, test_range_builder_simple) +{ + using V = std::vector; + std::size_t constexpr MAX = 10 * 100; + + std::vector vs(10); + std::size_t val = 0; + for (auto& v : vs) + for (std::size_t i = 0; i < 100; ++i) + v.emplace_back(val++); + + EXPECT_EQ(val, MAX); + EXPECT_EQ(_check_ranges(make_balanced_ranges(vs)), MAX); +} + +TEST(Ranges, test_range_builder_v0) +{ + using V = std::vector; + std::size_t constexpr MAX = 70; + + std::vector vs(5); + std::size_t val = 0; + + for (std::size_t i = 0; i < 10; ++i) + vs[0].emplace_back(val++); + + for (std::size_t i = 0; i < 20; ++i) + vs[1].emplace_back(val++); + + for (std::size_t i = 0; i < 10; ++i) + vs[2].emplace_back(val++); + + for (std::size_t i = 0; i < 20; ++i) + vs[3].emplace_back(val++); + + for (std::size_t i = 0; i < 10; ++i) + vs[4].emplace_back(val++); + + EXPECT_EQ(val, MAX); + EXPECT_EQ(_check_ranges(make_balanced_ranges(vs)), MAX); +} + + +TEST(Ranges, test_range_builder_v1) +{ + using V = std::vector; + std::size_t constexpr MAX = 430; + + std::vector vs(5); + std::size_t val = 0; + + for (std::size_t i = 0; i < 10; ++i) + vs[0].emplace_back(val++); + + for (std::size_t i = 0; i < 200; ++i) + vs[1].emplace_back(val++); + + for (std::size_t i = 0; i < 10; ++i) + vs[2].emplace_back(val++); + + for (std::size_t i = 0; i < 200; ++i) + vs[3].emplace_back(val++); + + for (std::size_t i = 0; i < 10; ++i) + vs[4].emplace_back(val++); + + EXPECT_EQ(val, MAX); + EXPECT_EQ(_check_ranges(make_balanced_ranges(vs)), MAX); +} + +namespace detail +{ + struct S + { + S(std::size_t i_) + : i{i_} + { + } + + std::size_t i = 0; + }; + + struct V + { + std::vector s; + }; + +} // namespace detail + +TEST(Ranges, test_range_builder_v2) +{ + std::vector vs(2); + std::size_t constexpr SPLIT_IN = 10; + std::size_t constexpr MAX = 300; + + { + std::size_t val = 0; + for (std::size_t i = 0; i < MAX / 2 - 5; ++i) + vs[0].s.emplace_back(val++); + for (std::size_t i = 0; i < MAX / 2 + 5; ++i) + vs[1].s.emplace_back(val++); + + EXPECT_EQ(val, MAX); + EXPECT_EQ(vs[1].s.back().i + 1, val); + } + + auto ranges_vec = make_balanced_ranges( + vs, SPLIT_IN, [](auto& el) -> auto& { return el.s; }); + + std::size_t val = 0; + for (auto const& ranges : ranges_vec) + { + std::size_t ranges_cover = 0; + EXPECT_GE(ranges.size(), 1); + for (auto const& range : ranges) + { + ranges_cover += range.size(); + for (auto const& s : range) + EXPECT_EQ(s.i, val++); + } + EXPECT_EQ(ranges_cover, 30); + } + + EXPECT_EQ(val, 300); +} + + +namespace detail +{ + struct R : public Range::iterator> + { + using Super = Range::iterator>; + + R(Super&& super) + : Super{std::forward(super)} + { + } + }; +} // namespace detail + +TEST(Ranges, test_range_builder_v3) +{ + using Range_t = Range::iterator>; + std::size_t constexpr SPLIT_IN = 10; + std::size_t constexpr MAX = 300; + + std::vector vs(2); + + { + std::size_t val = 0; + for (std::size_t i = 0; i < MAX / 2 - 5; ++i) + vs[0].s.emplace_back(val++); + for (std::size_t i = 0; i < MAX / 2 + 5; ++i) + vs[1].s.emplace_back(val++); + EXPECT_EQ(val, MAX); + EXPECT_EQ(vs[1].s.back().i + 1, val); + } + + auto ranges_vec = make_balanced_ranges( + vs, SPLIT_IN, [](auto& el) -> auto& { return el.s; }, + [](auto&& range, auto& el) { return detail::R{std::forward(range)}; }); + + EXPECT_EQ(ranges_vec.size(), SPLIT_IN); + + std::size_t val = 0; + for (auto const& ranges : ranges_vec) + { + std::size_t ranges_cover = 0; + EXPECT_GE(ranges.size(), 1); + for (auto const& range : ranges) + { + ranges_cover += range.size(); + for (auto const& s : range) + EXPECT_EQ(s.i, val++); + } + EXPECT_EQ(ranges_cover, 30); + } + + EXPECT_EQ(val, MAX); +} + +} // namespace PHARE::core + +#endif /* PHARE_TEST_UTILITY_TEST_RANGES_HPP */ diff --git a/tests/diagnostic/CMakeLists.txt b/tests/diagnostic/CMakeLists.txt index 3a5964a5f..0a84d07f3 100644 --- a/tests/diagnostic/CMakeLists.txt +++ b/tests/diagnostic/CMakeLists.txt @@ -30,14 +30,16 @@ if(HighFive) add_phare_test(${src_name} ${CMAKE_CURRENT_BINARY_DIR}) + endfunction(_add_diagnostics_test) _add_diagnostics_test(test-diagnostics_1d) _add_diagnostics_test(test-diagnostics_2d) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/job_1d.py.in ${CMAKE_CURRENT_BINARY_DIR}/job_1d.py @ONLY) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/job_2d.py.in ${CMAKE_CURRENT_BINARY_DIR}/job_2d.py @ONLY) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py ${CMAKE_CURRENT_BINARY_DIR}/__init__.py @ONLY) + if(NOT ${PHARE_PROJECT_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + file(GLOB PYFILES "*.py") + file(COPY ${PYFILES} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + endif() message(STATUS "diagnostic working directory " ${PHARE_PROJECT_DIR}) diff --git a/tests/diagnostic/job_1d.py.in b/tests/diagnostic/job_1d.py similarity index 81% rename from tests/diagnostic/job_1d.py.in rename to tests/diagnostic/job_1d.py index 66937087a..64fd7007e 100644 --- a/tests/diagnostic/job_1d.py.in +++ b/tests/diagnostic/job_1d.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 import pyphare.pharein as ph from pyphare.pharein import ElectronModel @@ -8,7 +7,7 @@ out = "phare_outputs/diags_1d/" simInput = {"diag_options": {"format": "phareh5", "options": {"dir": out, "mode" : "overwrite"}}} -ph.Simulation(**basicSimulatorArgs(dim = 1, interp = 1, **simInput)) +ph.Simulation(**basicSimulatorArgs(ndim = 1, interp = 1, **simInput)) model = makeBasicModel() ElectronModel(closure="isothermal",Te = 0.12) dump_all_diags(model.populations) diff --git a/tests/diagnostic/job_2d.py.in b/tests/diagnostic/job_2d.py similarity index 81% rename from tests/diagnostic/job_2d.py.in rename to tests/diagnostic/job_2d.py index 21712c2c7..3b34af6bb 100644 --- a/tests/diagnostic/job_2d.py.in +++ b/tests/diagnostic/job_2d.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python3 import pyphare.pharein as ph from pyphare.pharein import ElectronModel @@ -8,7 +7,7 @@ out = "phare_outputs/diags_2d/" simInput = {"diag_options": {"format": "phareh5", "options": {"dir": out, "mode" : "overwrite"}}} -ph.Simulation(**basicSimulatorArgs(dim = 2, interp = 1, **simInput)) +ph.Simulation(**basicSimulatorArgs(ndim = 2, interp = 1, **simInput)) model = makeBasicModel() ElectronModel(closure="isothermal",Te = 0.12) dump_all_diags(model.populations) diff --git a/tests/diagnostic/test_diagnostics.hpp b/tests/diagnostic/test_diagnostics.hpp index 076eb2ab5..04c9d5dc9 100644 --- a/tests/diagnostic/test_diagnostics.hpp +++ b/tests/diagnostic/test_diagnostics.hpp @@ -160,40 +160,64 @@ void validateElectromagDump(Simulator& sim, Hi5Diagnostic& hi5) template void validateParticleDump(Simulator& sim, Hi5Diagnostic& hi5) { - using GridLayout = typename Simulator::PHARETypes::GridLayout_t; + using GridLayout = typename Simulator::PHARETypes::GridLayout_t; + auto constexpr dim = GridLayout::dimension; auto& hybridModel = *sim.getHybridModel(); auto checkParticles = [&](auto& hifile, auto& particles, auto path) { + using ParticleArray_t = std::decay_t; + if (!particles.size()) return; + auto weightV = hifile.template read_data_set_flat(path + "weight"); auto chargeV = hifile.template read_data_set_flat(path + "charge"); - auto vV = hifile.template read_data_set_flat(path + "v"); - auto iCellV = hifile.template read_data_set_flat(path + "iCell"); - auto deltaV = hifile.template read_data_set_flat(path + "delta"); - - core::ParticlePacker packer{particles}; - - auto first = packer.empty(); - std::size_t iCellSize = std::get<2>(first).size(); - std::size_t deltaSize = std::get<3>(first).size(); - std::size_t vSize = std::get<4>(first).size(); - std::size_t part_idx = 0; - while (packer.hasNext()) + auto vV = hifile.template read_data_set(path + "v"); + auto iCellV = hifile.template read_data_set(path + "iCell"); + auto deltaV = hifile.template read_data_set(path + "delta"); + + if constexpr (ParticleArray_t::is_contiguous) { - auto next = packer.next(); + for (std::size_t pi = 0; pi < particles.size(); ++pi) + { + auto& iCell = particles.iCell(pi); + for (std::size_t i = 0; i < iCell.size(); i++) + EXPECT_EQ(iCellV[pi][i], iCell[i]); + + auto& delta = particles.delta(pi); + for (std::size_t i = 0; i < delta.size(); i++) + EXPECT_FLOAT_EQ(deltaV[pi][i], delta[i]); + + auto& v = particles.v(pi); + for (std::size_t i = 0; i < v.size(); i++) + EXPECT_FLOAT_EQ(vV[pi][i], v[i]); + } + } + else + { + core::ParticlePacker packer{particles}; + + auto first = packer.empty(); + std::size_t iCellSize = std::get<2>(first).size(); + std::size_t deltaSize = std::get<3>(first).size(); + std::size_t vSize = std::get<4>(first).size(); + std::size_t part_idx = 0; + while (packer.hasNext()) + { + auto next = packer.next(); - for (std::size_t i = 0; i < iCellSize; i++) - EXPECT_EQ(iCellV[(part_idx * iCellSize) + i], std::get<2>(next)[i]); + for (std::size_t i = 0; i < iCellSize; i++) + EXPECT_EQ(iCellV[part_idx][i], std::get<2>(next)[i]); - for (std::size_t i = 0; i < deltaSize; i++) - EXPECT_FLOAT_EQ(deltaV[(part_idx * deltaSize) + i], std::get<3>(next)[i]); + for (std::size_t i = 0; i < deltaSize; i++) + EXPECT_FLOAT_EQ(deltaV[part_idx][i], std::get<3>(next)[i]); - for (std::size_t i = 0; i < vSize; i++) - EXPECT_FLOAT_EQ(vV[(part_idx * vSize) + i], std::get<4>(next)[i]); + for (std::size_t i = 0; i < vSize; i++) + EXPECT_FLOAT_EQ(vV[part_idx][i], std::get<4>(next)[i]); - part_idx++; + part_idx++; + } } }; diff --git a/tests/initializer/job.py b/tests/initializer/job.py index 2f4f363a7..fc7590d34 100644 --- a/tests/initializer/job.py +++ b/tests/initializer/job.py @@ -1,14 +1,9 @@ #!/usr/bin/env python3 -import pyphare.pharein -from pyphare.pharein import Simulation -from pyphare.pharein import MaxwellianFluidModel -from pyphare.pharein import ElectromagDiagnostics -from pyphare.pharein import ElectronModel +import pyphare.pharein as ph # configure the simulation - -Simulation( +ph.Simulation( smallest_patch_size=10, largest_patch_size=64, time_step_nbr=1000, # number of time steps (not specified if time_step and final_time provided) @@ -40,12 +35,10 @@ "vthz": vthz, } -MaxwellianFluidModel( - bx=bx, - by=by, - bz=bz, - protons={"charge": 1, "density": density, **vvv, "init": {"seed": 1337}}, - alpha={"charge": 1, "density": density, **vvv, "init": {"seed": 2}}, +ph.MaxwellianFluidModel( + bx=bx, by=by, bz=bz, + protons={"charge":1, "density":density, **vvv, "init":{"seed":1337}}, + alpha={"charge":1, "density":density, **vvv, "init":{"seed":2}}, ) -ElectronModel(closure="isothermal", Te=0.12) +ph.ElectronModel(closure="isothermal",Te = 0.12) diff --git a/tests/initializer/test_initializer.cpp b/tests/initializer/test_initializer.cpp index 10c34baa6..cf8296c62 100644 --- a/tests/initializer/test_initializer.cpp +++ b/tests/initializer/test_initializer.cpp @@ -8,12 +8,12 @@ #include "initializer/python_data_provider.hpp" #include "initializer/restart_data_provider.hpp" - #include "core/data/grid/gridlayoutdefs.hpp" #include "core/utilities/index/index.hpp" #include "core/data/electromag/electromag.hpp" #include "core/data/grid/gridlayout.hpp" #include "core/data/grid/gridlayoutimplyee.hpp" +#include "core/data/particles/particle.hpp" #include "core/data/particles/particle_array.hpp" @@ -124,5 +124,18 @@ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); + try + { + return RUN_ALL_TESTS(); + } + catch (std::runtime_error const& e) + { + PHARE_LOG_LINE_STR(e.what()); + } + catch (...) + { + PHARE_LOG_LINE_STR("UNKNOWN ERROR"); + } + + return 1; } diff --git a/tests/simulator/__init__.py b/tests/simulator/__init__.py index 3f0f5c928..edc14be16 100644 --- a/tests/simulator/__init__.py +++ b/tests/simulator/__init__.py @@ -1,8 +1,10 @@ import unittest from datetime import datetime -import pyphare.pharein as ph, numpy as np -from pyphare.pharein import ElectronModel + +import numpy as np +import pyphare.pharein as ph +from pyphare.core.phare_utilities import np_array_ify def parse_cli_args(pop_from_sys = True): @@ -25,28 +27,28 @@ def __setitem__(self, k, v): return super(NoOverwriteDict, self).__setitem__(k, v) -def basicSimulatorArgs(dim: int, interp: int, **kwargs): +def basicSimulatorArgs(ndim: int, interp: int, **kwargs): from pyphare.pharein.simulation import valid_refined_particle_nbr from pyphare.pharein.simulation import check_patch_size + from pyphare.core.phare_utilities import np_array_ify + + cells = np_array_ify(kwargs.get("cells", 20), ndim) - cells = kwargs.get("cells", [20 for i in range(dim)]) - if not isinstance(cells, (list, tuple)): - cells = [cells] * dim + _, smallest_patch_size = check_patch_size(ndim, interp_order=interp, cells=cells) + dl = 1.0 / cells + b0 = [[3] * ndim, [8] * ndim] - _, smallest_patch_size = check_patch_size(dim, interp_order=interp, cells=cells) - dl = [1.0 / v for v in cells] - b0 = [[3] * dim, [8] * dim] args = { "interp_order": interp, "smallest_patch_size": smallest_patch_size, - "largest_patch_size": [20] * dim, + "largest_patch_size": [20] * ndim, "time_step_nbr": 1000, "final_time": 1.0, - "boundary_types": ["periodic"] * dim, + "boundary_types": ["periodic"] * ndim, "cells": cells, "dl": dl, "refinement_boxes": {"L0": {"B0": b0}}, - "refined_particle_nbr": valid_refined_particle_nbr[dim][interp][0], + "refined_particle_nbr": valid_refined_particle_nbr[ndim][interp][0], "diag_options": {}, "nesting_buffer": 0, "strict": True, @@ -54,7 +56,8 @@ def basicSimulatorArgs(dim: int, interp: int, **kwargs): for k, v in kwargs.items(): if k in args: args[k] = v - + args["cells"] = np_array_ify(args["cells"], ndim) + args["dl"] = np_array_ify(args["dl"], ndim) return args def meshify(*xyz): @@ -103,19 +106,19 @@ def defaultPopulationSettings(sim, density_fn, vbulk_fn): } -def makeBasicModel(extra_pops={}): +def makeBasicModel(extra_pops={}, ppc=100): sim = ph.global_vars.sim _density_fn_periodic = globals()["density_"+str(sim.ndim)+"d_periodic"] pops = { "protons": { **defaultPopulationSettings(sim, _density_fn_periodic, fn_periodic), - "nbr_part_per_cell": 100, + "nbr_part_per_cell": ppc, "init": {"seed": 1337}, }, "alpha": { **defaultPopulationSettings(sim, _density_fn_periodic, fn_periodic), - "nbr_part_per_cell": 100, + "nbr_part_per_cell": ppc, "init": {"seed": 13337}, }, } @@ -141,7 +144,7 @@ def populate_simulation(dim, interp, **input): if "diags_fn" in input: input["diags_fn"](model) - ElectronModel(closure="isothermal", Te=0.12) + ph.ElectronModel(closure="isothermal", Te=0.12) return simulation diff --git a/tests/simulator/advance/test_fields_advance_2d.py b/tests/simulator/advance/test_fields_advance_2d.py index 2b41fee8e..6a48f1a53 100644 --- a/tests/simulator/advance/test_fields_advance_2d.py +++ b/tests/simulator/advance/test_fields_advance_2d.py @@ -63,7 +63,7 @@ def test_overlaped_fields_are_equal_with_min_max_patch_size_of_max_ghosts(self, *per_interp(({"L0": {"B0": Box2D(6, 23)}})), *per_interp(({"L0": {"B0": Box2D( 2, 12), "B1": Box2D(13, 25)}})), *per_interp(({"L0": {"B0": Box2D( 5, 20)}, "L1": {"B0": Box2D(15, 19)}})), - *per_interp(({"L0": {"B0": Box2D( 5, 20)}, "L1": {"B0": Box2D(12, 38)}, "L2": {"B0": Box2D(30, 52)} })), + *per_interp(({"L0": {"B0": Box2D( 5, 20)}, "L1": {"B0": Box2D(15, 39)}, "L2": {"B0": Box2D(35, 49)} })), ) @unpack def test_field_coarsening_via_subcycles(self, interp_order, refinement_boxes): diff --git a/tests/simulator/advance/test_particles_advance_1d.py b/tests/simulator/advance/test_particles_advance_1d.py index 5cadf13f4..d6ce96691 100644 --- a/tests/simulator/advance/test_particles_advance_1d.py +++ b/tests/simulator/advance/test_particles_advance_1d.py @@ -31,12 +31,14 @@ class AdvanceTest(AdvanceTestBase): ) @unpack def test_overlapped_particledatas_have_identical_particles(self, interp_order, refinement_boxes): + print(f"{self._testMethodName}_{ndim}d") self._test_overlapped_particledatas_have_identical_particles(ndim, interp_order, refinement_boxes) @data(*interp_orders) def test_L0_particle_number_conservation(self, interp): + print(f"{self._testMethodName}_{ndim}d") self._test_L0_particle_number_conservation(ndim, interp) diff --git a/tests/simulator/advance/test_particles_advance_2d.py b/tests/simulator/advance/test_particles_advance_2d.py index ed3c73a97..308a50868 100644 --- a/tests/simulator/advance/test_particles_advance_2d.py +++ b/tests/simulator/advance/test_particles_advance_2d.py @@ -31,11 +31,13 @@ class AdvanceTest(AdvanceTestBase): ) @unpack def test_overlapped_particledatas_have_identical_particles(self, interp_order, refinement_boxes): + print(f"{self._testMethodName}_{ndim}d") self._test_overlapped_particledatas_have_identical_particles( ndim, interp_order, refinement_boxes, ppc=ppc, cells=40, largest_patch_size=20) @data(*interp_orders) def test_L0_particle_number_conservation(self, interp): + print(f"{self._testMethodName}_{ndim}d") self._test_L0_particle_number_conservation(ndim, interp, ppc=ppc) diff --git a/tests/simulator/refinement/test_2d_10_core.py b/tests/simulator/refinement/test_2d_10_core.py index b7af1b7fe..913c6f7e7 100644 --- a/tests/simulator/refinement/test_2d_10_core.py +++ b/tests/simulator/refinement/test_2d_10_core.py @@ -27,8 +27,8 @@ def config(diag_outputs, model_init={}, refinement_boxes=None): Simulation( smallest_patch_size=10, largest_patch_size=20, - time_step_nbr= 1, - final_time= 0.001, + time_step_nbr=100, + time_step=0.001, #boundary_types="periodic", cells=(50, 100), dl=(0.40, 0.40), @@ -153,19 +153,20 @@ def check_hier(hier): return hier def post_advance(new_time): - if cpp.mpi_rank() == 0: - L0_datahier = check_hier(get_hier(L0_diags)) - L0L1_datahier = check_hier(get_hier(L0L1_diags)) - extra_collections = [] - errors = test.base_test_overlaped_fields_are_equal(L0L1_datahier, new_time) - errors = test.base_test_field_level_ghosts_via_subcycles_and_coarser_interpolation(L0_datahier, L0L1_datahier) - print(f"errors {type(errors)}") - if isinstance(errors, list): - extra_collections += [{ - "boxes": errors, - "facecolor": "black", - }] - make_fig(L0L1_datahier, L0L1_diags.split("/")[-1], 1, extra_collections) + ... + # if cpp.mpi_rank() == 0: + # L0_datahier = check_hier(get_hier(L0_diags)) + # L0L1_datahier = check_hier(get_hier(L0L1_diags)) + # extra_collections = [] + # errors = test.base_test_overlaped_fields_are_equal(L0L1_datahier, new_time) + # errors = test.base_test_field_level_ghosts_via_subcycles_and_coarser_interpolation(L0_datahier, L0L1_datahier) + # print(f"errors {type(errors)}") + # if isinstance(errors, list): + # extra_collections += [{ + # "boxes": errors, + # "facecolor": "black", + # }] + # make_fig(L0L1_datahier, L0L1_diags.split("/")[-1], 1, extra_collections) def main(): import random diff --git a/tests/simulator/refinement/test_2d_2_core.py b/tests/simulator/refinement/test_2d_2_core.py index e31134486..b50d4f69a 100644 --- a/tests/simulator/refinement/test_2d_2_core.py +++ b/tests/simulator/refinement/test_2d_2_core.py @@ -27,7 +27,7 @@ def config(diag_outputs, model_init={}, refinement_boxes=None): Simulation( # smallest_patch_size=6, # largest_patch_size=(30, 15), - time_step_nbr= 1, + time_step_nbr=1, final_time= 0.001, #boundary_types="periodic", cells=(30, 30), @@ -79,7 +79,7 @@ def vthz(x, y): vvv = { "vbulkx": vx, "vbulky": vy, "vbulkz": vz, "vthx": vthx, "vthy": vthy, "vthz": vthz, - "nbr_part_per_cell":100, + "nbr_part_per_cell": 10, "init": model_init, } @@ -148,11 +148,8 @@ def get_hier(path): L0_diags = "phare_outputs/test_homo_0" L0L1_diags = "phare_outputs/test_homo_1" -def post_advance_0(new_time): - if cpp.mpi_rank() == 0: - pass -def post_advance_1(new_time): +def post_advance(new_time): if cpp.mpi_rank() == 0: L0_datahier = get_hier(L0_diags) L0L1_datahier = get_hier(L0L1_diags) @@ -173,9 +170,9 @@ def main(): refinement_boxes={"L0": {"B0": [( 10, 10), ( 14, 14)]}} - Simulator(config(L0_diags, {"seed": rando}), post_advance=post_advance_0).run().reset() + Simulator(config(L0_diags, {"seed": rando})).run().reset() sim = config(L0L1_diags, {"seed": rando}, refinement_boxes) - Simulator(sim, post_advance=post_advance_1).run() + Simulator(sim, post_advance=post_advance).run() if __name__=="__main__": main() diff --git a/tests/simulator/test_advance.py b/tests/simulator/test_advance.py index 4b71c576b..c90f1f1f7 100644 --- a/tests/simulator/test_advance.py +++ b/tests/simulator/test_advance.py @@ -1,25 +1,26 @@ -from pyphare.cpp import cpp_lib +import unittest +from pyphare.cpp import cpp_lib cpp = cpp_lib() -import unittest - import numpy as np -import pyphare.core.box as boxm from ddt import ddt + +import pyphare.core.box as boxm from pyphare.core.box import Box from pyphare.core.phare_utilities import np_array_ify + from pyphare.pharein import ElectronModel, MaxwellianFluidModel from pyphare.pharein.diagnostics import ( ElectromagDiagnostics, FluidDiagnostics, ParticleDiagnostics, ) -from pyphare.pharein.simulation import Simulation +from pyphare.pharein.simulation import Simulation, supported_dimensions from pyphare.pharesee.geometry import hierarchy_overlaps, level_ghost_boxes from pyphare.pharesee.hierarchy import hierarchy_from, merge_particles +from pyphare.pharesee.particles import aggregate as aggregate_particles from pyphare.simulator.simulator import Simulator - from tests.diagnostic import all_timestamps from tests.simulator import SimulatorTest, diff_boxes @@ -53,7 +54,6 @@ def getHierarchy( ndim=1, block_merging_particles=False, ): - diag_outputs = f"phare_outputs/advance/{diag_outputs}" from pyphare.pharein import global_vars @@ -252,7 +252,6 @@ def base_test_overlaped_fields_are_equal(self, datahier, coarsest_time): self.assertEqual(pd1.quantity, pd2.quantity) if pd1.quantity == "field": - # we need to transform the AMR overlap box, which is thus # (because AMR) common to both pd1 and pd2 into local index # boxes that will allow to slice the data @@ -352,13 +351,15 @@ def _test_overlapped_particledatas_have_identical_particles( **kwargs, ) + if cpp.mpi_rank() > 0: + return + for time_step_idx in range(time_step_nbr + 1): coarsest_time = time_step_idx * time_step overlaps = hierarchy_overlaps(datahier, coarsest_time) for ilvl, lvl in datahier.patch_levels.items(): - print("testing level {}".format(ilvl)) for overlap in overlaps[ilvl]: pd1, pd2 = overlap["pdatas"] @@ -368,7 +369,6 @@ def _test_overlapped_particledatas_have_identical_particles( self.assertEqual(pd1.quantity, pd2.quantity) if "particles" in pd1.quantity: - # the following uses 'offset', we need to remember that offset # is the quantity by which a patch has been moved to detect # overlap with the other one. @@ -410,6 +410,9 @@ def _test_L0_particle_number_conservation(self, ndim, interp_order, ppc=100): ndim=ndim, ) + if cpp.mpi_rank() > 0: + return + for time_step_idx in range(time_step_nbr + 1): coarsest_time = time_step_idx * time_step n_particles_at_t = 0 @@ -449,6 +452,9 @@ def _test_field_coarsening_via_subcycles( **kwargs, ) + if cpp.mpi_rank() > 0: + return + qties = ["rho"] qties += [f"{qty}{xyz}" for qty in ["E", "B", "V"] for xyz in ["x", "y", "z"]] lvl_steps = global_vars.sim.level_time_steps @@ -543,7 +549,6 @@ def _test_field_coarsening_via_subcycles( def base_test_field_level_ghosts_via_subcycles_and_coarser_interpolation( self, L0_datahier, L0L1_datahier, quantities=None ): - if quantities is None: quantities = [f"{EM}{xyz}" for EM in ["E", "B"] for xyz in ["x", "y", "z"]] @@ -595,11 +600,9 @@ def assert_time_in_hier(*ts): ) for qty in quantities: for fine_level_ghost_box_data in fine_level_qty_ghost_boxes[qty]: - fine_subcycle_pd = fine_level_ghost_box_data["pdata"] for fine_level_ghost_box_info in fine_level_ghost_box_data["boxes"]: - # trim the border level ghost nodes from the primal fields to ignore them in comparison checks fine_level_ghost_boxes = fine_level_ghost_box_info - boxm.grow( fine_subcycle_pd.box, fine_subcycle_pd.primal_directions() @@ -616,14 +619,12 @@ def assert_time_in_hier(*ts): ) for fine_level_ghost_box in fine_level_ghost_boxes: - upper_dims = ( fine_level_ghost_box.lower > fine_subcycle_pd.box.upper ) for refinedInterpolatedField in interpolated_fields[qty][ fine_subcycle_time ]: - lvlOverlap = ( refinedInterpolatedField.box * fine_level_ghost_box ) @@ -735,6 +736,8 @@ def _getHier(diag_dir, boxes=[]): f"phare_lvl_ghost_interpolation_L0L1_diags/{ndim}/{interp_order}/{self.ddt_test_id()}", refinement_boxes, ) + if cpp.mpi_rank() > 0: + return quantities = [f"{EM}{xyz}" for EM in ["E", "B"] for xyz in ["x", "y", "z"]] checks = ( diff --git a/tests/simulator/test_diagnostics.py b/tests/simulator/test_diagnostics.py index d956feb64..58ca4bd9f 100644 --- a/tests/simulator/test_diagnostics.py +++ b/tests/simulator/test_diagnostics.py @@ -11,7 +11,7 @@ import h5py import numpy as np import pyphare.pharein as ph -from ddt import data, ddt +from ddt import ddt, data, unpack from pyphare.pharein.simulation import supported_dimensions from pyphare.pharesee.hierarchy import h5_filename_from, h5_time_grp_key, hierarchy_from from pyphare.simulator.simulator import Simulator, startMPI @@ -119,13 +119,21 @@ def dup(dic): return dic +interp_orders = [1, 2, 3] + + +def per_dim_per_interp(dic): + return [ + (dim, interp, dic) for interp in interp_orders for dim in supported_dimensions() + ] + + @ddt class DiagnosticsTest(unittest.TestCase): - _test_cases = ( - dup({"smallest_patch_size": 10, "largest_patch_size": 20}), - dup({"smallest_patch_size": 20, "largest_patch_size": 20}), - dup({"smallest_patch_size": 20, "largest_patch_size": 40}), + *per_dim_per_interp(dup({"smallest_patch_size": 10, "largest_patch_size": 20})), + *per_dim_per_interp(dup({"smallest_patch_size": 20, "largest_patch_size": 20})), + *per_dim_per_interp(dup({"smallest_patch_size": 20, "largest_patch_size": 40})), ) def __init__(self, *args, **kwargs): @@ -146,18 +154,16 @@ def ddt_test_id(self): return self._testMethodName.split("_")[-1] @data(*_test_cases) - def test_dump_diags(self, simInput): - for ndim in supported_dimensions(): - self._test_dump_diags(ndim, **simInput) - - def _test_dump_diags(self, dim, **simInput): + @unpack + def test_dump_diags(self, dim, interp, simInput_original): test_id = self.ddt_test_id() + simInput = simInput_original.copy() # save backup as we modify it below # configure simulation dim sized values for key in ["cells", "dl", "boundary_types"]: - simInput[key] = [simInput[key] for d in range(dim)] + simInput[key] = [simInput[key]] * dim - b0 = [[10 for i in range(dim)], [19 for i in range(dim)]] + b0 = [[10] * dim, [19] * dim] simInput["refinement_boxes"] = {"L0": {"B0": b0}} py_attrs = [f"{dep}_version" for dep in ["samrai", "highfive", "pybind"]] @@ -171,6 +177,8 @@ def _test_dump_diags(self, dim, **simInput): ) simInput["diag_options"]["options"]["dir"] = local_out + print("test cells", simInput["cells"]) + print("test dl", simInput["dl"]) simulation = ph.Simulation(**simInput) self.assertTrue(len(simulation.cells) == dim) diff --git a/tests/simulator/test_initialization.py b/tests/simulator/test_initialization.py index af15e5ecd..3a98c1a8a 100644 --- a/tests/simulator/test_initialization.py +++ b/tests/simulator/test_initialization.py @@ -1,24 +1,27 @@ -from pyphare.cpp import cpp_lib +import unittest +from pyphare.cpp import cpp_lib cpp = cpp_lib() -import unittest - import numpy as np from ddt import ddt + from pyphare.core.box import nDBox -from pyphare.pharein import ElectronModel, MaxwellianFluidModel +from pyphare.pharein import ElectronModel, MaxwellianFluidModel, fn_wrapper from pyphare.pharein.diagnostics import ( ElectromagDiagnostics, FluidDiagnostics, ParticleDiagnostics, ) from pyphare.pharein.simulation import Simulation -from pyphare.pharesee.geometry import level_ghost_boxes +from pyphare.pharesee.geometry import ( + hierarchy_overlaps, + level_ghost_boxes, + touch_domain_border, +) from pyphare.pharesee.hierarchy import hierarchy_from, merge_particles from pyphare.pharesee.particles import aggregate as aggregate_particles from pyphare.simulator.simulator import Simulator - from tests.simulator import SimulatorTest @@ -254,7 +257,6 @@ def vthz(*xyz): return mom_hier def _test_B_is_as_provided_by_user(self, dim, interp_order, **kwargs): - print( "test_B_is_as_provided_by_user : dim {} interp_order : {}".format( dim, interp_order @@ -269,6 +271,9 @@ def _test_B_is_as_provided_by_user(self, dim, interp_order, **kwargs): **kwargs, ) + if cpp.mpi_rank() > 0: + return + from pyphare.pharein import global_vars model = global_vars.sim.model @@ -279,8 +284,8 @@ def _test_B_is_as_provided_by_user(self, dim, interp_order, **kwargs): for ilvl, level in hier.levels().items(): self.assertTrue(ilvl == 0) # only level 0 is expected perfect precision print("checking level {}".format(ilvl)) - for patch in level.patches: + for patch in level.patches: bx_pd = patch.patch_datas["Bx"] by_pd = patch.patch_datas["By"] bz_pd = patch.patch_datas["Bz"] @@ -337,6 +342,9 @@ def _test_bulkvel_is_as_provided_by_user(self, dim, interp_order): diag_outputs=f"test_bulkV/{dim}/{interp_order}/{self.ddt_test_id()}", ) + if cpp.mpi_rank() > 0: + return + from pyphare.pharein import global_vars model = global_vars.sim.model @@ -445,7 +453,6 @@ def reshape(patch_data, nGhosts): self.assertTrue(np.std(vexp - vact) < 1e-2) def _test_density_is_as_provided_by_user(self, dim, interp_order): - empirical_dim_devs = { 1: 6e-3, 2: 3e-2, @@ -467,6 +474,9 @@ def _test_density_is_as_provided_by_user(self, dim, interp_order): diag_outputs=f"test_density/{dim}/{interp_order}/{self.ddt_test_id()}", ) + if cpp.mpi_rank() > 0: + return + from pyphare.pharein import global_vars model = global_vars.sim.model @@ -538,7 +548,6 @@ def _test_density_decreases_as_1overSqrtN(self, dim, interp_order): noise = np.zeros(len(nbr_particles)) for inbr, nbrpart in enumerate(nbr_particles): - hier = self.getHierarchy( interp_order, None, @@ -552,32 +561,41 @@ def _test_density_decreases_as_1overSqrtN(self, dim, interp_order): dl=0.0125, ) - from pyphare.pharein import global_vars - model = global_vars.sim.model - protons = model.model_dict["protons"] - density_fn = protons["density"] + if cpp.mpi_rank() == 0: + from pyphare.pharein import global_vars + + model = global_vars.sim.model + protons = model.model_dict["protons"] + density_fn = protons["density"] - patch = hier.level(0).patches[0] - ion_density = patch.patch_datas["rho"].dataset[:] - x = patch.patch_datas["rho"].x + patch = hier.level(0).patches[0] + ion_density = patch.patch_datas["rho"].dataset[:] + x = patch.patch_datas["rho"].x + + layout = patch.patch_datas["rho"].layout + centering = layout.centering["X"][patch.patch_datas["rho"].field_name] + nbrGhosts = layout.nbrGhosts(interp_order, centering) - layout = patch.patch_datas["rho"].layout - centering = layout.centering["X"][patch.patch_datas["rho"].field_name] - nbrGhosts = layout.nbrGhosts(interp_order, centering) + expected = density_fn(x[nbrGhosts:-nbrGhosts]) + actual = ion_density[nbrGhosts:-nbrGhosts] + noise[inbr] = np.std(expected - actual) + print( + "noise is {} for {} particles per cell".format(noise[inbr], nbrpart) + ) - expected = density_fn(x[nbrGhosts:-nbrGhosts]) - actual = ion_density[nbrGhosts:-nbrGhosts] - noise[inbr] = np.std(expected - actual) - print("noise is {} for {} particles per cell".format(noise[inbr], nbrpart)) + plt.figure() + plt.plot(x[nbrGhosts:-nbrGhosts], actual, label="actual") + plt.plot(x[nbrGhosts:-nbrGhosts], expected, label="expected") + plt.legend() + plt.title(r"$\sigma =$ {}".format(noise[inbr])) + plt.savefig( + "noise_{}_interp_{}_{}.png".format(nbrpart, dim, interp_order) + ) + plt.close("all") - plt.figure() - plt.plot(x[nbrGhosts:-nbrGhosts], actual, label="actual") - plt.plot(x[nbrGhosts:-nbrGhosts], expected, label="expected") - plt.legend() - plt.title(r"$\sigma =$ {}".format(noise[inbr])) - plt.savefig("noise_{}_interp_{}_{}.png".format(nbrpart, dim, interp_order)) - plt.close("all") + if cpp.mpi_rank() > 0: + return plt.figure() plt.plot(nbr_particles, noise / noise[0], label=r"$\sigma/\sigma_0$") @@ -620,6 +638,9 @@ def _test_nbr_particles_per_cell_is_as_provided( diag_outputs=f"ppc/{dim}/{interp_order}/{ddt_test_id}", ) + if cpp.mpi_rank() > 0: + return + for patch in datahier.level(0).patches: pd = patch.patch_datas["protons_particles"] icells = pd.dataset[patch.box].iCells @@ -661,6 +682,9 @@ def _test_domainparticles_have_correct_split_from_coarser_particle( **kwargs, ) + if cpp.mpi_rank() > 0: + return + from pyphare.pharein.global_vars import sim assert sim is not None and len(sim.cells) == ndim @@ -717,6 +741,9 @@ def _test_patch_ghost_on_refined_level_case(self, dim, has_patch_ghost, **kwargs **kwargs, ) + if cpp.mpi_rank() > 0: + return + self.assertTrue( any( [ @@ -741,7 +768,6 @@ def _test_levelghostparticles_have_correct_split_from_coarser_particle( self.assertTrue(len(particle_level_ghost_boxes_per_level.items()) > 0) for ilvl, particle_gaboxes in particle_level_ghost_boxes_per_level.items(): - self.assertTrue(ilvl > 0) # has no level 0 lvlParticles = self._domainParticles_for(datahier, ilvl - 1) diff --git a/tests/simulator/test_python_concurrent.py b/tests/simulator/test_python_concurrent.py index 89e0d9fa4..4f7163ea7 100644 --- a/tests/simulator/test_python_concurrent.py +++ b/tests/simulator/test_python_concurrent.py @@ -27,7 +27,7 @@ PRINT = int(os.environ["PRINT"]) if "PRINT" in os.environ else 0 def test_cmd(clazz, test_id): - return f"mpirun -n {MPI_RUN} python3 -m {clazz.__module__} {clazz.__name__}.{test_id}" + return f"mpirun -n {MPI_RUN} python3 -Om {clazz.__module__} {clazz.__name__}.{test_id}" if __name__ == "__main__": @@ -49,9 +49,9 @@ def test_cmd(clazz, test_id): for suite in loader.loadTestsFromTestCase(test_class): tests += [test_cmd(type(suite), suite._testMethodName)] - from tools.python3 import run_mp if PRINT: for test in tests: print(test) else: + from tools.python3 import run_mp run_mp(tests, N_CORES, check=True) diff --git a/tests/simulator/test_restarts.py b/tests/simulator/test_restarts.py index 00fe3e736..f08bd4a50 100644 --- a/tests/simulator/test_restarts.py +++ b/tests/simulator/test_restarts.py @@ -149,6 +149,7 @@ def test_restarts(self, dim, interp, simInput, expected_num_levels): diag_dir0 = local_out + print("RESTARTING") # second restarted simulation local_out = f"{local_out}_n2" simput["diag_options"]["options"]["dir"] = local_out @@ -217,7 +218,6 @@ def count_levels_and_patches(qty): - def test_mode_conserve(self, dim = 1, interp = 1 , simput = dup(simArgs)): print(f"test_mode_conserve dim/interp:{dim}/{interp}") diff --git a/tools/bench/amr/data/particles/copy_data.cpp b/tools/bench/amr/data/particles/copy_data.cpp index fe72a6767..71a71dacc 100644 --- a/tools/bench/amr/data/particles/copy_data.cpp +++ b/tools/bench/amr/data/particles/copy_data.cpp @@ -1,6 +1,7 @@ - +#ifndef PHARE_HAS_HIGHFIVE #define PHARE_HAS_HIGHFIVE 0 +#endif #include "bench/core/bench.hpp" diff --git a/tools/bench/core/bench.hpp b/tools/bench/core/bench.hpp index 932b1d650..e3d2f7c11 100644 --- a/tools/bench/core/bench.hpp +++ b/tools/bench/core/bench.hpp @@ -2,7 +2,9 @@ #define PHARE_BENCH_CORE_BENCH_H #include "phare_core.hpp" -#include "benchmark/benchmark.hpp" +#include "core/utilities/types.hpp" + +#include "benchmark/benchmark.h" namespace PHARE::core::bench @@ -18,11 +20,14 @@ using VecField template PHARE::core::Particle particle(int icell = 15) { - return {/*.weight = */ 0, - /*.charge = */ 1, - /*.iCell = */ PHARE::core::ConstArray(icell), - /*.delta = */ PHARE::core::ConstArray(.5), - /*.v = */ {{.00001, .00001, .00001}}}; + return { + /*.weight = */ 0, + /*.charge = */ 1, + /*.iCell = */ PHARE::core::ConstArray(icell), + /*.delta = */ PHARE::core::ConstArray(.5), + /*.v = */ {{0, 0, 0}} // + // /*.v = */ {{.00001, .00001, .00001}} // + }; } template @@ -47,7 +52,7 @@ void disperse(Particles& particles, Point lo, Point up, std::optional seed { std::uniform_int_distribution<> distrib(lo[i], up[i]); for (auto& particle : particles) - particle.iCell[i] = distrib(gen); + particle.iCell()[i] = distrib(gen); } } template @@ -69,10 +74,10 @@ auto make_particles(std::size_t ppc, Box disperse_in, std::optional seed = template -Field field(std::string key, Quantity type, GridLayout const& layout) +auto field(std::string key, Quantity type, GridLayout const& layout) { - Field feeld{key, type, layout.allocSize(type)}; - std::fill(feeld.begin(), feeld.end(), 1); + auto feeld = std::make_unique>(key, type, layout.allocSize(type)); + std::fill(feeld->begin(), feeld->end(), 1); return feeld; } @@ -129,13 +134,13 @@ class Flux : public VecField field("Fy", HybridQuantity::Scalar::Vy, layout), field("Fz", HybridQuantity::Scalar::Vz, layout)} { - Super::setBuffer("F_x", &xyz[0]); - Super::setBuffer("F_y", &xyz[1]); - Super::setBuffer("F_z", &xyz[2]); + Super::setBuffer("F_x", xyz[0].get()); + Super::setBuffer("F_y", xyz[1].get()); + Super::setBuffer("F_z", xyz[2].get()); } private: - std::array, 3> xyz; + std::array>, 3> xyz; }; template @@ -152,12 +157,12 @@ class Electromag : public PHARE::core::Electromag contiguous{size}; + auto constexpr SOA = true; + PHARE::core::ParticleArray contiguous{size}; for (std::size_t i = 0; i < size; i++) { auto view = contiguous[i]; diff --git a/tools/bench/core/numerics/interpolator/bench_interpolator.hpp b/tools/bench/core/numerics/interpolator/bench_interpolator.hpp new file mode 100644 index 000000000..ca29071b4 --- /dev/null +++ b/tools/bench/core/numerics/interpolator/bench_interpolator.hpp @@ -0,0 +1,68 @@ +#ifndef PHARE_BENCH_CORE_INTERPOLATOR +#define PHARE_BENCH_CORE_INTERPOLATOR + +#include "bench/core/bench.hpp" +#include "core/numerics/interpolator/interpolator.hpp" +#include "tests/core/data/gridlayout/test_gridlayout.hpp" + +template +void interpolate(benchmark::State& state) +{ + constexpr static std::uint32_t cells = 30; + constexpr static std::uint32_t n_parts = 1e7; + constexpr static auto alloc_mode = ParticleArray::alloc_mode; + + using PHARE_Types = PHARE::core::PHARE_Types; + using GridLayout_t = typename PHARE_Types::GridLayout_t; + + + ParticleArray particles{n_parts, PHARE::core::bench::particle()}; + TestGridLayout layout{cells}; + PHARE::core::bench::Electromag em{layout}; + PHARE::core::bench::Flux flux{layout}; + auto rho = PHARE::core::bench::rho(layout); + + PHARE::core::bench::disperse(particles, 0, cells - 1); + + if constexpr (sort_particles) + std::sort(particles); + + if constexpr (alloc_mode == PHARE::AllocatorMode::CPU) + { + PHARE::core::Interpolator interpolator; + while (state.KeepRunning()) + { + // meshToParticle + interpolator(particles, em, layout); + // particleToMesh + interpolator(particles, *rho, flux, layout); + } + } + else if constexpr (alloc_mode == PHARE::AllocatorMode::GPU) + { + static_assert(not std::decay_t::is_host_mem); + static_assert(not std::decay_t::is_host_mem); + + auto em_view = em.view(); + auto flux_view = flux.view(); + auto rho_view = rho->view(); + auto ps = particles.view(); + + while (state.KeepRunning()) + { + PHARE_WITH_MKN_GPU( // + mkn::gpu::GDLauncher{n_parts}([=] __device__() mutable { + if (mkn::gpu::idx() >= n_parts) + return; + PHARE::core::Interpolator interpolator; + auto& particle = ps.data()[mkn::gpu::idx()]; + interpolator.meshToParticle(particle, em_view, layout); + interpolator.particleToMesh(particle, rho_view, flux_view, layout); + });) + } + } + else + std::abort(); +} + +#endif /*PHARE_BENCH_CORE_INTERPOLATOR*/ diff --git a/tools/bench/core/numerics/interpolator/bench_interpolator.ipp b/tools/bench/core/numerics/interpolator/bench_interpolator.ipp new file mode 100644 index 000000000..e673193d7 --- /dev/null +++ b/tools/bench/core/numerics/interpolator/bench_interpolator.ipp @@ -0,0 +1,17 @@ +BENCHMARK_TEMPLATE(bench, 1, 1)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(bench, 1, 2)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(bench, 1, 3)->Unit(benchmark::kMicrosecond); + +BENCHMARK_TEMPLATE(bench, 2, 1)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(bench, 2, 2)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(bench, 2, 3)->Unit(benchmark::kMicrosecond); + +BENCHMARK_TEMPLATE(bench, 3, 1)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(bench, 3, 2)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(bench, 3, 3)->Unit(benchmark::kMicrosecond); + +int main(int argc, char** argv) +{ + ::benchmark::Initialize(&argc, argv); + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/tools/bench/core/numerics/interpolator/bench_interpolator_aos.cpp b/tools/bench/core/numerics/interpolator/bench_interpolator_aos.cpp new file mode 100644 index 000000000..9a99ef26d --- /dev/null +++ b/tools/bench/core/numerics/interpolator/bench_interpolator_aos.cpp @@ -0,0 +1,11 @@ + +#include "bench_interpolator.hpp" + +template +void bench(benchmark::State& state) +{ + using ParticleArray = PHARE::core::AoSParticleArray; + interpolate(state); +} + +#include "bench_interpolator.ipp" diff --git a/tools/bench/core/numerics/interpolator/bench_interpolator_gpu.cpp b/tools/bench/core/numerics/interpolator/bench_interpolator_gpu.cpp new file mode 100644 index 000000000..d26e6a20b --- /dev/null +++ b/tools/bench/core/numerics/interpolator/bench_interpolator_gpu.cpp @@ -0,0 +1,51 @@ + +#include "bench_interpolator.hpp" + +namespace PHARE::core +{ +template +using AoSGPUParticleArray + = ParticleArray>; + +template +using SoAGPUParticleArray + = ParticleArray>; + +} // namespace PHARE::core + +template +void bench_soa(benchmark::State& state) +{ + interpolate>(state); +} + +template +void bench_aos(benchmark::State& state) +{ + interpolate>(state); +} + +template +void bench_gpu_soa(benchmark::State& state) +{ + interpolate>(state); +} + +template +void bench_gpu_aos(benchmark::State& state) +{ + interpolate>(state); +} + +// BENCHMARK_TEMPLATE(bench_soa, 3, 3)->Unit(benchmark::kMicrosecond); +// BENCHMARK_TEMPLATE(bench_aos, 3, 3)->Unit(benchmark::kMicrosecond); +// BENCHMARK_TEMPLATE(bench_gpu_soa, 3, 3)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(bench_gpu_aos, 3, 3)->Unit(benchmark::kMicrosecond); + +int main(int argc, char** argv) +{ + ::benchmark::Initialize(&argc, argv); + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/tools/bench/core/numerics/interpolator/bench_interpolator_soa.cpp b/tools/bench/core/numerics/interpolator/bench_interpolator_soa.cpp new file mode 100644 index 000000000..63ae0493c --- /dev/null +++ b/tools/bench/core/numerics/interpolator/bench_interpolator_soa.cpp @@ -0,0 +1,11 @@ + +#include "bench_interpolator.hpp" + +template +void bench(benchmark::State& state) +{ + using ParticleArray = PHARE::core::SoAParticleArray; + interpolate(state); +} + +#include "bench_interpolator.ipp" diff --git a/tools/bench/core/numerics/interpolator/bench_main.cpp b/tools/bench/core/numerics/interpolator/bench_main.cpp deleted file mode 100644 index ab7c6fd4c..000000000 --- a/tools/bench/core/numerics/interpolator/bench_main.cpp +++ /dev/null @@ -1,52 +0,0 @@ - -#include "bench/core/bench.hpp" -#include "core/numerics/interpolator/interpolator.hpp" - -template -void interpolate(benchmark::State& state) -{ - constexpr std::uint32_t cells = 30; - constexpr std::uint32_t n_parts = 1e7; - - using PHARE_Types = PHARE::core::PHARE_Types; - using GridLayout_t = typename PHARE_Types::GridLayout_t; - using ParticleArray = typename PHARE_Types::ParticleArray_t; - - PHARE::core::Interpolator interpolator; - ParticleArray particles{n_parts, PHARE::core::bench::particle()}; - GridLayout_t layout{PHARE::core::ConstArray(1.0 / cells), - PHARE::core::ConstArray(cells), - PHARE::core::Point{PHARE::core::ConstArray(0)}}; - PHARE::core::bench::Electromag em{layout}; - PHARE::core::bench::Flux flux{layout}; - auto rho = PHARE::core::bench::rho(layout); - - PHARE::core::bench::disperse(particles, 0, cells - 1); - - while (state.KeepRunning()) - { - // meshToParticle - interpolator(particles, em, layout); - - // particleToMesh - interpolator(particles, rho, flux, layout); - } -} - -BENCHMARK_TEMPLATE(interpolate, 1, 1)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(interpolate, 1, 2)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(interpolate, 1, 3)->Unit(benchmark::kMicrosecond); - -BENCHMARK_TEMPLATE(interpolate, 2, 1)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(interpolate, 2, 2)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(interpolate, 2, 3)->Unit(benchmark::kMicrosecond); - -BENCHMARK_TEMPLATE(interpolate, 3, 1)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(interpolate, 3, 2)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(interpolate, 3, 3)->Unit(benchmark::kMicrosecond); - -int main(int argc, char** argv) -{ - ::benchmark::Initialize(&argc, argv); - ::benchmark::RunSpecifiedBenchmarks(); -} diff --git a/tools/bench/core/numerics/interpolator/eg/aos_sorted.txt b/tools/bench/core/numerics/interpolator/eg/aos_sorted.txt new file mode 100644 index 000000000..c35584871 --- /dev/null +++ b/tools/bench/core/numerics/interpolator/eg/aos_sorted.txt @@ -0,0 +1,23 @@ +Project: /home/p/git/phare/mkn [bench] +Creating bin: /home/p/git/phare/mkn/bin/bench/phare +2023-08-29T00:57:57+02:00 +Running bin/bench/phare +Run on (32 X 3400 MHz CPU s) +CPU Caches: + L1 Data 32 KiB (x16) + L1 Instruction 32 KiB (x16) + L2 Unified 512 KiB (x16) + L3 Unified 32768 KiB (x2) +Load Average: 1.13, 0.88, 1.12 +------------------------------------------------------ +Benchmark Time CPU Iterations +------------------------------------------------------ +bench<1, 1> 130553 us 130552 us 5 +bench<1, 2> 139773 us 139764 us 5 +bench<1, 3> 151307 us 151137 us 5 +bench<2, 1> 183527 us 183228 us 4 +bench<2, 2> 425532 us 425286 us 2 +bench<2, 3> 559742 us 558824 us 1 +bench<3, 1> 366314 us 366310 us 2 +bench<3, 2> 1025590 us 1025439 us 1 +bench<3, 3> 3269542 us 3265306 us 1 \ No newline at end of file diff --git a/tools/bench/core/numerics/interpolator/eg/aos_unsorted.txt b/tools/bench/core/numerics/interpolator/eg/aos_unsorted.txt new file mode 100644 index 000000000..bb157bfdf --- /dev/null +++ b/tools/bench/core/numerics/interpolator/eg/aos_unsorted.txt @@ -0,0 +1,23 @@ +Project: /home/p/git/phare/mkn [bench] +Creating bin: /home/p/git/phare/mkn/bin/bench/phare +2023-08-29T00:57:26+02:00 +Running bin/bench/phare +Run on (32 X 4586.58 MHz CPU s) +CPU Caches: + L1 Data 32 KiB (x16) + L1 Instruction 32 KiB (x16) + L2 Unified 512 KiB (x16) + L3 Unified 32768 KiB (x2) +Load Average: 0.46, 0.75, 1.08 +------------------------------------------------------ +Benchmark Time CPU Iterations +------------------------------------------------------ +bench<1, 1> 133768 us 133765 us 5 +bench<1, 2> 147957 us 147171 us 5 +bench<1, 3> 156880 us 156873 us 5 +bench<2, 1> 199691 us 198584 us 3 +bench<2, 2> 440969 us 440948 us 2 +bench<2, 3> 590114 us 590107 us 1 +bench<3, 1> 694848 us 689795 us 1 +bench<3, 2> 1873664 us 1868640 us 1 +bench<3, 3> 4569831 us 4569656 us 1 \ No newline at end of file diff --git a/tools/bench/core/numerics/interpolator/eg/soa_sorted.txt b/tools/bench/core/numerics/interpolator/eg/soa_sorted.txt new file mode 100644 index 000000000..6f5a97a16 --- /dev/null +++ b/tools/bench/core/numerics/interpolator/eg/soa_sorted.txt @@ -0,0 +1,23 @@ +Project: /home/p/git/phare/mkn [bench] +Creating bin: /home/p/git/phare/mkn/bin/bench/phare +2023-08-29T00:59:40+02:00 +Running bin/bench/phare +Run on (32 X 3638.83 MHz CPU s) +CPU Caches: + L1 Data 32 KiB (x16) + L1 Instruction 32 KiB (x16) + L2 Unified 512 KiB (x16) + L3 Unified 32768 KiB (x2) +Load Average: 2.57, 1.37, 1.26 +------------------------------------------------------ +Benchmark Time CPU Iterations +------------------------------------------------------ +bench<1, 1> 72788 us 72786 us 9 +bench<1, 2> 98882 us 98878 us 7 +bench<1, 3> 127149 us 127147 us 5 +bench<2, 1> 141079 us 141076 us 5 +bench<2, 2> 382130 us 382126 us 2 +bench<2, 3> 555394 us 555379 us 1 +bench<3, 1> 329914 us 329911 us 2 +bench<3, 2> 962023 us 961981 us 1 +bench<3, 3> 2289444 us 2289372 us 1 diff --git a/tools/bench/core/numerics/interpolator/eg/soa_unsorted.txt b/tools/bench/core/numerics/interpolator/eg/soa_unsorted.txt new file mode 100644 index 000000000..781a361a4 --- /dev/null +++ b/tools/bench/core/numerics/interpolator/eg/soa_unsorted.txt @@ -0,0 +1,23 @@ +Project: /home/p/git/phare/mkn [bench] +Creating bin: /home/p/git/phare/mkn/bin/bench/phare +2023-08-29T00:59:06+02:00 +Running bin/bench/phare +Run on (32 X 3606.48 MHz CPU s) +CPU Caches: + L1 Data 32 KiB (x16) + L1 Instruction 32 KiB (x16) + L2 Unified 512 KiB (x16) + L3 Unified 32768 KiB (x2) +Load Average: 1.57, 1.06, 1.16 +------------------------------------------------------ +Benchmark Time CPU Iterations +------------------------------------------------------ +bench<1, 1> 71638 us 71636 us 10 +bench<1, 2> 111568 us 111563 us 6 +bench<1, 3> 134032 us 134002 us 5 +bench<2, 1> 158760 us 158742 us 4 +bench<2, 2> 404606 us 404591 us 2 +bench<2, 3> 602820 us 598343 us 1 +bench<3, 1> 654562 us 652235 us 1 +bench<3, 2> 1981350 us 1981011 us 1 +bench<3, 3> 4353906 us 4353782 us 1 diff --git a/tools/bench/core/numerics/pusher/CMakeLists.txt b/tools/bench/core/numerics/pusher/CMakeLists.txt index 75f386ee6..f1db60192 100644 --- a/tools/bench/core/numerics/pusher/CMakeLists.txt +++ b/tools/bench/core/numerics/pusher/CMakeLists.txt @@ -2,4 +2,4 @@ cmake_minimum_required (VERSION 3.9) project(phare_bench_pusher) -add_phare_cpp_benchmark(11 ${PROJECT_NAME} pusher ${CMAKE_CURRENT_BINARY_DIR}) +add_phare_cpp_benchmark(11 ${PROJECT_NAME} boris ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/tools/bench/core/numerics/pusher/boris.cpp b/tools/bench/core/numerics/pusher/boris.cpp new file mode 100644 index 000000000..38d858667 --- /dev/null +++ b/tools/bench/core/numerics/pusher/boris.cpp @@ -0,0 +1,71 @@ + +#include "benchmark/benchmark.h" +#include "pusher_bench.hpp" + +using namespace PHARE::core::bench; + + +template +void push(benchmark::State& state) +{ + constexpr std::uint32_t cells = 65; + constexpr std::uint32_t parts = 1e8; + + using PHARE_Types = PHARE::core::PHARE_Types; + using Interpolator = PHARE::core::Interpolator; + using BoundaryCondition = PHARE::core::BoundaryCondition; + using Ions_t = typename PHARE_Types::Ions_t; + using Electromag_t = typename PHARE_Types::Electromag_t; + using GridLayout_t = typename PHARE_Types::GridLayout_t; + using ParticleArray = typename Ions_t::particle_array_type; + using PartIterator = typename ParticleArray::iterator; + + + using BorisPusher_t = PHARE::core::BorisPusher; + + Interpolator interpolator; + ParticleArray domainParticles{parts, particle(/*icell =*/34)}; + + auto range = PHARE::core::makeRange(domainParticles); + auto meshSize = PHARE::core::ConstArray(1.0 / cells); + auto nCells = PHARE::core::ConstArray(cells); + auto origin = PHARE::core::Point{PHARE::core::ConstArray(0)}; + + GridLayout_t layout{meshSize, nCells, origin}; + + PHARE::core::bench::Electromag> electromag{layout}; + + BorisPusher_t pusher; + pusher.setMeshAndTimeStep(layout.meshSize(), .001); + + while (state.KeepRunning()) + { + pusher.move( + /*ParticleRange const&*/ range, + /*ParticleRange&*/ range, + /*Electromag const&*/ electromag, + /*double mass*/ 1, + /*Interpolator&*/ interpolator, + /*ParticleSelector const&*/ [](auto const& /*part*/) { return true; }, + /*GridLayout const&*/ layout); + } +} +BENCHMARK_TEMPLATE(push, /*dim=*/1, /*interp=*/1)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(push, /*dim=*/1, /*interp=*/2)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(push, /*dim=*/1, /*interp=*/3)->Unit(benchmark::kMicrosecond); + +BENCHMARK_TEMPLATE(push, /*dim=*/2, /*interp=*/1)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(push, /*dim=*/2, /*interp=*/2)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(push, /*dim=*/2, /*interp=*/3)->Unit(benchmark::kMicrosecond); + +BENCHMARK_TEMPLATE(push, /*dim=*/3, /*interp=*/1)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(push, /*dim=*/3, /*interp=*/2)->Unit(benchmark::kMicrosecond); +BENCHMARK_TEMPLATE(push, /*dim=*/3, /*interp=*/3)->Unit(benchmark::kMicrosecond); + + +int main(int argc, char** argv) +{ + ::benchmark::Initialize(&argc, argv); + ::benchmark::RunSpecifiedBenchmarks(); +} diff --git a/tools/bench/core/numerics/pusher/pusher.cpp b/tools/bench/core/numerics/pusher/pusher.cpp deleted file mode 100644 index 2bd49b791..000000000 --- a/tools/bench/core/numerics/pusher/pusher.cpp +++ /dev/null @@ -1,108 +0,0 @@ - -#include "benchmark/benchmark.hpp" - -#include "phare_core.hpp" -#include "core/numerics/pusher/boris.hpp" -#include "core/numerics/ion_updater/ion_updater.hpp" - -template -using Field = PHARE::core::Field, - typename PHARE::core::HybridQuantity::Scalar>; -template -using VecField - = PHARE::core::VecField, typename PHARE::core::HybridQuantity>; - -template -PHARE::core::Particle particle() -{ - return {// - /*.weight = */ 0, - /*.charge = */ 1, - /*.iCell = */ PHARE::core::ConstArray(35), - /*.delta = */ PHARE::core::ConstArray(.01), - /*.v = */ {{0, 10., 0}}}; -} - -template -Field field(std::string key, Quantity type, GridLayout const& layout) -{ - Field feeld{key, type, layout.allocSize(type)}; - std::fill(feeld.begin(), feeld.end(), 1); - return feeld; -} - -template -void push(benchmark::State& state) -{ - constexpr std::uint32_t cells = 65; - constexpr std::uint32_t parts = 1e7; - - using PHARE_Types = PHARE::core::PHARE_Types; - using Interpolator = PHARE::core::Interpolator; - using BoundaryCondition = PHARE::core::BoundaryCondition; - using Ions_t = typename PHARE_Types::Ions_t; - using Electromag_t = typename PHARE_Types::Electromag_t; - using GridLayout_t = typename PHARE_Types::GridLayout_t; - using ParticleArray = typename Ions_t::particle_array_type; - using PartIterator = typename ParticleArray::iterator; - - using BorisPusher_t = PHARE::core::BorisPusher; - - Interpolator interpolator; - ParticleArray domainParticles{parts, particle()}; - ParticleArray tmpDomain{domainParticles.size(), particle()}; - - auto rangeIn = PHARE::core::makeRange(domainParticles); - auto rangeOut = PHARE::core::makeRange(tmpDomain); - - auto meshSize = PHARE::core::ConstArray(1.0 / cells); - auto nCells = PHARE::core::ConstArray(cells); - auto origin = PHARE::core::Point{PHARE::core::ConstArray(0)}; - GridLayout_t layout{meshSize, nCells, origin}; - - Field bx = field("Bx", PHARE::core::HybridQuantity::Scalar::Bx, layout); - Field by = field("By", PHARE::core::HybridQuantity::Scalar::By, layout); - Field bz = field("Bz", PHARE::core::HybridQuantity::Scalar::Bz, layout); - - Field ex = field("Ex", PHARE::core::HybridQuantity::Scalar::Ex, layout); - Field ey = field("Ey", PHARE::core::HybridQuantity::Scalar::Ey, layout); - Field ez = field("Ez", PHARE::core::HybridQuantity::Scalar::Ez, layout); - - PHARE::core::Electromag> emFields{std::string{"EM"}}; - emFields.B.setBuffer("EM_B_x", &bx); - emFields.B.setBuffer("EM_B_y", &by); - emFields.B.setBuffer("EM_B_z", &bz); - emFields.E.setBuffer("EM_E_x", &ex); - emFields.E.setBuffer("EM_E_y", &ey); - emFields.E.setBuffer("EM_E_z", &ez); - - BorisPusher_t pusher; - pusher.setMeshAndTimeStep(layout.meshSize(), .001); - - while (state.KeepRunning()) - { - pusher.move( - /*ParticleRange const&*/ rangeIn, /*ParticleRange&*/ rangeOut, - /*Electromag const&*/ emFields, /*double mass*/ 1, /*Interpolator&*/ interpolator, - /*ParticleSelector const&*/ [](auto const& /*part*/) { return true; }, - /*GridLayout const&*/ layout); - } -} -BENCHMARK_TEMPLATE(push, /*dim=*/1, /*interp=*/1)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(push, /*dim=*/1, /*interp=*/2)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(push, /*dim=*/1, /*interp=*/3)->Unit(benchmark::kMicrosecond); - -BENCHMARK_TEMPLATE(push, /*dim=*/2, /*interp=*/1)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(push, /*dim=*/2, /*interp=*/2)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(push, /*dim=*/2, /*interp=*/3)->Unit(benchmark::kMicrosecond); - -BENCHMARK_TEMPLATE(push, /*dim=*/3, /*interp=*/1)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(push, /*dim=*/3, /*interp=*/2)->Unit(benchmark::kMicrosecond); -BENCHMARK_TEMPLATE(push, /*dim=*/3, /*interp=*/3)->Unit(benchmark::kMicrosecond); - -int main(int argc, char** argv) -{ - ::benchmark::Initialize(&argc, argv); - ::benchmark::RunSpecifiedBenchmarks(); -} diff --git a/tools/bench/core/numerics/pusher/pusher_bench.hpp b/tools/bench/core/numerics/pusher/pusher_bench.hpp new file mode 100644 index 000000000..5bf984580 --- /dev/null +++ b/tools/bench/core/numerics/pusher/pusher_bench.hpp @@ -0,0 +1,100 @@ +#ifndef PHARE_CORE_PUSHER_BENCH_HPP +#define PHARE_CORE_PUSHER_BENCH_HPP + +#include "phare_core.h" +#include "core/numerics/ion_updater/ion_updater.h" + +namespace PHARE::core::bench +{ +template +using Field = PHARE::core::Field, + typename PHARE::core::HybridQuantity::Scalar>; +template +using VecField + = PHARE::core::VecField, typename PHARE::core::HybridQuantity>; + + +template +PHARE::core::Particle particle(int icell = 15) +{ + return {// + /*.weight = */ 0, + /*.charge = */ 1, + /*.iCell = */ PHARE::core::ConstArray(icell), + /*.delta = */ PHARE::core::ConstArray(.5), + /*.v = */ {{.00001, .00001, .00001}}}; +} + +template +void disperse(ParticleArray& particles, std::size_t lower, std::size_t upper) +{ + std::random_device rd; + std::seed_seq seed_seq{rd(), rd(), rd(), rd(), rd(), rd(), rd()}; + std::mt19937 gen{seed_seq}; + std::uniform_int_distribution<> distrib(lower, upper); + + for (auto& particle : particles) + for (std::size_t i = 0; i < ParticleArray::dimension; i++) + particle.iCell[i] = distrib(gen); +} + +template +Field field(std::string key, Quantity type, GridLayout const& layout) +{ + Field feeld{key, type, layout.allocSize(type)}; + std::fill(feeld.begin(), feeld.end(), 1); + return feeld; +} + + +template +auto E(GridLayout const& layout) +{ + return std::make_tuple(field("Ex", PHARE::core::HybridQuantity::Scalar::Ex, layout), + field("Ey", PHARE::core::HybridQuantity::Scalar::Ey, layout), + field("Ez", PHARE::core::HybridQuantity::Scalar::Ez, layout)); +} + +template +auto B(GridLayout const& layout) +{ + return std::make_tuple(field("Bx", PHARE::core::HybridQuantity::Scalar::Bx, layout), + field("By", PHARE::core::HybridQuantity::Scalar::By, layout), + field("Bz", PHARE::core::HybridQuantity::Scalar::Bz, layout)); +} + +template +auto EM(GridLayout const& layout) +{ + return std::make_tuple(E(layout), B(layout)); +} + +template +class Electromag : public PHARE::core::Electromag +{ +public: + using Super = PHARE::core::Electromag; + + Electromag(GridLayout const& layout) + : Super{"EM"} + , emFields{EM(layout)} + { + auto& [E, B] = emFields; + auto& [ex, ey, ez] = E; + auto& [bx, by, bz] = B; + + Super::B.setBuffer("EM_B_x", &bx); + Super::B.setBuffer("EM_B_y", &by); + Super::B.setBuffer("EM_B_z", &bz); + Super::E.setBuffer("EM_E_x", &ex); + Super::E.setBuffer("EM_E_y", &ey); + Super::E.setBuffer("EM_E_z", &ez); + } + +private: + decltype(EM(*static_cast(0))) emFields; +}; + +} // namespace PHARE::core::bench + +#endif /* PHARE_CORE_PUSHER_BENCH_HPP */ diff --git a/tools/bench/hi5/write_particles.cpp b/tools/bench/hi5/write_particles.cpp index 545438e73..deaa58068 100644 --- a/tools/bench/hi5/write_particles.cpp +++ b/tools/bench/hi5/write_particles.cpp @@ -1,6 +1,8 @@ -#include "benchmark/benchmark.hpp" +#ifndef PHARE_DIAG_DOUBLES #define PHARE_DIAG_DOUBLES 0 +#endif + #include "diagnostic/detail/h5writer.hpp" #include "diagnostic/detail/h5_utils.hpp" #include "diagnostic/diagnostic_manager.hpp" @@ -11,16 +13,19 @@ #include "phare/phare.hpp" +#include "benchmark/benchmark.h" + + constexpr std::size_t dim = 1; namespace PHARE::diagnostic { void do_bench(benchmark::State& state) { - using HiFile = HighFive::File; - using Packer = core::ParticlePacker; - using ContiguousParticles = core::ContiguousParticles; - using ParticleArray = core::ParticleArray; + using HiFile = HighFive::File; + using Packer = core::ParticlePacker; + using ParticleArray = core::ParticleArray; + using ParticleArray_SOA = core::ParticleArray; auto getSize = [](auto const& value) -> std::size_t { using ValueType = std::decay_t; @@ -49,6 +54,7 @@ void do_bench(benchmark::State& state) datasets[4].write(particles.v.data()); }; + auto keys = core::packer_keys(); std::string path{"/lol/"}; while (state.KeepRunning()) { @@ -56,14 +62,14 @@ void do_bench(benchmark::State& state) auto d = hi5.file_.createDataSet("/No", 1); std::vector datasets; - ContiguousParticles particles{100000}; + ParticleArray_SOA particles{100000}; ParticleArray particleArray(100000); Packer{particleArray}.pack(particles); std::size_t part_idx = 0; core::apply(Packer::empty(), [&](auto const& arg) { - datasets.emplace_back(createDataSet_(hi5, path + Packer::keys()[part_idx], - getSize(arg) * particles.size(), arg)); + datasets.emplace_back( + createDataSet_(hi5, path + keys[part_idx], getSize(arg) * particles.size(), arg)); part_idx++; }); writeParticles(datasets, particles); diff --git a/tools/bench/real/bench_harris.py b/tools/bench/real/bench_harris.py index 554e2ce67..395c8a1c0 100644 --- a/tools/bench/real/bench_harris.py +++ b/tools/bench/real/bench_harris.py @@ -1,14 +1,21 @@ import numpy as np -from pyphare.cpp import cpp_lib # must be first -cpp_lib("pybindlibs.cpp_sim_2_1_4") +from pyphare.cpp import cpp_lib import pyphare.pharein as ph +if not ph.PHARE_EXE: cpp_lib("pybindlibs.cpp_sim_2_1_4") + seed = 133333333337 -cells, dl = 100, .2 -patch_sizes = [50,100] +cells, dl = 500, .2 +patch_sizes = [50,50] diag_outputs="tools/bench/real/harris/outputs" +time_step_nbr=1 +time_step=0.001 +nbr_part_per_cell=500 + + +simulation_particles = nbr_part_per_cell * cells ** 2 def density(x, y): L = ph.global_vars.sim.simulation_domain()[1] @@ -30,8 +37,7 @@ def by(x, y): def S(y, y0, l): return 0.5*(1. + np.tanh((y-y0)/l)) def bx(x, y): sim = ph.global_vars.sim - Lx = sim.simulation_domain()[0] - Ly = sim.simulation_domain()[1] + Lx, Ly = sim.simulation_domain() w1, w2 = 0.2, 1.0 x0 = (x - 0.5 * Lx) y1 = (y - 0.3 * Ly) @@ -51,15 +57,15 @@ def vthxyz(x, y): return np.sqrt(T(x, y)) def config(): ph.Simulation(# strict=True, smallest_patch_size=patch_sizes[0], largest_patch_size=patch_sizes[1], - time_step_nbr=10, time_step=0.001, + time_step_nbr=time_step_nbr, time_step=time_step, cells=[cells] * 2, dl=[dl] * 2, resistivity=0.001, hyper_resistivity=0.001, diag_options={"format": "phareh5", "options": {"dir": diag_outputs, "mode":"overwrite"}}, - refinement_boxes={}, + refinement_boxes={} ) ph.MaxwellianFluidModel( bx=bx, by=by, bz=bz, protons={"charge": 1, "density": density, "init":{"seed": seed}, - **{ "nbr_part_per_cell":100, + **{ "nbr_part_per_cell":nbr_part_per_cell, "vbulkx": vxyz, "vbulky": vxyz, "vbulkz": vxyz, "vthx": vthxyz, "vthy": vthxyz, "vthz": vthxyz, } @@ -82,4 +88,4 @@ def config(): if __name__=="__main__": from pyphare.simulator.simulator import Simulator - Simulator(ph.global_vars.sim).run() + Simulator(ph.global_vars.sim).run() \ No newline at end of file diff --git a/tools/cmake.sh b/tools/cmake.sh new file mode 100755 index 000000000..fb7598ef2 --- /dev/null +++ b/tools/cmake.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# usage: +# copy this file to tools/cmake.sh - and edit as you wish +# tools/cmake.sh is ignored by git +# +set -ex +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" && cd $SCRIPT_DIR/.. && CWD=$PWD # move to project root + +THREADS=${THREADS:="11"} +BUILD_DIR=${BUILD_DIR:="$CWD/build"} +SAMRAI=${SAMRAI:="/mkn/r/llnl/samrai/master"} # "" = as subproject +FFF=("${BUILD_DIR}") +CMAKE_BASE_CONFIG="-DdevMode=OFF -Dasan=OFF -Dbench=OFF -DwithCaliper=OFF -DtestMPI=OFF -DtestDuringBuild=ON" + +## SUPER RELEASE +CMAKE_CXX_FLAGS="-DNDEBUG -g0 -DPHARE_DIAG_DOUBLES=1 -O3 -march=native -mtune=native" +CMAKE_CONFIG="${CMAKE_BASE_CONFIG} -DCMAKE_BUILD_TYPE=Release" + +## OPTIMZ AND DEBUG +# CMAKE_CXX_FLAGS="-g3 -DPHARE_DIAG_DOUBLES=1 -O3 -march=native -mtune=native -fno-omit-frame-pointer" +# CMAKE_CONFIG="${CMAKE_BASE_CONFIG} -DCMAKE_BUILD_TYPE=Debug" + +## PURE DEBUG +CMAKE_CXX_FLAGS="-g3 -DPHARE_DIAG_DOUBLES=1 -O0 -fno-omit-frame-pointer" +CMAKE_CONFIG="${CMAKE_BASE_CONFIG} -DCMAKE_BUILD_TYPE=Debug" + +exec 19>$CWD/.cmake.sh.cmd # set -x redirect +export BASH_XTRACEFD=19 # set -x redirect + +CC=${CC:="gcc"} +CXX=${CXX:="g++"} +set -xe +time ( + date + [ -n "$CLEAN" ] && (( $CLEAN == 1 )) && for f in ${FFF[@]}; do rm -rf $f; done + [ ! -f "$CWD/CMakeLists.txt" ] && echo "script expected to be run from project root" && exit 1 + [ ! -d "$CWD/subprojects/cppdict/include" ] && git submodule update --init + mkdir -p ${BUILD_DIR} + [[ -n "${SAMRAI}" ]] && SAMRAI=-DSAMRAI_ROOT="${SAMRAI}" + ( export CC CXX + cd ${BUILD_DIR} && cmake $CWD ${SAMRAI} -G Ninja ${CMAKE_CONFIG} \ + -DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}" ) + mold --run \ + ninja -C ${BUILD_DIR} -v -j${THREADS} + (cd build && ctest -j${THREADS} ) + date +) 1> >(tee $CWD/.cmake.sh.out ) 2> >(tee $CWD/.cmake.sh.err >&2 ) diff --git a/tools/mkn.sh b/tools/mkn.sh new file mode 100755 index 000000000..0de325999 --- /dev/null +++ b/tools/mkn.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +set -ex +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" && cd $SCRIPT_DIR/.. && CWD=$PWD +THREADS=${THREADS:="11"} + +time ( + date + + # FILE="-M tests/core/data/particles/test_particles.cpp" + # FILE="-M tests/amr/data/particles/stream_pack/test_main.cpp" + # FILE="-M tests/core/data/particles/sorting/test_particle_sorting.cpp" + # FILE="-M tests/core/data/particles/test_edge_bisection_mapper.cpp" + # FILE="-M tests/core/data/particles/test_bisection_range_mapper.cpp" + FILE="-M tests/core/utilities/range/test_range.cpp" + ARGS="${FILE} -l -pthread -x res/mkn/clang_asan" + + # mkn clean build run -p test_amr $ARGS -WgO 9 || true + mkn clean build run -p test_core $ARGS -WgO $@ || true + date +# ) 1> >(tee $CWD/.mkn.sh.out ) 2> >(tee $CWD/.mkn.sh.err >&2 ) +) 1> $CWD/.mkn.sh.out 2> $CWD/.mkn.sh.err >&2 + +# cat $CWD/.mkn.sh.err | head -33 +cat $CWD/.mkn.sh.err #| tail -22 diff --git a/tools/mkn/bench.sh b/tools/mkn/bench.sh new file mode 100755 index 000000000..ff8619e3c --- /dev/null +++ b/tools/mkn/bench.sh @@ -0,0 +1,12 @@ +set -ex + +export KLOG=3 +FILE="tools/bench/core/numerics/interpolator/bench_interpolator_gpu.cpp" +CARG="-DPHARE_HAVE_MKN_GPU=1 -DMKN_GPU_ROCM=1" +XFIL="-Ox res/mkn/hip.yaml" +WITH="-w mkn.gpu" + +# export MKN_DBG="gdb -batch -ex run -ex bt --args" +export MKN_DBG="/opt/rocm/bin/rocprofv2 --kernel-trace" +export MKN_DBG="/opt/rocm/bin/rocprof -d outputFolder --hip-trace" +mkn clean build -M "${FILE}" -a "${CARG}" -p bench ${DOPT} ${WITH} ${XFIL} run diff --git a/tools/mkn/test.sh b/tools/mkn/test.sh new file mode 100755 index 000000000..6512aef00 --- /dev/null +++ b/tools/mkn/test.sh @@ -0,0 +1,14 @@ +set -ex + +export KLOG=3 +FILE="tests/core/data/gridlayout/test_gridlayout_gpu.cpp" +FILE="tests/core/data/field/test_field_gpu.cpp" +CARG="-DPHARE_HAVE_MKN_GPU=1 -DMKN_GPU_ROCM=1" +DOPT="" +XFIL="-Ox res/mkn/hip.yaml" +WITH="-w mkn.gpu,google.test" +# export MKN_DBG="gdb -batch -ex run -ex bt --args" +export MKN_DBG="/opt/rocm/bin/rocprofv2 --kernel-trace" +export MKN_DBG="/opt/rocm/bin/rocprof -d outputFolder --hip-trace" + +mkn clean build -M "${FILE}" -a "${CARG}" -tp bench ${DOPT} ${WITH} ${XFIL} dbg