diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml new file mode 100644 index 00000000000..fd8b1e2e7c5 --- /dev/null +++ b/.azure-pipelines.yml @@ -0,0 +1,61 @@ +# -*- mode: yaml -*- + +pool: + vmImage: 'ubuntu-latest' + +variables: + WARPX_CI_CCACHE: 'TRUE' + WARPX_CI_OPENPMD: 'TRUE' + FFTW_HOME: '/usr/' + BLASPP_HOME: '/usr/local/' + LAPACKPP_HOME: '/usr/local/' + OMP_NUM_THREADS: 1 + +strategy: + matrix: + cartesian: + WARPX_CI_REGULAR_CARTESIAN: 'TRUE' + psatd: + WARPX_CI_PSATD: 'TRUE' + python: + WARPX_CI_PYTHON_MAIN: 'TRUE' + single_precision: + WARPX_CI_SINGLE_PRECISION: 'TRUE' + rz_or_nompi: + WARPX_CI_RZ_OR_NOMPI: 'TRUE' + qed: + WARPX_CI_QED: 'TRUE' + +steps: +- script: | + cat /proc/cpuinfo | grep "model name" | sort -u + sudo apt-get update + sudo apt-get install -y ccache gcc gfortran g++ openmpi-bin libopenmpi-dev \ + libfftw3-dev libfftw3-mpi-dev libhdf5-openmpi-dev pkg-config make \ + python3 python3-pip python3-setuptools libblas-dev liblapack-dev + sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 2 + sudo update-alternatives --set python /usr/bin/python3 + python -m pip install --upgrade pip + python -m pip install --upgrade wheel + python -m pip install --upgrade cmake matplotlib==3.2.2 mpi4py numpy scipy yt + export CEI_CMAKE="$HOME/.local/bin/cmake" + export CEI_SUDO="sudo" + sudo curl -L -o /usr/local/bin/cmake-easyinstall https://git.io/JvLxY + sudo chmod a+x /usr/local/bin/cmake-easyinstall + if [ "${WARPX_CI_OPENPMD:-FALSE}" == "TRUE" ]; then + cmake-easyinstall --prefix=/usr/local git+https://github.com/openPMD/openPMD-api.git \ + -DopenPMD_USE_PYTHON=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF + fi + if [ "${WARPX_CI_RZ_OR_NOMPI:-FALSE}" == "TRUE" ]; then + cmake-easyinstall --prefix=/usr/local git+https://bitbucket.org/icl/blaspp.git \ + -Duse_openmp=OFF -Dbuild_tests=OFF + cmake-easyinstall --prefix=/usr/local git+https://bitbucket.org/icl/lapackpp.git \ + -DBUILD_LAPACKPP_TESTS=OFF + fi + displayName: 'Install dependencies' + +- script: | + export WARPX_TEST_COMMIT=$BUILD_SOURCEVERSION + ./run_test.sh + timeoutInMinutes: 360 + displayName: 'Build & test' diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..b63a89f6c6f --- /dev/null +++ b/.editorconfig @@ -0,0 +1,32 @@ +# http://EditorConfig.org +# +# precedence of rules is bottom to top + +# this is the top-most EditorConfig file +root = true + + +[*] +# 4 space indentation +indent_style = space +indent_size = 4 + +# no end of line whitespaces +trim_trailing_whitespace = true + +# unix-style newlines +end_of_line = lf + +# newline ending in files +insert_final_newline = true + + +[*.md] +# two end of line whitespaces are newlines without a paragraph +trim_trailing_whitespace = false + + +[Makefile] +# TABs are part of its syntax +indent_style = tab +indent_size = unset diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 127909d7dd9..dd266435181 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -20,7 +20,6 @@ jobs: sudo wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/7fa2af80.pub sudo apt-key add 7fa2af80.pub echo "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64 /" | sudo tee /etc/apt/sources.list.d/cuda.list - echo "deb https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64 /" | sudo tee /etc/apt/sources.list.d/nvidia-ml.list sudo apt-get update sudo apt-get install -y cuda-command-line-tools-11-0 cuda-compiler-11-0 cuda-cupti-dev-11-0 cuda-minimal-build-11-0 cuda-nvml-dev-11-0 cuda-nvtx-11-0 libcurand-dev-11-0 sudo ln -s cuda-11.0 /usr/local/cuda @@ -54,7 +53,7 @@ jobs: sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list sudo apt-get update - sudo apt-get install -y intel-oneapi-icc intel-oneapi-ifort + sudo apt-get install -y intel-oneapi-dpcpp-cpp-compiler-pro set +e source /opt/intel/oneapi/setvars.sh set -e @@ -80,10 +79,7 @@ jobs: cmake .. -DCMAKE_VERBOSE_MAKEFILE=ON -DWarpX_MPI=OFF -DWarpX_OPENPMD=ON -DWarpX_openpmd_internal=OFF -DWarpX_PRECISION=single make -j 2 - # broken in beta08 - # https://github.com/intel/llvm/issues/2187 build_dpcc: - if: false name: oneAPI DPC++ SP [Linux] runs-on: ubuntu-latest steps: @@ -101,9 +97,10 @@ jobs: sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2023.PUB echo "deb https://apt.repos.intel.com/oneapi all main" | sudo tee /etc/apt/sources.list.d/oneAPI.list sudo apt-get update - sudo apt-get install -y intel-oneapi-dpcpp-compiler intel-oneapi-mkl + sudo apt-get install -y intel-oneapi-dpcpp-cpp-compiler intel-oneapi-mkl-devel set +e source /opt/intel/oneapi/setvars.sh + source /opt/intel/oneapi/compiler/2021.1-beta08/env/vars.sh set -e git clone https://github.com/openPMD/openPMD-api.git mkdir openPMD-api/build @@ -116,6 +113,7 @@ jobs: run: | set +e source /opt/intel/oneapi/setvars.sh + source /opt/intel/oneapi/compiler/2021.1-beta08/env/vars.sh set -e export CXX=$(which dpcpp) export CC=$(which clang) diff --git a/.gitignore b/.gitignore index 590bf1871ad..a83c9170a65 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ Python/pywarpx/libwarpx*.so d/ f/ o/ +build/ tmp_build_dir/ test_dir test_dir/ @@ -34,6 +35,14 @@ Docs/doxyhtml/ Docs/doxyxml/ Docs/source/_static/ +#################### +# Package Managers # +#################### +# anonymous Spack environments +# https://spack.readthedocs.io/en/latest/environments.html#anonymous-environments +.spack-env/ +spack.lock + ####### # IDE # ####### diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 18f224e8310..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright 2018-2019 Axel Huebl, David Grote, Luca Fedeli -# Maxence Thevenet, Remi Lehe -# -# This file is part of WarpX. -# -# License: BSD-3-Clause-LBNL - -dist: bionic -language: c++ -sudo: true -cache: pip - -env: - global: - - WARPX_CI_CCACHE=TRUE - matrix: - - WARPX_CI_REGULAR_CARTESIAN=TRUE WARPX_CI_OPENPMD=FALSE - - WARPX_CI_PSATD=TRUE - - WARPX_CI_PYTHON_MAIN=TRUE - - WARPX_CI_SINGLE_PRECISION=TRUE - - WARPX_CI_RZ_OR_NOMPI=TRUE - - WARPX_CI_QED=TRUE - -before_install: - - sudo apt-get update - - sudo apt-get install -y ccache gcc gfortran g++ openmpi-bin libopenmpi-dev libfftw3-dev libfftw3-mpi-dev libhdf5-openmpi-dev pkg-config make python3 python3-pip python3-setuptools libblas-dev liblapack-dev - # xenial misses "libadios-openmpi-dev" - - sudo update-alternatives --install /usr/bin/python python /usr/bin/python3 2 - - sudo update-alternatives --set python /usr/bin/python3 - -install: - - python -m pip install --upgrade pip - - python -m pip install --upgrade cmake matplotlib==3.2.2 mpi4py numpy scipy yt - - export CEI_CMAKE="/home/travis/.local/bin/cmake" - - export CEI_SUDO="sudo" - - sudo curl -L -o /usr/local/bin/cmake-easyinstall https://git.io/JvLxY && sudo chmod a+x /usr/local/bin/cmake-easyinstall - - if [ "${WARPX_CI_OPENPMD}" != "FALSE" ]; then - cmake-easyinstall --prefix=/usr/local git+https://github.com/openPMD/openPMD-api.git - -DopenPMD_USE_PYTHON=OFF -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF; - fi - - if [ "${WARPX_CI_RZ_OR_NOMPI:-FALSE}" == "TRUE" ]; then - cmake-easyinstall --prefix=/usr/local git+https://bitbucket.org/icl/blaspp.git - -DBLAS_LIBRARY=generic -DUSE_OPENMP=OFF -DBLASPP_BUILD_TESTS=OFF; - cmake-easyinstall --prefix=/usr/local git+https://bitbucket.org/icl/lapackpp.git - -DUSE_OPENMP=off -DLAPACKPP_BUILD_TESTS=OFF; - fi - -script: - - export FFTW_HOME=/usr/ - - export BLASPP_HOME=/usr/local/ - - export LAPACKPP_HOME=/usr/local/ - - if [ -z ${WARPX_CI_OPENPMD} ]; then WARPX_CI_OPENPMD=TRUE; fi - - if [ -z ${WARPX_CI_PSATD} ]; then WARPX_CI_PSATD=TRUE; fi - - # Run the tests on the current commit - - export WARPX_TEST_COMMIT=$TRAVIS_COMMIT - - # Run the script that prepares the test environment and runs the tests - - export OMP_NUM_THREADS=1 - - travis_wait 50 ./run_test.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a1c21484d1..78acf7395d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,13 @@ if(NOT WarpX_COMPUTE IN_LIST WarpX_COMPUTE_VALUES) message(FATAL_ERROR "WarpX_PRECISION (${WarpX_COMPUTE}) must be one of ${WarpX_COMPUTE_VALUES}") endif() +option(WarpX_MPI_THREAD_MULTIPLE "MPI thread-multiple support, i.e. for async_io" ON) +mark_as_advanced(WarpX_MPI_THREAD_MULTIPLE) + +set(WarpX_PARSER_DEPTH 24 CACHE STRING + "Maximum parser depth for input file functions") +mark_as_advanced(WarpX_PARSER_DEPTH) + option(WarpX_amrex_internal "Download & build AMReX" ON) # change the default build type to RelWithDebInfo (or Release) instead of Debug @@ -222,6 +229,9 @@ if(WarpX_PSATD) target_compile_definitions(WarpX PRIVATE WARPX_USE_PSATD) endif() +target_compile_definitions(WarpX PRIVATE + WARPX_PARSER_DEPTH=${WarpX_PARSER_DEPTH}) + # Warnings #################################################################### # diff --git a/Docs/source/building/building.rst b/Docs/source/building/building.rst index bcc276b9548..8aad5a56dd2 100644 --- a/Docs/source/building/building.rst +++ b/Docs/source/building/building.rst @@ -54,6 +54,7 @@ options are: * ``USE_GPU=TRUE`` or ``FALSE``: Whether to compile for Nvidia GPUs (requires CUDA). * ``USE_OPENPMD=TRUE`` or ``FALSE``: Whether to support openPMD for I/O (requires openPMD-api). * ``MPI_THREAD_MULTIPLE=TRUE`` or ``FALSE``: Whether to initialize MPI with thread multiple support. Required to use asynchronous IO with more than ``amrex.async_out_nfiles`` (by default, 64) MPI tasks. Please see :doc:`../visualization/visualization` for more information. + * ``PRECISION=FLOAT USE_SINGLE_PRECISION_PARTICLES=TRUE``: Switch from default double precision to single precision (experimental). For a description of these different options, see the `corresponding page `__ in the AMReX documentation. @@ -104,3 +105,5 @@ Building for specific platforms cori summit juwels + lassen + quartz diff --git a/Docs/source/building/cmake.rst b/Docs/source/building/cmake.rst index 577c3525eb5..42cabf4c221 100644 --- a/Docs/source/building/cmake.rst +++ b/Docs/source/building/cmake.rst @@ -8,6 +8,13 @@ Until we have transitioned our documentation and functionality completely, pleas Progress status: `see on GitHub `_ +Introduction to CMake +===================== + +If you are new to CMake, `this short tutorial `_ from the HEP Software foundation is the perfect place to get started with it. + +If you just want to use CMake to build the project, jump into sections *1. Introduction*, *2. Building with CMake* and *9. Finding Packages*. + Dependencies ============ @@ -25,7 +32,7 @@ Optional dependencies include: - `OpenMP 3.1+ `_: for threaded CPU execution (currently not fully accelerated) - `FFTW3 `_: for spectral solver (PSATD) support - `Boost 1.66.0+ `_: for QED support -- `openPMD-api 0.11.1+ `_: we automatically download and compile a copy of openPMD-api for openPMD I/O support +- `openPMD-api 0.12.0+ `_: we automatically download and compile a copy of openPMD-api for openPMD I/O support - see `optional I/O backends `_ - `CCache `_: to speed up rebuilds (needs 3.7.9+ for CUDA) @@ -39,11 +46,12 @@ macOS/Linux: spack env create warpx-dev spack env activate warpx-dev + spack add adios2 spack add ccache spack add cmake spack add fftw + spack add hdf5 spack add mpi - spack add openpmd-api spack add pkgconfig # for fftw # optional: # spack add cuda @@ -56,13 +64,14 @@ or macOS/Linux: .. code-block:: bash brew update + brew install adios2 brew install ccache brew install cmake brew install fftw + brew install hdf5-mpi brew install libomp brew install pkg-config # for fftw brew install open-mpi - brew install openpmd-api Now, ``cmake --version`` should be at version 3.14.0 or newer. @@ -109,23 +118,25 @@ You can inspect and modify build options after running ``cmake ..`` with either or by providing arguments to the CMake call: ``cmake .. -D= -D=`` -=========================== ============================================ ======================================================= -CMake Option Default & Values Description -=========================== ============================================ ======================================================= -``CMAKE_BUILD_TYPE`` **RelWithDebInfo**/Release/Debug Type of build, symbols & optimizations -``WarpX_ASCENT`` ON/**OFF** Ascent in situ visualization -``WarpX_COMPUTE`` NOACC/**OMP**/CUDA/DPCPP On-node, accelerated computing backend -``WarpX_DIMS`` **3**/2/RZ Simulation dimensionality -``WarpX_MPI`` **ON**/OFF Multi-node support (message-passing) -``WarpX_OPENPMD`` ON/**OFF** openPMD I/O (HDF5, ADIOS) -``WarpX_PRECISION`` **double**/single Floating point precision (single/double) -``WarpX_PSATD`` ON/**OFF** Spectral solver -``WarpX_QED`` ON/**OFF** PICSAR QED (requires Boost and PICSAR) -``WarpX_amrex_repo`` ``https://github.com/AMReX-Codes/amrex.git`` Repository URI to pull and build AMReX from -``WarpX_amrex_branch`` ``development`` Repository branch for ``WarpX_amrex_repo`` -``WarpX_amrex_internal`` **ON**/OFF Needs a pre-installed AMReX library if set to ``OFF`` -``WarpX_openpmd_internal`` **ON**/OFF Needs a pre-installed openPMD library if set to ``OFF`` -=========================== ============================================ ======================================================= +============================= ============================================ ======================================================= +CMake Option Default & Values Description +============================= ============================================ ======================================================= +``CMAKE_BUILD_TYPE`` **RelWithDebInfo**/Release/Debug Type of build, symbols & optimizations +``WarpX_ASCENT`` ON/**OFF** Ascent in situ visualization +``WarpX_COMPUTE`` NOACC/**OMP**/CUDA/DPCPP On-node, accelerated computing backend +``WarpX_DIMS`` **3**/2/RZ Simulation dimensionality +``WarpX_MPI`` **ON**/OFF Multi-node support (message-passing) +``WarpX_MPI_THREAD_MULTIPLE`` **ON**/OFF MPI thread-multiple support, i.e. for ``async_io`` +``WarpX_OPENPMD`` ON/**OFF** openPMD I/O (HDF5, ADIOS) +``WarpX_PARSER_DEPTH`` **24** Maximum parser depth for input file functions +``WarpX_PRECISION`` **double**/single Floating point precision (single/double) +``WarpX_PSATD`` ON/**OFF** Spectral solver +``WarpX_QED`` ON/**OFF** PICSAR QED (requires Boost and PICSAR) +``WarpX_amrex_repo`` ``https://github.com/AMReX-Codes/amrex.git`` Repository URI to pull and build AMReX from +``WarpX_amrex_branch`` ``development`` Repository branch for ``WarpX_amrex_repo`` +``WarpX_amrex_internal`` **ON**/OFF Needs a pre-installed AMReX library if set to ``OFF`` +``WarpX_openpmd_internal`` **ON**/OFF Needs a pre-installed openPMD library if set to ``OFF`` +============================= ============================================ ======================================================= For example, one can also build against a local AMReX git repo. Assuming AMReX' source is located in ``$HOME/src/amrex`` and changes are committed into a branch such as ``my-amrex-branch`` then pass to ``cmake`` the arguments: ``-DWarpX_amrex_repo=file://$HOME/src/amrex -DWarpX_amrex_branch=my-amrex-branch``. diff --git a/Docs/source/building/juwels.rst b/Docs/source/building/juwels.rst index 639540ae925..586d3c63a6d 100644 --- a/Docs/source/building/juwels.rst +++ b/Docs/source/building/juwels.rst @@ -25,7 +25,7 @@ Use the following commands to download the WarpX source code and switch to the c cd ~/src git clone https://github.com/ECP-WarpX/WarpX.git warpx - git clone --branch QED https://github.com/ECP-WarpX/picsar.git + git clone --branch development https://github.com/ECP-WarpX/picsar.git git clone --branch development https://github.com/AMReX-Codes/amrex.git We use the following modules and environments on the system. diff --git a/Docs/source/building/lassen.rst b/Docs/source/building/lassen.rst new file mode 100644 index 00000000000..0adc4113a28 --- /dev/null +++ b/Docs/source/building/lassen.rst @@ -0,0 +1,92 @@ +.. _building-lassen: + +Lassen (LLNL) +============= + +The `Lassen V100 GPU cluster `_ is located at LLNL. + +If you are new to this system, please see the following resources: + +* `LLNL user account `_ +* `Lassen user guide `_ +* Batch system: `LSF `_ +* `Production directories `_: + + * ``/p/gpfs1/$(whoami)``: personal directory on the parallel filesystem + * Note that the ``$HOME`` directory and the ``/usr/workspace/$(whoami)`` space are NFS mounted and *not* suitable for production quality data generation. + + +Installation +------------ + +Use the following commands to download the WarpX source code and switch to the correct branch: + +.. code-block:: bash + + git clone https://github.com/ECP-WarpX/WarpX.git $HOME/src/warpx + +We use the following modules and environments on the system. + +.. code-block:: bash + + # please set your project account + export proj= + + # required dependencies + module load cmake/3.16.8 + module load gcc/8.3.1 + module load cuda/11.0.2 + + # optional: for PSATD support + module load fftw/3.3.8 + + # optional: for QED support + module load boost/1.70.0 + + # optional: for openPMD support + # TODO ADIOS2 & HDF5 + + # optional: for PSATD in RZ geometry support + # TODO: blaspp lapackpp + + # optional: for Python bindings + module load python/3.8.2 + + # optional: an alias to request an interactive node for two hours + alias getNode="bsub -G $proj -W 2:00 -nnodes 1 -Is /bin/bash" + + # fix system defaults: do not escape $ with a \ on tab completion + shopt -s direxpand + + # compiler environment hints + export CC=$(which gcc) + export CXX=$(which g++) + export FC=$(which gfortran) + export CUDACXX=$(which nvcc) + export CUDAHOSTCXX=$(which g++) + + +We recommend to store the above lines in a file, such as ``$HOME/lassen_warpx.profile``, and load it into your shell after a login: + +.. code-block:: bash + + source $HOME/lassen_warpx.profile + +Then, ``cd`` into the directory ``$HOME/src/warpx`` and use the following commands to compile: + +.. code-block:: bash + + rm -rf build/ + cmake -B build -DWarpX_COMPUTE=CUDA -DWarpX_OPENPMD=ON + cmake --build build -j 10 + +This will build an executable in ``build/bin/``. +The other :ref:`general compile-time options ` apply as usual. + + +Running +------- + +Please see :ref:`our example job scripts ` on how to run WarpX on Lassen. + +See :doc:`../visualization/yt` for more information on how to visualize the simulation results. diff --git a/Docs/source/building/openpmd.rst b/Docs/source/building/openpmd.rst index bf8413e0091..33f70f4ec24 100644 --- a/Docs/source/building/openpmd.rst +++ b/Docs/source/building/openpmd.rst @@ -3,13 +3,13 @@ Building WarpX with support for openPMD output ============================================== -WarpX can dump data in the `openPMD format `__. +WarpX can dump data in the `openPMD format `_. This feature currently requires to have a parallel version of HDF5 installed ; therefore we recommend to use `spack `__ in order to facilitate the installation. More specifically, we recommend that you try installing the -`openPMD-api library 0.11.0a or newer `__ +`openPMD-api library 0.12.0a or newer `_ using spack (first section below). If this fails, a back-up solution is to install parallel HDF5 with spack, and then install the openPMD-api library from source. @@ -79,10 +79,18 @@ Installing openPMD-api from source You can also build openPMD-api from source, e.g. to build against the module environment of a supercomputer cluster. First, load the according modules of the cluster to support the openPMD-api dependencies. -You can find the `required and optional dependencies here `__. You usually just need a C++ compiler, CMake, and one or more file backend libraries, such as HDF5 and/or ADIOS2. -See for example `our installation guidelines for Cori :ref``. +See for example :ref:`our installation guidelines for Cori `. + +If optional dependencies are installed in non-system paths, one needs to `hint their installation location `_ with an environment variable during the build phase: + +.. code-block:: bash + + # optional: only if you manually installed HDF5 and/or ADIOS2 in custom directories + export HDF5_ROOT=$HOME/path_to_installed_software/hdf5-1.12.0/ + export ADIOS2_ROOT=$HOME/path_to_installed_software/adios2-2.6.0/ Then, in the ``$HOME/warpx_directory/``, download and build openPMD-api: diff --git a/Docs/source/building/quartz.rst b/Docs/source/building/quartz.rst new file mode 100644 index 00000000000..e8e398d5c50 --- /dev/null +++ b/Docs/source/building/quartz.rst @@ -0,0 +1,94 @@ +.. _building-quartz: + +Quartz (LLNL) +============= + +The `Quartz Intel CPU cluster `_ is located at LLNL. + +If you are new to this system, please see the following resources: + +* `LLNL user account `_ +* `Quartz user guide `_ +* Batch system: `Slurm `_ +* `Production directories `_: + + * ``/p/lustre1/$(whoami)`` and ``/p/lustre2/$(whoami)``: personal directory on the parallel filesystem + * Note that the ``$HOME`` directory and the ``/usr/workspace/$(whoami)`` space are NFS mounted and *not* suitable for production quality data generation. + + +Installation +------------ + +Use the following commands to download the WarpX source code and switch to the correct branch: + +.. code-block:: bash + + git clone https://github.com/ECP-WarpX/WarpX.git $HOME/src/warpx + +We use the following modules and environments on the system. + +.. code-block:: bash + + # please set your project account + export proj= + + # required dependencies + module load cmake/3.16.8 + module load intel/19.1.2 + module load mvapich2/2.3 + + # optional: for PSATD support + module load fftw/3.3.8 + + # optional: for QED support + module load boost/1.73.0 + + # optional: for openPMD support + # TODO ADIOS2 + module load hdf5-parallel/1.10.2 + + # optional: for PSATD in RZ geometry support + # TODO: blaspp lapackpp + + # optional: for Python bindings + module load python/3.8.2 + + # optional: an alias to request an interactive node for two hours + alias getNode="srun --time=0:30:00 --nodes=1 --ntasks-per-node=2 --cpus-per-task=18 -p pdebug --pty bash" + + # fix system defaults: do not escape $ with a \ on tab completion + shopt -s direxpand + + # compiler environment hints + export CC=$(which icc) + export CXX=$(which icpc) + export FC=$(which ifort) + # we need a newer libstdc++: + export CFLAGS="-gcc-name=/usr/tce/packages/gcc/gcc-8.3.1/bin/gcc ${CFLAGS}" + export CXXFLAGS="-gxx-name=/usr/tce/packages/gcc/gcc-8.3.1/bin/g++ ${CXXFLAGS}" + + +We recommend to store the above lines in a file, such as ``$HOME/quartz_warpx.profile``, and load it into your shell after a login: + +.. code-block:: bash + + source $HOME/quartz_warpx.profile + +Then, ``cd`` into the directory ``$HOME/src/warpx`` and use the following commands to compile: + +.. code-block:: bash + + rm -rf build/ + cmake -B build -DWarpX_OPENPMD=ON + cmake --build build -j 6 + +This will build an executable in ``build/bin/``. +The other :ref:`general compile-time options ` apply as usual. + + +Running +------- + +Please see :ref:`our example job scripts ` on how to run WarpX on Quartz. + +See :doc:`../visualization/yt` for more information on how to visualize the simulation results. diff --git a/Docs/source/building/rzgeometry.rst b/Docs/source/building/rzgeometry.rst index 94e9124944d..8922d920892 100644 --- a/Docs/source/building/rzgeometry.rst +++ b/Docs/source/building/rzgeometry.rst @@ -31,18 +31,28 @@ package and setting ``USE_PSATD=TRUE``. export FFTW_HOME=/usr/ - - Download and build the blaspp and lapackpp packages. These can be obtained from bitbucket using mercurial. + - Download and build the blaspp and lapackpp packages. These can be obtained from bitbucket. :: - hg clone https://bitbucket.org/icl/blaspp - hg clone https://bitbucket.org/icl/lapackpp + git clone https://bitbucket.org/icl/blaspp.git + git clone https://bitbucket.org/icl/lapackpp.git + + The two packages can be built in multiple ways. A recommended method is to follow the cmake instructions + provided in the INSTALL.md that comes with the packages. They can also be installed using spack. - Set the environment variables BLASPP_HOME and LAPACKPP_HOME to the locations where - the packages were downloaded and built. For example, using bash: + the packages libraries were installed. For example, using bash: + :: + + export BLASPP_HOME=/location/of/installation/blaspp + export LAPACKPP_HOME=/location/of/installation/lapackpp + + - In some case, the blas and lapack libraries need to be specified. + If needed, this can be done by setting the BLAS_LIB and LAPACK_LIB + environment variables appropriately. For example, using bash: :: - export BLASPP_HOME=/location/of/packages/blaspp - export LAPACKPP_HOME=/location/of/packages/lapackpp + export BLAS_LIB=-lblas - Set ``USE_PSATD=TRUE`` when compiling: :: diff --git a/Docs/source/building/summit.rst b/Docs/source/building/summit.rst index 509ae51d248..bd3b9779a03 100644 --- a/Docs/source/building/summit.rst +++ b/Docs/source/building/summit.rst @@ -29,7 +29,7 @@ Use the following commands to download the WarpX source code and switch to the c cd ~/src git clone https://github.com/ECP-WarpX/WarpX.git warpx - git clone --branch QED https://github.com/ECP-WarpX/picsar.git + git clone --branch development https://github.com/ECP-WarpX/picsar.git git clone --branch development https://github.com/AMReX-Codes/amrex.git We use the following modules and environments on the system. @@ -40,6 +40,7 @@ We use the following modules and environments on the system. export proj= # required dependencies + module load cmake module load gcc/6.4.0 module load cuda @@ -53,11 +54,14 @@ We use the following modules and environments on the system. module load boost/1.66.0 # optional: for openPMD support - module load cmake - module load hdf5/1.10.4 - module load adios2/2.5.0 - export PKG_CONFIG_PATH=$HOME/sw/openPMD-api-install/lib64/pkgconfig:$PKG_CONFIG_PATH - export CMAKE_PREFIX_PATH=$HOME/sw/openPMD-api-install:$CMAKE_PREFIX_PATH + module load ums + module load ums-aph114 + module load openpmd-api/0.12.0 + + # optional: for PSATD in RZ geometry support + # note: needs the ums modules above + module load blaspp + module load lapackpp # optional: Ascent in situ support # note: build WarpX with CMake @@ -99,7 +103,7 @@ We recommend to store the above lines in a file, such as ``$HOME/warpx.profile`` source $HOME/warpx.profile -Optionally, download and build openPMD-api for I/O: +Optionally, download and build openPMD-api for I/O (only needed if you did not load our module above): .. code-block:: bash diff --git a/Docs/source/running_cpp/examples.rst b/Docs/source/running_cpp/examples.rst index fcede2292ed..3d8a48f9f6a 100644 --- a/Docs/source/running_cpp/examples.rst +++ b/Docs/source/running_cpp/examples.rst @@ -3,16 +3,16 @@ Example input files This section allows you to **download input files** that correspond to different physical situations. For a definition of the different parameters that are set in these files, see the section :doc:`parameters`. For a complete list of all example input files, have a look at our ``Examples/`` directory. It contains folders and subfolders with self-describing names that you can try. All these input files are automatically tested, so they should always be up-to-date. -Beam-driven acceleration ------------------------- +Beam-driven electron acceleration +--------------------------------- * :download:`2D case <../../../Examples/Physics_applications/plasma_acceleration/inputs_2d>` * :download:`2D case in boosted frame <../../../Examples/Physics_applications/plasma_acceleration/inputs_2d_boost>` * :download:`3D case in boosted frame <../../../Examples/Physics_applications/plasma_acceleration/inputs_3d_boost>` -Laser-driven acceleration -------------------------- +Laser-driven electron acceleration +---------------------------------- * :download:`2D case <../../../Examples/Physics_applications/laser_acceleration/inputs_2d>` * :download:`2D case in boosted frame <../../../Examples/Physics_applications/laser_acceleration/inputs_2d_boost>` @@ -23,6 +23,16 @@ Plasma mirror :download:`2D case <../../../Examples/Physics_applications/plasma_mirror/inputs_2d>` +Laser-ion acceleration +---------------------- + +:download:`2D case <../../../Examples/Physics_applications/laser_ion/inputs>` + +.. note:: + + The resolution of this 2D case is extremely low by default. + You will need a computing cluster for adequate resolution of the target density, see comments in the input file. + Uniform plasma -------------- diff --git a/Docs/source/running_cpp/parameters.rst b/Docs/source/running_cpp/parameters.rst index fca7ef846c2..f914ed25057 100644 --- a/Docs/source/running_cpp/parameters.rst +++ b/Docs/source/running_cpp/parameters.rst @@ -147,6 +147,12 @@ Setting up the field mesh Distribution across MPI ranks and parallelization ------------------------------------------------- +* ``warpx.numprocs`` (`2 ints` for 2D, `3 ints` for 3D) optional (default `none`) + This optional parameter can be used to control the domain decomposition on the + coarsest level. The domain will be chopped into the exact number of pieces in each + dimension as specified by this parameter. If it's not specified, the domain + decomposition will be determined by the parameters that will be discussed below. If + specified, the product of the numbers must be equal to the number of MPI processes. * ``amr.max_grid_size`` (`integer`) optional (default `128`) Maximum allowable size of each **subdomain** @@ -270,6 +276,12 @@ Particle initialization * ``particles.use_fdtd_nci_corr`` (`0` or `1`) optional (default `0`) Whether to activate the FDTD Numerical Cherenkov Instability corrector. + Not currently available in the RZ configuration. + +* ``particles.boundary_conditions`` (`string`) optional (default `none`) + Boundary conditions applied to particles. Options are: + * ``none``: the boundary conditions applied to particles is determined by ``geometry.is_periodic``. + * ``absorbing``: particles exiting the simulation domain are discarded. * ``particles.rigid_injected_species`` (`strings`, separated by spaces) List of species injected using the rigid injection method. The rigid injection @@ -410,8 +422,8 @@ Particle initialization temperature parameter ``.theta`` as an input, where theta is kb*T/(m*c^2), kb is the Boltzmann constant, c is the speed of light, and m is the mass of the species. It also includes the optional parameter ``.beta`` where beta is equal to v/c. - The plasma will be initialized to move at drift velocity beta*c in the - ``.drift_vel_dir = (+/-) 'x', 'y', 'z'`` direction. Please leave no whitespace + The plasma will be initialized to move at bulk velocity beta*c in the + ``.bulk_vel_dir = (+/-) 'x', 'y', 'z'`` direction. Please leave no whitespace between the sign and the character on input. A direction without a sign will be treated as positive. The MB distribution is initialized in the drifting frame by sampling three Gaussian distributions in each dimension using, the Box Mueller method, and then the distribution is @@ -427,7 +439,7 @@ Particle initialization to kb*T/(m*c^2), where kb is the Boltzmann constant, and m is the mass of the species. It also includes the optional parameter ``.beta`` where beta is equal to v/c. The plasma will be initialized to move at velocity beta*c in the - ``.drift_vel_dir = (+/-) 'x', 'y', 'z'`` direction. Please leave no whitespace + ``.bulk_vel_dir = (+/-) 'x', 'y', 'z'`` direction. Please leave no whitespace between the sign and the character on input. A direction without a sign will be treated as positive. The MJ distribution will be initialized in the moving frame using the Sobol method, and then the distribution will be transformed to the simulation frame using the flipping method. @@ -552,36 +564,67 @@ Particle initialization * ``.do_qed`` (`int`) optional (default `0`) If `.do_qed = 0` all the QED effects are disabled for this species. If `.do_qed = 1` QED effects can be enabled for this species (see below). - **Implementation of this feature is in progress. It requires `picsar` on the `QED` branch and to compile with QED=TRUE** + **This feature requires to compile with QED=TRUE** * ``.do_qed_quantum_sync`` (`int`) optional (default `0`) It only works if `.do_qed = 1`. Enables Quantum synchrotron emission for this species. Quantum synchrotron lookup table should be either generated or loaded from disk to enable this process (see "Lookup tables for QED modules" section below). `` must be either an electron or a positron species. - **Implementation of this feature is in progress. It requires `picsar` on the `QED` branch and to compile with QED=TRUE** + **This feature requires to compile with QED=TRUE** * ``.do_qed_breit_wheeler`` (`int`) optional (default `0`) It only works if `.do_qed = 1`. Enables non-linear Breit-Wheeler process for this species. Breit-Wheeler lookup table should be either generated or loaded from disk to enable this process (see "Lookup tables for QED modules" section below). `` must be a photon species. - **Implementation of this feature is in progress. It requires `picsar` on the `QED` branch and to compile with QED=TRUE** + **This feature requires to compile with QED=TRUE** * ``.qed_quantum_sync_phot_product_species`` (`string`) If an electron or a positron species has the Quantum synchrotron process, a photon product species must be specified (the name of an existing photon species must be provided) - **Implementation of this feature is in progress. It requires `picsar` on the `QED` branch and to compile with QED=TRUE** + **This feature requires to compile with QED=TRUE** * ``.qed_breit_wheeler_ele_product_species`` (`string`) If a photon species has the Breit-Wheeler process, an electron product species must be specified (the name of an existing electron species must be provided) - **Implementation of this feature is in progress. It requires `picsar` on the `QED` branch and to compile with QED=TRUE** + **This feature requires to compile with QED=TRUE** * ``.qed_breit_wheeler_pos_product_species`` (`string`) If a photon species has the Breit-Wheeler process, a positron product species must be specified (the name of an existing positron species must be provided). - **Implementation of this feature is in progress. It requires `picsar` on the `QED` branch and to compile with QED=TRUE** + **This feature requires to compile with QED=TRUE** + +* ``.do_resampling`` (`0` or `1`) optional (default `0`) + If `1` resampling is performed for this species. This means that the number of macroparticles + will be reduced at specific timesteps while preserving the distribution function as much as + possible (in particular the weight of the remaining particles will be increased on average). + This can be useful in situations with continuous creation of particles (e.g. with ionization + or with QED effects). At least one resampling trigger (see below) must be specified to actually + perform resampling. + +* ``.resampling_algorithm`` (`string`) optional (default `leveling_thinning`) + The algorithm used for resampling. Currently there is only one option, which is already set by + default: + + * ``leveling_thinning`` This algorithm is defined in `Muraviev et al., arXiv:2006.08593 (2020) `_. + It has two parameters: + + * ``.resampling_algorithm_target_ratio`` (`float`) optional (default `1.5`) + This **roughly** corresponds to the ratio between the number of particles before and + after resampling. + + * ``.resampling_algorithm_min_ppc`` (`int`) optional (default `1`) + Resampling is not performed in cells with a number of macroparticles strictly smaller + than this parameter. + +* ``.resampling_trigger_intervals`` (`string`) optional (default `0`) + Using the `Intervals parser`_ syntax, this string defines timesteps at which resampling is + performed. + +* ``.resampling_trigger_max_avg_ppc`` (`float`) optional (default `infinity`) + Resampling is performed everytime the number of macroparticles per cell of the species + averaged over the whole simulation domain exceeds this parameter. .. _running-cpp-parameters-laser: @@ -754,6 +797,10 @@ Laser initialization ``.profile_focal_distance`` in the laboratory frame, and use ``warpx.gamma_boost`` to automatically perform the conversion to the boosted frame. +* ``.phi0`` (`float`; in radians) + The Carrier Envelope Phase, i.e. the phase of the laser oscillation, at the + position where the laser enveloppe is maximum (only used for the ``"gaussian"`` profile) + * ``.stc_direction`` (`3 floats`) optional (default `1. 0. 0.`) Direction of laser spatio-temporal couplings. See definition in Akturk et al., Opt Express, vol 12, no 19 (2014). @@ -879,7 +926,7 @@ Laser initialization the first and second dimensions are `x` and `z`, respectively, and the value of the `By` component is set to zero. Note that the current implementation of the parser for B-field on particles - does not work with RZ and the code will abort with an error message. + is applied in cartesian co-ordinates as a function of (x,y,z) even for RZ. * ``particles.E_ext_particle_init_style`` (string) optional (default is "default") This parameter determines the type of initialization for the external @@ -899,8 +946,8 @@ Laser initialization using ``my_constants``. For a two-dimensional simulation, similar to the B-field, it is assumed that the first and second dimensions are `x` and `z`, respectively, and the value of the `Ey` component is set to zero. - The current implementation of the parser for the E-field on particles does not work - with RZ and the code will abort with an error message. + The current implementation of the parser for B-field on particles + is applied in cartesian co-ordinates as a function of (x,y,z) even for RZ. * ``particles.E_external_particle`` & ``particles.B_external_particle`` (list of `float`) optional (default `0. 0. 0.`) Two separate parameters which add an externally applied uniform E-field or @@ -915,27 +962,32 @@ Collision initialization WarpX provides a relativistic elastic Monte Carlo binary collision model, following the algorithm given by `Perez et al. (Phys. Plasmas 19, 083104, 2012) `_. -* ``collisions.ncollisions`` (`int`) optional (default `0`) - Number of collision types. - * ``collisions.collision_names`` (`strings`, separated by spaces) - The name of each collision type. It must be provided if ``collisions.ncollisions`` is not zero. + The name of each collision type. This is then used in the rest of the input deck; in this documentation we use ```` as a placeholder. - The number of strings provided should match the number of collision types, - i.e. ``collisions.ncollisions``. * ``.species`` (`strings`, two species names separated by spaces) The names of two species, between which the collision will be considered. - It must be provided if ``collisions.ncollisions`` is not zero, and - the number of provided ``.species`` should match - the number of collision types, i.e. ``collisions.ncollisions``. + The number of provided ``.species`` should match + the number of collision names, i.e. ``collisions.collision_names``. * ``.CoulombLog`` (`float`) optional A provided fixed Coulomb logarithm of the collision type ````. + For example, a typical Coulomb logarithm has a form of + :math:`\ln(\lambda_D/R)`, + where :math:`\lambda_D` is the Debye length, + :math:`R\approx1.4A^{1/3}` is the effective Coulombic radius of the nucleus, + :math:`A` is the mass number. If this is not provided, or if a non-positive value is provided, a Coulomb logarithm will be computed automatically according to the algorithm. + a Coulomb logarithm will be computed automatically according to the algorithm in + `Perez et al. (Phys. Plasmas 19, 083104, 2012) `_. + +* ``.ndt`` (`int`) optional + Execute collision every # time steps. + The default value is 1. .. _running-cpp-parameters-numerics: @@ -967,6 +1019,17 @@ Numerics and algorithms This requires `warpx.use_kspace_filter=1` and is only supported with the RZ spectral solver. +* ``warpx.use_damp_fields_in_z_guard`` (`0` or `1`) + When using the RZ spectrol solver, specifies whether to apply a + damping factor to the E and B fields in the guard cells + along z that extend beyond the edge of the domain. + When the boundary conditions along z are not periodic, this defaults to + true, otherwise false. The damping profile is + a sine squared and is applied to the fields on the outer half of the guards. + This damping is useful for damping high frequency numerical artifacts that + occur when there is parallel decomposition along z with non-periodic boundary + conditions. + * ``algo.current_deposition`` (`string`, optional) This parameter selects the algorithm for the deposition of the current density. Available options are: ``direct``, ``esirkepov``, and ``vay``. The default choice @@ -1036,7 +1099,7 @@ Numerics and algorithms The medium for evaluating the Maxwell solver. Available options are : - ``vacuum``: vacuum properties are used in the Maxwell solver. - - ``macroscopic``: macroscopic Maxwell equation is evaluated. If this option is selected, then the corresponding properties of the medium must be provided using ``macroscopic.sigma``, ``macroscopic.epsilon``, and ``macroscopic.mu``. + - ``macroscopic``: macroscopic Maxwell equation is evaluated. If this option is selected, then the corresponding properties of the medium must be provided using ``macroscopic.sigma``, ``macroscopic.epsilon``, and ``macroscopic.mu`` for each case where the initialization style is ``constant``. Otherwise if the initialization style uses the parser, ``macroscopic.sigma_function(x,y,z)``, ``macroscopic.epsilon_function(x,y,z)`` and/or ``macroscopic.mu_function(x,y,z)`` must be provided using the parser initialization style for spatially varying macroscopic properties. If ``algo.em_solver_medium`` is not specified, ``vacuum`` is the default. @@ -1047,9 +1110,16 @@ Numerics and algorithms - ``laxwendroff`` is the semi-implicit, second order in time scheme for E-update. Comparing the two methods, Lax-Wendroff is more prone to developing oscillations and requires a smaller timestep for stability. On the other hand, Backward Euler is more robust but it is first-order accurate in time compared to the second-order Lax-Wendroff method. +* ``macroscopic.sigma_function(x,y,z)``, ``macroscopic.epsilon_function(x,y,z)``, ``macroscopic.mu_function(x,y,z)`` (`string`) + To initialize spatially varying conducitivy, permittivity, and permeability, respectively, + using a mathematical function in the input. Constants required in the + mathematical expression can be set using ``my_constants``. These parameters are parsed + if ``algo.em_solver_medium=macroscopic``. + * ``macroscopic.sigma``, ``macroscopic.epsilon``, ``macroscopic.mu`` (`double`) - The conductivity, permittivity, and permeability of the computational medium, respectively. - If ``algo.em_solver_medium`` is set to macroscopic, then these properties must be provided. + To initialize a constant conductivity, permittivity, and permeability of the + computational medium, respectively. The default values are the corresponding values + in vacuum. * ``interpolation.nox``, ``interpolation.noy``, ``interpolation.noz`` (`1`, `2`, or `3` ; default: 1) The order of the shape factors for the macroparticles, for the 3 dimensions of space. @@ -1116,51 +1186,95 @@ Numerics and algorithms for more information. * ``psatd.current_correction`` (`0` or `1`; default: `0`) - If true, the current correction `(Vay et al, JCP 243, 2013) `_ + If true, a current correction scheme in Fourier space is applied in order to guarantee charge conservation. + + If ``psatd.v_galilean`` is zero, the spectral solver used is the standard PSATD scheme described in (`Vay et al, JCP 243, 2013 `_) and the current correction reads + + .. math:: + \widehat{\boldsymbol{J}}^{\,n+1/2}_{\mathrm{correct}} = \widehat{\boldsymbol{J}}^{\,n+1/2} + - \bigg(\boldsymbol{k}\cdot\widehat{\boldsymbol{J}}^{\,n+1/2} + - i \frac{\widehat{\rho}^{n+1} - \widehat{\rho}^{n}}{\Delta{t}}\bigg) \frac{\boldsymbol{k}}{k^2} + + If ``psatd.v_galilean`` is non-zero, the spectral solver used is the Galilean PSATD scheme described in (`Lehe et al, PRE 94, 2016 `_) and the current correction reads .. math:: - \widetilde{\boldsymbol{J}}^{\,n+1/2}_{\mathrm{correct}} = \widetilde{\boldsymbol{J}}^{\,n+1/2} - -\bigg[\boldsymbol{k}\cdot\widetilde{\boldsymbol{J}}^{\,n+1/2} - -i\frac{\widetilde{\rho}^{n+1}-\widetilde{\rho}^{n}}{\Delta t}\bigg] - \frac{\boldsymbol{k}}{k^2} + \widehat{\boldsymbol{J}}^{\,n+1/2}_{\mathrm{correct}} = \widehat{\boldsymbol{J}}^{\,n+1/2} + - \bigg(\boldsymbol{k}\cdot\widehat{\boldsymbol{J}}^{\,n+1/2} - (\boldsymbol{k}\cdot\boldsymbol{v}_G) + \,\frac{\widehat\rho^{n+1} - \widehat\rho^{n}\theta^2}{1 - \theta^2}\bigg) \frac{\boldsymbol{k}}{k^2} + + where :math:`\theta=\exp(i\,\boldsymbol{k}\cdot\boldsymbol{v}_G\,\Delta{t}/2)`. - is applied. This option guarantees charge conservation only when used in combination - with ``psatd.periodic_single_box_fft=1``, that is, only for periodic single-box - simulations with global FFTs without guard cells. The implementation for domain - decomposition with local FFTs over guard cells is planned but not yet completed. + This option is currently implemented only for the standard PSATD and Galilean PSATD schemes, while it is not yet available for the averaged Galilean PSATD scheme (activated by the input parameter ``psatd.do_time_averaging``). + + This option guarantees charge conservation only when used in combination with ``psatd.periodic_single_box_fft=1``, namely for periodic single-box simulations with global FFTs without guard cells. + The implementation for domain decomposition with local FFTs over guard cells is planned but not yet completed. * ``psatd.update_with_rho`` (`0` or `1`; default: `0`) - If false, the update equation for the electric field reads + If true, the update equation for the electric field is expressed in terms of both the current density and the charge density, namely :math:`\widehat{\boldsymbol{J}}^{\,n+1/2}`, :math:`\widehat\rho^{n}`, and :math:`\widehat\rho^{n+1}`. + If false, instead, the update equation for the electric field is expressed in terms of the current density :math:`\widehat{\boldsymbol{J}}^{\,n+1/2}` only. + If charge is expected to be conserved (by setting, for example, ``psatd.current_correction=1``), then the two formulations are expected to be equivalent. + + This option is currently implemented only for the standard PSATD and Galilean PSATD schemes, while it is not yet available for the averaged Galilean PSATD scheme (activated by the input parameter ``psatd.do_time_averaging``). + + If ``psatd.v_galilean`` is zero, the spectral solver used is the standard PSATD scheme described in (`Vay et al, JCP 243, 2013 `_): + + 1. if ``psatd.update_with_rho=0``, the update equation for the electric field reads + + .. math:: + \begin{split} + \widehat{\boldsymbol{E}}^{\,n+1}= & \: + C \widehat{\boldsymbol{E}}^{\,n} + i \, \frac{S c}{k} \boldsymbol{k}\times\widehat{\boldsymbol{B}}^{\,n} + - \frac{S}{\epsilon_0 c \, k} \widehat{\boldsymbol{J}}^{\,n+1/2} \\[0.2cm] + & +\frac{1-C}{k^2} (\boldsymbol{k}\cdot\widehat{\boldsymbol{E}}^{\,n}) \boldsymbol{k} + + \frac{1}{\epsilon_0 k^2} \left(\frac{S}{c \, k}-\Delta{t}\right) + (\boldsymbol{k}\cdot\widehat{\boldsymbol{J}}^{\,n+1/2}) \boldsymbol{k} + \end{split} + + 2. if ``psatd.update_with_rho=1``, the update equation for the electric field reads .. math:: \begin{split} - \widetilde{\boldsymbol{E}}^{\,n+1}= & \: - C\widetilde{\boldsymbol{E}}^{\,n}+i\frac{S}{c\,k}\,c^2\,\boldsymbol{k} - \times\widetilde{\boldsymbol{B}}^{\,n}-\frac{1}{\epsilon_0}\,\frac{S}{c\,k}\, - \,\widetilde{\boldsymbol{J}}^{\,n+1/2} \\ - & +\frac{1-C}{k^2}\,(\boldsymbol{k}\cdot\widetilde{\boldsymbol{E}}^{\,n})\,\boldsymbol{k} - +\frac{1}{\epsilon_0}\,\frac{1}{k^2}\,\left(\frac{S}{c\,k}-\Delta t\right)\, - (\boldsymbol{k}\cdot\widetilde{\boldsymbol{J}}^{\,n+1/2})\,\boldsymbol{k} + \widehat{\boldsymbol{E}}^{\,n+1}= & \: + C\widehat{\boldsymbol{E}}^{\,n} + i \, \frac{S c}{k} \boldsymbol{k}\times\widehat{\boldsymbol{B}}^{\,n} + - \frac{S}{\epsilon_0 c \, k} \widehat{\boldsymbol{J}}^{\,n+1/2} \\[0.2cm] + & + \frac{i}{\epsilon_0 k^2} \left(C-\frac{S}{c\,k}\frac{1}{\Delta{t}}\right) + \widehat{\rho}^{n} \boldsymbol{k} - \frac{i}{\epsilon_0 k^2} \left(1-\frac{S}{c \, k} + \frac{1}{\Delta{t}}\right)\widehat{\rho}^{n+1} \boldsymbol{k} \end{split} - where :math:`C=\cos(k\,c\,\Delta t)` and :math:`S=\sin(k\,c\,\Delta t)`, respectively. + The coefficients :math:`C` and :math:`S` are defined in (`Vay et al, JCP 243, 2013 `_). + + If ``psatd.v_galilean`` is non-zero, the spectral solver used is the Galilean PSATD scheme described in (`Lehe et al, PRE 94, 2016 `_): + + 1. if ``psatd.update_with_rho=0``, the update equation for the electric field reads + + .. math:: + \begin{split} + \widehat{\boldsymbol{E}}^{\,n+1} = & \: + \theta^{2} C \widehat{\boldsymbol{E}}^{\,n} + i \, \theta^{2} \frac{S c}{k} + \boldsymbol{k}\times\widehat{\boldsymbol{B}}^{\,n} + + \frac{i \, \nu \, \theta \, \chi_1 - \theta^{2} S}{\epsilon_0 c \, k} + \widehat{\boldsymbol{J}}^{\,n+1/2} \\[0.2cm] + & + \theta^{2} \frac{\chi_2-\chi_3}{k^{2}} + (\boldsymbol{k}\cdot\widehat{\boldsymbol{E}}^{\,n}) \boldsymbol{k} + + i \, \frac{\chi_2\left(\theta^{2}-1\right)}{\epsilon_0 c \, k^{3} \nu} + (\boldsymbol{k}\cdot\widehat{\boldsymbol{J}}^{\,n+1/2}) \boldsymbol{k} + \end{split} - If true, the update equation for the electric field reads instead + 2. if ``psatd.update_with_rho=1``, the update equation for the electric field reads .. math:: \begin{split} - \widetilde{\boldsymbol{E}}^{\,n+1}= & \: - C\widetilde{\boldsymbol{E}}^{\,n}+i\frac{S}{c\,k}\,c^2\,\boldsymbol{k} - \times\widetilde{\boldsymbol{B}}^{\,n}-\frac{1}{\epsilon_0}\,\frac{S}{c\,k} - \,\widetilde{\boldsymbol{J}}^{\,n+1/2} \\ - & -i\frac{1}{\epsilon_0}\,\frac{1}{k^2}\,\bigg[ - \left(1-\frac{S}{c\,k}\frac{1}{\Delta t}\right)\widetilde{\rho}^{n+1} - -\left(C-\frac{S}{c\,k}\frac{1}{\Delta t}\right)\widetilde{\rho}^{n}\bigg] - \,\boldsymbol{k} + \widehat{\boldsymbol{E}}^{\,n+1} = & \: + \theta^{2} C \widehat{\boldsymbol{E}}^{\,n} + i \, \theta^{2} \frac{S c}{k} + \boldsymbol{k}\times\widehat{\boldsymbol{B}}^{\,n} + + \frac{i \, \nu \, \theta \, \chi_1 - \theta^{2} S}{\epsilon_0 c \, k} + \widehat{\boldsymbol{J}}^{\,n+1/2} \\[0.2cm] + & + i \, \frac{\theta^{2} \chi_3}{\epsilon_0 k^{2}} \widehat{\rho}^{\,n} \boldsymbol{k} + - i \, \frac{\chi_2}{\epsilon_0 k^{2}} \widehat{\rho}^{\,n+1} \boldsymbol{k} \end{split} - See `(Vay et al, JCP 243, 2013) `_ - for more details about the derivation of these equations. + The coefficients :math:`C`, :math:`S`, :math:`\theta`, :math:`\nu`, :math:`\chi_1`, :math:`\chi_2`, and :math:`\chi_3` are defined in (`Lehe et al, PRE 94, 2016 `_). * ``pstad.v_galilean`` (`3 floats`, in units of the speed of light; default `0. 0. 0.`) Defines the galilean velocity. @@ -1191,7 +1305,7 @@ Numerics and algorithms * ``warpx.quantum_xi`` ('float'; default: 1.3050122.e-52) Overwrites the actual quantum parameter used in Maxwell's QED equations. Assigning a value here will make the simulation unphysical, but will allow QED effects to become more apparent. - Note that this option will only have an effect if the warpx.use_Hybrid_QED flag is also triggered. + Note that this option will only have an effect if the ``warpx.use_Hybrid_QED`` flag is also triggered. * ``warpx.do_device_synchronize_before_profile`` (`bool`) optional (default `1`) When running in an accelerated platform, whether to call a deviceSynchronize around profiling regions. @@ -1267,6 +1381,9 @@ Note that some parameter (those that do not start with a ``.`` prefix This should be changed in the future. In-situ capabilities can be used by turning on Sensei or Ascent (provided they are installed) through the output format, see below. +* ``diagnostics.enable`` (`0` or `1`, optional, default `1`) + Whether to enable or disable diagnostics. This flag overwrites all other diagnostics input parameters. + * ``diagnostics.diags_names`` (list of `string` optional, default `empty`) Name of each diagnostics. example: ``diagnostics.diags_names = diag1 my_second_diag``. @@ -1286,7 +1403,7 @@ In-situ capabilities can be used by turning on Sensei or Ascent (provided they a * ``plotfile`` for native AMReX format. - * ``checkpoint`` for a checkpoint file, only wirks with ``.diag_type = Full``. + * ``checkpoint`` for a checkpoint file, only works with ``.diag_type = Full``. * ``openpmd`` for OpenPMD format `openPMD `_. Requires to build WarpX with ``USE_OPENPMD=TRUE`` (see :ref:`instructions `). @@ -1315,9 +1432,8 @@ In-situ capabilities can be used by turning on Sensei or Ascent (provided they a Whether to write one file per timestep. * ``.fields_to_plot`` (list of `strings`, optional) - Fields written to plotfiles. Possible values: ``Ex`` ``Ey`` ``Ez`` - ``Bx`` ``By`` ``Bz`` ``jx`` ``jy`` ``jz`` ``part_per_cell`` ``rho`` - ``F`` ``part_per_grid`` ``part_per_proc`` ``divE`` ``divB``. + Fields written to output. + Possible values: ``Ex`` ``Ey`` ``Ez`` ``Bx`` ``By`` ``Bz`` ``jx`` ``jy`` ``jz`` ``part_per_cell`` ``rho`` ``F`` ``part_per_grid`` ``part_per_proc`` ``divE`` ``divB`` and ``rho_``, where ```` must match the name of one of the available particle species. Default is ``.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz``. * ``.plot_raw_fields`` (`0` or `1`) optional (default `0`) @@ -1352,24 +1468,23 @@ In-situ capabilities can be used by turning on Sensei or Ascent (provided they a Root for output file names. Supports sub-directories. * ``.diag_lo`` (list `float`, 1 per dimension) optional (default `-infinity -infinity -infinity`) - Lower corner of the output fields (if smaller than ``warpx.dom_lo``, then set to ``warpx.dom_lo``). + Lower corner of the output fields (if smaller than ``warpx.dom_lo``, then set to ``warpx.dom_lo``). Currently, when the ``diag_lo`` is different from ``warpx.dom_lo``, particle output is disabled. * ``.diag_hi`` (list `float`, 1 per dimension) optional (default `+infinity +infinity +infinity`) - Higher corner of the output fields (if larger than ``warpx.dom_hi``, then set to ``warpx.dom_hi``). + Higher corner of the output fields (if larger than ``warpx.dom_hi``, then set to ``warpx.dom_hi``). Currently, when the ``diag_hi`` is different from ``warpx.dom_hi``, particle output i +s disabled. + +* ``.write_species`` (`0` or `1`) optional (default `1`) + Whether to write species output or not. For checkpoint format, always set this parameter to 1. * ``.species`` (list of `string`, default all physical species in the simulation) Which species dumped in this diagnostics. * ``..variables`` (list of `strings` separated by spaces, optional) - List of particle quantities to write to output file. - By defaults, all quantities are written to file. Choices are - - * ``w`` for the particle weight, - - * ``ux`` ``uy`` ``uz`` for the particle momentum, - - The particle positions are always included. - Use ``.variables = none`` to plot no particle data, except particle position. + List of particle quantities to write to output. + Choices are ``w`` for the particle weight and ``ux`` ``uy`` ``uz`` for the particle momenta. + By default, all particle quantities are written. + If ``..variables = none``, no particle data are written, except for particle positions, which are always included. * ``..random_fraction`` (`float`) optional If provided ``..random_fraction = a``, only `a` fraction of the particle data of this species will be dumped randomly in diag ````, i.e. if `rand() < a`, this particle will be dumped, where `rand()` denotes a random number generator. @@ -1529,6 +1644,32 @@ Reduced Diagnostics :math:`E` field energy, :math:`B` field energy, at mesh refinement levels from 0 to :math:`n`. + * ``FieldMaximum`` + This type computes the maximum value of each component of the electric and magnetic fields + and of the norm of the electric and magnetic field vectors. + Measuring maximum fields in a plasma might be very noisy in PIC, use this instead + for analysis of scenarios such as an electromagnetic wave propagating in vacuum. + + The output columns are + the maximum value of the :math:`E_x` field, + the maximum value of the :math:`E_y` field, + the maximum value of the :math:`E_z` field, + the maximum value of the norm :math:`|E|` of the electric field, + the maximum value of the :math:`B_x` field, + the maximum value of the :math:`B_y` field, + the maximum value of the :math:`B_z` field and + the maximum value of the norm :math:`|B|` of the magnetic field, + at mesh refinement levels from 0 to :math:`n`. + + * ``ParticleNumber`` + This type computes the total number of macroparticles in the simulation (for each species + and summed over all species). It can be useful in particular for simulations with creation + (ionization, QED processes) or removal (resampling) of particles. + + The output columns are + total number of macroparticles summed over all species and + total number of macroparticles of each species. + * ``BeamRelevant`` This type computes properties of a particle beam relevant for particle accelerators, like position, momentum, emittance, etc. @@ -1652,8 +1793,9 @@ Reduced Diagnostics using the histogram reduced diagnostics are given in ``Examples/Tests/initial_distribution/``. -* ``.frequency`` (`int`) - The output frequency (every # time steps). +* ``.frequency`` (`string`) optional (default ``1``) + Using the `Intervals Parser`_ syntax, this string defines the timesteps at which reduced + diagnostics are written to file. * ``.path`` (`string`) optional (default `./diags/reducedfiles/`) The path that the output file will be stored. @@ -1665,23 +1807,22 @@ Reduced Diagnostics The separator between row values in the output file. The default separator is a whitespace. -Lookup tables and other settings for QED modules (implementation in progress) +Lookup tables and other settings for QED modules ----------------------------------------------------------------------------- Lookup tables store pre-computed values for functions used by the QED modules. -**Implementation of this feature is in progress. It requires `picsar` on the `QED` branch and to compile with QED=TRUE** +**This feature requires to compile with QED=TRUE (and also with QED_TABLE_GEN=TRUE for table generation) ** * ``qed_bw.lookup_table_mode`` (`string`) There are three options to prepare the lookup table required by the Breit-Wheeler module: - * ``dummy_builtin``: a built-in table is used (Warning: the quality of the table is very low, - so this option has to be used only for test purposes). + * ``builtin``: a built-in table is used (Warning: the table gives reasonable results but its resolution + is quite low). * ``generate``: a new table is generated. This option requires Boost math library - (version >= 1.67) and to compile with QED_TABLE_GEN=TRUE. All - the following parameters must be specified: - - * ``qed_bw.chi_min`` (`float`): minimum chi parameter to be considered by the engine + (version >= 1.66) and to compile with ``QED_TABLE_GEN=TRUE``. All + the following parameters must be specified (table 1 is used to evolve the optical depth + of the photons, while table 2 is used for pair generation): * ``qed_bw.tab_dndt_chi_min`` (`float`): minimum chi parameter for lookup table 1 ( used for the evolution of the optical depth of the photons) @@ -1698,8 +1839,8 @@ Lookup tables store pre-computed values for functions used by the QED modules. * ``qed_bw.tab_pair_chi_how_many`` (`int`): number of points to be used for chi axis in lookup table 2 * ``qed_bw.tab_pair_frac_how_many`` (`int`): number of points to be used for the second axis in lookup table 2 - (the second axis is the ratio between the energy of the less energetic particle of the pair and the - energy of the photon). + (the second axis is the ratio between the quantum parameter of the less energetic particle of the pair and the + quantum parameter of the photon). * ``qed_bw.save_table_in`` (`string`): where to save the lookup table @@ -1711,14 +1852,13 @@ Lookup tables store pre-computed values for functions used by the QED modules. * ``qed_qs.lookup_table_mode`` (`string`) There are three options to prepare the lookup table required by the Quantum Synchrotron module: - * ``dummy_builtin``: a built-in table is used (Warning: the quality of the table is very low, - so this option has to be used only for test purposes). + * ``builtin``: a built-in table is used (Warning: the table gives reasonable results but its resolution + is quite low). * ``generate``: a new table is generated. This option requires Boost math library - (version >= 1.67) and to compile with QED_TABLE_GEN=TRUE. All - the following parameters must be specified: - - * ``qed_qs.chi_min`` (`float`): minimum chi parameter to be considered by the engine + (version >= 1.66) and to compile with ``QED_TABLE_GEN=TRUE``. All + the following parameters must be specified (table 1 is used to evolve the optical depth + of the particles, while table 2 is used for photon emission): * ``qed_qs.tab_dndt_chi_min`` (`float`): minimum chi parameter for lookup table 1 ( used for the evolution of the optical depth of electrons and positrons) @@ -1734,8 +1874,11 @@ Lookup tables store pre-computed values for functions used by the QED modules. * ``qed_qs.tab_em_chi_how_many`` (`int`): number of points to be used for chi axis in lookup table 2 - * ``qed_qs.tab_em_prob_how_many`` (`int`): number of points to be used for the second axis in lookup table 2 - (the second axis is a cumulative probability). + * ``qed_qs.tab_em_frac_how_many`` (`int`): number of points to be used for the second axis in lookup table 2 + (the second axis is the ratio between the quantum parameter of the photon and the + quantum parameter of the charged particle). + + * ``qed_qs.tab_em_frac_min`` (`float`): minimum value to be considered for the second axis of lookup table 2 * ``qed_bw.save_table_in`` (`string`): where to save the lookup table @@ -1744,12 +1887,18 @@ Lookup tables store pre-computed values for functions used by the QED modules. * ``qed_qs.load_table_from`` (`string`): name of the lookup table file to read from. -* ``qed_qs.photon_creation_energy_threshold`` (`float`) optional (default `2*me*c^2`) - Energy threshold for photon particle creation in SI units. +* ``qed_bw.chi_min`` (`float`): minimum chi parameter to be considered by the Breit-Wheeler engine + (suggested value : 0.01) + +* ``qed_qs.chi_min`` (`float`): minimum chi parameter to be considered by the Quantum Synchrotron engine + (suggested value : 0.001) + +* ``qed_qs.photon_creation_energy_threshold`` (`float`) optional (default `2`) + Energy threshold for photon particle creation in `*me*c^2` units. * ``warpx.do_qed_schwinger`` (`bool`) optional (default `0`) If this is 1, Schwinger electron-positron pairs can be generated in vacuum in the cells where the EM field is high enough. - Activating the Schwinger process requires the code to be compiled with ``QED=TRUE`` and ``PICSAR`` on the branch ``QED``. + Activating the Schwinger process requires the code to be compiled with ``QED=TRUE`` and ``PICSAR``. If ``warpx.do_qed_schwinger = 1``, Schwinger product species must be specified with ``qed_schwinger.ele_product_species`` and ``qed_schwinger.pos_product_species``. **Note: implementation of this feature is in progress.** diff --git a/Docs/source/running_cpp/platforms.rst b/Docs/source/running_cpp/platforms.rst index 2a3ead3817a..925eacccd00 100644 --- a/Docs/source/running_cpp/platforms.rst +++ b/Docs/source/running_cpp/platforms.rst @@ -131,3 +131,61 @@ parameters provided good performance: * **Sixteen `64x64x64` grids per MPI rank** (with default tiling in WarpX, this results in ~49 tiles per OpenMP thread) + +.. _running-cpp-lassen: + +Running on Lassen at LLNL +------------------------- + +.. _running-cpp-lassen-V100-GPUs: + +V100 GPUs +^^^^^^^^^ + +The batch script below can be used to run a WarpX simulation on 2 nodes on the supercomputer Lassen at LLNL. +Replace descriptions between chevrons ``<>`` by relevant values, for instance ```` could be ``plasma_mirror_inputs``. +Note that the only option so far is to run with one MPI rank per GPU. + +.. literalinclude:: ../../../Tools/BatchScripts/batch_lassen.sh + :language: bash + +To run a simulation, copy the lines above to a file ``batch_lassen.sh`` and run +:: + + bsub batch_lassen.sh + +to submit the job. + +For a 3D simulation with a few (1-4) particles per cell using FDTD Maxwell +solver on V100 GPUs for a well load-balanced problem (in our case laser +wakefield acceleration simulation in a boosted frame in the quasi-linear +regime), the following set of parameters provided good performance: + +* ``amr.max_grid_size=256`` and ``amr.blocking_factor=128``. + +* **One MPI rank per GPU** (e.g., 4 MPI ranks for the 4 GPUs on each Lassen + node) + +* **Two `128x128x128` grids per GPU**, or **one `128x128x256` grid per GPU**. + + +Running on Quartz at LLNL +------------------------- + +.. _running-cpp-quartz-CPUs: + +Intel Xeon E5-2695 v4 CPUs +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The batch script below can be used to run a WarpX simulation on 2 nodes on the supercomputer Quartz at LLNL. +Replace descriptions between chevrons ``<>`` by relevant values, for instance ```` could be ``plasma_mirror_inputs``. + +.. literalinclude:: ../../../Tools/BatchScripts/batch_quartz.sh + :language: bash + +To run a simulation, copy the lines above to a file ``batch_quartz.sh`` and run +:: + + sbatch batch_quartz.sh + +to submit the job. diff --git a/Docs/spack.yaml b/Docs/spack.yaml new file mode 100644 index 00000000000..434d0820929 --- /dev/null +++ b/Docs/spack.yaml @@ -0,0 +1,26 @@ +# This is a Spack environment file. +# +# This environment can be used to install all dependencies to build the manual +# locally. +# +# Activating and installing this environment will provide all dependencies +# that are needed for full-feature development. +# https://spack.readthedocs.io/en/latest/environments.html#anonymous-environments +# +# Inside WarpX' Docs/ directory: +# spack env activate -d . +# spack install # only needed the first time +# make clean +# make html +# +spack: + specs: + # note: the spec "doxygen+graphviz" causes an environment issue for me + - doxygen + - graphviz + - python + - py-sphinx + - py-breathe + - py-recommonmark + - py-pygments + - py-sphinx-rtd-theme diff --git a/Examples/Modules/qed/breit_wheeler/analysis.py b/Examples/Modules/qed/breit_wheeler/analysis.py new file mode 100755 index 00000000000..280e8e2a85f --- /dev/null +++ b/Examples/Modules/qed/breit_wheeler/analysis.py @@ -0,0 +1,298 @@ +#! /usr/bin/env python + +# Copyright 2019 Luca Fedeli, Maxence Thevenet +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + +# -*- coding: utf-8 -*- + +import yt +import numpy as np +import sys +import scipy.special as spe +import scipy.integrate as integ +import scipy.stats as st +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +import matplotlib.pyplot as plt + +# This script performs detailed checks of the Breit-Wheeler pair production process. +# Four populations of photons are initialized with different momenta in different +# directions in a background EM field (with non-zero components along each direction). +# Specifically the script checks that: +# +# - The expected number of generated pairs n_pairs is in agreement with theory +# (the maximum tolerated error is 5*sqrt(n_pairs) +# - The weight of the generated particles is equal to the weight of the photon +# - Momenta of the residual photons are still equal to the original momentum +# - The generated particles are emitted in the right direction +# - Total energy is conserved in each event +# - The energy distribution of the generated particles is in agreement with theory +# - The optical depths of the product species are correctly initialized (QED effects are +# enabled for product species too). +# +# More details on the theoretical formulas used in this script can be found in +# the Jupyter notebook picsar/src/multi_physics/QED_tests/validation/validation.ipynb +# +# References: +# 1) R. Duclous et al 2011 Plasma Phys. Control. Fusion 53 015009 +# 2) A. Gonoskov et al. 2015 Phys. Rev. E 92, 023305 +# 3) M. Lobet. PhD thesis "Effets radiatifs et d'electrodynamique +# quantique dans l'interaction laser-matiere ultra-relativiste" +# URL: https://tel.archives-ouvertes.fr/tel-01314224 + + +# Tolerances +tol = 1.e-8 +tol_red = 2.e-2 + +# Physical constants (from CODATA 2018, see: https://physics.nist.gov/cuu/Constants/index.html ) +me = 9.1093837015e-31 #electron mass +c = 299792458 #speed of light +hbar = 6.62607015e-34/(2*np.pi) #reduced Plank constant +fine_structure = 7.2973525693e-3 #fine structure constant +qe = 1.602176634e-19#elementary charge +E_s = (me**2 * c**3)/(qe * hbar) #Schwinger E field +B_s = E_s/c #Schwinger B field + +mec = me*c +mec2 = mec*c +#______________ + +# Initial parameters +spec_names_phot = ["p1", "p2", "p3", "p4"] +spec_names_ele = ["ele1", "ele2", "ele3", "ele4"] +spec_names_pos = ["pos1", "pos2", "pos3", "pos4"] +initial_momenta = [ + np.array([2000.0,0,0])*mec, + np.array([0.0,5000.0,0.0])*mec, + np.array([0.0,0.0,10000.0])*mec, + np.array([57735.02691896, 57735.02691896, 57735.02691896])*mec +] +initial_particle_number = 1048576 + +E_f = np.array([-2433321316961438., 973328526784575., 1459992790176863.]) +B_f = np.array([2857142.85714286, 4285714.28571428, 8571428.57142857]) + +NNS = [128,128,128,128] #bins for energy distribution comparison. +#______________ + +def calc_chi_gamma(p, E, B): + pnorm = np.linalg.norm(p) + v = c*(p/pnorm) + EpvvecB = E + np.cross(v,B) + vdotEoverc = np.dot(v,E)/c + ff = np.sqrt(np.dot(EpvvecB,EpvvecB) - np.dot(vdotEoverc,vdotEoverc)) + gamma_phot = pnorm/mec + return gamma_phot*ff/E_s + +#Auxiliary functions +@np.vectorize +def BW_inner(x): + return integ.quad(lambda s: np.sqrt(s)*spe.kv(1./3., 2./3. * s**(3./2.)), x, np.inf)[0] + +def BW_X(chi_phot, chi_ele): + div = (chi_ele*(chi_phot-chi_ele)) + div = np.where(np.logical_and(chi_phot > chi_ele, chi_ele != 0), div, 1.0); + res = np.where(np.logical_and(chi_phot > chi_ele, chi_ele != 0), np.power(chi_phot/div, 2./3.), np.inf) + return res + +def BW_F(chi_phot, chi_ele): + X = BW_X(chi_phot, chi_ele) + res = np.where(np.logical_or(chi_phot == chi_ele, chi_ele == 0), 0.0, + BW_inner(X) - (2.0 - chi_phot* X**(3./2.))*spe.kv(2./3., 2./3. * X**(3./2.)) ) + return res + +@np.vectorize +def BW_T(chi_phot): + coeff = 1./(np.pi * np.sqrt(3.) * (chi_phot**2)) + return coeff*integ.quad(lambda chi_ele: BW_F(chi_phot, chi_ele), 0, chi_phot)[0] + +def small_diff(vv, val): + if(val != 0.0): + return np.max(np.abs((vv - val)/val)) < tol + else: + return np.max(np.abs(vv)) < tol +#__________________ + +# Breit-Wheeler total and differential cross sections +def BW_dN_dt(chi_phot, gamma_phot): + coeff_BW = fine_structure * me*c**2/hbar + return coeff_BW*BW_T(chi_phot)*(chi_phot/gamma_phot) + +def BW_d2N_dt_dchi(chi_phot, gamma_phot, chi_ele): + coeff_BW = fine_structure * me*c**2/hbar + return coeff_BW*BW_F(chi_phot, chi_ele)*(gamma_phot/gamma_phot) +#__________________ + +# Get data for a species +def get_spec(ytdata, specname, is_photon): + px = ytdata[specname,"particle_momentum_x"].v + pz = ytdata[specname,"particle_momentum_z"].v + py = ytdata[specname,"particle_momentum_y"].v + + w = ytdata[specname,"particle_weighting"].v + + if (is_photon): + opt = ytdata[specname,"particle_optical_depth_BW"].v + else: + opt = ytdata[specname,"particle_optical_depth_QSR"].v + + return {"px" : px, "py" : py, "pz" : pz, "w" : w, "opt" : opt} + +# Individual tests + +def check_number_of_pairs(ytdataset, phot_name, ele_name, pos_name, chi_phot, gamma_phot, dt, particle_number): + dNBW_dt_theo = BW_dN_dt(chi_phot, gamma_phot) + expected_pairs = (1.-np.exp(-dNBW_dt_theo*dt))*particle_number + expected_pairs_tolerance = 5.0*np.sqrt(expected_pairs) + n_ele = ytdataset.particle_type_counts[ele_name] + n_pos = ytdataset.particle_type_counts[pos_name] + n_phot = ytdataset.particle_type_counts[phot_name] + n_lost = initial_particle_number-n_phot + assert((n_ele == n_pos) and (n_ele == n_lost)) + assert( np.abs(n_ele-expected_pairs) < expected_pairs_tolerance) + print(" [OK] generated pair number is within expectations") + return n_lost + +def check_weights(phot_data, ele_data, pos_data): + assert(np.all(phot_data["w"] == phot_data["w"][0])) + assert(np.all(ele_data["w"] == phot_data["w"][0])) + assert(np.all(pos_data["w"] == phot_data["w"][0])) + print(" [OK] particles weights are the expected ones") + +def check_momenta(phot_data, ele_data, pos_data, p0, p_ele, p_pos): + assert(small_diff(phot_data["px"], p0[0])) + assert(small_diff(phot_data["py"], p0[1])) + assert(small_diff(phot_data["pz"], p0[2])) + print(" [OK] residual photons still have initial momentum") + + pdir = p0/np.linalg.norm(p0) + assert(small_diff(ele_data["px"]/p_ele, pdir[0])) + assert(small_diff(ele_data["py"]/p_ele, pdir[1])) + assert(small_diff(ele_data["pz"]/p_ele, pdir[2])) + assert(small_diff(pos_data["px"]/p_pos, pdir[0])) + assert(small_diff(pos_data["py"]/p_pos, pdir[1])) + assert(small_diff(pos_data["pz"]/p_pos, pdir[2])) + print(" [OK] pairs move along the initial photon direction") + +def check_energy(energy_phot, energy_ele, energy_pos): + # Sorting the arrays is required because electrons and positrons are not + # necessarily dumped in the same order. + s_energy_ele = np.sort(energy_ele) + is_energy_pos = np.sort(energy_pos)[::-1] + product_energy = s_energy_ele + is_energy_pos + assert(small_diff(product_energy, energy_phot)) + print(" [OK] energy is conserved in each event") + +def check_opt_depths(phot_data, ele_data, pos_data): + data = (phot_data, ele_data, pos_data) + for dd in data: + loc, scale = st.expon.fit(dd["opt"]) + assert( np.abs(loc - 0) < tol_red ) + assert( np.abs(scale - 1) < tol_red ) + print(" [OK] optical depth distributions are still exponential") + +def check_energy_distrib(energy_ele, energy_pos, gamma_phot, + chi_phot, n_lost, NN, idx, do_plot=False): + gamma_min = 1.0001 + gamma_max = gamma_phot-1.0001 + h_gamma_ele, c_gamma = np.histogram(energy_ele/mec2, bins=NN, range=[gamma_min,gamma_max]) + h_gamma_pos, _ = np.histogram(energy_pos/mec2, bins=NN, range=[gamma_min,gamma_max]) + + cchi_part_min = chi_phot*(gamma_min - 1)/(gamma_phot - 2) + cchi_part_max = chi_phot*(gamma_max - 1)/(gamma_phot - 2) + + #Rudimentary integration over npoints for each bin + npoints= 20 + aux_chi = np.linspace(cchi_part_min, cchi_part_max, NN*npoints) + distrib = BW_d2N_dt_dchi(chi_phot, gamma_phot, aux_chi) + distrib = np.sum(distrib.reshape(-1, npoints),1) + distrib = n_lost*distrib/np.sum(distrib) + + if do_plot : + # Visual comparison of distributions + c_gamma_centered = 0.5*(c_gamma[1:]+c_gamma[:-1]) + plt.clf() + plt.xlabel("γ_particle") + plt.ylabel("N") + plt.title("χ_photon = {:f}".format(chi_phot)) + plt.plot(c_gamma_centered, distrib,label="theory") + plt.plot(c_gamma_centered, h_gamma_ele,label="BW electrons") + plt.plot(c_gamma_centered, h_gamma_pos,label="BW positrons") + plt.legend() + plt.savefig("case_{:d}".format(idx+1)) + + discr_ele = np.abs(h_gamma_ele-distrib) + discr_pos = np.abs(h_gamma_pos-distrib) + max_discr = 5.0 * np.sqrt(distrib) + assert(np.all(discr_ele < max_discr)) + assert(np.all(discr_pos < max_discr)) + print(" [OK] energy distribution is within expectations") + +#__________________ + +def check(): + filename_end = sys.argv[1] + data_set_end = yt.load(filename_end) + + sim_time = data_set_end.current_time.to_value() + all_data_end = data_set_end.all_data() + + for idx in range(4): + phot_name = spec_names_phot[idx] + ele_name = spec_names_ele[idx] + pos_name = spec_names_pos[idx] + p0 = initial_momenta[idx] + + p2_phot = p0[0]**2 + p0[1]**2 + p0[2]**2 + p_phot = np.sqrt(p2_phot) + energy_phot = p_phot*c + chi_phot = calc_chi_gamma(p0, E_f, B_f) + gamma_phot = np.linalg.norm(p0)/mec + + print("** Case {:d} **".format(idx+1)) + print(" initial momentum: ", p0) + print(" quantum parameter: {:f}".format(chi_phot)) + print(" normalized photon energy: {:f}".format(gamma_phot)) + print(" timestep: {:f} fs".format(sim_time*1e15)) + + phot_data = get_spec(all_data_end, phot_name, is_photon=True) + ele_data = get_spec(all_data_end, ele_name, is_photon=False) + pos_data = get_spec(all_data_end, pos_name, is_photon=False) + + p2_ele = ele_data["px"]**2 + ele_data["py"]**2 + ele_data["pz"]**2 + p_ele = np.sqrt(p2_ele) + energy_ele = np.sqrt(1.0 + p2_ele/mec**2 )*mec2 + p2_pos = pos_data["px"]**2 + pos_data["py"]**2 + pos_data["pz"]**2 + p_pos = np.sqrt(p2_pos) + energy_pos = np.sqrt(1.0 + p2_pos/mec**2 )*mec2 + + n_lost = check_number_of_pairs(data_set_end, + phot_name, ele_name, pos_name, + chi_phot, gamma_phot, sim_time, + initial_particle_number) + + check_weights(phot_data, ele_data, pos_data) + + check_momenta(phot_data, ele_data, pos_data, p0, p_ele, p_pos) + + check_energy(energy_phot, energy_ele, energy_pos) + + check_energy_distrib(energy_ele, energy_pos, gamma_phot, chi_phot, n_lost, NNS[idx], idx) + + check_opt_depths(phot_data, ele_data, pos_data) + + print("*************\n") + + test_name = filename_end[:-9] # Could also be os.path.split(os.getcwd())[1] + checksumAPI.evaluate_checksum(test_name, filename_end) + +def main(): + check() + +if __name__ == "__main__": + main() diff --git a/Examples/Modules/qed/breit_wheeler/analysis_2d_tau_init.py b/Examples/Modules/qed/breit_wheeler/analysis_2d_tau_init.py deleted file mode 100755 index d4b7666bd16..00000000000 --- a/Examples/Modules/qed/breit_wheeler/analysis_2d_tau_init.py +++ /dev/null @@ -1,50 +0,0 @@ -#! /usr/bin/env python - -# Copyright 2019 Luca Fedeli, Maxence Thevenet -# -# This file is part of WarpX. -# -# License: BSD-3-Clause-LBNL - -import yt -import numpy as np -import scipy.stats as st -import sys -sys.path.insert(1, '../../../../warpx/Regression/Checksum/') -import checksumAPI - -# This script checks if photons initialized with Breit Wheeler process enabled -# do actually have an exponentially distributed optical depth - -# Tolerance -tolerance_rel = 1e-2 - -def check(): - filename = sys.argv[1] - data_set = yt.load(filename) - - all_data = data_set.all_data() - res_tau = all_data["photons", 'particle_optical_depth_BW'] - - loc, scale = st.expon.fit(res_tau) - - # loc should be very close to 0, scale should be very close to 1 - - error_rel = np.abs(loc - 0) - print("error_rel for location: " + str(error_rel)) - print("tolerance_rel: " + str(tolerance_rel)) - assert( error_rel < tolerance_rel ) - - error_rel = np.abs(scale - 1) - print("error_rel for scale: " + str(error_rel)) - print("tolerance_rel: " + str(tolerance_rel)) - assert( error_rel < tolerance_rel ) - - test_name = filename[:-9] # Could also be os.path.split(os.getcwd())[1] - checksumAPI.evaluate_checksum(test_name, filename) - -def main(): - check() - -if __name__ == "__main__": - main() diff --git a/Examples/Modules/qed/breit_wheeler/analysis_3d_optical_depth_evolution.py b/Examples/Modules/qed/breit_wheeler/analysis_3d_optical_depth_evolution.py deleted file mode 100755 index 876b6eac40b..00000000000 --- a/Examples/Modules/qed/breit_wheeler/analysis_3d_optical_depth_evolution.py +++ /dev/null @@ -1,145 +0,0 @@ -#! /usr/bin/env python - -# Copyright 2019 Luca Fedeli, Maxence Thevenet -# -# This file is part of WarpX. -# -# License: BSD-3-Clause-LBNL - -# -*- coding: utf-8 -*- - -import yt -import numpy as np -import sys -import math as m -import scipy.special as spe -import scipy.integrate as integ -import scipy.stats as st -sys.path.insert(1, '../../../../warpx/Regression/Checksum/') -import checksumAPI - -# This script checks if the optical depth of photons undergoing the -# Breit Wheeler process behaves as expected. Four populations of photons -# are initialized with different momenta in a background EM field. The decrease -# of the optical depth (averaged for each population) is used to estimate the -# Breit Wheeler cross section, which is then compared with the theoretical -# formula. Relative discrepancy should be smaller than 2% -# -# References: -# 1) R. Duclous et al 2011 Plasma Phys. Control. Fusion 53 015009 -# 2) A. Gonoskov et al. 2015 Phys. Rev. E 92, 023305 -# 3) M. Lobet. PhD thesis "Effets radiatifs et d'electrodynamique -# quantique dans l'interaction laser-matiere ultra-relativiste" -# URL: https://tel.archives-ouvertes.fr/tel-01314224 - - -# Tolerance -tol = 7.e-2 - -# EM fields -E_f = np.array([-2433321316961438, 973328526784575, 1459992790176863]) -B_f = np.array([2857142.85714286, 4285714.28571428, 8571428.57142857]) - -# Physical constants -electron_mass = 9.10938356e-31 -elementary_charge = 1.6021766208e-19 -speed_of_light = 299792458 -reduced_plank = 1.054571800e-34 -vacuum_permittivity = 8.854187817e-12 -fine_structure_constant = 0.0072973525664 -classical_elec_radius = (1./4./np.pi/vacuum_permittivity)*( elementary_charge**2 / (electron_mass * speed_of_light**2)) -lambda_ref = 1.0e-6 -field_reference = 2.0 * np.pi * electron_mass*speed_of_light * speed_of_light / (elementary_charge*lambda_ref) -schwinger_field_SI = electron_mass**2 * speed_of_light**3/(reduced_plank*elementary_charge) -schwinger_field_norm = electron_mass*speed_of_light*lambda_ref/(2.0*reduced_plank*m.pi) -#______________ - -# Initial parameters -spec_names = ["p1", "p2", "p3", "p4"] -mec = electron_mass*speed_of_light -p_begin = { - "p1": np.array([2000.0,0,0])*mec, - "p2": np.array([0.0,5000.0,0.0])*mec, - "p3": np.array([0.0,0.0,10000.0])*mec, - "p4": np.array([57735.02691896, 57735.02691896, 57735.02691896])*mec -} -initial_particle_number = 16384 -#______________ - -def calc_chi_gamma(p, E, B): - p = p / electron_mass / speed_of_light - E = E / field_reference - B = B * speed_of_light / field_reference - gamma_phot = np.linalg.norm(p) - c = p/gamma_phot - loc_field = gamma_phot * np.linalg.norm( E - np.dot(c,E)*c + np.cross(c,B)) - return loc_field/schwinger_field_norm - -#Auxiliary functions -def X(chi_phot, chi_ele): - if (chi_phot > chi_ele and chi_ele != 0): - return np.power(chi_phot/(chi_ele*(chi_phot-chi_ele)), 2./3.) - else: - return 1.0e30 - -def T(chi_phot): - coeff = 1./(np.pi * np.sqrt(3.) * chi_phot * chi_phot) - inner = lambda x : integ.quad(lambda s: np.sqrt(s)*spe.kv(1./3., 2./3. * s**(3./2.)), x, np.inf)[0] - return integ.quad(lambda chi_ele: - coeff*(inner(X(chi_phot, chi_ele)) - - (2.0 - chi_phot*np.power(X(chi_phot, chi_ele), 3./2.))*spe.kv(2./3., 2./3. *X(chi_phot, chi_ele)**(3./2.)) ) - , 0, chi_phot)[0] -#__________________ - -# Breit-Wheeler total cross section -def dNBW_dt(chi_phot, energy_phot): - energy_phot = energy_phot/electron_mass/speed_of_light/speed_of_light - return ((electron_mass*(speed_of_light)**2)*fine_structure_constant/reduced_plank)*(chi_phot/energy_phot)*T(chi_phot) -#__________________ - -def check(): - filename_end = sys.argv[1] - data_set_end = yt.load(filename_end) - - sim_time = data_set_end.current_time.to_value() - all_data_end = data_set_end.all_data() - - for name in spec_names: - opt_depth = all_data_end[name, 'particle_optical_depth_BW'] - - #check that the distribution is still exponential with scale 1 and loc 0 - opt_depth_loc, opt_depth_scale = st.expon.fit(opt_depth) - exp_loc = 0.0 - exp_scale = 1.0 - loc_discrepancy = np.abs(opt_depth_loc-exp_loc) - scale_discrepancy = np.abs(opt_depth_scale-exp_scale) - print("tolerance_rel: " + str(tol)) - print("species " + name) - print("exp distrib loc tol = " + str(tol)) - print("exp distrib loc discrepancy = " + str(loc_discrepancy)) - assert(loc_discrepancy < tol) - print("exp distrib scale tol = " + str(tol)) - print("exp distrib scale discrepancy = " + str(scale_discrepancy/exp_scale)) - assert(scale_discrepancy/exp_scale < tol) - ### - - #check if number of lost photons is (n0* (1 - exp(-rate*t)) ) - dNBW_dt_theo = dNBW_dt( - calc_chi_gamma(p_begin[name], E_f, B_f), - np.linalg.norm(p_begin[name]*speed_of_light)) - exp_lost= initial_particle_number*(1.0 - np.exp(-dNBW_dt_theo*sim_time)) - lost = initial_particle_number-np.size(opt_depth) - discrepancy_lost = np.abs(exp_lost-lost) - print("lost fraction tol = " + str(tol)) - print("lost fraction discrepancy = " + str(discrepancy_lost/exp_lost)) - assert(discrepancy_lost/exp_lost < tol) - ### - - test_name = filename_end[:-9] # Could also be os.path.split(os.getcwd())[1] - checksumAPI.evaluate_checksum(test_name, filename_end) - -def main(): - check() - -if __name__ == "__main__": - main() diff --git a/Examples/Modules/qed/breit_wheeler/inputs_2d b/Examples/Modules/qed/breit_wheeler/inputs_2d new file mode 100644 index 00000000000..2fa4690e11e --- /dev/null +++ b/Examples/Modules/qed/breit_wheeler/inputs_2d @@ -0,0 +1,266 @@ +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 1 +amr.n_cell = 32 32 +amr.max_grid_size = 16 # maximum size of each AMReX box, used to decompose the domain +amr.blocking_factor = 8 # minimum size of each AMReX box, used to decompose the domain +geometry.coord_sys = 0 # 0: Cartesian +geometry.is_periodic = 1 1 1 # Is periodic? +geometry.prob_lo = -0.5e-6 -0.5e-6 # physical domain +geometry.prob_hi = 0.5e-6 0.5e-6 +amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) + +################################# +############ NUMERICS ########### +################################# +algo.current_deposition = esirkepov +algo.charge_deposition = standard +algo.field_gathering = energy-conserving +algo.particle_pusher = boris +interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z +interpolation.noy = 3 +interpolation.noz = 3 +warpx.verbose = 1 +warpx.do_dive_cleaning = 0 +warpx.use_filter = 1 +warpx.cfl = 1. # if 1., the time step is set to its CFL limit +warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition +warpx.serialize_ics = 1 + +################################# +############ PLASMA ############# +################################# +particles.species_names = p1 p2 p3 p4 ele1 ele2 ele3 ele4 pos1 pos2 pos3 pos4 dummy_phot +particles.photon_species = p1 p2 p3 p4 dummy_phot +################################# + +p1.species_type = "photon" +p1.injection_style = "NUniformPerCell" +p1.profile = "constant" +p1.num_particles_per_cell_each_dim = 32 32 +p1.density = 1e19 +p1.profile = "constant" +p1.momentum_distribution_type = "gaussian" +p1.ux_m = 2000.0 +##########QED#################### +p1.do_qed = 1 +p1.do_qed_breit_wheeler = 1 +p1.qed_breit_wheeler_ele_product_species = ele1 +p1.qed_breit_wheeler_pos_product_species = pos1 +################################# + +p2.species_type = "photon" +p2.injection_style = "NUniformPerCell" +p2.profile = "constant" +p2.num_particles_per_cell_each_dim = 32 32 +p2.density = 1e19 +p2.profile = "constant" +p2.momentum_distribution_type = "gaussian" +p2.uy_m = 5000.0 +##########QED#################### +p2.do_qed = 1 +p2.do_qed_breit_wheeler = 1 +p2.qed_breit_wheeler_ele_product_species = ele2 +p2.qed_breit_wheeler_pos_product_species = pos2 +################################# + +p3.species_type = "photon" +p3.injection_style = "NUniformPerCell" +p3.profile = "constant" +p3.num_particles_per_cell_each_dim = 32 32 +p3.density = 1e19 +p3.profile = "constant" +p3.momentum_distribution_type = "gaussian" +p3.uz_m = 10000.0 +##########QED#################### +p3.do_qed = 1 +p3.do_qed_breit_wheeler = 1 +p3.qed_breit_wheeler_ele_product_species = ele3 +p3.qed_breit_wheeler_pos_product_species = pos3 +################################# + +p4.species_type = "photon" +p4.injection_style = "NUniformPerCell" +p4.profile = "constant" +p4.num_particles_per_cell_each_dim = 32 32 +p4.density = 1e19 +p4.profile = "constant" +p4.momentum_distribution_type = "gaussian" +p4.ux_m = 57735.02691896 +p4.uy_m = 57735.02691896 +p4.uz_m = 57735.02691896 +##########QED#################### +p4.do_qed = 1 +p4.do_qed_breit_wheeler = 1 +p4.qed_breit_wheeler_ele_product_species = ele4 +p4.qed_breit_wheeler_pos_product_species = pos4 +################################# + +### PRODUCT SPECIES ### +ele1.species_type = "electron" +ele1.injection_style = nuniformpercell +ele1.num_particles_per_cell_each_dim = 0 0 +ele1.profile = constant +ele1.density = 0.0 +ele1.momentum_distribution_type = "gaussian" +ele1.do_not_push = 1 +ele1.do_qed = 1 +ele1.do_qed_quantum_sync = 1 +ele1.qed_quantum_sync_phot_product_species = dummy_phot + +ele2.species_type = "electron" +ele2.injection_style = nuniformpercell +ele2.num_particles_per_cell_each_dim = 1 1 +ele2.profile = constant +ele2.density = 0.0 +ele2.momentum_distribution_type = "gaussian" +ele2.do_not_push = 1 +ele2.do_qed = 1 +ele2.do_qed_quantum_sync = 1 +ele2.qed_quantum_sync_phot_product_species = dummy_phot + +ele3.species_type = "electron" +ele3.injection_style = nuniformpercell +ele3.num_particles_per_cell_each_dim = 1 1 +ele3.profile = constant +ele3.density = 0.0 +ele3.momentum_distribution_type = "gaussian" +ele3.do_not_push = 1 +ele3.do_qed = 1 +ele3.do_qed_quantum_sync = 1 +ele3.qed_quantum_sync_phot_product_species = dummy_phot + +ele4.species_type = "electron" +ele4.injection_style = nuniformpercell +ele4.num_particles_per_cell_each_dim = 1 1 +ele4.profile = constant +ele4.density = 0.0 +ele4.momentum_distribution_type = "gaussian" +ele4.do_not_push = 1 +ele4.do_qed = 1 +ele4.do_qed_quantum_sync = 1 +ele4.qed_quantum_sync_phot_product_species = dummy_phot + +pos1.species_type = "positron" +pos1.injection_style = nuniformpercell +pos1.num_particles_per_cell_each_dim = 0 0 +pos1.profile = constant +pos1.density = 0.0 +pos1.momentum_distribution_type = "gaussian" +pos1.do_not_push = 1 +pos1.do_qed = 1 +pos1.do_qed_quantum_sync = 1 +pos1.qed_quantum_sync_phot_product_species = dummy_phot + +pos2.species_type = "positron" +pos2.injection_style = nuniformpercell +pos2.num_particles_per_cell_each_dim = 0 0 +pos2.profile = constant +pos2.density = 0.0 +pos2.momentum_distribution_type = "gaussian" +pos2.do_not_push = 1 +pos2.do_qed = 1 +pos2.do_qed_quantum_sync = 1 +pos2.qed_quantum_sync_phot_product_species = dummy_phot + +pos3.species_type = "positron" +pos3.injection_style = nuniformpercell +pos3.num_particles_per_cell_each_dim = 0 0 +pos3.profile = constant +pos3.density = 0.0 +pos3.momentum_distribution_type = "gaussian" +pos3.do_not_push = 1 +pos3.do_qed = 1 +pos3.do_qed_quantum_sync = 1 +pos3.qed_quantum_sync_phot_product_species = dummy_phot + +pos4.species_type = "positron" +pos4.injection_style = nuniformpercell +pos4.num_particles_per_cell_each_dim = 0 0 +pos4.profile = constant +pos4.density = 0.0 +pos4.momentum_distribution_type = "gaussian" +pos4.do_not_push = 1 +pos4.do_qed = 1 +pos4.do_qed_quantum_sync = 1 +pos4.qed_quantum_sync_phot_product_species = dummy_phot + +dummy_phot.species_type = "photon" +dummy_phot.injection_style = nuniformpercell +dummy_phot.num_particles_per_cell_each_dim = 0 0 +dummy_phot.profile = constant +dummy_phot.density = 0.0 +dummy_phot.momentum_distribution_type = "gaussian" +dummy_phot.do_qed = 0 + +################################# + +##########QED TABLES#################### +qed_bw.chi_min = 0.001 + +qed_bw.lookup_table_mode = "builtin" + +#qed_bw.lookup_table_mode = "generate" +#qed_bw.tab_dndt_chi_min = 0.01 +#qed_bw.tab_dndt_chi_max = 1000.0 +#qed_bw.tab_dndt_how_many = 256 +#qed_bw.tab_pair_chi_min = 0.01 +#qed_bw.tab_pair_chi_max = 1000.0 +#qed_bw.tab_pair_chi_how_many = 256 +#qed_bw.tab_pair_frac_how_many = 256 +#qed_bw.save_table_in = "bw_table" + +#qed_bw.lookup_table_mode = "load" +#qed_bw.load_table_from = "bw_table" + +qed_qs.chi_min = 0.001 + +qed_qs.lookup_table_mode = "builtin" + +qed_qs.photon_creation_energy_threshold = 2 + +#qed_qs.lookup_table_mode = "generate" +#qed_qs.tab_dndt_chi_min = 0.001 +#qed_qs.tab_dndt_chi_max = 1000.0 +#qed_qs.tab_dndt_how_many = 256 +#qed_qs.tab_em_chi_min = 0.001 +#qed_qs.tab_em_frac_min = 1.0e-12 +#qed_qs.tab_em_chi_max = 1000.0 +#qed_qs.tab_em_chi_how_many = 256 +#qed_qs.tab_em_frac_how_many = 256 +#qed_qs.save_table_in = "qs_table" + +#qed_qs.lookup_table_mode = "load" +#qed_qs.load_table_from = "qs_table" +################################# + +### EXTERNAL E FIELD ### (3e15 * [-0.811107105653813 0.324442842261525 0.486664263392288] ) +particles.E_ext_particle_init_style = "constant" +particles.E_external_particle = -2433321316961438 973328526784575 1459992790176863 +#### + +### EXTERNAL B FIELD ### (1e7 * [0.28571429 0.42857143 0.85714286] ) +particles.B_ext_particle_init_style = "constant" +particles.B_external_particle = 2857142.85714286 4285714.28571428 8571428.57142857 +#### + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.period = 1 +diag1.diag_type = Full +diag1.fields_to_plot = Ex +diag1.p1.variables = ux uy uz w +diag1.p2.variables = ux uy uz w +diag1.p3.variables = ux uy uz w +diag1.p4.variables = ux uy uz w + +diag1.ele1.variables = ux uy uz w +diag1.ele2.variables = ux uy uz w +diag1.ele3.variables = ux uy uz w +diag1.ele4.variables = ux uy uz w + +diag1.pos1.variables = ux uy uz w +diag1.pos2.variables = ux uy uz w +diag1.pos3.variables = ux uy uz w +diag1.pos4.variables = ux uy uz w diff --git a/Examples/Modules/qed/breit_wheeler/inputs_2d_tau_init b/Examples/Modules/qed/breit_wheeler/inputs_2d_tau_init deleted file mode 100644 index af77c97e0ef..00000000000 --- a/Examples/Modules/qed/breit_wheeler/inputs_2d_tau_init +++ /dev/null @@ -1,107 +0,0 @@ -################################# -####### GENERAL PARAMETERS ###### -################################# -max_step = 1 -amr.n_cell = 128 128 -amr.max_grid_size = 128 # maximum size of each AMReX box, used to decompose the domain -amr.blocking_factor = 32 # minimum size of each AMReX box, used to decompose the domain -geometry.coord_sys = 0 # 0: Cartesian -geometry.is_periodic = 0 0 # Is periodic? -geometry.prob_lo = -32.e-6 -32.e-6 # physical domain -geometry.prob_hi = 32.e-6 32.e-6 -amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) - -################################# -############ NUMERICS ########### -################################# -algo.current_deposition = esirkepov -algo.charge_deposition = standard -algo.field_gathering = energy-conserving -algo.particle_pusher = boris -interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z -interpolation.noy = 3 -interpolation.noz = 3 -warpx.verbose = 1 -warpx.do_dive_cleaning = 0 -warpx.use_filter = 1 -warpx.cfl = 1. # if 1., the time step is set to its CFL limit -warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition -warpx.serialize_ics = 1 - -################################# -############ PLASMA ############# -################################# -particles.species_names = photons ele_bw pos_bw -particles.photon_species = photons -################################# - -photons.species_type = "photon" -photons.injection_style = "NUniformPerCell" -photons.profile = "constant" -photons.xmin = -30e-6 -photons.ymin = -30e-6 -photons.zmin = -30e-6 -photons.xmax = 30e-6 -photons.ymax = 30e-6 -photons.zmax = 30e-6 -photons.num_particles_per_cell_each_dim = 2 2 -photons.density = 1e19 -photons.profile = "constant" -photons.momentum_distribution_type = "gaussian" -photons.ux_m = 0.0 -photons.uy_m = 0.0 -photons.uz_m = 0.0 -photons.ux_th = 100. -photons.uy_th = 100. -photons.uz_th = 100. -##########QED#################### -photons.do_qed = 1 -photons.do_qed_breit_wheeler = 1 -photons.qed_breit_wheeler_ele_product_species = ele_bw -photons.qed_breit_wheeler_pos_product_species = pos_bw -################################# - -### PRODUCT SPECIES ### -ele_bw.species_type = "electron" -ele_bw.injection_style = nuniformpercell -ele_bw.num_particles_per_cell_each_dim = 1 1 -ele_bw.profile = constant -ele_bw.density = 0.0 -ele_bw.momentum_distribution_type = "gaussian" -ele_bw.xmin = 1 ## Ugly trick to avoid electrons at T=0 -ele_bw.xmax = -1 ## Ugly trick to avoid electrons at T=0 -ele_bw.do_qed = 0 - -pos_bw.species_type = "positron" -pos_bw.injection_style = nuniformpercell -pos_bw.num_particles_per_cell_each_dim = 1 1 -pos_bw.profile = constant -pos_bw.density = 0.0 -pos_bw.momentum_distribution_type = "gaussian" -pos_bw.xmin = 1 ## Ugly trick to avoid positrons at T=0 -pos_bw.xmax = -1 ## Ugly trick to avoid positrons at T=0 -pos_bw.do_qed = 0 -################################# - -#######QED ENGINE PARAMETERS##### -qed_bw.lookup_table_mode = "dummy_builtin" - -#qed_bw.lookup_table_mode = "generate" -#qed_bw.chi_min = 0.001 -#qed_bw.tab_dndt_chi_min = 0.1 -#qed_bw.tab_dndt_chi_max = 200 -#qed_bw.tab_dndt_how_many = 64 -#qed_bw.tab_pair_chi_min = 0.01 -#qed_bw.tab_pair_chi_max = 200 -#qed_bw.tab_pair_chi_how_many = 2 -#qed_bw.tab_pair_frac_how_many = 2 -#qed_bw.save_table_in = "bw_micro_table" - -#qed_bw.lookup_table_mode = "load" -#qed_bw.load_table_from = "bw_micro_table" -################################# - -# Diagnostics -diagnostics.diags_names = diag1 -diag1.period = 1 -diag1.diag_type = Full diff --git a/Examples/Modules/qed/breit_wheeler/inputs_3d b/Examples/Modules/qed/breit_wheeler/inputs_3d new file mode 100644 index 00000000000..af479d7069a --- /dev/null +++ b/Examples/Modules/qed/breit_wheeler/inputs_3d @@ -0,0 +1,266 @@ +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 1 +amr.n_cell = 16 16 16 +amr.max_grid_size = 16 # maximum size of each AMReX box, used to decompose the domain +amr.blocking_factor = 8 # minimum size of each AMReX box, used to decompose the domain +geometry.coord_sys = 0 # 0: Cartesian +geometry.is_periodic = 1 1 1 # Is periodic? +geometry.prob_lo = -0.5e-6 -0.5e-6 -0.5e-6 # physical domain +geometry.prob_hi = 0.5e-6 0.5e-6 0.5e-6 +amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) + +################################# +############ NUMERICS ########### +################################# +algo.current_deposition = esirkepov +algo.charge_deposition = standard +algo.field_gathering = energy-conserving +algo.particle_pusher = boris +interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z +interpolation.noy = 3 +interpolation.noz = 3 +warpx.verbose = 1 +warpx.do_dive_cleaning = 0 +warpx.use_filter = 1 +warpx.cfl = 1. # if 1., the time step is set to its CFL limit +warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition +warpx.serialize_ics = 1 + +################################# +############ PLASMA ############# +################################# +particles.species_names = p1 p2 p3 p4 ele1 ele2 ele3 ele4 pos1 pos2 pos3 pos4 dummy_phot +particles.photon_species = p1 p2 p3 p4 dummy_phot +################################# + +p1.species_type = "photon" +p1.injection_style = "NUniformPerCell" +p1.profile = "constant" +p1.num_particles_per_cell_each_dim = 8 8 4 +p1.density = 1e19 +p1.profile = "constant" +p1.momentum_distribution_type = "gaussian" +p1.ux_m = 2000.0 +##########QED#################### +p1.do_qed = 1 +p1.do_qed_breit_wheeler = 1 +p1.qed_breit_wheeler_ele_product_species = ele1 +p1.qed_breit_wheeler_pos_product_species = pos1 +################################# + +p2.species_type = "photon" +p2.injection_style = "NUniformPerCell" +p2.profile = "constant" +p2.num_particles_per_cell_each_dim = 8 8 4 +p2.density = 1e19 +p2.profile = "constant" +p2.momentum_distribution_type = "gaussian" +p2.uy_m = 5000.0 +##########QED#################### +p2.do_qed = 1 +p2.do_qed_breit_wheeler = 1 +p2.qed_breit_wheeler_ele_product_species = ele2 +p2.qed_breit_wheeler_pos_product_species = pos2 +################################# + +p3.species_type = "photon" +p3.injection_style = "NUniformPerCell" +p3.profile = "constant" +p3.num_particles_per_cell_each_dim = 8 8 4 +p3.density = 1e19 +p3.profile = "constant" +p3.momentum_distribution_type = "gaussian" +p3.uz_m = 10000.0 +##########QED#################### +p3.do_qed = 1 +p3.do_qed_breit_wheeler = 1 +p3.qed_breit_wheeler_ele_product_species = ele3 +p3.qed_breit_wheeler_pos_product_species = pos3 +################################# + +p4.species_type = "photon" +p4.injection_style = "NUniformPerCell" +p4.profile = "constant" +p4.num_particles_per_cell_each_dim = 8 8 4 +p4.density = 1e19 +p4.profile = "constant" +p4.momentum_distribution_type = "gaussian" +p4.ux_m = 57735.02691896 +p4.uy_m = 57735.02691896 +p4.uz_m = 57735.02691896 +##########QED#################### +p4.do_qed = 1 +p4.do_qed_breit_wheeler = 1 +p4.qed_breit_wheeler_ele_product_species = ele4 +p4.qed_breit_wheeler_pos_product_species = pos4 +################################# + +### PRODUCT SPECIES ### +ele1.species_type = "electron" +ele1.injection_style = nuniformpercell +ele1.num_particles_per_cell_each_dim = 0 0 +ele1.profile = constant +ele1.density = 0.0 +ele1.momentum_distribution_type = "gaussian" +ele1.do_not_push = 1 +ele1.do_qed = 1 +ele1.do_qed_quantum_sync = 1 +ele1.qed_quantum_sync_phot_product_species = dummy_phot + +ele2.species_type = "electron" +ele2.injection_style = nuniformpercell +ele2.num_particles_per_cell_each_dim = 0 0 +ele2.profile = constant +ele2.density = 0.0 +ele2.momentum_distribution_type = "gaussian" +ele2.do_not_push = 1 +ele2.do_qed = 1 +ele2.do_qed_quantum_sync = 1 +ele2.qed_quantum_sync_phot_product_species = dummy_phot + +ele3.species_type = "electron" +ele3.injection_style = nuniformpercell +ele3.num_particles_per_cell_each_dim = 0 0 +ele3.profile = constant +ele3.density = 0.0 +ele3.momentum_distribution_type = "gaussian" +ele3.do_not_push = 1 +ele3.do_qed = 1 +ele3.do_qed_quantum_sync = 1 +ele3.qed_quantum_sync_phot_product_species = dummy_phot + +ele4.species_type = "electron" +ele4.injection_style = nuniformpercell +ele4.num_particles_per_cell_each_dim = 0 0 +ele4.profile = constant +ele4.density = 0.0 +ele4.momentum_distribution_type = "gaussian" +ele4.do_not_push = 1 +ele4.do_qed = 1 +ele4.do_qed_quantum_sync = 1 +ele4.qed_quantum_sync_phot_product_species = dummy_phot + +pos1.species_type = "positron" +pos1.injection_style = nuniformpercell +pos1.num_particles_per_cell_each_dim = 0 0 +pos1.profile = constant +pos1.density = 0.0 +pos1.momentum_distribution_type = "gaussian" +pos1.do_not_push = 1 +pos1.do_qed = 1 +pos1.do_qed_quantum_sync = 1 +pos1.qed_quantum_sync_phot_product_species = dummy_phot + +pos2.species_type = "positron" +pos2.injection_style = nuniformpercell +pos2.num_particles_per_cell_each_dim = 0 0 +pos2.profile = constant +pos2.density = 0.0 +pos2.momentum_distribution_type = "gaussian" +pos2.do_not_push = 1 +pos2.do_qed = 1 +pos2.do_qed_quantum_sync = 1 +pos2.qed_quantum_sync_phot_product_species = dummy_phot + +pos3.species_type = "positron" +pos3.injection_style = nuniformpercell +pos3.num_particles_per_cell_each_dim = 0 0 +pos3.profile = constant +pos3.density = 0.0 +pos3.momentum_distribution_type = "gaussian" +pos3.do_not_push = 1 +pos3.do_qed = 1 +pos3.do_qed_quantum_sync = 1 +pos3.qed_quantum_sync_phot_product_species = dummy_phot + +pos4.species_type = "positron" +pos4.injection_style = nuniformpercell +pos4.num_particles_per_cell_each_dim = 0 0 +pos4.profile = constant +pos4.density = 0.0 +pos4.momentum_distribution_type = "gaussian" +pos4.do_not_push = 1 +pos4.do_qed = 1 +pos4.do_qed_quantum_sync = 1 +pos4.qed_quantum_sync_phot_product_species = dummy_phot + +dummy_phot.species_type = "photon" +dummy_phot.injection_style = nuniformpercell +dummy_phot.num_particles_per_cell_each_dim = 0 0 +dummy_phot.profile = constant +dummy_phot.density = 0.0 +dummy_phot.momentum_distribution_type = "gaussian" +dummy_phot.do_qed = 0 + +################################# + +##########QED TABLES#################### +qed_bw.chi_min = 0.001 + +qed_bw.lookup_table_mode = "builtin" + +#qed_bw.lookup_table_mode = "generate" +#qed_bw.tab_dndt_chi_min = 0.01 +#qed_bw.tab_dndt_chi_max = 1000.0 +#qed_bw.tab_dndt_how_many = 256 +#qed_bw.tab_pair_chi_min = 0.01 +#qed_bw.tab_pair_chi_max = 1000.0 +#qed_bw.tab_pair_chi_how_many = 256 +#qed_bw.tab_pair_frac_how_many = 256 +#qed_bw.save_table_in = "bw_table" + +#qed_bw.lookup_table_mode = "load" +#qed_bw.load_table_from = "bw_table" + +qed_qs.chi_min = 0.001 + +qed_qs.lookup_table_mode = "builtin" + +qed_qs.photon_creation_energy_threshold = 2 + +#qed_qs.lookup_table_mode = "generate" +#qed_qs.tab_dndt_chi_min = 0.001 +#qed_qs.tab_dndt_chi_max = 1000.0 +#qed_qs.tab_dndt_how_many = 256 +#qed_qs.tab_em_chi_min = 0.001 +#qed_qs.tab_em_frac_min = 1.0e-12 +#qed_qs.tab_em_chi_max = 1000.0 +#qed_qs.tab_em_chi_how_many = 256 +#qed_qs.tab_em_frac_how_many = 256 +#qed_qs.save_table_in = "qs_table" + +#qed_qs.lookup_table_mode = "load" +#qed_qs.load_table_from = "qs_table" +################################# + +### EXTERNAL E FIELD ### (3e15 * [-0.811107105653813 0.324442842261525 0.486664263392288] ) +particles.E_ext_particle_init_style = "constant" +particles.E_external_particle = -2433321316961438 973328526784575 1459992790176863 +#### + +### EXTERNAL B FIELD ### (1e7 * [0.28571429 0.42857143 0.85714286] ) +particles.B_ext_particle_init_style = "constant" +particles.B_external_particle = 2857142.85714286 4285714.28571428 8571428.57142857 +#### + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.period = 1 +diag1.diag_type = Full +diag1.fields_to_plot = Ex +diag1.p1.variables = ux uy uz w +diag1.p2.variables = ux uy uz w +diag1.p3.variables = ux uy uz w +diag1.p4.variables = ux uy uz w + +diag1.ele1.variables = ux uy uz w +diag1.ele2.variables = ux uy uz w +diag1.ele3.variables = ux uy uz w +diag1.ele4.variables = ux uy uz w + +diag1.pos1.variables = ux uy uz w +diag1.pos2.variables = ux uy uz w +diag1.pos3.variables = ux uy uz w +diag1.pos4.variables = ux uy uz w diff --git a/Examples/Modules/qed/breit_wheeler/inputs_3d_optical_depth_evolution b/Examples/Modules/qed/breit_wheeler/inputs_3d_optical_depth_evolution deleted file mode 100644 index db4ec895e53..00000000000 --- a/Examples/Modules/qed/breit_wheeler/inputs_3d_optical_depth_evolution +++ /dev/null @@ -1,200 +0,0 @@ -################################# -####### GENERAL PARAMETERS ###### -################################# -max_step = 5 -amr.n_cell = 32 32 16 -amr.max_grid_size = 32 # maximum size of each AMReX box, used to decompose the domain -amr.blocking_factor = 8 # minimum size of each AMReX box, used to decompose the domain -geometry.coord_sys = 0 # 0: Cartesian -geometry.is_periodic = 1 1 1 # Is periodic? -geometry.prob_lo = -0.5e-6 -0.5e-6 -0.25e-6 # physical domain -geometry.prob_hi = 0.5e-6 0.5e-6 0.25e-6 -amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) - -################################# -############ NUMERICS ########### -################################# -algo.current_deposition = esirkepov -algo.charge_deposition = standard -algo.field_gathering = energy-conserving -algo.particle_pusher = boris -interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z -interpolation.noy = 3 -interpolation.noz = 3 -warpx.verbose = 1 -warpx.do_dive_cleaning = 0 -warpx.use_filter = 1 -warpx.cfl = 1. # if 1., the time step is set to its CFL limit -warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition -warpx.serialize_ics = 1 - -################################# -############ PLASMA ############# -################################# -particles.species_names = p1 p2 p3 p4 ele_bw pos_bw -particles.photon_species = p1 p2 p3 p4 -################################# - -p1.species_type = "photon" -p1.injection_style = "NUniformPerCell" -p1.profile = "constant" -p1.xmin = -0.6e-6 -p1.ymin = -0.6e-6 -p1.zmin = -0.6e-6 -p1.xmax = 0.6e6 -p1.ymax = 0.6e6 -p1.zmax = 0.6e6 -p1.num_particles_per_cell_each_dim = 1 1 1 -p1.density = 1e19 -p1.profile = "constant" -p1.momentum_distribution_type = "gaussian" -p1.ux_m = 2000.0 -p1.uy_m = 0.0 -p1.uz_m = 0.0 -p1.ux_th = 0. -p1.uy_th = 0. -p1.uz_th = 0. -##########QED#################### -p1.do_qed = 1 -p1.do_qed_breit_wheeler = 1 -p1.qed_breit_wheeler_ele_product_species = ele_bw -p1.qed_breit_wheeler_pos_product_species = pos_bw -################################# - -p2.species_type = "photon" -p2.injection_style = "NUniformPerCell" -p2.profile = "constant" -p2.xmin = -0.6e-6 -p2.ymin = -0.6e-6 -p2.zmin = -0.6e-6 -p2.xmax = 0.6e6 -p2.ymax = 0.6e6 -p2.zmax = 0.6e6 -p2.num_particles_per_cell_each_dim = 1 1 1 -p2.density = 1e19 -p2.profile = "constant" -p2.momentum_distribution_type = "gaussian" -p2.ux_m = 0.0 -p2.uy_m = 5000.0 -p2.uz_m = 0.0 -p2.ux_th = 0. -p2.uy_th = 0. -p2.uz_th = 0. -##########QED#################### -p2.do_qed = 1 -p2.do_qed_breit_wheeler = 1 -p2.qed_breit_wheeler_ele_product_species = ele_bw -p2.qed_breit_wheeler_pos_product_species = pos_bw -################################# - -p3.species_type = "photon" -p3.injection_style = "NUniformPerCell" -p3.profile = "constant" -p3.xmin = -0.6e-6 -p3.ymin = -0.6e-6 -p3.zmin = -0.6e-6 -p3.xmax = 0.6e6 -p3.ymax = 0.6e6 -p3.zmax = 0.6e6 -p3.num_particles_per_cell_each_dim = 1 1 1 -p3.density = 1e19 -p3.profile = "constant" -p3.momentum_distribution_type = "gaussian" -p3.ux_m = 0.0 -p3.uy_m = 0.0 -p3.uz_m = 10000.0 -p3.ux_th = 0. -p3.uy_th = 0. -p3.uz_th = 0. -##########QED#################### -p3.do_qed = 1 -p3.do_qed_breit_wheeler = 1 -p3.qed_breit_wheeler_ele_product_species = ele_bw -p3.qed_breit_wheeler_pos_product_species = pos_bw -################################# - -p4.species_type = "photon" -p4.injection_style = "NUniformPerCell" -p4.profile = "constant" -p4.xmin = -0.6e-6 -p4.ymin = -0.6e-6 -p4.zmin = -0.6e-6 -p4.xmax = 0.6e6 -p4.ymax = 0.6e6 -p4.zmax = 0.6e6 -p4.num_particles_per_cell_each_dim = 1 1 1 -p4.density = 1e19 -p4.profile = "constant" -p4.momentum_distribution_type = "gaussian" -p4.ux_m = 57735.02691896 -p4.uy_m = 57735.02691896 -p4.uz_m = 57735.02691896 -p4.ux_th = 0. -p4.uy_th = 0. -p4.uz_th = 0. -##########QED#################### -p4.do_qed = 1 -p4.do_qed_breit_wheeler = 1 -p4.qed_breit_wheeler_ele_product_species = ele_bw -p4.qed_breit_wheeler_pos_product_species = pos_bw -################################# - -### PRODUCT SPECIES ### -ele_bw.species_type = "electron" -ele_bw.injection_style = nuniformpercell -ele_bw.num_particles_per_cell_each_dim = 1 1 -ele_bw.profile = constant -ele_bw.density = 0.0 -ele_bw.momentum_distribution_type = "gaussian" -ele_bw.xmin = 1 ## Ugly trick to avoid electrons at T=0 -ele_bw.xmax = -1 ## Ugly trick to avoid electrons at T=0 -ele_bw.do_qed = 0 - -pos_bw.species_type = "positron" -pos_bw.injection_style = nuniformpercell -pos_bw.num_particles_per_cell_each_dim = 1 1 -pos_bw.profile = constant -pos_bw.density = 0.0 -pos_bw.momentum_distribution_type = "gaussian" -pos_bw.xmin = 1 ## Ugly trick to avoid positrons at T=0 -pos_bw.xmax = -1 ## Ugly trick to avoid positrons at T=0 -pos_bw.do_qed = 0 -################################# - -##########QED TABLES#################### -qed_bw.lookup_table_mode = "dummy_builtin" - -#qed_bw.lookup_table_mode = "generate" -#qed_bw.chi_min = 0.001 -#qed_bw.tab_dndt_chi_min = 0.1 -#qed_bw.tab_dndt_chi_max = 200 -#qed_bw.tab_dndt_how_many = 64 -#qed_bw.tab_pair_chi_min = 0.01 -#qed_bw.tab_pair_chi_max = 200 -#qed_bw.tab_pair_chi_how_many = 2 -#qed_bw.tab_pair_frac_how_many = 2 -#qed_bw.save_table_in = "bw_micro_table" - -#qed_bw.lookup_table_mode = "load" -#qed_bw.load_table_from = "bw_micro_table" -################################# - -### EXTERNAL E FIELD ### (3e15 * [-0.811107105653813 0.324442842261525 0.486664263392288] ) -particles.E_ext_particle_init_style = "constant" -particles.E_external_particle = -2433321316961438 973328526784575 1459992790176863 -#### - -### EXTERNAL B FIELD ### (1e7 * [0.28571429 0.42857143 0.85714286] ) -particles.B_ext_particle_init_style = "constant" -particles.B_external_particle = 2857142.85714286 4285714.28571428 8571428.57142857 -#### - -# Diagnostics -diagnostics.diags_names = diag1 -diag1.period = 1 -diag1.diag_type = Full -diag1.fields_to_plot = Ex -diag1.p1.variables = ux uy uz -diag1.p2.variables = ux uy uz -diag1.p3.variables = ux uy uz -diag1.p4.variables = ux uy uz diff --git a/Examples/Modules/qed/quantum_synchrotron/analysis.py b/Examples/Modules/qed/quantum_synchrotron/analysis.py new file mode 100755 index 00000000000..5c2f778a177 --- /dev/null +++ b/Examples/Modules/qed/quantum_synchrotron/analysis.py @@ -0,0 +1,298 @@ +#! /usr/bin/env python + +# Copyright 2019 Luca Fedeli, Maxence Thevenet +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + +# -*- coding: utf-8 -*- + +import yt +import numpy as np +import sys +import scipy.special as spe +import scipy.integrate as integ +import scipy.stats as st +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +import matplotlib.pyplot as plt + +# This script performs detailed checks of the Quantum Synchrotron photon emission process. +# Two electron populations and two positron populations are initialized with different momenta in different +# directions in a background EM field (with non-zero components along each direction). +# Specifically the script checks that: +# +# - The expected number of generated photons n_phot is in agreement with theory. +# The maximum tolerated error is 5*sqrt(n_phot), except for the last 8 points, +# for which a higher tolerance is used (this is due to the fact that the resolution +# of the builtin table is quite limited). +# - The weight of the generated particles is equal to the weight of the photon +# - The generated particles are emitted in the right direction +# - The energy distribution of the generated particles is in agreement with theory +# - The optical depths of the product species are correctly initialized (QED effects are +# enabled for product species too). +# +# More details on the theoretical formulas used in this script can be found in +# the Jupyter notebook picsar/src/multi_physics/QED_tests/validation/validation.ipynb +# +# References: +# 1) R. Duclous et al 2011 Plasma Phys. Control. Fusion 53 015009 +# 2) A. Gonoskov et al. 2015 Phys. Rev. E 92, 023305 +# 3) M. Lobet. PhD thesis "Effets radiatifs et d'electrodynamique +# quantique dans l'interaction laser-matiere ultra-relativiste" +# URL: https://tel.archives-ouvertes.fr/tel-01314224 + + +# Tolerances +tol = 1.e-8 +tol_red = 1.e-2 + +# Physical constants (from CODATA 2018, see: https://physics.nist.gov/cuu/Constants/index.html ) +me = 9.1093837015e-31 #electron mass +c = 299792458 #speed of light +hbar = 6.62607015e-34/(2*np.pi) #reduced Plank constant +fine_structure = 7.2973525693e-3 #fine structure constant +qe = 1.602176634e-19#elementary charge +E_s = (me**2 * c**3)/(qe * hbar) #Schwinger E field +B_s = E_s/c #Schwinger B field + +mec = me*c +mec2 = mec*c +#______________ + +# Initial parameters +spec_names = ["p1", "p2", "p3", "p4"] +spec_names_phot = ["qsp_1", "qsp_2", "qsp_3", "qsp_4"] +initial_momenta = [ + np.array([10.0,0,0])*mec, + np.array([0.0,100.0,0.0])*mec, + np.array([0.0,0.0,1000.0])*mec, + np.array([5773.502691896, 5773.502691896, 5773.502691896])*mec +] +csign = [-1,-1,1,1] +initial_particle_number = 1048576 + +E_f = np.array([-2433321316961438., 973328526784575., 1459992790176863.]) +B_f = np.array([2857142.85714286, 4285714.28571428, 8571428.57142857]) + +NNS = [64,64,64,64] #bins for energy distribution comparison. +#______________ + +def calc_chi_part(p, E, B): + gamma_part = np.sqrt(1.0 + np.dot(p,p)/mec**2) + v = p/(gamma_part*me) + EpvvecB = E + np.cross(v,B) + vdotEoverc = np.dot(v,E)/c + ff = np.sqrt(np.dot(EpvvecB,EpvvecB) - np.dot(vdotEoverc,vdotEoverc)) + return gamma_part*ff/E_s + +#Auxiliary functions +@np.vectorize +def IC_inner_alternative(y): + ff = lambda x : np.exp(-y*(1+(4*x**2)/3)*np.sqrt(1+x*x/3))*(9+36*x**2 + 16*x**4)/(3 + 4*x**2)/np.sqrt(1+(x**2)/3) + return integ.quad(ff, 0, np.inf)[0]/np.sqrt(3) + +def IC_Y(chi_ele, xi): + div = (chi_ele*(1-xi)) + div = np.where(np.logical_and(xi < 1, chi_ele != 0), div, 1.0); + res = (2/3)*np.where(np.logical_and(xi < 1, chi_ele != 0), xi/div, np.inf) + return res + +def IC_S(chi_ele, xi): + Y = IC_Y(chi_ele, xi) + coeff = np.sqrt(3)/2.0/np.pi + first = IC_inner_alternative(Y) + div = np.where(xi == 1, 1.0, 1.0/(1-xi) ) + res = np.where(np.logical_or(xi == 1, xi == 0), 0.0, + coeff*xi*( first + (xi**2 * spe.kv(2./3.,Y)*div ) ) ) + return res + +def IC_SXI(chi_ele, xi): + div = np.where(xi != 0, xi, 1.0) + return np.where(xi != 0, IC_S(chi_ele, xi)/div, np.inf) + +@np.vectorize +def IC_G(chi_ele): + return integ.quad(lambda xi: IC_SXI(chi_ele, xi), 0, 1)[0] + +def small_diff(vv, val): + if(val != 0.0): + return np.max(np.abs((vv - val)/val)) < tol + else: + return np.max(np.abs(vv)) < tol + +def boris(pp, dt, charge_sign): + econst = 0.5*qe*dt*charge_sign/me + u = pp/(me) + u += econst*E_f + inv_gamma = 1/np.sqrt(1 + np.dot(u,u)/c**2) + t = econst*B_f*inv_gamma + s = 2*t/(1 + np.dot(t,t)) + u_p = u + np.cross(u,t) + u += np.cross(u_p, s) + u += econst*E_f + return u *me +#__________________ + +# Quantum Synchrotron total and differential cross sections +def QS_dN_dt(chi_ele, gamma_ele): + coeff_IC = (2./3.) * fine_structure * me*c**2/hbar + return coeff_IC*IC_G(chi_ele)/gamma_ele + +def QS_d2N_dt_dchi(chi, gamma_ele, chi_phot): + coeff_IC = (2./3.) * fine_structure * me*c**2/hbar + return coeff_IC*IC_S(chi, chi_phot/chi)/chi_phot/gamma_ele +#__________________ + +# Get data for a species +def get_spec(ytdata, specname, is_photon): + px = ytdata[specname,"particle_momentum_x"].v + pz = ytdata[specname,"particle_momentum_z"].v + py = ytdata[specname,"particle_momentum_y"].v + + w = ytdata[specname,"particle_weighting"].v + + if (is_photon): + opt = ytdata[specname,"particle_optical_depth_BW"].v + else: + opt = ytdata[specname,"particle_optical_depth_QSR"].v + + return {"px" : px, "py" : py, "pz" : pz, "w" : w, "opt" : opt} + +# Individual tests +def check_number_of_photons(ytdataset, part_name, phot_name, chi_part, gamma_part, dt, particle_number): + dNQS_dt_theo = QS_dN_dt(chi_part, gamma_part) + expected_photons = (1.-np.exp(-dNQS_dt_theo*dt))*particle_number + expected_photons_tolerance = 5.0*np.sqrt(expected_photons) + n_phot = ytdataset.particle_type_counts[phot_name] + assert( np.abs(n_phot-expected_photons) < expected_photons_tolerance) + print(" [OK] generated photons number is within expectations") + return n_phot + +def check_weights(part_data, phot_data): + assert(np.all(part_data["w"] == part_data["w"][0])) + assert(np.all(phot_data["w"] == part_data["w"][0])) + print(" [OK] particles weights are the expected ones") + +def check_momenta(phot_data, p_phot, p0): + pdir = p0/np.linalg.norm(p0) + assert(small_diff(phot_data["px"]/p_phot, pdir[0])) + assert(small_diff(phot_data["py"]/p_phot, pdir[1])) + assert(small_diff(phot_data["pz"]/p_phot, pdir[2])) + print(" [OK] photons move along the initial particle direction") + +def check_opt_depths(part_data, phot_data): + data = (part_data, phot_data) + for dd in data: + loc, scale = st.expon.fit(dd["opt"]) + assert( np.abs(loc - 0) < tol_red ) + assert( np.abs(scale - 1) < tol_red ) + print(" [OK] optical depth distributions are still exponential") + +def check_energy_distrib(gamma_phot, chi_part, + gamma_part, n_phot, NN, idx, do_plot=False): + gamma_phot_min = 1e-12*gamma_part + gamma_phot_max = gamma_part + h_log_gamma_phot, c_gamma_phot = np.histogram(gamma_phot, bins=np.logspace(np.log10(gamma_phot_min),np.log10(gamma_phot_max),NN+1)) + + cchi_phot_min = chi_part*(gamma_phot_min)/(gamma_part-1) + cchi_phot_max = chi_part*(gamma_phot_max)/(gamma_part-1) + + #Rudimentary integration over npoints for each bin + npoints= 20 + aux_chi = np.logspace(np.log10(cchi_phot_min),np.log10(cchi_phot_max), NN*npoints) + distrib = QS_d2N_dt_dchi(chi_part, gamma_part, aux_chi)*aux_chi + distrib = np.sum(distrib.reshape(-1, npoints),1) + distrib = n_phot*distrib/np.sum(distrib) + + if do_plot : + # Visual comparison of distributions + c_gamma_phot = np.exp(0.5*(np.log(c_gamma_phot[1:])+np.log(c_gamma_phot[:-1]))) + plt.clf() + + fig, (ax1, ax2) = plt.subplots(1, 2) + fig.suptitle("χ_particle = {:f}".format(chi_part)) + ax1.plot(c_gamma_phot, distrib,label="theory") + ax1.loglog(c_gamma_phot, h_log_gamma_phot,label="QSR photons") + ax1.set_xlim(1e-12*(gamma_part-1),gamma_part-1) + ax1.set_ylim(1,1e5) + ax2.plot(c_gamma_phot, distrib,label="theory") + ax2.semilogy(c_gamma_phot, h_log_gamma_phot,label="QSR photons") + ax2.set_ylim(1,1e5) + ax2.set_xlim(1e-12*(gamma_part-1),gamma_part-1) + ax1.set_xlabel("γ_photon") + ax1.set_xlabel("N") + ax2.set_xlabel("γ_photon") + ax2.set_xlabel("N") + plt.legend() + plt.savefig("case_{:d}".format(idx+1)) + + discr = np.abs(h_log_gamma_phot-distrib) + + max_discr = np.sqrt(distrib)*5.0 + # Use a higer tolerance for the last 8 points (this is due to limitations + # of the builtin table) + max_discr[-8:] *= 2.0 + assert(np.all( discr < max_discr )) + + print(" [OK] energy distribution is within expectations") + +#__________________ + +def check(): + filename_end = sys.argv[1] + data_set_end = yt.load(filename_end) + + sim_time = data_set_end.current_time.to_value() + all_data_end = data_set_end.all_data() + + for idx in range(4): + part_name = spec_names[idx] + phot_name = spec_names_phot[idx] + t_pi = initial_momenta[idx] + pm = boris(t_pi,-sim_time*0.5,csign[idx]) + p0 = boris(pm,sim_time*1.0,csign[idx]) + + p2_part = p0[0]**2 + p0[1]**2 + p0[2]**2 + energy_part = np.sqrt(mec2**2 + p2_part*c**2) + gamma_part = energy_part/mec2 + chi_part = calc_chi_part(p0, E_f, B_f) + + print("** Case {:d} **".format(idx+1)) + print(" initial momentum: ", t_pi) + print(" quantum parameter: {:f}".format(chi_part)) + print(" normalized particle energy: {:f}".format(gamma_part)) + print(" timestep: {:f} fs".format(sim_time*1e15)) + + part_data_final = get_spec(all_data_end, part_name, is_photon=False) + phot_data = get_spec(all_data_end, phot_name, is_photon=True) + + p_phot = np.sqrt(phot_data["px"]**2 + phot_data["py"]**2 + phot_data["pz"]**2) + energy_phot = p_phot*c + gamma_phot = energy_phot/mec2 + + n_phot = check_number_of_photons(data_set_end, + part_name, phot_name, + chi_part, gamma_part, sim_time, + initial_particle_number) + + check_weights(part_data_final, phot_data) + + check_momenta(phot_data, p_phot, p0) + + check_energy_distrib(gamma_phot, chi_part, gamma_part, n_phot, NNS[idx], idx) + + check_opt_depths(part_data_final, phot_data) + + print("*************\n") + + test_name = filename_end[:-9] # Could also be os.path.split(os.getcwd())[1] + checksumAPI.evaluate_checksum(test_name, filename_end) + +def main(): + check() + +if __name__ == "__main__": + main() diff --git a/Examples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py b/Examples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py deleted file mode 100755 index b21441fb7d8..00000000000 --- a/Examples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py +++ /dev/null @@ -1,60 +0,0 @@ -#! /usr/bin/env python - -# Copyright 2019 Luca Fedeli, Maxence Thevenet -# -# This file is part of WarpX. -# -# License: BSD-3-Clause-LBNL - -import yt -import numpy as np -import scipy.stats as st -import sys -sys.path.insert(1, '../../../../warpx/Regression/Checksum/') -import checksumAPI - -# This script checks if electrons and positrons initialized with -# Quantum Synchrotron process enabled -# do actually have an exponentially distributed optical depth - -# Tolerance -tolerance_rel = 1e-2 -print("tolerance_rel: " + str(tolerance_rel)) - -def check(): - filename = sys.argv[1] - data_set = yt.load(filename) - - all_data = data_set.all_data() - res_ele_tau = all_data["electrons", 'particle_optical_depth_QSR'] - res_pos_tau = all_data["positrons", 'particle_optical_depth_QSR'] - - loc_ele, scale_ele = st.expon.fit(res_ele_tau) - loc_pos, scale_pos = st.expon.fit(res_pos_tau) - - # loc should be very close to 0, scale should be very close to 1 - error_rel = np.abs(loc_ele - 0) - print("error_rel loc_ele: " + str(error_rel)) - assert( error_rel < tolerance_rel ) - - error_rel = np.abs(loc_pos - 0) - print("error_rel loc_pos: " + str(error_rel)) - assert( error_rel < tolerance_rel ) - - error_rel = np.abs(scale_ele - 1) - print("error_rel scale_ele: " + str(error_rel)) - assert( error_rel < tolerance_rel ) - - error_rel = np.abs(scale_pos - 1) - print("error_rel scale_pos: " + str(error_rel)) - assert( error_rel < tolerance_rel ) - - test_name = filename[:-9] # Could also be os.path.split(os.getcwd())[1] - checksumAPI.evaluate_checksum(test_name, filename) - -def main(): - check() - -if __name__ == "__main__": - main() - diff --git a/Examples/Modules/qed/quantum_synchrotron/inputs_2d b/Examples/Modules/qed/quantum_synchrotron/inputs_2d new file mode 100644 index 00000000000..c57e04793dc --- /dev/null +++ b/Examples/Modules/qed/quantum_synchrotron/inputs_2d @@ -0,0 +1,224 @@ +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 1 +amr.n_cell = 32 32 +amr.max_grid_size = 16 # maximum size of each AMReX box, used to decompose the domain +amr.blocking_factor = 8 # minimum size of each AMReX box, used to decompose the domain +geometry.coord_sys = 0 # 0: Cartesian +geometry.is_periodic = 1 1 1 # Is periodic? +geometry.prob_lo = -0.25e-6 -0.25e-6 # physical domain +geometry.prob_hi = 0.25e-6 0.25e-6 +amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) + +################################# +############ NUMERICS ########### +################################# +algo.current_deposition = esirkepov +algo.charge_deposition = standard +algo.field_gathering = energy-conserving +algo.particle_pusher = boris +interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z +interpolation.noy = 3 +interpolation.noz = 3 +warpx.verbose = 1 +warpx.do_dive_cleaning = 0 +warpx.use_filter = 1 +warpx.cfl = 1. # if 1., the time step is set to its CFL limit +warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition +warpx.serialize_ics = 1 + +################################# +############ PLASMA ############# +################################# +particles.species_names = p1 p2 p3 p4 qsp_1 qsp_2 qsp_3 qsp_4 dummy_ele dummy_pos +particles.photon_species = qsp_1 qsp_2 qsp_3 qsp_4 +################################# + +p1.species_type = "electron" +p1.injection_style = "NUniformPerCell" +p1.profile = "constant" +p1.num_particles_per_cell_each_dim = 32 32 +p1.density = 1e1 +p1.profile = "constant" +p1.momentum_distribution_type = "gaussian" +p1.ux_m = 10.0 +##########QED#################### +p1.do_qed = 1 +p1.do_qed_quantum_sync = 1 +p1.qed_quantum_sync_phot_product_species = qsp_1 +################################# + +p2.species_type = "electron" +p2.injection_style = "NUniformPerCell" +p2.profile = "constant" +p2.num_particles_per_cell_each_dim = 32 32 +p2.density = 1e1 +p2.profile = "constant" +p2.momentum_distribution_type = "gaussian" +p2.uy_m = 100.0 +##########QED#################### +p2.do_qed = 1 +p2.do_qed_quantum_sync = 1 +p2.qed_quantum_sync_phot_product_species = qsp_2 +################################# + +p3.species_type = "positron" +p3.injection_style = "NUniformPerCell" +p3.profile = "constant" +p3.num_particles_per_cell_each_dim = 32 32 +p3.density = 1e1 +p3.profile = "constant" +p3.momentum_distribution_type = "gaussian" +p3.uz_m = 1000.0 +##########QED#################### +p3.do_qed = 1 +p3.do_qed_quantum_sync = 1 +p3.qed_quantum_sync_phot_product_species = qsp_3 +################################# + +p4.species_type = "positron" +p4.injection_style = "NUniformPerCell" +p4.profile = "constant" +p4.num_particles_per_cell_each_dim = 32 32 +p4.density = 1e1 +p4.profile = "constant" +p4.momentum_distribution_type = "gaussian" +p4.ux_m = 5773.502691896 +p4.uy_m = 5773.502691896 +p4.uz_m = 5773.502691896 +##########QED#################### +p4.do_qed = 1 +p4.do_qed_quantum_sync = 1 +p4.qed_quantum_sync_phot_product_species = qsp_4 +################################# + +### PRODUCT SPECIES ### +qsp_1.species_type = "photon" +qsp_1.injection_style = nuniformpercell +qsp_1.num_particles_per_cell_each_dim = 0 0 +qsp_1.profile = constant +qsp_1.density = 0.0 +qsp_1.momentum_distribution_type = "gaussian" +qsp_1.do_qed = 1 +qsp_1.do_qed_breit_wheeler = 1 +qsp_1.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_1.qed_breit_wheeler_pos_product_species = dummy_pos + +qsp_2.species_type = "photon" +qsp_2.injection_style = nuniformpercell +qsp_2.num_particles_per_cell_each_dim = 0 0 +qsp_2.profile = constant +qsp_2.density = 0.0 +qsp_2.momentum_distribution_type = "gaussian" +qsp_2.do_qed = 1 +qsp_2.do_qed_breit_wheeler = 1 +qsp_2.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_2.qed_breit_wheeler_pos_product_species = dummy_pos + +qsp_3.species_type = "photon" +qsp_3.injection_style = nuniformpercell +qsp_3.num_particles_per_cell_each_dim = 0 0 +qsp_3.profile = constant +qsp_3.density = 0.0 +qsp_3.momentum_distribution_type = "gaussian" +qsp_3.do_qed = 1 +qsp_3.do_qed_breit_wheeler = 1 +qsp_3.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_3.qed_breit_wheeler_pos_product_species = dummy_pos + +qsp_4.species_type = "photon" +qsp_4.injection_style = nuniformpercell +qsp_4.num_particles_per_cell_each_dim = 0 0 +qsp_4.profile = constant +qsp_4.density = 0.0 +qsp_4.momentum_distribution_type = "gaussian" +qsp_4.do_qed = 1 +qsp_4.do_qed_breit_wheeler = 1 +qsp_4.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_4.qed_breit_wheeler_pos_product_species = dummy_pos + +################################# + +dummy_ele.species_type = "electron" +dummy_ele.injection_style = nuniformpercell +dummy_ele.num_particles_per_cell_each_dim = 0 0 +dummy_ele.profile = constant +dummy_ele.density = 0.0 +dummy_ele.momentum_distribution_type = "gaussian" +dummy_ele.do_qed = 0 + +dummy_pos.species_type = "positron" +dummy_pos.injection_style = nuniformpercell +dummy_pos.num_particles_per_cell_each_dim = 0 0 +dummy_pos.profile = constant +dummy_pos.density = 0.0 +dummy_pos.momentum_distribution_type = "gaussian" +dummy_pos.do_qed = 0 + + +################################# + +##########QED TABLES#################### +qed_bw.chi_min = 0.001 + +qed_bw.lookup_table_mode = "builtin" + +#qed_bw.lookup_table_mode = "generate" +#qed_bw.tab_dndt_chi_min = 0.01 +#qed_bw.tab_dndt_chi_max = 1000.0 +#qed_bw.tab_dndt_how_many = 256 +#qed_bw.tab_pair_chi_min = 0.01 +#qed_bw.tab_pair_chi_max = 1000.0 +#qed_bw.tab_pair_chi_how_many = 256 +#qed_bw.tab_pair_frac_how_many = 256 +#qed_bw.save_table_in = "bw_table" + +#qed_bw.lookup_table_mode = "load" +#qed_bw.load_table_from = "bw_table" + +qed_qs.chi_min = 0.001 + +qed_qs.lookup_table_mode = "builtin" + +qed_qs.photon_creation_energy_threshold = 0.0 + +#qed_qs.lookup_table_mode = "generate" +#qed_qs.tab_dndt_chi_min = 0.001 +#qed_qs.tab_dndt_chi_max = 1000.0 +#qed_qs.tab_dndt_how_many = 256 +#qed_qs.tab_em_chi_min = 0.001 +#qed_qs.tab_em_frac_min = 1.0e-12 +#qed_qs.tab_em_chi_max = 1000.0 +#qed_qs.tab_em_chi_how_many = 256 +#qed_qs.tab_em_frac_how_many = 256 +#qed_qs.save_table_in = "qs_table" + +#qed_qs.lookup_table_mode = "load" +#qed_qs.load_table_from = "qs_table" +################################# + +### EXTERNAL E FIELD ### (3e15 * [-0.811107105653813 0.324442842261525 0.486664263392288] ) +particles.E_ext_particle_init_style = "constant" +particles.E_external_particle = -2433321316961438 973328526784575 1459992790176863 +#### + +### EXTERNAL B FIELD ### (1e7 * [0.28571429 0.42857143 0.85714286] ) +particles.B_ext_particle_init_style = "constant" +particles.B_external_particle = 2857142.85714286 4285714.28571428 8571428.57142857 +#### + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.period = 1 +diag1.diag_type = Full +diag1.fields_to_plot = Ex +diag1.p1.variables = ux uy uz w +diag1.p2.variables = ux uy uz w +diag1.p3.variables = ux uy uz w +diag1.p4.variables = ux uy uz w + +diag1.qsp_1.variables = ux uy uz w +diag1.qsp_2.variables = ux uy uz w +diag1.qsp_3.variables = ux uy uz w +diag1.qsp_4.variables = ux uy uz w diff --git a/Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init b/Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init deleted file mode 100644 index a29adf28719..00000000000 --- a/Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init +++ /dev/null @@ -1,122 +0,0 @@ -################################# -####### GENERAL PARAMETERS ###### -################################# -max_step = 1 -amr.n_cell = 128 128 -amr.max_grid_size = 128 # maximum size of each AMReX box, used to decompose the domain -amr.blocking_factor = 32 # minimum size of each AMReX box, used to decompose the domain -geometry.coord_sys = 0 # 0: Cartesian -geometry.is_periodic = 0 0 # Is periodic? -geometry.prob_lo = -32.e-6 -32.e-6 # physical domain -geometry.prob_hi = 32.e-6 32.e-6 -amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) - -################################# -############ NUMERICS ########### -################################# -algo.current_deposition = esirkepov -algo.charge_deposition = standard -algo.field_gathering = energy-conserving -algo.particle_pusher = boris -interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z -interpolation.noy = 3 -interpolation.noz = 3 -warpx.verbose = 1 -warpx.do_dive_cleaning = 0 -warpx.use_filter = 1 -warpx.cfl = 1. # if 1., the time step is set to its CFL limit -warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition -warpx.serialize_ics = 1 - - -################################# -############ PLASMA ############# -################################# -particles.species_names = electrons positrons qs_phot -particles.photon_species = qs_phot -################################# - -electrons.species_type = "electron" -electrons.injection_style = "NUniformPerCell" -electrons.profile = "constant" -electrons.xmin = -30e-6 -electrons.ymin = -30e-6 -electrons.zmin = -30e-6 -electrons.xmax = 30e-6 -electrons.ymax = 30e-6 -electrons.zmax = 30e-6 -electrons.num_particles_per_cell_each_dim = 2 2 -electrons.density = 1e19 -electrons.profile = "constant" -electrons.momentum_distribution_type = "gaussian" -electrons.ux_m = 0.0 -electrons.uy_m = 0.0 -electrons.uz_m = 0.0 -electrons.ux_th = 100. -electrons.uy_th = 100. -electrons.uz_th = 100. -##########QED#################### -electrons.do_qed = 1 -electrons.do_qed_quantum_sync = 1 -electrons.qed_quantum_sync_phot_product_species = qs_phot -################################# - -positrons.species_type = "positron" -positrons.injection_style = "NUniformPerCell" -positrons.profile = "constant" -positrons.xmin = -30e-6 -positrons.ymin = -30e-6 -positrons.zmin = -30e-6 -positrons.xmax = 30e-6 -positrons.ymax = 30e-6 -positrons.zmax = 30e-6 -positrons.num_particles_per_cell_each_dim = 2 2 -positrons.density = 1e19 -positrons.profile = "constant" -positrons.momentum_distribution_type = "gaussian" -positrons.ux_m = 0.0 -positrons.uy_m = 0.0 -positrons.uz_m = 0.0 -positrons.ux_th = 100. -positrons.uy_th = 100. -positrons.uz_th = 100. -##########QED#################### -positrons.do_qed = 1 -positrons.do_qed_quantum_sync = 1 -positrons.qed_quantum_sync_phot_product_species = qs_phot -################################# - -### PRODUCT SPECIES ### -qs_phot.species_type = "photon" -qs_phot.injection_style = nuniformpercell -qs_phot.num_particles_per_cell_each_dim = 1 1 -qs_phot.profile = constant -qs_phot.xmin = 1 ## Ugly trick to avoid photons at T=0 -qs_phot.xmax = -1 ## Ugly trick to avoid photons at T=0 -qs_phot.density = 0.0 -qs_phot.momentum_distribution_type = "gaussian" -################################# - -#######QED ENGINE PARAMETERS##### -qed_qs.lookup_table_mode = "dummy_builtin" -qed_qs.photon_creation_energy_threshold = 1.63742112993e-13 - -#qed_qs.lookup_table_mode = "generate" -#qed_qs.chi_min = 0.001 -#qed_qs.tab_dndt_chi_min = 0.001 -#qed_qs.tab_dndt_chi_max = 200 -#qed_qs.tab_dndt_how_many = 32 -#qed_qs.tab_em_chi_min = 0.001 -#qed_qs.tab_em_chi_max = 200 -#qed_qs.tab_em_chi_how_many = 2 -#qed_qs.tab_em_prob_how_many = 2 -#qed_qs.save_table_in = "qs_micro_table" - -#qed_qs.lookup_table_mode = "load" -#qed_qs.load_table_from = "qs_micro_table" -################################# - -# Diagnostics -diagnostics.diags_names = diag1 -diag1.period = 1 -diag1.diag_type = Full diff --git a/Examples/Modules/qed/quantum_synchrotron/inputs_3d b/Examples/Modules/qed/quantum_synchrotron/inputs_3d new file mode 100644 index 00000000000..66670b4dbb3 --- /dev/null +++ b/Examples/Modules/qed/quantum_synchrotron/inputs_3d @@ -0,0 +1,224 @@ +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 1 +amr.n_cell = 16 16 16 +amr.max_grid_size = 16 # maximum size of each AMReX box, used to decompose the domain +amr.blocking_factor = 8 # minimum size of each AMReX box, used to decompose the domain +geometry.coord_sys = 0 # 0: Cartesian +geometry.is_periodic = 1 1 1 # Is periodic? +geometry.prob_lo = -0.25e-6 -0.25e-6 -0.25e-6 # physical domain +geometry.prob_hi = 0.25e-6 0.25e-6 0.25e-6 +amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) + +################################# +############ NUMERICS ########### +################################# +algo.current_deposition = esirkepov +algo.charge_deposition = standard +algo.field_gathering = energy-conserving +algo.particle_pusher = boris +interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z +interpolation.noy = 3 +interpolation.noz = 3 +warpx.verbose = 1 +warpx.do_dive_cleaning = 0 +warpx.use_filter = 1 +warpx.cfl = 1. # if 1., the time step is set to its CFL limit +warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition +warpx.serialize_ics = 1 + +################################# +############ PLASMA ############# +################################# +particles.species_names = p1 p2 p3 p4 qsp_1 qsp_2 qsp_3 qsp_4 dummy_ele dummy_pos +particles.photon_species = qsp_1 qsp_2 qsp_3 qsp_4 +################################# + +p1.species_type = "electron" +p1.injection_style = "NUniformPerCell" +p1.profile = "constant" +p1.num_particles_per_cell_each_dim = 8 8 4 +p1.density = 1e1 +p1.profile = "constant" +p1.momentum_distribution_type = "gaussian" +p1.ux_m = 10.0 +##########QED#################### +p1.do_qed = 1 +p1.do_qed_quantum_sync = 1 +p1.qed_quantum_sync_phot_product_species = qsp_1 +################################# + +p2.species_type = "electron" +p2.injection_style = "NUniformPerCell" +p2.profile = "constant" +p2.num_particles_per_cell_each_dim = 8 8 4 +p2.density = 1e1 +p2.profile = "constant" +p2.momentum_distribution_type = "gaussian" +p2.uy_m = 100.0 +##########QED#################### +p2.do_qed = 1 +p2.do_qed_quantum_sync = 1 +p2.qed_quantum_sync_phot_product_species = qsp_2 +################################# + +p3.species_type = "positron" +p3.injection_style = "NUniformPerCell" +p3.profile = "constant" +p3.num_particles_per_cell_each_dim = 8 8 4 +p3.density = 1e1 +p3.profile = "constant" +p3.momentum_distribution_type = "gaussian" +p3.uz_m = 1000.0 +##########QED#################### +p3.do_qed = 1 +p3.do_qed_quantum_sync = 1 +p3.qed_quantum_sync_phot_product_species = qsp_3 +################################# + +p4.species_type = "positron" +p4.injection_style = "NUniformPerCell" +p4.profile = "constant" +p4.num_particles_per_cell_each_dim = 8 8 4 +p4.density = 1e1 +p4.profile = "constant" +p4.momentum_distribution_type = "gaussian" +p4.ux_m = 5773.502691896 +p4.uy_m = 5773.502691896 +p4.uz_m = 5773.502691896 +##########QED#################### +p4.do_qed = 1 +p4.do_qed_quantum_sync = 1 +p4.qed_quantum_sync_phot_product_species = qsp_4 +################################# + +### PRODUCT SPECIES ### +qsp_1.species_type = "photon" +qsp_1.injection_style = nuniformpercell +qsp_1.num_particles_per_cell_each_dim = 0 0 +qsp_1.profile = constant +qsp_1.density = 0.0 +qsp_1.momentum_distribution_type = "gaussian" +qsp_1.do_qed = 1 +qsp_1.do_qed_breit_wheeler = 1 +qsp_1.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_1.qed_breit_wheeler_pos_product_species = dummy_pos + +qsp_2.species_type = "photon" +qsp_2.injection_style = nuniformpercell +qsp_2.num_particles_per_cell_each_dim = 0 0 +qsp_2.profile = constant +qsp_2.density = 0.0 +qsp_2.momentum_distribution_type = "gaussian" +qsp_2.do_qed = 1 +qsp_2.do_qed_breit_wheeler = 1 +qsp_2.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_2.qed_breit_wheeler_pos_product_species = dummy_pos + +qsp_3.species_type = "photon" +qsp_3.injection_style = nuniformpercell +qsp_3.num_particles_per_cell_each_dim = 0 0 +qsp_3.profile = constant +qsp_3.density = 0.0 +qsp_3.momentum_distribution_type = "gaussian" +qsp_3.do_qed = 1 +qsp_3.do_qed_breit_wheeler = 1 +qsp_3.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_3.qed_breit_wheeler_pos_product_species = dummy_pos + +qsp_4.species_type = "photon" +qsp_4.injection_style = nuniformpercell +qsp_4.num_particles_per_cell_each_dim = 0 0 +qsp_4.profile = constant +qsp_4.density = 0.0 +qsp_4.momentum_distribution_type = "gaussian" +qsp_4.do_qed = 1 +qsp_4.do_qed_breit_wheeler = 1 +qsp_4.qed_breit_wheeler_ele_product_species = dummy_ele +qsp_4.qed_breit_wheeler_pos_product_species = dummy_pos + +################################# + +dummy_ele.species_type = "electron" +dummy_ele.injection_style = nuniformpercell +dummy_ele.num_particles_per_cell_each_dim = 0 0 +dummy_ele.profile = constant +dummy_ele.density = 0.0 +dummy_ele.momentum_distribution_type = "gaussian" +dummy_ele.do_qed = 0 + +dummy_pos.species_type = "positron" +dummy_pos.injection_style = nuniformpercell +dummy_pos.num_particles_per_cell_each_dim = 0 0 +dummy_pos.profile = constant +dummy_pos.density = 0.0 +dummy_pos.momentum_distribution_type = "gaussian" +dummy_pos.do_qed = 0 + + +################################# + +##########QED TABLES#################### +qed_bw.chi_min = 0.001 + +qed_bw.lookup_table_mode = "builtin" + +#qed_bw.lookup_table_mode = "generate" +#qed_bw.tab_dndt_chi_min = 0.01 +#qed_bw.tab_dndt_chi_max = 1000.0 +#qed_bw.tab_dndt_how_many = 256 +#qed_bw.tab_pair_chi_min = 0.01 +#qed_bw.tab_pair_chi_max = 1000.0 +#qed_bw.tab_pair_chi_how_many = 256 +#qed_bw.tab_pair_frac_how_many = 256 +#qed_bw.save_table_in = "bw_table" + +#qed_bw.lookup_table_mode = "load" +#qed_bw.load_table_from = "bw_table" + +qed_qs.chi_min = 0.001 + +qed_qs.lookup_table_mode = "builtin" + +qed_qs.photon_creation_energy_threshold = 0.0 + +#qed_qs.lookup_table_mode = "generate" +#qed_qs.tab_dndt_chi_min = 0.001 +#qed_qs.tab_dndt_chi_max = 1000.0 +#qed_qs.tab_dndt_how_many = 256 +#qed_qs.tab_em_chi_min = 0.001 +#qed_qs.tab_em_frac_min = 1.0e-12 +#qed_qs.tab_em_chi_max = 1000.0 +#qed_qs.tab_em_chi_how_many = 256 +#qed_qs.tab_em_frac_how_many = 256 +#qed_qs.save_table_in = "qs_table" + +#qed_qs.lookup_table_mode = "load" +#qed_qs.load_table_from = "qs_table" +################################# + +### EXTERNAL E FIELD ### (3e15 * [-0.811107105653813 0.324442842261525 0.486664263392288] ) +particles.E_ext_particle_init_style = "constant" +particles.E_external_particle = -2433321316961438 973328526784575 1459992790176863 +#### + +### EXTERNAL B FIELD ### (1e7 * [0.28571429 0.42857143 0.85714286] ) +particles.B_ext_particle_init_style = "constant" +particles.B_external_particle = 2857142.85714286 4285714.28571428 8571428.57142857 +#### + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.period = 1 +diag1.diag_type = Full +diag1.fields_to_plot = Ex +diag1.p1.variables = ux uy uz w +diag1.p2.variables = ux uy uz w +diag1.p3.variables = ux uy uz w +diag1.p4.variables = ux uy uz w + +diag1.qsp_1.variables = ux uy uz w +diag1.qsp_2.variables = ux uy uz w +diag1.qsp_3.variables = ux uy uz w +diag1.qsp_4.variables = ux uy uz w diff --git a/Examples/Modules/qed/schwinger/analysis_schwinger.py b/Examples/Modules/qed/schwinger/analysis_schwinger.py index f52512478e3..25dadeb42b6 100755 --- a/Examples/Modules/qed/schwinger/analysis_schwinger.py +++ b/Examples/Modules/qed/schwinger/analysis_schwinger.py @@ -21,9 +21,9 @@ # define some parameters c = 299792458. -m_e = 9.10938356e-31 -e = 1.6021766208e-19 -hbar = 1.054571800e-34 +m_e = 9.1093837015e-31 +e =1.602176634e-19 +hbar = 1.054571817e-34 E_S = m_e**2*c**3/e/hbar # Schwinger field dV = (1.e-6)**3 # total simulation volume diff --git a/Examples/Modules/resampling/analysis_leveling_thinning.py b/Examples/Modules/resampling/analysis_leveling_thinning.py new file mode 100755 index 00000000000..dc96b01385a --- /dev/null +++ b/Examples/Modules/resampling/analysis_leveling_thinning.py @@ -0,0 +1,138 @@ +#! /usr/bin/env python + +# Copyright 2020 Neil Zaim +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL + +## In this test, we check that leveling thinning works as expected on two simple cases. Each case +## corresponds to a different particle species. + +import yt +import numpy as np +import sys +from scipy.special import erf +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +fn_final = sys.argv[1] +fn0 = fn_final[:-4] + '0000' + +ds0 = yt.load(fn0) +ds = yt.load(fn_final) + +ad0 = ds0.all_data() +ad = ds.all_data() + +numcells = 16*16 +t_r = 1.3 # target ratio +relative_tol = 1.e-13 # tolerance for machine precision errors + + +#### Tests for first species #### +# Particles are present in all simulation cells and all have the same weight + +ppc = 400. +numparts_init = numcells*ppc + +w0 = ad0['resampled_part1','particle_weight'].to_ndarray() # weights before resampling +w = ad['resampled_part1','particle_weight'].to_ndarray() # weights after resampling + +# Renormalize weights for easier calculations +w0 = w0*ppc +w = w*ppc + +# Check that initial number of particles is indeed as expected +assert(w0.shape[0] == numparts_init) +# Check that all initial particles have the same weight +assert(np.unique(w0).shape[0] == 1) +# Check that this weight is 1 (to machine precision) +assert(abs(w0[0] - 1) < relative_tol) + +# Check that the number of particles after resampling is as expected +numparts_final = w.shape[0] +expected_numparts_final = numparts_init/t_r**2 +error = np.abs(numparts_final - expected_numparts_final) +std_numparts_final = np.sqrt(numparts_init/t_r**2*(1.-1./t_r**2)) +# 5 sigma test that has an intrisic probability to fail of 1 over ~2 millions +print("difference between expected and actual final number of particles (1st species): " + str(error)) +print("tolerance: " + str(5*std_numparts_final)) +assert(error<5*std_numparts_final) + +# Check that the final weight is the same for all particles (to machine precision) and is as +# expected +final_weight = t_r**2 +assert(np.amax(np.abs(w-final_weight)) < relative_tol*final_weight ) + + +#### Tests for second species #### +# Particles are only present in a single cell and have a gaussian weight distribution +# Using a single cell makes the analysis easier because leveling thinning is done separately in +# each cell + +ppc = 100000. +numparts_init = ppc + +w0 = ad0['resampled_part2','particle_weight'].to_ndarray() # weights before resampling +w = ad['resampled_part2','particle_weight'].to_ndarray() # weights after resampling + +# Renormalize and sort weights for easier calculations +w0 = np.sort(w0)*ppc +w = np.sort(w)*ppc + +## First we verify that the initial distribution is as expected + +# Check that the mean initial weight is as expected +mean_initial_weight = np.average(w0) +expected_mean_initial_weight = 2.*np.sqrt(2.) +error = np.abs(mean_initial_weight - expected_mean_initial_weight) +expected_std_initial_weight = 1./np.sqrt(2.) +std_mean_initial_weight = expected_std_initial_weight/np.sqrt(numparts_init) +# 5 sigma test that has an intrisic probability to fail of 1 over ~2 millions +print("difference between expected and actual mean initial weight (2nd species): " + str(error)) +print("tolerance: " + str(5*std_mean_initial_weight)) +assert(error<5*std_mean_initial_weight) + +# Check that the initial weight variance is as expected +variance_initial_weight = np.var(w0) +expected_variance_initial_weight = 0.5 +error = np.abs(variance_initial_weight - expected_variance_initial_weight) +std_variance_initial_weight = expected_variance_initial_weight*np.sqrt(2./numparts_init) +# 5 sigma test that has an intrisic probability to fail of 1 over ~2 millions +print("difference between expected and actual variance of initial weight (2nd species): " + str(error)) +print("tolerance: " + str(5*std_variance_initial_weight)) + +## Next we verify that the resampling worked as expected + +# Check that the level weight value is as expected from the initial distribution +level_weight = w[0] +assert(np.abs(level_weight - mean_initial_weight*t_r) < level_weight*relative_tol) + +# Check that the number of particles at the level weight is the same as predicted from analytic +# calculations +numparts_leveled = np.argmax(w > level_weight) # This returns the first index for which +# w > level_weight, which thus corresponds to the number of particles at the level weight +expected_numparts_leveled = numparts_init/(2.*t_r)*(1+erf(expected_mean_initial_weight*(t_r-1.)) \ + -1./(np.sqrt(np.pi)*expected_mean_initial_weight)* \ + np.exp(-(expected_mean_initial_weight*(t_r-1.))**2)) +error = np.abs(numparts_leveled - expected_numparts_leveled) +std_numparts_leveled = np.sqrt(expected_numparts_leveled - numparts_init/np.sqrt(np.pi)/(t_r* \ + expected_mean_initial_weight)**2*(np.sqrt(np.pi)/4.* \ + (2.*expected_mean_initial_weight**2+1.)*(1.-erf(expected_mean_initial_weight* \ + (t_r-1.)))-0.5*np.exp(-(expected_mean_initial_weight*(t_r-1.))**2* \ + (expected_mean_initial_weight*(t_r+1.))))) +# 5 sigma test that has an intrisic probability to fail of 1 over ~2 millions +print("difference between expected and actual number of leveled particles (2nd species): " + str(error)) +print("tolerance: " + str(5*std_numparts_leveled)) + +numparts_unaffected = w.shape[0] - numparts_leveled +numparts_unaffected_anticipated = w0.shape[0] - np.argmax(w0 > level_weight) +# Check that number of particles with weight higher than level weight is the same before and after +# resampling +assert(numparts_unaffected == numparts_unaffected_anticipated) +# Check that particles with weight higher than level weight are unaffected by resampling. +assert(np.all(w[-numparts_unaffected:] == w0[-numparts_unaffected:])) + +test_name = fn_final[:-9] # Could also be os.path.split(os.getcwd())[1] +checksumAPI.evaluate_checksum(test_name, fn_final) diff --git a/Examples/Modules/resampling/inputs_leveling_thinning b/Examples/Modules/resampling/inputs_leveling_thinning new file mode 100644 index 00000000000..86208a92c4e --- /dev/null +++ b/Examples/Modules/resampling/inputs_leveling_thinning @@ -0,0 +1,67 @@ +max_step = 8 +amr.n_cell = 16 16 +amr.blocking_factor = 8 +amr.max_grid_size = 8 +geometry.is_periodic = 1 1 +geometry.prob_lo = 0. 0. +geometry.prob_hi = 16. 16. +amr.max_level = 0 + +warpx.do_pml = 0 + +particles.species_names = resampled_part1 resampled_part2 + +my_constants.pi = 3.141592653589793 + +# First particle species. The particles are distributed throughout the simulation box and all have +# the same weight. +resampled_part1.species_type = electron +resampled_part1.injection_style = NRandomPerCell +resampled_part1.num_particles_per_cell = 400 +resampled_part1.profile = constant +resampled_part1.density = 1. +resampled_part1.momentum_distribution_type = constant +resampled_part1.do_not_deposit = 1 +resampled_part1.do_not_gather = 1 +resampled_part1.do_not_push = 1 +resampled_part1.do_resampling = 1 +resampled_part1.resampling_algorithm = leveling_thinning +resampled_part1.resampling_algorithm_target_ratio = 1.3 +# This should trigger resampling at timestep 5 only in this case +resampled_part1.resampling_trigger_intervals = 5 +# This should trigger resampling at first timestep +resampled_part1.resampling_trigger_max_avg_ppc = 395 + +# Second particle species. The particles are only present in one cell and have a Gaussian weight +# distribution. +resampled_part2.species_type = electron +resampled_part2.injection_style = NRandomPerCell +resampled_part2.num_particles_per_cell = 100000 +resampled_part2.zmin = 0. +resampled_part2.zmax = 1. +resampled_part2.xmin = 0. +resampled_part2.xmax = 1. +resampled_part2.profile = parse_density_function +# Trick to get a Gaussian weight distribution is to do a Box-Muller transform using the position +# within the cell as the two random variables. Here, we have a distribution with standard deviation +# sigma = 1/sqrt(2) and mean 4*sigma. +resampled_part2.density_function(x,y,z) = 2.*sqrt(2.)+sqrt(-log(x))*cos(2*pi*z) +resampled_part2.momentum_distribution_type = constant +resampled_part2.do_not_deposit = 1 +resampled_part2.do_not_gather = 1 +resampled_part2.do_not_push = 1 +resampled_part2.do_resampling = 1 +resampled_part2.resampling_algorithm = leveling_thinning +resampled_part2.resampling_algorithm_target_ratio = 1.3 +# This should prevent actual resampling at timestep 7 +resampled_part2.resampling_algorithm_min_ppc = 80000 +# This should trigger resampling at timestep 6 and 7 in this case. The rest is here to test the +# intervals parser syntax. +resampled_part2.resampling_trigger_intervals = 100 :120:3, 6:6:6 , 7 +# This should not trigger resampling: this species only has ~391 ppc on average. +resampled_part2.resampling_trigger_max_avg_ppc = 395 + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.period = 8 +diag1.diag_type = Full diff --git a/Examples/Physics_applications/laser_ion/inputs b/Examples/Physics_applications/laser_ion/inputs new file mode 100644 index 00000000000..fb3f948c02f --- /dev/null +++ b/Examples/Physics_applications/laser_ion/inputs @@ -0,0 +1,195 @@ +################################# +# Domain, Resolution & Numerics +# + +# time-scale with highly kinetic dynamics +stop_time = 0.2e-12 # [s] +# time-scale for converged ion energy +# notes: - effective acc. time depends on laser pulse +# - ions will start to leave the box +#stop_time = 1.0e-12 # [s] + +# quick tests at ultra-low res. (CI) +#amr.n_cell = 384 512 + +# proper resolution for 10 n_c excl. acc. length +# (>=1x V100) +amr.n_cell = 2688 3712 + +# proper resolution for 30 n_c (dx<=3.33nm) incl. acc. length +# (>=6x V100) +#amr.n_cell = 7488 14720 + +# simulation box, no MR +# note: increase z (space & cells) for converging ion energy +amr.max_level = 0 +geometry.prob_lo = -7.5e-6 -5.e-6 +geometry.prob_hi = 7.5e-6 25.e-6 +geometry.is_periodic = 0 0 0 # non-periodic (default) + +# macro-particle shape +interpolation.nox = 3 +interpolation.noy = 3 +interpolation.noz = 3 + +# numerical tuning +warpx.cfl = 0.999 +warpx.use_filter = 1 # bilinear current/charge filter + + +################################# +# Performance Tuning +# +amr.max_grid_size = 64 +amr.blocking_factor = 64 +warpx.do_dynamic_scheduling = 0 + +# load balancing +warpx.load_balance_int = 10 +warpx.load_balance_with_sfc = 0 +#warpx.load_balance_efficiency_ratio_threshold = 1.1 +algo.load_balance_costs_update = Heuristic +#algo.costs_heuristic_particles_wt = 0.9 +#algo.costs_heuristic_cells_wt = 0.1 + +# particle bin-sorting on GPU (ideal defaults not investigated in 2D) +#warpx.sort_int = 4 +#warpx.sort_bin_size = 4 4 4 + + +################################# +# Target Profile +# +particles.species_names = electrons hydrogen + +# particle species +hydrogen.species_type = hydrogen +hydrogen.injection_style = NUniformPerCell +hydrogen.num_particles_per_cell_each_dim = 2 2 4 +hydrogen.momentum_distribution_type = constant +hydrogen.ux = 0.0 +hydrogen.uy = 0.0 +hydrogen.uz = 0.0 +#hydrogen.zmin = -10.0e-6 +#hydrogen.zmax = 10.0e-6 +hydrogen.profile = parse_density_function + +electrons.species_type = electron +electrons.injection_style = NUniformPerCell +electrons.num_particles_per_cell_each_dim = 2 2 4 +electrons.momentum_distribution_type = "gaussian" +electrons.ux_th = .01 +electrons.uz_th = .01 +#electrons.zmin = -10.0e-6 +#electrons.zmax = 10.0e-6 + +# ionization physics (field ionization/ADK) +# [i1] none (fully pre-ionized): +electrons.profile = parse_density_function +# [i2] field ionization (ADK): +#hydrogen.do_field_ionization = 1 +#hydrogen.physical_element = H +#hydrogen.ionization_initial_level = 0 +#hydrogen.ionization_product_species = electrons +#electrons.profile = constant +#electrons.density = 0.0 + +# collisional physics (binary MC model after Nanbu/Perez) +#collisions.collision_names = c_eH c_ee c_HH +#c_eH.species = electrons hydrogen +#c_ee.species = electrons electrons +#c_HH.species = hydrogen hydrogen +#c_eH.CoulombLog = 15.9 +#c_ee.CoulombLog = 15.9 +#c_HH.CoulombLog = 15.9 + +# number density: "fully ionized" electron density as reference +# [material 1] cryogenic H2 +my_constants.nc = 1.742e27 # [m^-3] 1.11485e21 * 1.e6 / 0.8**2 +my_constants.n0 = 30.0 # [n_c] +# [material 2] liquid crystal +#my_constants.n0 = 192 +# [material 3] PMMA +#my_constants.n0 = 230 +# [material 4] Copper (ion density: 8.49e28/m^3; times ionization level) +#my_constants.n0 = 1400 + +# profiles +# pre-plasma +my_constants.L = 0.05e-6 # [1/m] scale length (>0) +my_constants.Lcut = 2.0e-6 # [1/m] hard cutoff from surface +# core: flat foil, cylinder or sphere +my_constants.r0 = 2.5e-6 # [m] radius or half-thickness + +# [target 1] flat foil (thickness = 2*r0) +#electrons.density_function(x,y,z) = "nc*n0*( +# (abs(z)<=r0) + +# (abs(z)>r0)*(abs(z) E_0 = 64.22 TV/m + + +################################# +# Diagnostics +# +diagnostics.diags_names = diag1 openPMDh5 + +diag1.period = 100 +diag1.diag_type = Full +diag1.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho rho_electrons rho_hydrogen +diag1.electrons.variables = w ux uy uz +diag1.hydrogen.variables = w ux uy uz + +openPMDh5.period = 100 +openPMDh5.diag_type = Full +openPMDh5.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho rho_electrons rho_hydrogen +openPMDh5.electrons.variables = w ux uy uz +openPMDh5.hydrogen.variables = w ux uy uz +openPMDh5.format = openpmd +openPMDh5.openpmd_backend = h5 + + +################################# +# Physical Background +# +# This example is modeled after a target similar to the hydrogen jet here: +# [1] https://doi.org/10.1038/s41598-017-10589-3 +# [2] https://arxiv.org/abs/1903.06428 +# +authors = "Axel Huebl " diff --git a/Examples/Physics_applications/plasma_acceleration/PICMI_inputs_plasma_acceleration_mr.py b/Examples/Physics_applications/plasma_acceleration/PICMI_inputs_plasma_acceleration_mr.py index 665c78bc741..ceef8cc3754 100644 --- a/Examples/Physics_applications/plasma_acceleration/PICMI_inputs_plasma_acceleration_mr.py +++ b/Examples/Physics_applications/plasma_acceleration/PICMI_inputs_plasma_acceleration_mr.py @@ -33,7 +33,7 @@ hi = [25e-6, 25e-6, 200.e-6]) solver = picmi.ElectromagneticSolver(grid=grid, cfl=1, - warpx_do_pml = 1, + warpx_do_pml = True, warpx_pml_ncell = 10) beam_distribution = picmi.UniformDistribution(density = 1.e23, diff --git a/Examples/Tests/Langmuir/PICMI_inputs_langmuir_rz_multimode_analyze.py b/Examples/Tests/Langmuir/PICMI_inputs_langmuir_rz_multimode_analyze.py index e820a87db26..e947d9fc1da 100644 --- a/Examples/Tests/Langmuir/PICMI_inputs_langmuir_rz_multimode_analyze.py +++ b/Examples/Tests/Langmuir/PICMI_inputs_langmuir_rz_multimode_analyze.py @@ -89,7 +89,7 @@ moving_window_zvelocity = 0., warpx_max_grid_size=64) -solver = picmi.ElectromagneticSolver(grid=grid, cfl=1., warpx_do_pml=0) +solver = picmi.ElectromagneticSolver(grid=grid, cfl=1., warpx_do_pml=False) ########################## # diagnostics diff --git a/Examples/Tests/Langmuir/analysis_langmuir_multi_rz.py b/Examples/Tests/Langmuir/analysis_langmuir_multi_rz.py index e98c24d7e54..9e39428038e 100755 --- a/Examples/Tests/Langmuir/analysis_langmuir_multi_rz.py +++ b/Examples/Tests/Langmuir/analysis_langmuir_multi_rz.py @@ -13,6 +13,7 @@ # $$ E_r = -\partial_r \phi = \epsilon \,\frac{mc^2}{e}\frac{2\,r}{w_0^2} \exp\left(-\frac{r^2}{w_0^2}\right) \sin(k_0 z) \sin(\omega_p t) # $$ E_z = -\partial_z \phi = - \epsilon \,\frac{mc^2}{e} k_0 \exp\left(-\frac{r^2}{w_0^2}\right) \cos(k_0 z) \sin(\omega_p t) import sys +import re import matplotlib matplotlib.use('Agg') import matplotlib.pyplot as plt @@ -28,6 +29,9 @@ test_name = fn[:-9] # Could also be os.path.split(os.getcwd())[1] +# Parse test name and check if current correction (psatd.current_correction) is applied +current_correction = True if re.search('current_correction', fn) else False + # Parameters (these parameters must match the parameters in `inputs.multi.rz.rt`) epsilon = 0.01 n = 2.e24 @@ -102,11 +106,26 @@ def Ez( z, r, epsilon, k0, w0, wp, t) : plt.savefig(test_name+'_analysis.png') error_rel = overall_max_error -tolerance_rel = 0.04 + +if current_correction: + tolerance_rel = 0.06 +else: + tolerance_rel = 0.04 print("error_rel : " + str(error_rel)) print("tolerance_rel: " + str(tolerance_rel)) assert( error_rel < tolerance_rel ) +# Check charge conservation (relative L-infinity norm of error) with current correction +if current_correction: + divE = data['divE'].to_ndarray() + rho = data['rho' ].to_ndarray() / epsilon_0 + error_rel = np.amax(np.abs(divE - rho)) / max(np.amax(divE), np.amax(rho)) + tolerance = 1.e-9 + print("Check charge conservation:") + print("error_rel = {}".format(error_rel)) + print("tolerance = {}".format(tolerance)) + assert( error_rel < tolerance ) + checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Tests/collision/inputs_3d b/Examples/Tests/collision/inputs_3d index c09beb10fb1..6cea1db9413 100644 --- a/Examples/Tests/collision/inputs_3d +++ b/Examples/Tests/collision/inputs_3d @@ -59,6 +59,9 @@ collision3.species = ion ion collision1.CoulombLog = 15.9 collision2.CoulombLog = 15.9 collision3.CoulombLog = 15.9 +collision1.ndt = 10 +collision2.ndt = 10 +collision3.ndt = 10 # Diagnostics diagnostics.diags_names = diag1 diff --git a/Examples/Tests/galilean/analysis_2d.py b/Examples/Tests/galilean/analysis_2d.py index a5fdd15fb77..ac4ea2efdbb 100755 --- a/Examples/Tests/galilean/analysis_2d.py +++ b/Examples/Tests/galilean/analysis_2d.py @@ -24,11 +24,13 @@ filename = sys.argv[1] +# Parse test name +averaged = True if re.search( 'averaged', filename ) else False +current_correction = True if re.search( 'current_correction', filename ) else False +dims_RZ = True if re.search('rz', filename) else False ds = yt.load( filename ) -averaged = True if re.search( 'averaged', filename ) else False - Ex= ds.index.grids[0]['boxlib', 'Ex'].squeeze().v Ey= ds.index.grids[0]['boxlib', 'Ey'].squeeze().v Ez= ds.index.grids[0]['boxlib', 'Ez'].squeeze().v @@ -37,10 +39,24 @@ # energyE_ref was calculated with Galilean PSATD method (v_galilean = (0,0,0.99498743710662)) energyE_ref = 26913.546573259937 tolerance_rel = 1e-5 -else: +elif (not dims_RZ and not current_correction): # energyE_ref was calculated with standard PSATD method (v_galilean = (0.,0.,0.)) energyE_ref = 38362.88743899688 - tolerance_rel = 1e-8; + tolerance_rel = 1e-8 +elif (not dims_RZ and current_correction): + # energyE_ref was calculated with standard PSATD method (v_galilean = (0.,0.,0.)): + # difference with respect to reference energy above due to absence of real-space filter + energyE_ref = 745973.5742103161 + tolerance_rel = 1e-8 +elif (dims_RZ and not current_correction): + # energyE_ref was calculated with standard PSATD method (v_galilean = (0.,0.,0.)) + energyE_ref = 178013.54481470847 + tolerance_rel = 1e-8 +elif (dims_RZ and current_correction): + # energyE_ref was calculated with standard PSATD method (v_galilean = (0.,0.,0.)) + # difference with respect to reference energy above due to absence of k-space filter + energyE_ref = 10955626.277865639 + tolerance_rel = 1e-8 energyE = np.sum(scc.epsilon_0/2*(Ex**2+Ey**2+Ez**2)) @@ -51,5 +67,16 @@ assert( error_rel < tolerance_rel ) +# Check charge conservation (relative L-infinity norm of error) with current correction +if current_correction: + divE = ds.index.grids[0]['boxlib', 'divE'].squeeze().v + rho = ds.index.grids[0]['boxlib', 'rho' ].squeeze().v / scc.epsilon_0 + error_rel = np.amax(np.abs(divE - rho)) / max(np.amax(divE), np.amax(rho)) + tolerance = 1e-9 + print("Check charge conservation:") + print("error_rel = {}".format(error_rel)) + print("tolerance = {}".format(tolerance)) + assert( error_rel < tolerance ) + test_name = filename[:-9] # Could also be os.path.split(os.getcwd())[1] checksumAPI.evaluate_checksum(test_name, filename) diff --git a/Examples/Tests/galilean/analysis_3d.py b/Examples/Tests/galilean/analysis_3d.py index e3c2d827c63..83f82d70da0 100755 --- a/Examples/Tests/galilean/analysis_3d.py +++ b/Examples/Tests/galilean/analysis_3d.py @@ -24,7 +24,9 @@ filename = sys.argv[1] +# Parse test name averaged = True if re.search( 'averaged', filename ) else False +current_correction = True if re.search( 'current_correction', filename ) else False ds = yt.load( filename ) @@ -36,6 +38,11 @@ # energyE_ref was calculated with Galilean PSATD method (v_galilean = (0,0,0.99498743710662)) energyE_ref = 460315.9845556079 tolerance_rel = 1e-4 +elif (current_correction): + # energyE_ref was calculated with standard PSATD method (v_galilean = (0.,0.,0.)): + # difference with respect to reference energy below due to absence of filter + energyE_ref = 1632431.0574508277 + tolerance_rel = 1e-7; else: # energyE_ref was calculated with standard PSATD method (v_galilean = (0.,0.,0.)) energyE_ref = 8218.678808709019 @@ -50,5 +57,16 @@ assert( error_rel < tolerance_rel ) +# Check charge conservation (relative L-infinity norm of error) with current correction +if current_correction: + rho = ds.index.grids[0]['boxlib', 'rho' ].squeeze().v + divE = ds.index.grids[0]['boxlib', 'divE'].squeeze().v + error_rel = np.amax( np.abs( divE - rho/scc.epsilon_0 ) ) / np.amax( np.abs( rho/scc.epsilon_0 ) ) + tolerance = 1e-9 + print("Check charge conservation:") + print("error_rel = {}".format(error_rel)) + print("tolerance = {}".format(tolerance)) + assert( error_rel < tolerance ) + test_name = filename[:-9] # Could also be os.path.split(os.getcwd())[1] checksumAPI.evaluate_checksum(test_name, filename) diff --git a/Examples/Tests/galilean/inputs_rz b/Examples/Tests/galilean/inputs_rz new file mode 100644 index 00000000000..ceaba5af945 --- /dev/null +++ b/Examples/Tests/galilean/inputs_rz @@ -0,0 +1,78 @@ +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 400 + +amr.n_cell = 64 128 +amr.max_grid_size = 128 +amr.blocking_factor = 128 +amr.max_level = 0 +psatd.v_galilean = 0. 0. 0.99498743710662 + +geometry.coord_sys = 1 +geometry.is_periodic = 0 1 +geometry.prob_lo = 0. -38.68 +geometry.prob_hi = 38.68 38.68 + +################################# +############ NUMERICS ########### +################################# +warpx.verbose = 1 + +algo.current_deposition = direct +algo.particle_pusher = vay + +warpx.cfl = 1. +interpolation.nox = 3 +interpolation.noy = 3 +interpolation.noz = 3 + + +################################# +############ PLASMA ############# +################################# +particles.species_names = electrons ions + +warpx.do_nodal = 1 +warpx.use_kspace_filter = 1 +warpx.do_pml = 0 + +psatd.nox = 16 +psatd.noy = 16 +psatd.noz = 16 + +electrons.charge = -q_e +electrons.mass = m_e +electrons.injection_style = "NUniformPerCell" +electrons.num_particles_per_cell_each_dim = 2 2 +electrons.profile = constant +electrons.density = 282197938148984.7 +electrons.momentum_distribution_type = "gaussian" +electrons.uz_m = 9.9498743710661994 +electrons.xmin = 0. +electrons.xmax = 38.68 +electrons.zmin = -38.68 +electrons.zmax = 38.68 +electrons.ux_th = 0.0001 +electrons.uz_th = 0.0001 + +ions.charge = q_e +ions.mass = m_p +ions.injection_style = "NUniformPerCell" +ions.num_particles_per_cell_each_dim = 2 2 +ions.profile = constant +ions.density = 282197938148984.7 +ions.momentum_distribution_type = "gaussian" +ions.uz_m = 9.9498743710661994 +ions.xmin = 0. +ions.xmax = 38.68 +ions.zmin = -38.68 +ions.zmax =38.68 +ions.ux_th = 0.0001 +ions.uz_th = 0.0001 + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.period = 100 +diag1.diag_type = Full +diag1.fields_to_plot = Ex Ey Ez By jx jz rho divE diff --git a/Examples/Tests/reduced_diags/analysis_reduced_diags.py b/Examples/Tests/reduced_diags/analysis_reduced_diags.py index b615228b628..cc0c10dd778 100755 --- a/Examples/Tests/reduced_diags/analysis_reduced_diags.py +++ b/Examples/Tests/reduced_diags/analysis_reduced_diags.py @@ -8,11 +8,13 @@ # This script tests the reduced diagnostics. # The setup is a uniform plasma with electrons, protons and photons. -# Particle energy and field energy will be outputed -# using the reduced diagnostics. +# Particle energy, field energy and maximum field values will be +# outputed using the reduced diagnostics. # And they will be compared with the data in the plotfiles. -# Tolerance: 1.0e-8 for particle energy, 1.0e-3 for field energy. +# Tolerance: 1.0e-8 for particle energy, 1.0e-3 for field energy, +# 1.0e-9 for the maximum electric field and 1.0e-18 for the +# maximum magnetic field. # The difference of the field energy is relatively large, # because fields data in plotfiles are cell-centered, # but fields data in reduced diagnostics are staggered. @@ -23,6 +25,7 @@ import yt import numpy as np import scipy.constants as scc +import read_raw_data sys.path.insert(1, '../../../../warpx/Regression/Checksum/') import checksumAPI @@ -40,12 +43,15 @@ pz = ad['electrons','particle_momentum_z'].to_ndarray() w = ad['electrons','particle_weight'].to_ndarray() EPyt = EPyt + np.sum( (np.sqrt((px**2+py**2+pz**2)*scc.c**2+scc.m_e**2*scc.c**4)-scc.m_e*scc.c**2)*w ) +num_electron = w.shape[0] + # proton px = ad['protons','particle_momentum_x'].to_ndarray() py = ad['protons','particle_momentum_y'].to_ndarray() pz = ad['protons','particle_momentum_z'].to_ndarray() w = ad['protons','particle_weight'].to_ndarray() EPyt = EPyt + np.sum( (np.sqrt((px**2+py**2+pz**2)*scc.c**2+scc.m_p**2*scc.c**4)-scc.m_p*scc.c**2)*w ) +num_proton = w.shape[0] # photon px = ad['photons','particle_momentum_x'].to_ndarray() @@ -53,6 +59,23 @@ pz = ad['photons','particle_momentum_z'].to_ndarray() w = ad['photons','particle_weight'].to_ndarray() EPyt = EPyt + np.sum( (np.sqrt(px**2+py**2+pz**2)*scc.c)*w ) +num_photon = w.shape[0] +num_total = num_electron + num_proton + num_photon + +# Use raw field plotfiles to compare with maximum field reduced diag +ad_raw = read_raw_data.read_data(fn) +Ex_raw = ad_raw[0]['Ex_aux'] +Ey_raw = ad_raw[0]['Ey_aux'] +Ez_raw = ad_raw[0]['Ez_aux'] +Bx_raw = ad_raw[0]['Bx_aux'] +By_raw = ad_raw[0]['By_aux'] +Bz_raw = ad_raw[0]['Bz_aux'] +max_Ex = np.amax(np.abs(Ex_raw)) +max_Ey = np.amax(np.abs(Ey_raw)) +max_Ez = np.amax(np.abs(Ez_raw)) +max_Bx = np.amax(np.abs(Bx_raw)) +max_By = np.amax(np.abs(By_raw)) +max_Bz = np.amax(np.abs(Bz_raw)) ad = ds.covering_grid(level=0, left_edge=ds.domain_left_edge, dims=ds.domain_dimensions) Ex = ad['Ex'].to_ndarray() @@ -61,6 +84,10 @@ Bx = ad['Bx'].to_ndarray() By = ad['By'].to_ndarray() Bz = ad['Bz'].to_ndarray() +# Maximum value of |E| and |B| are obtained after interpolation so we do not use raw fields for +# these +max_E = np.sqrt(np.amax(Ex**2+Ey**2+Ez**2)) +max_B = np.sqrt(np.amax(Bx**2+By**2+Bz**2)) Es = np.sum(Ex**2)+np.sum(Ey**2)+np.sum(Ez**2) Bs = np.sum(Bx**2)+np.sum(By**2)+np.sum(Bz**2) N = np.array( ds.domain_width / ds.domain_dimensions ) @@ -71,18 +98,46 @@ EFdata = np.genfromtxt("./diags/reducedfiles/EF.txt") EPdata = np.genfromtxt("./diags/reducedfiles/EP.txt") +MFdata = np.genfromtxt("./diags/reducedfiles/MF.txt") +NPdata = np.genfromtxt("./diags/reducedfiles/NP.txt") EF = EFdata[1][2] EP = EPdata[1][2] +max_Exdata = MFdata[1][2] +max_Eydata = MFdata[1][3] +max_Ezdata = MFdata[1][4] +max_Edata = MFdata[1][5] +max_Bxdata = MFdata[1][6] +max_Bydata = MFdata[1][7] +max_Bzdata = MFdata[1][8] +max_Bdata = MFdata[1][9] +num_total_data = NPdata[1][2] +num_electron_data = NPdata[1][3] +num_proton_data = NPdata[1][4] +num_photon_data = NPdata[1][5] # PART3: print and assert +max_diffEmax = max(abs(max_Exdata-max_Ex),abs(max_Eydata-max_Ey), + abs(max_Ezdata-max_Ez),abs(max_Edata-max_E)) +max_diffBmax = max(abs(max_Bxdata-max_Bx),abs(max_Bydata-max_By), + abs(max_Bzdata-max_Bz),abs(max_Bdata-max_B)) +max_diff_number = max(abs(num_total_data-num_total),abs(num_electron_data-num_electron), + abs(num_proton_data-num_proton),abs(num_photon_data-num_photon)) + print('difference of field energy:', abs(EFyt-EF)) print('tolerance of field energy:', 1.0e-3) print('difference of particle energy:', abs(EPyt-EP)) print('tolerance of particle energy:', 1.0e-8) +print('maximum difference of maximum electric field:', max_diffEmax) +print('tolerance of maximum electric field difference:', 1.0e-9) +print('maximum difference of maximum magnetic field:', max_diffBmax) +print('tolerance of maximum magnetic field difference:', 1.0e-18) assert(abs(EFyt-EF) < 1.0e-3) assert(abs(EPyt-EP) < 1.0e-8) +assert(max_diffEmax < 1.0e-9) +assert(max_diffBmax < 1.0e-18) +assert(max_diff_number < 0.5) test_name = fn[:-9] # Could also be os.path.split(os.getcwd())[1] checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Tests/reduced_diags/inputs b/Examples/Tests/reduced_diags/inputs index 61666967fac..de2b8b37f9d 100644 --- a/Examples/Tests/reduced_diags/inputs +++ b/Examples/Tests/reduced_diags/inputs @@ -71,13 +71,18 @@ photons.uz_th = 0.2 ################################# ###### REDUCED DIAGS ############ ################################# -warpx.reduced_diags_names = EP EF +warpx.reduced_diags_names = EP NP EF MF EP.type = ParticleEnergy EP.frequency = 200 EF.type = FieldEnergy EF.frequency = 200 +MF.type = FieldMaximum +MF.frequency = 200 +NP.type = ParticleNumber +NP.frequency = 200 # Diagnostics diagnostics.diags_names = diag1 diag1.period = 200 diag1.diag_type = Full +diag1.plot_raw_fields = 1 diff --git a/Python/pywarpx/Bucket.py b/Python/pywarpx/Bucket.py index 7817f8f8647..61276c75ab0 100644 --- a/Python/pywarpx/Bucket.py +++ b/Python/pywarpx/Bucket.py @@ -60,6 +60,8 @@ def attrlist(self): # --- For lists, tuples, and arrays make a space delimited string of the values. # --- The lambda is needed in case this is a list of strings. rhs = ' '.join(map(lambda s : repr(s).strip("'\""), value)) + elif isinstance(value, bool): + rhs = 1 if value else 0 else: rhs = value attrstring = '{0}.{1} = {2}'.format(self.instancename, attr, repr(rhs).strip("'\"")) diff --git a/Python/pywarpx/Constants.py b/Python/pywarpx/Constants.py index 8a17c9ccc74..1ae1dda0d77 100644 --- a/Python/pywarpx/Constants.py +++ b/Python/pywarpx/Constants.py @@ -4,6 +4,8 @@ # # License: BSD-3-Clause-LBNL +import re + from .Bucket import Bucket class Constants(Bucket): @@ -16,7 +18,36 @@ def __init__(self): def __setattr__(self, name, value): # Make sure that any constants redefined have a consistent value if name in self.argvattrs: - assert self.argvattrs[name] == value, Exception('An consistent values given for user defined constants') + assert self.argvattrs[name] == value, Exception('Inconsistent values given for user defined constants') Bucket.__setattr__(self, name, value) + def add_keywords(self, kwdict): + mangle_dict = {} + for k,v in kwdict.items(): + # Check if keyword has already been defined + # WarpX has a single global dictionary of expression variables so each + # variable must be unique + if k in self.argvattrs: + # if so, mangle the name by appending a numerical suffix + mangle_number = 1 + k_mangled = f'{k}{mangle_number}' + while k_mangled in self.argvattrs: + # make sure that the mangled name has also not already been defined + mangle_number += 1 + k_mangled = f'{k}{mangle_number}' + mangle_dict[k] = k_mangled + k = k_mangled + setattr(self, k, v) + return mangle_dict + + def mangle_expression(self, expression, mangle_dict): + if expression is None: + return None + # For each key in mangle_dict, modify the expression replacing + # the key with its value, the mangled version of key + for k,v in mangle_dict.items(): + expression = re.sub(r'\b%s\b'%k, v, expression) + return expression + + my_constants = Constants() diff --git a/Python/pywarpx/Particles.py b/Python/pywarpx/Particles.py index 3d08e1fa528..15d439a9627 100644 --- a/Python/pywarpx/Particles.py +++ b/Python/pywarpx/Particles.py @@ -6,7 +6,7 @@ from .Bucket import Bucket -particles = Bucket('particles', species_names=[]) +particles = Bucket('particles', species_names=[], rigid_injected_species=[]) particles_list = [] electrons = Bucket('electrons') diff --git a/Python/pywarpx/picmi.py b/Python/pywarpx/picmi.py index 0579b43373c..e6d3f53a968 100644 --- a/Python/pywarpx/picmi.py +++ b/Python/pywarpx/picmi.py @@ -64,7 +64,10 @@ def init(self, kw): if self.mass is None: self.mass = element.mass*periodictable.constants.atomic_mass_constant - def initialize_inputs(self, layout, initialize_self_fields=False): + def initialize_inputs(self, layout, + initialize_self_fields = False, + injection_plane_position = None, + injection_plane_normal_vector = None): self.species_number = len(pywarpx.particles.species_names) if self.name is None: @@ -82,6 +85,14 @@ def initialize_inputs(self, layout, initialize_self_fields=False): if self.initial_distribution is not None: self.initial_distribution.initialize_inputs(self.species_number, layout, self.species, self.density_scale) + if injection_plane_position is not None: + if injection_plane_normal_vector is not None: + assert injection_plane_normal_vector[0] == 0. and injection_plane_normal_vector[1] == 0.,\ + Exception('Rigid injection can only be done along z') + pywarpx.particles.rigid_injected_species.append(self.name) + self.species.rigid_advance = 1 + self.species.zinject_plane = injection_plane_position + for interaction in self.interactions: assert interaction[0] == 'ionization' assert interaction[1] == 'ADK', 'WarpX only has ADK ionization model implemented' @@ -203,6 +214,9 @@ def initialize_inputs(self, species_number, layout, species, density_scale): class AnalyticDistribution(picmistandard.PICMI_AnalyticDistribution): + def init(self, kw): + self.mangle_dict = None + def initialize_inputs(self, species_number, layout, species, density_scale): if isinstance(layout, GriddedLayout): @@ -223,14 +237,17 @@ def initialize_inputs(self, species_number, layout, species, density_scale): species.zmin = self.lower_bound[2] species.zmax = self.upper_bound[2] + if self.mangle_dict is None: + # Only do this once so that the same variables are used in this distribution + # is used multiple times + self.mangle_dict = pywarpx.my_constants.add_keywords(self.user_defined_kw) + expression = pywarpx.my_constants.mangle_expression(self.density_expression, self.mangle_dict) + species.profile = "parse_density_function" if density_scale is None: - species.__setattr__('density_function(x,y,z)', self.density_expression) + species.__setattr__('density_function(x,y,z)', expression) else: - species.__setattr__('density_function(x,y,z)', "{}*({})".format(density_scale, self.density_expression)) - - for k,v in self.user_defined_kw.items(): - setattr(pywarpx.my_constants, k, v) + species.__setattr__('density_function(x,y,z)', "{}*({})".format(density_scale, expression)) # --- Note that WarpX takes gamma*beta as input if np.any(np.not_equal(self.momentum_expressions, None)): @@ -254,23 +271,17 @@ def initialize_inputs(self, species_number, layout, species, density_scale): species.do_continuous_injection = 1 def setup_parse_momentum_functions(self, species): - if self.momentum_expressions[0] is not None: - species.__setattr__('momentum_function_ux(x,y,z)', '({0})/{1}'.format(self.momentum_expressions[0], constants.c)) - else: - species.__setattr__('momentum_function_ux(x,y,z)', '({0})/{1}'.format(self.directed_velocity[0], constants.c)) - if self.momentum_expressions[1] is not None: - species.__setattr__('momentum_function_uy(x,y,z)', '({0})/{1}'.format(self.momentum_expressions[1], constants.c)) - else: - species.__setattr__('momentum_function_uy(x,y,z)', '({0})/{1}'.format(self.directed_velocity[1], constants.c)) - if self.momentum_expressions[2] is not None: - species.__setattr__('momentum_function_uz(x,y,z)', '({0})/{1}'.format(self.momentum_expressions[2], constants.c)) - else: - species.__setattr__('momentum_function_uz(x,y,z)', '({0})/{1}'.format(self.directed_velocity[2], constants.c)) + for sdir, idir in zip(['x', 'y', 'z'], [0, 1, 2]): + if self.momentum_expressions[idir] is not None: + expression = pywarpx.my_constants.mangle_expression(self.momentum_expressions[idir], self.mangle_dict) + else: + expression = f'{self.directed_velocity[idir]}' + species.__setattr__(f'momentum_function_u{sdir}(x,y,z)', f'({expression})/{constants.c}') class ParticleListDistribution(picmistandard.PICMI_ParticleListDistribution): def init(self, kw): - if len(x) > 1: + if len(self.x) > 1: raise Exception('Only a single particle can be loaded') def initialize_inputs(self, species_number, layout, species, density_scale): @@ -303,14 +314,13 @@ def init(self, kw): class BinomialSmoother(picmistandard.PICMI_BinomialSmoother): - def init(self, kw): - self.use_spectral = kw.pop('warpx_kspace_filter', None) - self.use_spectral_compensation = kw.pop('warpx_kspace_filter_compensation', None) def initialize_inputs(self, solver): - if self.use_spectral: + use_spectral = solver.method == 'PSATD' and isinstance(solver.grid, picmistandard.PICMI_CylindricalGrid) + if use_spectral: pywarpx.warpx.use_kspace_filter = 1 - pywarpx.warpx.use_filter_compensation = self.use_spectral_compensation + if self.compensation is not None: + pywarpx.warpx.use_filter_compensation = self.compensation[0] and self.compensation[1] else: pywarpx.warpx.use_filter = 1 if self.n_pass is None: @@ -458,9 +468,12 @@ def init(self, kw): self.pml_ncell = kw.pop('warpx_pml_ncell', None) if self.method == 'PSATD': - self.periodic_single_box_fft = kw.pop('warpx_periodic_single_box_fft', None) - self.fftw_plan_measure = kw.pop('warpx_fftw_plan_measure', None) - self.current_correction = kw.pop('warpx_current_correction', None) + self.psatd_periodic_single_box_fft = kw.pop('warpx_periodic_single_box_fft', None) + self.psatd_fftw_plan_measure = kw.pop('warpx_fftw_plan_measure', None) + self.psatd_current_correction = kw.pop('warpx_current_correction', None) + self.psatd_update_with_rho = kw.pop('warpx_psatd_update_with_rho', None) + self.psatd_do_time_averaging = kw.pop('warpx_psatd_do_time_averaging', None) + self.psatd_use_damp_fields_in_z_guard = kw.pop('warpx_use_damp_fields_in_z_guard', None) def initialize_inputs(self): @@ -471,16 +484,28 @@ def initialize_inputs(self): pywarpx.warpx.do_nodal = self.l_nodal if self.method == 'PSATD': - pywarpx.psatd.periodic_single_box_fft = self.periodic_single_box_fft - pywarpx.psatd.fftw_plan_measure = self.fftw_plan_measure - pywarpx.psatd.current_correction = self.current_correction + pywarpx.psatd.periodic_single_box_fft = self.psatd_periodic_single_box_fft + pywarpx.psatd.fftw_plan_measure = self.psatd_fftw_plan_measure + pywarpx.psatd.current_correction = self.psatd_current_correction + pywarpx.psatd.update_with_rho = self.psatd_update_with_rho + pywarpx.psatd.do_time_averaging = self.psatd_do_time_averaging + pywarpx.psatd.use_damp_fields_in_z_guard = self.psatd_use_damp_fields_in_z_guard + + if self.grid.guard_cells is not None: + pywarpx.psatd.nx_guard = self.grid.guard_cells[0] + if self.grid.number_of_dimensions == 3: + pywarpx.psatd.ny_guard = self.grid.guard_cells[1] + pywarpx.psatd.nz_guard = self.grid.guard_cells[-1] if self.stencil_order is not None: pywarpx.psatd.nox = self.stencil_order[0] - pywarpx.psatd.noy = self.stencil_order[1] - pywarpx.psatd.noz = self.stencil_order[2] + if self.grid.number_of_dimensions == 3: + pywarpx.psatd.noy = self.stencil_order[1] + pywarpx.psatd.noz = self.stencil_order[-1] if self.galilean_velocity is not None: + if self.grid.number_of_dimensions == 2: + self.galilean_velocity = [self.galilean_velocity[0], 0., self.galilean_velocity[1]] pywarpx.psatd.v_galilean = np.array(self.galilean_velocity)/constants.c else: @@ -516,9 +541,14 @@ def initialize_inputs(self): self.laser.zeta = self.zeta self.laser.beta = self.beta self.laser.phi2 = self.phi2 + self.laser.phi0 = self.phi0 + self.laser.do_continuous_injection = self.fill_in class AnalyticLaser(picmistandard.PICMI_AnalyticLaser): + def init(self, kw): + self.mangle_dict = None + def initialize_inputs(self): self.laser_number = len(pywarpx.lasers.names) + 1 if self.name is None: @@ -530,11 +560,14 @@ def initialize_inputs(self): self.laser.wavelength = self.wavelength # The wavelength of the laser (in meters) self.laser.e_max = self.Emax # Maximum amplitude of the laser field (in V/m) self.laser.polarization = self.polarization_direction # The main polarization vector - self.laser.__setattr__('field_function(X,Y,t)', self.field_expression) - - for k,v in self.user_defined_kw.items(): - setattr(pywarpx.my_constants, k, v) + self.laser.do_continuous_injection = self.fill_in + if self.mangle_dict is None: + # Only do this once so that the same variables are used in this distribution + # is used multiple times + self.mangle_dict = pywarpx.my_constants.add_keywords(self.user_defined_kw) + expression = pywarpx.my_constants.mangle_expression(self.field_expression, self.mangle_dict) + self.laser.__setattr__('field_function(X,Y,t)', expression) class LaserAntenna(picmistandard.PICMI_LaserAntenna): def initialize_inputs(self, laser): @@ -545,6 +578,68 @@ def initialize_inputs(self, laser): laser.laser.profile_t_peak = (self.position[2] - laser.centroid_position[2])/constants.c # The time at which the laser reaches its peak (in seconds) +class ConstantAppliedField(picmistandard.PICMI_ConstantAppliedField): + def initialize_inputs(self): + # Note that lower and upper_bound are not used by WarpX + + if (self.Ex is not None or + self.Ey is not None or + self.Ez is not None): + pywarpx.particles.E_ext_particle_init_style = 'constant' + pywarpx.particles.E_external_particle = [self.Ex or 0., self.Ey or 0., self.Ez or 0.] + + if (self.Bx is not None or + self.By is not None or + self.Bz is not None): + pywarpx.particles.B_ext_particle_init_style = 'constant' + pywarpx.particles.B_external_particle = [self.Bx or 0., self.By or 0., self.Bz or 0.] + + +class AnalyticAppliedField(picmistandard.PICMI_AnalyticAppliedField): + def init(self, kw): + self.mangle_dict = None + + def initialize_inputs(self): + # Note that lower and upper_bound are not used by WarpX + + if self.mangle_dict is None: + # Only do this once so that the same variables are used in this distribution + # is used multiple times + self.mangle_dict = pywarpx.my_constants.add_keywords(self.user_defined_kw) + + if (self.Ex_expression is not None or + self.Ey_expression is not None or + self.Ez_expression is not None): + pywarpx.particles.E_ext_particle_init_style = 'parse_e_ext_particle_function' + for sdir, expression in zip(['x', 'y', 'z'], [self.Ex_expression, self.Ey_expression, self.Ez_expression]): + expression = pywarpx.my_constants.mangle_expression(expression, self.mangle_dict) + pywarpx.particles.__setattr__(f'E{sdir}_external_particle_function(x,y,z,t)', expression) + + if (self.Bx_expression is not None or + self.By_expression is not None or + self.Bz_expression is not None): + pywarpx.particles.B_ext_particle_init_style = 'parse_b_ext_particle_function' + for sdir, expression in zip(['x', 'y', 'z'], [self.Bx_expression, self.By_expression, self.Bz_expression]): + expression = pywarpx.my_constants.mangle_expression(expression, self.mangle_dict) + pywarpx.particles.__setattr__(f'B{sdir}_external_particle_function(x,y,z,t)', expression) + + +class Mirror(picmistandard.PICMI_Mirror): + def initialize_inputs(self): + try: + pywarpx.warpx.num_mirrors + except AttributeError: + pywarpx.warpx.num_mirrors = 0 + pywarpx.warpx.mirror_z = [] + pywarpx.warpx.mirror_z_width = [] + pywarpx.warpx.mirror_z_npoints = [] + + pywarpx.warpx.num_mirrors += 1 + pywarpx.warpx.mirror_z.append(self.z_front_location) + pywarpx.warpx.mirror_z_width.append(self.depth) + pywarpx.warpx.mirror_z_npoints.append(self.number_of_cells) + + class Simulation(picmistandard.PICMI_Simulation): def init(self, kw): @@ -609,12 +704,18 @@ def initialize_inputs(self): self.solver.initialize_inputs() for i in range(len(self.species)): - self.species[i].initialize_inputs(self.layouts[i], self.initialize_self_fields[i]) + self.species[i].initialize_inputs(self.layouts[i], + self.initialize_self_fields[i], + self.injection_plane_positions[i], + self.injection_plane_normal_vectors[i]) for i in range(len(self.lasers)): self.lasers[i].initialize_inputs() self.laser_injection_methods[i].initialize_inputs(self.lasers[i]) + for applied_field in self.applied_fields: + applied_field.initialize_inputs() + for diagnostic in self.diagnostics: diagnostic.initialize_inputs() @@ -762,6 +863,17 @@ def init(self, kw): self.uniform_stride = kw.pop('warpx_uniform_stride', None) self.plot_filter_function = kw.pop('warpx_plot_filter_function', None) + self.user_defined_kw = {} + if self.plot_filter_function is not None: + # This allows variables to be used in the plot_filter_function, but + # in order not to break other codes, the variables must begin with "warpx_" + for k in list(kw.keys()): + if k.startswith('warpx_') and re.search(r'\b%s\b'%k, self.plot_filter_function): + self.user_defined_kw[k] = kw[k] + del kw[k] + + self.mangle_dict = None + def initialize_inputs(self): name = getattr(self, 'name', None) @@ -813,12 +925,18 @@ def initialize_inputs(self): else: species_list = [species] + if self.mangle_dict is None: + # Only do this once so that the same variables are used in this distribution + # is used multiple times + self.mangle_dict = pywarpx.my_constants.add_keywords(self.user_defined_kw) + for specie in species_list: diag = pywarpx.Bucket.Bucket(self.name + '.' + specie.name, variables = variables, random_fraction = self.random_fraction, uniform_stride = self.uniform_stride) - diag.__setattr__('plot_filter_function(t,x,y,z,ux,uy,uz)', self.plot_filter_function) + expression = pywarpx.my_constants.mangle_expression(self.plot_filter_function, self.mangle_dict) + diag.__setattr__('plot_filter_function(t,x,y,z,ux,uy,uz)', expression) self.diagnostic._species_dict[specie.name] = diag # ---------------------------- diff --git a/Python/setup.py b/Python/setup.py index 993ba70d5c1..2999ce9111c 100644 --- a/Python/setup.py +++ b/Python/setup.py @@ -33,6 +33,6 @@ package_dir = {'pywarpx':'pywarpx'}, description = """Wrapper of WarpX""", package_data = package_data, - install_requires=['numpy', 'picmistandard==0.0.8', 'periodictable'], + install_requires=['numpy', 'picmistandard==0.0.12', 'periodictable'], python_requires = '>=3.6' ) diff --git a/README.md b/README.md index d8205818d53..bcb1ccb1d0e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # WarpX -[![Code Status development](https://img.shields.io/travis/ECP-WarpX/WarpX/development.svg?label=development)](https://travis-ci.com/ECP-WarpX/WarpX/branches) +[![Code Status development](https://dev.azure.com/ECP-WarpX/WarpX/_apis/build/status/ECP-WarpX.WarpX?branchName=development)](https://dev.azure.com/ECP-WarpX/WarpX/_build/latest?definitionId=1&branchName=development) [![Documentation Status](https://readthedocs.org/projects/warpx/badge/?version=latest)](https://warpx.readthedocs.io/en/latest/?badge=latest) [![GitHub commits since last release](https://img.shields.io/github/commits-since/ECP-WarpX/WarpX/latest/development.svg)](https://github.com/ECP-WarpX/WarpX/compare/development) [![Language](https://img.shields.io/badge/language-C%2B%2B14-orange.svg)](https://isocpp.org/) diff --git a/Regression/Checksum/benchmark.py b/Regression/Checksum/benchmark.py index 2cd71c360b6..47f2535cd8a 100644 --- a/Regression/Checksum/benchmark.py +++ b/Regression/Checksum/benchmark.py @@ -1,3 +1,11 @@ +""" +Copyright 2020 + +This file is part of WarpX. + +License: BSD-3-Clause-LBNL +""" + import config import json import os diff --git a/Regression/Checksum/benchmarks_json/Langmuir_multi_rz_psatd_current_correction.json b/Regression/Checksum/benchmarks_json/Langmuir_multi_rz_psatd_current_correction.json new file mode 100644 index 00000000000..755ccdb6ddb --- /dev/null +++ b/Regression/Checksum/benchmarks_json/Langmuir_multi_rz_psatd_current_correction.json @@ -0,0 +1,33 @@ +{ + "electrons": { + "particle_cpu": 0.0, + "particle_id": 1815005440.0, + "particle_momentum_x": 1.2741450985804339e-20, + "particle_momentum_y": 1.2747912182450875e-20, + "particle_momentum_z": 2.788652064780249e-20, + "particle_position_x": 0.5290000916698099, + "particle_position_y": 0.5888, + "particle_theta": 92606.42261144849, + "particle_weight": 81147583679.15044 + }, + "ions": { + "particle_cpu": 0.0, + "particle_id": 5475928320.0, + "particle_momentum_x": 9.468760984312313e-22, + "particle_momentum_y": 9.505742271609918e-22, + "particle_momentum_z": 1.9177141146735318e-21, + "particle_position_x": 0.5290000096367489, + "particle_position_y": 0.5888, + "particle_theta": 92621.92887164818, + "particle_weight": 81147583679.15044 + }, + "lev=0": { + "By": 3.3876510906847885, + "Ex": 462288551669.3938, + "Ez": 654508949022.0504, + "divE": 4.0082894251404314e+17, + "jx": 893199738249705.1, + "jz": 1241183345696335.2, + "rho": 3549014.737825297 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/LaserAccelerationBoost.json b/Regression/Checksum/benchmarks_json/LaserAccelerationBoost.json index 6bbb7e19362..0c0129199bb 100644 --- a/Regression/Checksum/benchmarks_json/LaserAccelerationBoost.json +++ b/Regression/Checksum/benchmarks_json/LaserAccelerationBoost.json @@ -12,33 +12,33 @@ "electrons": { "particle_cpu": 1140.0, "particle_id": 1254690.0, - "particle_momentum_x": 4.851314637850439e-27, - "particle_momentum_y": 2.198172635551489e-22, - "particle_momentum_z": 3.0976485630303994e-18, + "particle_momentum_x": 4.851314637802899e-27, + "particle_momentum_y": 2.1981726355513887e-22, + "particle_momentum_z": 3.0976485630304e-18, "particle_position_x": 0.06840000000361957, "particle_position_y": 0.03930013819602824, - "particle_weight": 6.28658221073865e+16 + "particle_weight": 6.286582210738646e+16 }, "ions": { "particle_cpu": 1140.0, "particle_id": 1390110.0, - "particle_momentum_x": 3.360051969537704e-30, - "particle_momentum_y": 2.1981727064697293e-22, + "particle_momentum_x": 3.360051728064683e-30, + "particle_momentum_y": 2.1981727064696287e-22, "particle_momentum_z": 5.687755728408193e-15, "particle_position_x": 0.0684, "particle_position_y": 0.0393001381332615, - "particle_weight": 6.28658221073865e+16 + "particle_weight": 6.286582210738646e+16 }, "lev=0": { - "Bx": 7187.973258845824, - "By": 16.40364440449974, - "Bz": 426.10311891731305, - "Ex": 4521887802.403417, - "Ey": 1984359719957.4739, - "Ez": 6310696230.623689, - "jx": 5122915921.273047, - "jy": 551785946484956.7, - "jz": 448986139469.4807, - "rho": 1567.014402404752 + "Bx": 7187.973258845839, + "By": 16.403644404498884, + "Bz": 426.1031189173133, + "Ex": 4521887802.4032955, + "Ey": 1984359719957.4766, + "Ez": 6310696230.661058, + "jx": 5122915921.273045, + "jy": 551785946484956.44, + "jz": 448986139508.22723, + "rho": 1567.0144024077852 } } \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/LaserAccelerationMR.json b/Regression/Checksum/benchmarks_json/LaserAccelerationMR.json index 8263d9a26f6..71d1f37cfef 100644 --- a/Regression/Checksum/benchmarks_json/LaserAccelerationMR.json +++ b/Regression/Checksum/benchmarks_json/LaserAccelerationMR.json @@ -1,21 +1,21 @@ { "beam": { "particle_cpu": 0.0, - "particle_id": 214748364400.0, + "particle_id": 5050.0, "particle_momentum_x": 4.3785337042075496e-20, - "particle_momentum_y": 4.5440373609847146e-20, + "particle_momentum_y": 4.544037360984713e-20, "particle_momentum_z": 1.3481388908218515e-17, "particle_position_x": 3.836799666768163e-05, - "particle_position_y": 0.0014900742655842329, + "particle_position_y": 0.001490074265584233, "particle_weight": 12483018148921.525 }, "electrons": { "particle_cpu": 4788.0, "particle_id": 13694226.0, "particle_momentum_x": 4.2751178407573573e-20, - "particle_momentum_y": 1.9580305087551053e-19, - "particle_momentum_z": 8.913655221012199e-20, - "particle_position_x": 0.047419299682949334, + "particle_momentum_y": 1.9580305087551049e-19, + "particle_momentum_z": 8.913655221012198e-20, + "particle_position_x": 0.04741929968294934, "particle_position_y": 0.08483137297620588, "particle_weight": 119232421874999.98 }, diff --git a/Regression/Checksum/benchmarks_json/LaserAccelerationRZ.json b/Regression/Checksum/benchmarks_json/LaserAccelerationRZ.json index 7b68263418f..c4ebe719d0b 100644 --- a/Regression/Checksum/benchmarks_json/LaserAccelerationRZ.json +++ b/Regression/Checksum/benchmarks_json/LaserAccelerationRZ.json @@ -31,6 +31,6 @@ "jx": 913724361200.7623, "jy": 2.0581594676890458e+17, "jz": 1772971850765479.5, - "rho": 38886541.08517392 + "rho": 39161887.06938790 } -} \ No newline at end of file +} diff --git a/Regression/Checksum/benchmarks_json/LaserIonAcc2d.json b/Regression/Checksum/benchmarks_json/LaserIonAcc2d.json new file mode 100644 index 00000000000..7a2aca2b6fc --- /dev/null +++ b/Regression/Checksum/benchmarks_json/LaserIonAcc2d.json @@ -0,0 +1,36 @@ +{ + "electrons": { + "particle_cpu": 0.0, + "particle_id": 15042924788.0, + "particle_momentum_x": 5.740883863450743e-18, + "particle_momentum_y": 0.0, + "particle_momentum_z": 6.600655759890705e-18, + "particle_position_x": 0.21665941064456773, + "particle_position_y": 0.18710469426045745, + "particle_weight": 1.0679921777413857e+18 + }, + "hydrogen": { + "particle_cpu": 0.0, + "particle_id": 60113161418.0, + "particle_momentum_x": 9.042393503665328e-18, + "particle_momentum_y": 0.0, + "particle_momentum_z": 5.152241928627923e-18, + "particle_position_x": 0.21233168237125435, + "particle_position_y": 0.21236998062984017, + "particle_weight": 1.0679921777413861e+18 + }, + "lev=0": { + "Bx": 0.0, + "By": 179478923.04290944, + "Bz": 0.0, + "Ex": 5.025441751797579e+16, + "Ey": 0.0, + "Ez": 7804039501351161.0, + "jx": 1.1795616964119449e+20, + "jy": 0.0, + "jz": 7.413959422336308e+19, + "rho": 893318471772.7588, + "rho_electrons": 74759628935311.0, + "rho_hydrogen": 74759628935311.02 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/LaserIonAcc3d.json b/Regression/Checksum/benchmarks_json/LaserIonAcc3d.json new file mode 100644 index 00000000000..b90afef64ad --- /dev/null +++ b/Regression/Checksum/benchmarks_json/LaserIonAcc3d.json @@ -0,0 +1,36 @@ +{ + "electrons": { + "particle_cpu": 0.0, + "particle_id": 139215430638.0, + "particle_momentum_x": 1.6966182372218133e-16, + "particle_momentum_y": 2.6850066145197374e-17, + "particle_momentum_z": 2.0052710316284176e-16, + "particle_position_x": 0.3393352015355679, + "particle_position_y": 1.1078675395554147, + "particle_position_z": 0.3419438867441836, + "particle_weight": 26433181926540.81 + }, + "hydrogen": { + "particle_cpu": 0.0, + "particle_id": 564569068992.0, + "particle_momentum_x": 1.7161831722699107e-16, + "particle_momentum_y": 4.9196233343263506e-17, + "particle_momentum_z": 2.1370961936359413e-16, + "particle_position_x": 0.3375134789944616, + "particle_position_y": 1.1080021730384098, + "particle_position_z": 0.33939049172256086, + "particle_weight": 26441597005520.95 + }, + "lev=0": { + "Bx": 41555976.87146437, + "By": 175750876.1712573, + "Bz": 35156983.723599546, + "Ex": 3.872657491899755e+17, + "Ey": 3.3815796095277564e+16, + "Ez": 3.937276394651024e+17, + "jx": 3.5072653955241413e+21, + "jy": 4.011484251839508e+20, + "jz": 3.787151010057889e+21, + "rho": 7429502184315.598 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/PlasmaAccelerationMR.json b/Regression/Checksum/benchmarks_json/PlasmaAccelerationMR.json index 17942a1bf7c..46d0bbc96a7 100644 --- a/Regression/Checksum/benchmarks_json/PlasmaAccelerationMR.json +++ b/Regression/Checksum/benchmarks_json/PlasmaAccelerationMR.json @@ -1,12 +1,12 @@ { "beam": { "particle_cpu": 0.0, - "particle_id": 2147483644000.0, - "particle_momentum_x": 4.322181912633372e-19, - "particle_momentum_y": 4.515843726581635e-19, - "particle_momentum_z": 5.444315572524053e-16, + "particle_id": 500500.0, + "particle_momentum_x": 4.32218204518708e-19, + "particle_momentum_y": 4.51584372639501e-19, + "particle_momentum_z": 5.444315572723708e-16, "particle_position_x": 0.0003944213839574242, - "particle_position_y": 0.04700228854317312, + "particle_position_y": 0.04700228854317311, "particle_weight": 12483018148921.53 }, "driver": { @@ -15,38 +15,38 @@ "particle_momentum_x": 4.2756896805319457e-19, "particle_momentum_y": 4.1735339783006983e-19, "particle_momentum_z": 5.473681318004841e-16, - "particle_position_x": 0.0015628948447913337, - "particle_position_y": 0.03796762346701903, + "particle_position_x": 0.001562894844791334, + "particle_position_y": 0.03796762346701904, "particle_weight": 93622636116911.45 }, "lev=0": { "Bx": 10.97120605769572, - "By": 129160.70458077668, - "Bz": 4.774206155364002, - "Ex": 36269846569014.32, - "Ey": 3698302984.4105253, + "By": 129160.70458077671, + "Bz": 4.774206155364004, + "Ex": 36269846569014.33, + "Ey": 3698302984.4105244, "Ez": 41138364604231.72, - "jx": 2251964952778816.0, - "jy": 304840114298.38477, - "jz": 4252877895382316.0 + "jx": 2251964952778814.5, + "jy": 304840114298.38513, + "jz": 4252877895382317.0 }, "lev=1": { - "Bx": 5.377181678865647, + "Bx": 5.377181678865664, "By": 142098.79841193178, - "Bz": 4.658510073379045, - "Ex": 49368488395341.13, - "Ey": 2329098185.314148, - "Ez": 52339572250983.5, + "Bz": 4.658510073379044, + "Ex": 49368488395341.14, + "Ey": 2329098185.314154, + "Ez": 52339572250983.49, "jx": 3016726519391277.0, - "jy": 162992754789.04834, - "jz": 4308416589615174.5 + "jy": 162992754789.0487, + "jz": 4308416589615177.0 }, "plasma_e": { "particle_cpu": 3600.0, "particle_id": 6546600.0, - "particle_momentum_x": 1.6052131138342657e-19, - "particle_momentum_y": 7.604979610181104e-24, - "particle_momentum_z": 1.6653769795689454e-19, + "particle_momentum_x": 1.6052131138342664e-19, + "particle_momentum_y": 7.604979610181107e-24, + "particle_momentum_z": 1.6653769795689464e-19, "particle_position_x": 0.13410927422500246, "particle_position_y": 0.10349153092104842, "particle_weight": 823974609374999.9 diff --git a/Regression/Checksum/benchmarks_json/Python_Langmuir_rz_multimode.json b/Regression/Checksum/benchmarks_json/Python_Langmuir_rz_multimode.json index 8739247ef35..83864c13136 100644 --- a/Regression/Checksum/benchmarks_json/Python_Langmuir_rz_multimode.json +++ b/Regression/Checksum/benchmarks_json/Python_Langmuir_rz_multimode.json @@ -1,20 +1,20 @@ { "electrons": { - "particle_cpu": 353280.0, - "particle_id": 143802756480.0, - "particle_momentum_x": 2.10383079872169e-20, - "particle_momentum_y": 2.3745155589395647e-20, - "particle_momentum_z": 3.102444510250923e-20, - "particle_position_x": 6.612501190963988, - "particle_position_y": 14.720000000000002, - "particle_theta": 1156106.570713621, + "particle_cpu": 206080.0, + "particle_id": 169156013440.0, + "particle_momentum_x": 2.1038307987216888e-20, + "particle_momentum_y": 2.374515558939564e-20, + "particle_momentum_z": 3.1024445102509245e-20, + "particle_position_x": 6.61250119096399, + "particle_position_y": 14.719999999999999, + "particle_theta": 1156106.5707136206, "particle_weight": 81147583679.15047 }, "lev=0": { - "By": 2.4960978462281567, + "By": 2.496097846228156, "Ex": 879037550423.2649, "Ez": 1707660097260.0088, - "jx": 210208695620033.72, + "jx": 210208695620033.78, "jz": 407196128263304.25, "part_per_cell": 1472000.0 } diff --git a/Regression/Checksum/benchmarks_json/Python_LaserAccelerationMR.json b/Regression/Checksum/benchmarks_json/Python_LaserAccelerationMR.json index d12e9749597..535a3a6fe05 100644 --- a/Regression/Checksum/benchmarks_json/Python_LaserAccelerationMR.json +++ b/Regression/Checksum/benchmarks_json/Python_LaserAccelerationMR.json @@ -1,17 +1,17 @@ { "electrons": { "particle_cpu": 695224.0, - "particle_id": 285949191772.0, + "particle_id": 290501119140.0, "particle_momentum_x": 2.367557775119529e-19, "particle_momentum_y": 1.9290614631916863e-21, - "particle_momentum_z": 1.8938350321846783e-20, - "particle_position_x": 7.006554882095786, + "particle_momentum_z": 1.893835032184678e-20, + "particle_position_x": 7.0065548820957835, "particle_position_y": 7.006554897199218, - "particle_position_z": 4.600065455619854, + "particle_position_z": 4.600065455619851, "particle_weight": 21640883789.062504 }, "lev=0": { - "Bx": 2.0570941426919918, + "Bx": 2.057094142692001, "By": 2449255.0148639255, "Bz": 71239.03584863919, "Ex": 752773924385165.6, @@ -20,6 +20,6 @@ "jx": 1.928774119411483e+18, "jy": 69804337605269.28, "jz": 742686862695719.5, - "rho": 27910721385.36522 + "rho": 27910721385.365227 } -} +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/Python_PlasmaAccelerationMR.json b/Regression/Checksum/benchmarks_json/Python_PlasmaAccelerationMR.json index 2fbfc33a6c4..9df7d199cef 100644 --- a/Regression/Checksum/benchmarks_json/Python_PlasmaAccelerationMR.json +++ b/Regression/Checksum/benchmarks_json/Python_PlasmaAccelerationMR.json @@ -1,7 +1,7 @@ { "beam": { "particle_cpu": 0.0, - "particle_id": 46454366187008.0, + "particle_id": 354429504.0, "particle_momentum_x": 3.8119259477932654e-21, "particle_momentum_y": 3.811925947793233e-21, "particle_momentum_z": 1.9589051047404492e-17, @@ -30,7 +30,7 @@ }, "plasma": { "particle_cpu": 8388608.0, - "particle_id": 1161246952652800.0, + "particle_id": 37701227118592.0, "particle_momentum_x": 0.0, "particle_momentum_y": 0.0, "particle_momentum_z": 0.0, diff --git a/Regression/Checksum/benchmarks_json/galilean_2d_psatd_current_correction.json b/Regression/Checksum/benchmarks_json/galilean_2d_psatd_current_correction.json new file mode 100644 index 00000000000..d994e75aa5b --- /dev/null +++ b/Regression/Checksum/benchmarks_json/galilean_2d_psatd_current_correction.json @@ -0,0 +1,35 @@ +{ + "electrons": { + "particle_cpu": 0.0, + "particle_id": 2160099328.0, + "particle_momentum_x": 2.363063138903958e-21, + "particle_momentum_y": 0.0, + "particle_momentum_z": 1.7809214705332697e-16, + "particle_position_x": 1267465.6835605945, + "particle_position_y": 15724303.949723076, + "particle_weight": 1.6888332018290936e+18 + }, + "ions": { + "particle_cpu": 0.0, + "particle_id": 6522175488.0, + "particle_momentum_x": 2.609287699922942e-18, + "particle_momentum_y": 0.0, + "particle_momentum_z": 3.269760941123179e-13, + "particle_position_x": 1267465.6477179655, + "particle_position_y": 15724304.00122163, + "particle_weight": 1.6888332018290936e+18 + }, + "lev=0": { + "Bx": 0.0, + "By": 0.004642756370577648, + "Bz": 0.0, + "Ex": 1398225.2486995365, + "Ey": 0.0, + "Ez": 102959.67792678274, + "divE": 3014964.1213529613, + "jx": 292.81350713026325, + "jy": 0.0, + "jz": 7960.384412652155, + "rho": 2.669505857929691e-05 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/galilean_3d_psatd_current_correction.json b/Regression/Checksum/benchmarks_json/galilean_3d_psatd_current_correction.json new file mode 100644 index 00000000000..682cb6f5be9 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/galilean_3d_psatd_current_correction.json @@ -0,0 +1,37 @@ +{ + "electrons": { + "particle_cpu": 0.0, + "particle_id": 536887296.0, + "particle_momentum_x": 9.54665861476804e-22, + "particle_momentum_y": 9.601813820416699e-22, + "particle_momentum_z": 8.903841219870762e-17, + "particle_position_x": 158433.31156021034, + "particle_position_y": 158432.81745393327, + "particle_position_z": 5891662.9444190245, + "particle_weight": 2.041377132710917e+18 + }, + "ions": { + "particle_cpu": 0.0, + "particle_id": 1610629120.0, + "particle_momentum_x": 1.3136320808925818e-18, + "particle_momentum_y": 1.3108700891718219e-18, + "particle_momentum_z": 1.6348803647068623e-13, + "particle_position_x": 158433.31298844935, + "particle_position_y": 158432.84898609406, + "particle_position_z": 5891662.954960415, + "particle_weight": 2.041377132710917e+18 + }, + "lev=0": { + "Bx": 0.028648409524931785, + "By": 0.027662580405919915, + "Bz": 0.0008452507635579109, + "Ex": 8257369.123398185, + "Ey": 8556127.762117814, + "Ez": 357868.85900329496, + "divE": 22005900.695029423, + "jx": 988.0389164518385, + "jy": 978.6727849028426, + "jz": 58269.42112756228, + "rho": 0.00019484437774355495 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/galilean_rz_psatd.json b/Regression/Checksum/benchmarks_json/galilean_rz_psatd.json new file mode 100644 index 00000000000..e49a6a66b0c --- /dev/null +++ b/Regression/Checksum/benchmarks_json/galilean_rz_psatd.json @@ -0,0 +1,34 @@ +{ + "electrons": { + "particle_cpu": 0.0, + "particle_id": 540033024.0, + "particle_momentum_x": 6.991216723474512e-22, + "particle_momentum_y": 2.7161964260573423e-22, + "particle_momentum_z": 8.9036292669071e-17, + "particle_position_x": 633733.7278962254, + "particle_position_y": 7862151.377853205, + "particle_theta": 51362.0874841635, + "particle_weight": 1.0261080645329302e+20 + }, + "ions": { + "particle_cpu": 0.0, + "particle_id": 1630552064.0, + "particle_momentum_x": 1.312586801897356e-18, + "particle_momentum_y": 2.716175862542692e-22, + "particle_momentum_z": 1.634880615821668e-13, + "particle_position_x": 633733.7424931434, + "particle_position_y": 7862152.004348258, + "particle_theta": 51470.93289875299, + "particle_weight": 1.0261080645329302e+20 + }, + "lev=0": { + "By": 0.002498168642125331, + "Ex": 751403.0500041273, + "Ey": 131304.8882148022, + "Ez": 35783.1787975738, + "divE": 1349004.6304044977, + "jx": 163.91848307748262, + "jz": 50271.11378286561, + "rho": 0.0001686919398337135 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/galilean_rz_psatd_current_correction.json b/Regression/Checksum/benchmarks_json/galilean_rz_psatd_current_correction.json new file mode 100644 index 00000000000..bedea80f4ff --- /dev/null +++ b/Regression/Checksum/benchmarks_json/galilean_rz_psatd_current_correction.json @@ -0,0 +1,34 @@ +{ + "electrons": { + "particle_cpu": 0.0, + "particle_id": 540033024.0, + "particle_momentum_x": 8.788267200281192e-22, + "particle_momentum_y": 5.74665180053183e-22, + "particle_momentum_z": 8.90383871397027e-17, + "particle_position_x": 633732.2222171503, + "particle_position_y": 7862152.004049132, + "particle_theta": 51362.07588048758, + "particle_weight": 1.0261080645329302e+20 + }, + "ions": { + "particle_cpu": 0.0, + "particle_id": 1630552064.0, + "particle_momentum_x": 1.3125577573586479e-18, + "particle_momentum_y": 5.758720163092816e-22, + "particle_momentum_z": 1.634880594880298e-13, + "particle_position_x": 633733.7433175434, + "particle_position_y": 7862152.004007031, + "particle_theta": 51470.932902947745, + "particle_weight": 1.0261080645329302e+20 + }, + "lev=0": { + "By": 0.006969827132155427, + "Ex": 2093955.7927258983, + "Ey": 236405.56468992063, + "Ez": 70213.87522744841, + "divE": 8064705.111049001, + "jx": 187.55743091229465, + "jz": 21319.84771260529, + "rho": 7.140641370808197e-05 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/ionization_boost.json b/Regression/Checksum/benchmarks_json/ionization_boost.json index 869e901d475..c0c4722dc11 100644 --- a/Regression/Checksum/benchmarks_json/ionization_boost.json +++ b/Regression/Checksum/benchmarks_json/ionization_boost.json @@ -1,34 +1,34 @@ { "electrons": { - "particle_cpu": 27.0, - "particle_id": 1309618160.0, - "particle_momentum_x": 2.1096313075249046e-17, + "particle_cpu": 184.0, + "particle_id": 1300726794.0, + "particle_momentum_x": 2.1054238530089624e-17, "particle_momentum_y": 0.0, - "particle_momentum_z": 1.7264669463007245e-17, - "particle_position_x": 0.11048873689229222, - "particle_position_y": 1.7098255333343246, - "particle_weight": 3.0709523304687515e-09 + "particle_momentum_z": 1.7272033795713092e-17, + "particle_position_x": 0.11080912051867975, + "particle_position_y": 1.7108009959314832, + "particle_weight": 3.0702525710937457e-09 }, "ions": { "particle_cpu": 0.0, - "particle_id": 89154368.0, - "particle_ionization_level": 52663.0, - "particle_momentum_x": 3.631023150973392e-18, + "particle_id": 89142848.0, + "particle_ionization_level": 52651.0, + "particle_momentum_x": 3.586054549170934e-18, "particle_momentum_y": 0.0, - "particle_momentum_z": 1.043299530138721e-13, - "particle_position_x": 0.02143992896238411, - "particle_position_y": 0.47267660340847956, - "particle_weight": 5.000947000000002e-10 + "particle_momentum_z": 1.0432995298789921e-13, + "particle_position_x": 0.021439915649144235, + "particle_position_y": 0.47267660340600026, + "particle_weight": 5.000946999999993e-10 }, "lev=0": { "Bx": 0.0, - "By": 18262982.96212939, + "By": 18262982.96212995, "Bz": 0.0, - "Ex": 5472946208912828.0, + "Ex": 5472946208913378.0, "Ey": 0.0, - "Ez": 829.4153550071181, - "jx": 12255073032842.39, + "Ez": 829.924182334196, + "jx": 12255073032842.436, "jy": 0.0, - "jz": 83416.00000112198 + "jz": 83296.0000011226 } } \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/ionization_lab.json b/Regression/Checksum/benchmarks_json/ionization_lab.json index 768a3f5b079..8b0431828b4 100644 --- a/Regression/Checksum/benchmarks_json/ionization_lab.json +++ b/Regression/Checksum/benchmarks_json/ionization_lab.json @@ -1,23 +1,23 @@ { "electrons": { - "particle_cpu": 17826.0, - "particle_id": 835176910.0, - "particle_momentum_x": 4.423317346074595e-18, + "particle_cpu": 21617.0, + "particle_id": 808026564.0, + "particle_momentum_x": 4.431294152532881e-18, "particle_momentum_y": 0.0, - "particle_momentum_z": 2.654287388285012e-18, - "particle_position_x": 0.11034345761508463, - "particle_position_y": 0.6415606590060071, - "particle_weight": 3.440703125e-10 + "particle_momentum_z": 2.657548020991975e-18, + "particle_position_x": 0.11028197524535621, + "particle_position_y": 0.6442648056672895, + "particle_weight": 3.45546875e-10 }, "ions": { - "particle_cpu": 4864.0, - "particle_id": 45340928.0, - "particle_ionization_level": 72773.0, - "particle_momentum_x": 1.7335485753555193e-18, + "particle_cpu": 5888.0, + "particle_id": 42547456.0, + "particle_ionization_level": 72896.0, + "particle_momentum_x": 1.7611825794527892e-18, "particle_momentum_y": 0.0, - "particle_momentum_z": 3.5110738038450676e-23, - "particle_position_x": 0.03199996960212278, - "particle_position_y": 0.12800000461301925, + "particle_momentum_z": 3.6184757022222325e-23, + "particle_position_x": 0.03199998869821981, + "particle_position_y": 0.1280000046215487, "particle_weight": 9.999999999999999e-11 }, "lev=0": { @@ -26,9 +26,9 @@ "Bz": 0.0, "Ex": 7878018647090106.0, "Ey": 0.0, - "Ez": 2740.936450483133, + "Ez": 2740.9364504831324, "jx": 1.2111228466502686e+16, "jy": 0.0, - "jz": 1.362596673906476e-07 + "jz": 1.356952129096317e-07 } } \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/leveling_thinning.json b/Regression/Checksum/benchmarks_json/leveling_thinning.json new file mode 100644 index 00000000000..30cb28b20ef --- /dev/null +++ b/Regression/Checksum/benchmarks_json/leveling_thinning.json @@ -0,0 +1,33 @@ +{ + "resampled_part1": { + "particle_cpu": 91119.0, + "particle_id": 773368403.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_position_x": 483987.107681320863775908946990966796875, + "particle_position_y": 484901.85673470445908606052398681640625, + "particle_weight": 255.3040749999979084350343327969312667846680 + }, + "resampled_part2": { + "particle_cpu": 0.0, + "particle_id": 5754560102.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_position_x": 38483.4033272069573285989463329315185546875, + "particle_position_y": 38200.1600630077518871985375881195068359375, + "particle_weight": 2.8420376578928117083933102549053728580475 + }, + "lev=0": { + "Bx": 0.0, + "By": 0.0, + "Bz": 0.0, + "Ex": 0.0, + "Ey": 0.0, + "Ez": 0.0, + "jx": 0.0, + "jy": 0.0, + "jz": 0.0 + } +} diff --git a/Regression/Checksum/benchmarks_json/momentum-conserving-gather.json b/Regression/Checksum/benchmarks_json/momentum-conserving-gather.json index 04c09d6c123..b40ea329e1e 100644 --- a/Regression/Checksum/benchmarks_json/momentum-conserving-gather.json +++ b/Regression/Checksum/benchmarks_json/momentum-conserving-gather.json @@ -1,54 +1,54 @@ { "beam": { "particle_cpu": 0.0, - "particle_id": 2147483644000.0, - "particle_momentum_x": 4.323192428952614e-19, - "particle_momentum_y": 4.515789093587449e-19, - "particle_momentum_z": 5.444318109218598e-16, + "particle_id": 500500.0, + "particle_momentum_x": 4.323192509779988e-19, + "particle_momentum_y": 4.515789093284668e-19, + "particle_momentum_z": 5.444318109525372e-16, "particle_position_x": 0.0003944833021505297, - "particle_position_y": 0.047002288545028664, + "particle_position_y": 0.04700228854502865, "particle_weight": 12483018148921.53 }, "driver": { "particle_cpu": 0.0, "particle_id": 1500500.0, - "particle_momentum_x": 4.273571972654775e-19, + "particle_momentum_x": 4.273571972654774e-19, "particle_momentum_y": 4.1735282690939002e-19, "particle_momentum_z": 5.473699235390372e-16, "particle_position_x": 0.0015632600122319792, - "particle_position_y": 0.0379676234607913, + "particle_position_y": 0.037967623460791314, "particle_weight": 93622636116911.45 }, "lev=0": { - "Bx": 10.733012173941603, - "By": 123705.21142513888, + "Bx": 10.733012173941598, + "By": 123705.2114251389, "Bz": 4.499473350064483, - "Ex": 32786118366421.473, - "Ey": 3709615080.588276, + "Ex": 32786118366421.484, + "Ey": 3709615080.588275, "Ez": 40656586163467.92, - "jx": 2141101370253514.2, - "jy": 306062373710.40106, - "jz": 4320228054997731.0 + "jx": 2141101370253517.0, + "jy": 306062373710.40063, + "jz": 4320228054997732.0 }, "lev=1": { - "Bx": 5.256134811306936, + "Bx": 5.256134811306947, "By": 137245.47590665444, - "Bz": 4.270745951650612, - "Ex": 44351722683610.33, - "Ey": 3488288401.6961145, - "Ez": 51479977260499.836, + "Bz": 4.2707459516506106, + "Ex": 44351722683610.34, + "Ey": 3488288401.6961136, + "Ez": 51479977260499.83, "jx": 2867710882689230.5, - "jy": 165181878916.2902, - "jz": 4439666503460564.0 + "jy": 165181878916.29083, + "jz": 4439666503460567.0 }, "plasma_e": { "particle_cpu": 3600.0, "particle_id": 6546600.0, - "particle_momentum_x": 1.5130313200302403e-19, - "particle_momentum_y": 7.700274017007924e-24, - "particle_momentum_z": 1.7302395904705489e-19, - "particle_position_x": 0.13368254159485607, - "particle_position_y": 0.10348762049276919, + "particle_momentum_x": 1.513031320030241e-19, + "particle_momentum_y": 7.700274017007927e-24, + "particle_momentum_z": 1.7302395904705498e-19, + "particle_position_x": 0.1336825415948561, + "particle_position_y": 0.10348762049276918, "particle_weight": 823974609374999.9 } } \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/pml_x_psatd.json b/Regression/Checksum/benchmarks_json/pml_x_psatd.json index 2de37adde04..c2604f4daa9 100644 --- a/Regression/Checksum/benchmarks_json/pml_x_psatd.json +++ b/Regression/Checksum/benchmarks_json/pml_x_psatd.json @@ -1,12 +1,12 @@ { "lev=0": { - "Bx": 1.4955215245100567e-08, - "By": 2.5469842834335128e-08, - "Bz": 1.2057291224076326e-08, - "Ex": 6.069795070322746, - "Ey": 5.355918172750489, - "Ez": 5.016738378883853, - "divE": 202005.08108089963, - "rho": 1.6719346125116958e-06 + "Bx": 1.4955213883225615e-08, + "By": 2.5469838278883616e-08, + "Bz": 1.205728995299769e-08, + "Ex": 6.069794171544256, + "Ey": 5.355917208303502, + "Ez": 5.016737331847911, + "divE": 202005.08114094258, + "rho": 1.67193461204679e-06 } } \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/qed_breit_wheeler_2d.json b/Regression/Checksum/benchmarks_json/qed_breit_wheeler_2d.json new file mode 100644 index 00000000000..ac9292d037e --- /dev/null +++ b/Regression/Checksum/benchmarks_json/qed_breit_wheeler_2d.json @@ -0,0 +1,137 @@ +{ + "ele1": { + "particle_cpu": 47332.0, + "particle_id": 208304044359.0, + "particle_momentum_x": 2.58588597891772511344442318e-14, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 94696.4657961219345452263951301574707031250000, + "particle_position_x": 0.0236641383450765, + "particle_position_y": 0.02351792724609375, + "particle_weight": 900621.4141845703 + }, + "ele2": { + "particle_cpu": 6073.0, + "particle_id": 27130859500.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 8.1664154261955651619665085e-15, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 11835.1055475386856414843350648880004882812500, + "particle_position_x": 0.00300684326171875, + "particle_position_y": 0.00299909619140625, + "particle_weight": 114240.64636230469 + }, + "ele3": { + "particle_cpu": 63189.0, + "particle_id": 294193317389.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 1.725635030922431898577438009e-13, + "particle_optical_depth_QSR": 126207.2254516556131420657038688659667968750000, + "particle_position_x": 0.0316306396484375, + "particle_position_y": 0.03171556895151163, + "particle_weight": 1206226.3488769531 + }, + "ele4": { + "particle_cpu": 24210.0, + "particle_id": 117564341063.0, + "particle_momentum_x": 3.837082573229679014720902203e-13, + "particle_momentum_y": 3.837082573229679014720902203e-13, + "particle_momentum_z": 3.837082573229679014720902203e-13, + "particle_optical_depth_QSR": 48381.7888648896114318631589412689208984375000, + "particle_position_x": 0.012101709192233077, + "particle_position_y": 0.012111250324900506, + "particle_weight": 463647.84240722656 + }, + "lev=0": { + "Ex": 0.0 + }, + "p1": { + "particle_cpu": 476956.0, + "particle_id": 257854228043.0, + "particle_momentum_x": 5.211363201667322e-13, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_optical_depth_BW": 955462.1120497078, + "particle_position_x": 0.23847986165492352, + "particle_position_y": 0.2386260727539063, + "particle_weight": 9099378.58581543 + }, + "p2": { + "particle_cpu": 518215.0, + "particle_id": 840692712768.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 1.4154340878946218e-12, + "particle_momentum_z": 0.0, + "particle_optical_depth_BW": 1035921.4604132524, + "particle_position_x": 0.25913715673828125, + "particle_position_y": 0.2591449038085938, + "particle_weight": 9885759.353637695 + }, + "p3": { + "particle_cpu": 461099.0, + "particle_id": 1246333912261.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 2.5181691242461693e-12, + "particle_optical_depth_BW": 921660.859376024, + "particle_position_x": 0.23051336035156236, + "particle_position_y": 0.23042843104848845, + "particle_weight": 8793773.651123047 + }, + "p4": { + "particle_cpu": 500078.0, + "particle_id": 1892252377690.0, + "particle_momentum_x": 1.576635368257434e-11, + "particle_momentum_y": 1.576635368257434e-11, + "particle_momentum_z": 1.576635368257434e-11, + "particle_optical_depth_BW": 999134.306723253, + "particle_position_x": 0.2500422908077668, + "particle_position_y": 0.2500327496750996, + "particle_weight": 9536352.157592773 + }, + "pos1": { + "particle_cpu": 47332.0, + "particle_id": 209418933440.0, + "particle_momentum_x": 2.57211643025628750267839636e-14, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 94624.9490281959297135472297668457031250000000, + "particle_position_x": 0.0236641383450765, + "particle_position_y": 0.02351792724609375, + "particle_weight": 900621.4141845703 + }, + "pos2": { + "particle_cpu": 6073.0, + "particle_id": 27148807905.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 8.1904553211236055771791308e-15, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 12091.3263468917793943546712398529052734375000, + "particle_position_x": 0.00300684326171875, + "particle_position_y": 0.00299909619140625, + "particle_weight": 114240.64636230469 + }, + "pos3": { + "particle_cpu": 63189.0, + "particle_id": 296193112041.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 1.728492728991635470350296701e-13, + "particle_optical_depth_QSR": 125934.2625277330371318385004997253417968750000, + "particle_position_x": 0.0316306396484375, + "particle_position_y": 0.03171556895151163, + "particle_weight": 1206226.3488769531 + }, + "pos4": { + "particle_cpu": 24210.0, + "particle_id": 117859832476.0, + "particle_momentum_x": 3.828359863812495493291348548e-13, + "particle_momentum_y": 3.828359863812495493291348548e-13, + "particle_momentum_z": 3.828359863812495493291348548e-13, + "particle_optical_depth_QSR": 48662.7616644648805959150195121765136718750000, + "particle_position_x": 0.012101709192233077, + "particle_position_y": 0.012111250324900506, + "particle_weight": 463647.84240722656 + } +} diff --git a/Regression/Checksum/benchmarks_json/qed_breit_wheeler_3d.json b/Regression/Checksum/benchmarks_json/qed_breit_wheeler_3d.json new file mode 100644 index 00000000000..ab2ad86e3b6 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/qed_breit_wheeler_3d.json @@ -0,0 +1,149 @@ +{ + "ele1": { + "particle_cpu": 75032.0, + "particle_id": 322639139372.0, + "particle_momentum_x": 4.09769068083714937569857114e-14, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 149795.7110893558128736913204193115234375000000, + "particle_position_x": 0.0374394067025573, + "particle_position_y": 0.03750066406250001, + "particle_position_z": 0.037341921875, + "particle_weight": 1.4289093017578123 + }, + "ele2": { + "particle_cpu": 9793.0, + "particle_id": 43961074159.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 1.32721307693240310365736405e-14, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 19478.5291867527776048518717288970947265625000, + "particle_position_x": 0.00487769140625, + "particle_position_y": 0.004883663375988306, + "particle_position_z": 0.0048729921874999995, + "particle_weight": 0.18597602844238278 + }, + "ele3": { + "particle_cpu": 99155.0, + "particle_id": 464694872011.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 2.711561853643428955125070802e-13, + "particle_optical_depth_QSR": 198435.0529202320321928709745407104492187500000, + "particle_position_x": 0.04959472265624999, + "particle_position_y": 0.049601199218749996, + "particle_position_z": 0.049660153352435205, + "particle_weight": 1.8931293487548826 + }, + "ele4": { + "particle_cpu": 39055.0, + "particle_id": 195883550491.0, + "particle_momentum_x": 6.190484920544669620800655761e-13, + "particle_momentum_y": 6.190484920544669620800655761e-13, + "particle_momentum_z": 6.190484920544669620800655761e-13, + "particle_optical_depth_QSR": 78514.9343421634548576548695564270019531250000, + "particle_position_x": 0.019598247395833334, + "particle_position_y": 0.019667958333333336, + "particle_position_z": 0.019563541666666667, + "particle_weight": 0.7489013671874999 + }, + "lev=0": { + "Ex": 0.0 + }, + "p1": { + "particle_cpu": 449256.0, + "particle_id": 235491419135.0, + "particle_momentum_x": 4.908804072906878e-13, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_optical_depth_BW": 900178.7606581703, + "particle_position_x": 0.22470459329744272, + "particle_position_y": 0.2246433359375, + "particle_position_z": 0.22480207812499997, + "particle_weight": 8.571090698242186 + }, + "p2": { + "particle_cpu": 514495.0, + "particle_id": 809317053396.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 1.4051630807345168e-12, + "particle_momentum_z": 0.0, + "particle_optical_depth_BW": 1028419.7570255434, + "particle_position_x": 0.25726630859374994, + "particle_position_y": 0.2572603366240118, + "particle_position_z": 0.25727100781249995, + "particle_weight": 9.814023971557615 + }, + "p3": { + "particle_cpu": 425133.0, + "particle_id": 1114131389020.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 2.321468823070715e-12, + "particle_optical_depth_BW": 849662.7634565933, + "particle_position_x": 0.21254927734374993, + "particle_position_y": 0.21254280078124993, + "particle_position_z": 0.21248384664756478, + "particle_weight": 8.106870651245115 + }, + "p4": { + "particle_cpu": 485233.0, + "particle_id": 1779979854691.0, + "particle_momentum_x": 1.5294746941698488e-11, + "particle_momentum_y": 1.5294746941698488e-11, + "particle_momentum_z": 1.5294746941698488e-11, + "particle_optical_depth_BW": 969220.1746435867, + "particle_position_x": 0.2425457526041667, + "particle_position_y": 0.24247604166666667, + "particle_position_z": 0.24258045833333322, + "particle_weight": 9.251098632812498 + }, + "pos1": { + "particle_cpu": 75032.0, + "particle_id": 328251649436.0, + "particle_momentum_x": 4.08590066787212354571248997e-14, + "particle_momentum_y": 0.0, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 149619.0538811495061963796615600585937500000000, + "particle_position_x": 0.0374394067025573, + "particle_position_y": 0.03750066406250001, + "particle_position_z": 0.037341921875, + "particle_weight": 1.4289093017578123 + }, + "pos2": { + "particle_cpu": 9793.0, + "particle_id": 44056150976.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 1.33557460646864680765462173e-14, + "particle_momentum_z": 0.0, + "particle_optical_depth_QSR": 19267.4952943192693055607378482818603515625000, + "particle_position_x": 0.00487769140625, + "particle_position_y": 0.004883663375988306, + "particle_position_z": 0.0048729921874999995, + "particle_weight": 0.18597602844238278 + }, + "pos3": { + "particle_cpu": 99155.0, + "particle_id": 474546404744.0, + "particle_momentum_x": 0.0, + "particle_momentum_y": 0.0, + "particle_momentum_z": 2.709568803733161877263216213e-13, + "particle_optical_depth_QSR": 199114.2072337889694608747959136962890625000000, + "particle_position_x": 0.04959472265624999, + "particle_position_y": 0.049601199218749996, + "particle_position_z": 0.049660153352435205, + "particle_weight": 1.8931293487548826 + }, + "pos4": { + "particle_cpu": 39055.0, + "particle_id": 197425348561.0, + "particle_momentum_x": 6.191024913788928155183119361e-13, + "particle_momentum_y": 6.191024913788928155183119361e-13, + "particle_momentum_z": 6.191024913788928155183119361e-13, + "particle_optical_depth_QSR": 78364.7008082381944404914975166320800781250000, + "particle_position_x": 0.019598247395833334, + "particle_position_y": 0.019667958333333336, + "particle_position_z": 0.019563541666666667, + "particle_weight": 0.7489013671874999 + } +} diff --git a/Regression/Checksum/benchmarks_json/qed_breit_wheeler_opt_depth_evolution.json b/Regression/Checksum/benchmarks_json/qed_breit_wheeler_opt_depth_evolution.json deleted file mode 100644 index 2712d517fc1..00000000000 --- a/Regression/Checksum/benchmarks_json/qed_breit_wheeler_opt_depth_evolution.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "ele_bw": { - "particle_cpu": 7868.0, - "particle_id": 646554824.0, - "particle_momentum_x": 2.5371833905509472e-14, - "particle_momentum_y": 2.390094592886486e-14, - "particle_momentum_z": 3.195830540752922e-14, - "particle_position_x": 0.0038628680559308396, - "particle_position_y": 0.0038691826866907985, - "particle_position_z": 0.0019396899450056522, - "particle_weight": 4.751892089843749 - }, - "lev=0": { - "Ex": 18418958.618769623 - }, - "p1": { - "particle_cpu": 5543.0, - "particle_id": 47499255.0, - "particle_momentum_x": 6.069752862017886e-15, - "particle_momentum_y": 0.0, - "particle_momentum_z": 0.0, - "particle_optical_depth_BW": 11151.849063257036, - "particle_position_x": 0.0027782891957751693, - "particle_position_y": 0.002784171875, - "particle_position_z": 0.0013972031249999998 - }, - "p2": { - "particle_cpu": 7827.0, - "particle_id": 199091206.0, - "particle_momentum_x": 0.0, - "particle_momentum_y": 2.1396793698330848e-14, - "particle_momentum_z": 0.0, - "particle_optical_depth_BW": 15965.491577214689, - "particle_position_x": 0.00392103125, - "particle_position_y": 0.003911447515877365, - "particle_position_z": 0.0019578749999999995 - }, - "p3": { - "particle_cpu": 4835.0, - "particle_id": 205936781.0, - "particle_momentum_x": 0.0, - "particle_momentum_y": 0.0, - "particle_momentum_z": 2.6612859552040087e-14, - "particle_optical_depth_BW": 9728.073455792824, - "particle_position_x": 0.002453390625, - "particle_position_y": 0.002459640625, - "particle_position_z": 0.0012191973174102994 - }, - "p4": { - "particle_cpu": 6696.0, - "particle_id": 397669037.0, - "particle_momentum_x": 2.118611807411617e-13, - "particle_momentum_y": 2.118611807411617e-13, - "particle_momentum_z": 2.118611807411617e-13, - "particle_optical_depth_BW": 13376.081619606364, - "particle_position_x": 0.003368619791666666, - "particle_position_y": 0.0033608489583333333, - "particle_position_z": 0.0016801197916666664 - }, - "pos_bw": { - "particle_cpu": 7868.0, - "particle_id": 648632397.0, - "particle_momentum_x": 2.5161516094347012e-14, - "particle_momentum_y": 2.4559377145598413e-14, - "particle_momentum_z": 3.330888308559405e-14, - "particle_position_x": 0.0038621659133009625, - "particle_position_y": 0.003867460692695544, - "particle_position_z": 0.0019361455637321736, - "particle_weight": 4.751892089843749 - } -} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/qed_breit_wheeler_tau_init.json b/Regression/Checksum/benchmarks_json/qed_breit_wheeler_tau_init.json deleted file mode 100644 index 24663e19b2c..00000000000 --- a/Regression/Checksum/benchmarks_json/qed_breit_wheeler_tau_init.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "lev=0": { - "Bx": 0.0, - "By": 0.0, - "Bz": 0.0, - "Ex": 0.0, - "Ey": 0.0, - "Ez": 0.0, - "jx": 0.0, - "jy": 0.0, - "jz": 0.0 - }, - "photons": { - "particle_cpu": 28800.0, - "particle_id": 897828480.0, - "particle_momentum_x": 1.257725971841766e-15, - "particle_momentum_y": 1.2533271477501288e-15, - "particle_momentum_z": 1.2567654312332688e-15, - "particle_optical_depth_BW": 57886.05893096988, - "particle_position_x": 0.8640051523469104, - "particle_position_y": 0.8640331757950863, - "particle_weight": 36000000000.0 - } -} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/qed_quantum_sync_2d.json b/Regression/Checksum/benchmarks_json/qed_quantum_sync_2d.json new file mode 100644 index 00000000000..671eab2a1bc --- /dev/null +++ b/Regression/Checksum/benchmarks_json/qed_quantum_sync_2d.json @@ -0,0 +1,93 @@ +{ + "lev=0": { + "Ex": 2.4557665520375066e-13 + }, + "p1": { + "particle_cpu": 524288.0, + "particle_id": 283468365824.0, + "particle_momentum_x": 6.8967708469790264898971893e-15, + "particle_momentum_y": 4.6544555163678046641608873e-15, + "particle_momentum_z": 1.06978456723094022003418991e-14, + "particle_optical_depth_QSR": 1050156.6786004053428769111633300781250000000000, + "particle_position_x": 0.13107200000000005, + "particle_position_y": 0.13107200000000005, + "particle_weight": 2.4999999999999954e-12 + }, + "p2": { + "particle_cpu": 524288.0, + "particle_id": 850404048896.0, + "particle_momentum_x": 7.635368623237383502135230e-16, + "particle_momentum_y": 2.28415575523800556640511186e-14, + "particle_momentum_z": 3.9081460339209575333427882e-15, + "particle_optical_depth_QSR": 1047498.9017682682024314999580383300781250000000, + "particle_position_x": 0.13107200000000005, + "particle_position_y": 0.13107200000000002, + "particle_weight": 2.4999999999999954e-12 + }, + "p3": { + "particle_cpu": 524288.0, + "particle_id": 1417339731968.0, + "particle_momentum_x": 2.18668242584086149502629596e-14, + "particle_momentum_y": 1.15704338121745335946594339e-14, + "particle_momentum_z": 2.748648252185421483237385693e-13, + "particle_optical_depth_QSR": 1047320.3901455836603417992591857910156250000000, + "particle_position_x": 0.13107200000000008, + "particle_position_y": 0.13107199999999997, + "particle_weight": 2.4999999999999954e-12 + }, + "p4": { + "particle_cpu": 524288.0, + "particle_id": 1984275415040.0, + "particle_momentum_x": 1.5790004379896003355140614396e-12, + "particle_momentum_y": 1.5892318647516152626506462218e-12, + "particle_momentum_z": 1.5996357079880960792769882593e-12, + "particle_optical_depth_QSR": 1047445.3621248775161802768707275390625000000000, + "particle_position_x": 0.131072, + "particle_position_y": 0.131072, + "particle_weight": 2.4999999999999954e-12 + }, + "qsp_1": { + "particle_cpu": 113640.0, + "particle_id": 504148092945.0, + "particle_momentum_x": 7.9316153448628635737165e-18, + "particle_momentum_y": 2.8710553896582841054827e-18, + "particle_momentum_z": 8.5951416157507011308028e-18, + "particle_optical_depth_BW": 226981.5178113427245989441871643066406250000000, + "particle_position_x": 0.0284238987593396531805556293193149031140, + "particle_position_y": 0.0283669864186241772741059463669444085099, + "particle_weight": 0.0000000000005415463447570800359762889929 + }, + "qsp_2": { + "particle_cpu": 75577.0, + "particle_id": 350266079184.0, + "particle_momentum_x": 1.261872820684872434889e-19, + "particle_momentum_y": 6.92126883544961084394040e-17, + "particle_momentum_z": 5.5124803824458753990864e-18, + "particle_optical_depth_BW": 151587.5284573705284856259822845458984375000000, + "particle_position_x": 0.0189417864640534228115598125441465526819, + "particle_position_y": 0.0189437404606696974118662524233513977379, + "particle_weight": 0.0000000000003608798980712890230565097739 + }, + "qsp_3": { + "particle_cpu": 214525.0, + "particle_id": 1055977307234.0, + "particle_momentum_x": 7.893390443110882622852864e-16, + "particle_momentum_y": 4.022899725523587988963599e-16, + "particle_momentum_z": 2.00897967451482969460704614e-14, + "particle_optical_depth_BW": 428733.8518201413680799305438995361328125000000, + "particle_position_x": 0.0536690485208531978256196737220307113603, + "particle_position_y": 0.0537320477591076775536471643590630264953, + "particle_weight": 1.0237169265747073732665847163e-12 + }, + "qsp_4": { + "particle_cpu": 92827.0, + "particle_id": 485591963594.0, + "particle_momentum_x": 6.38012975324615379422793758e-14, + "particle_momentum_y": 6.40026551728551134797706892e-14, + "particle_momentum_z": 6.42086308151940342379244652e-14, + "particle_optical_depth_BW": 185825.2873240348708350211381912231445312500000, + "particle_position_x": 0.0232045216834043527276421059468702878803, + "particle_position_y": 0.0232208158701406250368215467005938990042, + "particle_weight": 4.430484771728514459905705423e-13 + } +} diff --git a/Regression/Checksum/benchmarks_json/qed_quantum_sync_3d.json b/Regression/Checksum/benchmarks_json/qed_quantum_sync_3d.json new file mode 100644 index 00000000000..820694a7c5c --- /dev/null +++ b/Regression/Checksum/benchmarks_json/qed_quantum_sync_3d.json @@ -0,0 +1,101 @@ +{ + "lev=0": { + "Ex": 1.3099432828042958e-12 + }, + "p1": { + "particle_cpu": 524288.0, + "particle_id": 274878431232.0, + "particle_momentum_x": 9.7758873858556601265577378e-15, + "particle_momentum_y": 6.7491814272393665591042944e-15, + "particle_momentum_z": 1.71477994554173681559450222e-14, + "particle_optical_depth_QSR": 1049804.4301339467056095600128173828125000000000, + "particle_position_x": 0.13107199999999988, + "particle_position_y": 0.13107199999999986, + "particle_position_z": 0.13107200000000002, + "particle_weight": 1.2499999999999974e-18 + }, + "p2": { + "particle_cpu": 524288.0, + "particle_id": 824634245120.0, + "particle_momentum_x": 5.285246447726641013282976e-16, + "particle_momentum_y": 2.03260776355233874905821628e-14, + "particle_momentum_z": 7.1931656971580576683125466e-15, + "particle_optical_depth_QSR": 1047187.1483892769319936633110046386718750000000, + "particle_position_x": 0.13107199999999986, + "particle_position_y": 0.13107199999999994, + "particle_position_z": 0.131072, + "particle_weight": 1.2499999999999974e-18 + }, + "p3": { + "particle_cpu": 524288.0, + "particle_id": 1374390059008.0, + "particle_momentum_x": 3.47722762807363544779370064e-14, + "particle_momentum_y": 1.92479435301000680850724330e-14, + "particle_momentum_z": 2.715836966953134431892876796e-13, + "particle_optical_depth_QSR": 1047538.3340144035173580050468444824218750000000, + "particle_position_x": 0.13107199999999974, + "particle_position_y": 0.13107200000000008, + "particle_position_z": 0.13107199999999994, + "particle_weight": 1.2499999999999974e-18 + }, + "p4": { + "particle_cpu": 524288.0, + "particle_id": 1924145872896.0, + "particle_momentum_x": 1.5384447264927685411396454024e-12, + "particle_momentum_y": 1.5550384450950911129388361328e-12, + "particle_momentum_z": 1.5717828196787002757557403112e-12, + "particle_optical_depth_QSR": 1047538.7244381272466853260993957519531250000000, + "particle_position_x": 0.1310719999999999, + "particle_position_y": 0.1310720000000001, + "particle_position_z": 0.13107200000000005, + "particle_weight": 1.2499999999999974e-18 + }, + "qsp_1": { + "particle_cpu": 139805.0, + "particle_id": 606005192579.0, + "particle_momentum_x": 1.48199792997207174737118e-17, + "particle_momentum_y": 5.8972276571918738543742e-18, + "particle_momentum_z": 1.99276981360354362974353e-17, + "particle_optical_depth_BW": 279468.6734266423736698925495147705078125000000, + "particle_position_x": 0.0349323837710382584242907455518434289843, + "particle_position_y": 0.0349456659958360602025173591300699627027, + "particle_position_z": 0.0349613596100997389704012618949491297826, + "particle_weight": 3.333568572998047452078e-19 + }, + "qsp_2": { + "particle_cpu": 131684.0, + "particle_id": 607246582470.0, + "particle_momentum_x": 3.5707458764937662296484e-18, + "particle_momentum_y": 1.221476476409600044639063e-16, + "particle_momentum_z": 1.91987389872474352190967e-17, + "particle_optical_depth_BW": 263591.0839790093596093356609344482421875000000, + "particle_position_x": 0.032904736176992049889733493728272151202, + "particle_position_y": 0.0330043572259953385539255066305486252531, + "particle_position_z": 0.0329819361187009690850047149979218374938, + "particle_weight": 3.143310546875000280363e-19 + }, + "qsp_3": { + "particle_cpu": 300729.0, + "particle_id": 1513976629339.0, + "particle_momentum_x": 1.7928128884821719703095933e-15, + "particle_momentum_y": 9.340408950640840272122927e-16, + "particle_momentum_z": 2.83530122341059918144489386e-14, + "particle_optical_depth_BW": 600650.3304399864282459020614624023437500000000, + "particle_position_x": 0.0750897563442807225442265917081385850906, + "particle_position_y": 0.0751763824300837740777225803867622744292, + "particle_position_z": 0.0751542666431599948317909820616478100419, + "particle_weight": 7.164263725280753934436e-19 + }, + "qsp_4": { + "particle_cpu": 143505.0, + "particle_id": 785173536704.0, + "particle_momentum_x": 9.77033632739354423654078982e-14, + "particle_momentum_y": 9.82087398113350840453304285e-14, + "particle_momentum_z": 9.87240415385684092180580877e-14, + "particle_optical_depth_BW": 286503.0337664368562400341033935546875000000000, + "particle_position_x": 0.0357990571419779662898363881140539888293, + "particle_position_y": 0.0358748468909192846920674924149352591485, + "particle_position_z": 0.0358245955166236890176989504652738105506, + "particle_weight": 3.414738178253174363875e-19 + } +} diff --git a/Regression/Checksum/benchmarks_json/qed_quantum_sync_tau_init.json b/Regression/Checksum/benchmarks_json/qed_quantum_sync_tau_init.json deleted file mode 100644 index 61c33ab3949..00000000000 --- a/Regression/Checksum/benchmarks_json/qed_quantum_sync_tau_init.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "electrons": { - "particle_cpu": 28800.0, - "particle_id": 897828480.0, - "particle_momentum_x": 1.2577259718364536e-15, - "particle_momentum_y": 1.2533271477447635e-15, - "particle_momentum_z": 1.2567654312277259e-15, - "particle_optical_depth_QSR": 57886.05893096988, - "particle_position_x": 0.8640051455090565, - "particle_position_y": 0.8640331765233288, - "particle_weight": 36000000000.0 - }, - "lev=0": { - "Bx": 0.07184886279682517, - "By": 0.10667815989827657, - "Bz": 0.07109069834396708, - "Ex": 84573993.77188347, - "Ey": 82514926.82089016, - "Ez": 88699981.5687077, - "jx": 634967161798.5266, - "jy": 619508037314.5645, - "jz": 665944376473.1846 - }, - "positrons": { - "particle_cpu": 28800.0, - "particle_id": 2682046080.0, - "particle_momentum_x": 1.2564867705042074e-15, - "particle_momentum_y": 1.2522720470651854e-15, - "particle_momentum_z": 1.2506801884403616e-15, - "particle_optical_depth_QSR": 57542.97683982639, - "particle_position_x": 0.8641071175220609, - "particle_position_y": 0.863981764528814, - "particle_weight": 36000000000.0 - } -} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/qed_schwinger2.json b/Regression/Checksum/benchmarks_json/qed_schwinger2.json index e2a52492996..ff413ba4cc6 100644 --- a/Regression/Checksum/benchmarks_json/qed_schwinger2.json +++ b/Regression/Checksum/benchmarks_json/qed_schwinger2.json @@ -2,34 +2,34 @@ "ele_schwinger": { "particle_cpu": 1024.0, "particle_id": 1573888.0, - "particle_momentum_x": 1.253856079274937e-09, - "particle_momentum_y": 7.988412729587993e-11, - "particle_momentum_z": 2.8023147035854144e-10, + "particle_momentum_x": 1.2538560192562119145907523613836e-09, + "particle_momentum_y": 7.98841234718825980904544944075e-11, + "particle_momentum_z": 2.802314569448479707680714186416e-10, "particle_position_x": 0.0005120000000000001, "particle_position_y": 0.000512, "particle_position_z": 0.000512, - "particle_weight": 8.300975111485836e+19 + "particle_weight": 8.3009747141536481280e+19 }, "lev=0": { "Bx": 3439183579241.8213, "By": 1076561948990.9731, "Bz": 3760851107750.2676, - "Ex": 1.6138262991542088e+26, - "Ey": 1.0214830999058132e+25, - "Ez": 3.5684391886872975e+25, - "jx": 1.4539784582497718e+31, - "jy": 9.202945669400098e+29, - "jz": 3.214948145600786e+30 + "Ex": 1.61382622190626294479192064e+26, + "Ey": 1.0214830510117843745374208e+25, + "Ez": 3.5684390178813696941228032e+25, + "jx": 1.4539783886540389390252047859712e+31, + "jy": 9.20294522889443324046591655936e+29, + "jz": 3.214947991714962836023450533888e+30 }, "pos_schwinger": { "particle_cpu": 1024.0, "particle_id": 2622464.0, - "particle_momentum_x": 1.2705200005945566e-09, - "particle_momentum_y": 8.002632204675343e-11, - "particle_momentum_z": 2.8093193376686925e-10, + "particle_momentum_x": 1.2705199397782006324793234588533e-09, + "particle_momentum_y": 8.00263182163650910948918243636e-11, + "particle_momentum_z": 2.809319203195289649657045801174e-10, "particle_position_x": 0.0005120000000000001, "particle_position_y": 0.000512, "particle_position_z": 0.000512, - "particle_weight": 8.30097511148584e+19 + "particle_weight": 8.3009747141536481280e+19 } -} \ No newline at end of file +} diff --git a/Regression/Checksum/benchmarks_json/qed_schwinger4.json b/Regression/Checksum/benchmarks_json/qed_schwinger4.json index b2276d5476e..1ea05692000 100644 --- a/Regression/Checksum/benchmarks_json/qed_schwinger4.json +++ b/Regression/Checksum/benchmarks_json/qed_schwinger4.json @@ -3,33 +3,33 @@ "particle_cpu": 1024.0, "particle_id": 1573888.0, "particle_momentum_x": 6.569424596732217e-12, - "particle_momentum_y": 3.358784455760675e-27, + "particle_momentum_y": 3.3588025031324e-27, "particle_momentum_z": 5.121822174810324e-12, "particle_position_x": 0.0005120000000000001, "particle_position_y": 0.0005120000000000001, "particle_position_z": 0.0005119999999999999, - "particle_weight": 114054.59650147014 + "particle_weight": 114054.5697167885082308202981948852539062500000 }, "lev=0": { - "Bx": 1.4000444810733876, + "Bx": 1.4000605828203265268427912815241143107414, "By": 1707847966720000.0, - "Bz": 0.3684660869216436, - "Ex": 1316425744.6551433, + "Bz": 0.3684660435860281357811629732168512418866, + "Ex": 1316425589.8291716575622558593750000000000000000000, "Ey": 0.0, "Ez": 5.119999999998239e+23, - "jx": 195136858233472.38, + "jx": 195136835283257.5000000000000000000000000000000000000000, "jy": 0.0, - "jz": 1.5866782361655698e+16 + "jz": 1.5866778635486594e+16 }, "pos_schwinger": { "particle_cpu": 1024.0, "particle_id": 2622464.0, "particle_momentum_x": 6.569424596732212e-12, - "particle_momentum_y": 3.4619573459705485e-27, + "particle_momentum_y": 3.4619972045013e-27, "particle_momentum_z": 5.1218221748102914e-12, "particle_position_x": 0.0005120000000000001, "particle_position_y": 0.0005120000000000001, "particle_position_z": 0.000512, - "particle_weight": 114054.59650147012 + "particle_weight": 114054.5697167885227827355265617370605468750000 } -} \ No newline at end of file +} diff --git a/Regression/Checksum/benchmarks_json/subcyclingMR.json b/Regression/Checksum/benchmarks_json/subcyclingMR.json index a2657cc966f..c977c3c791f 100644 --- a/Regression/Checksum/benchmarks_json/subcyclingMR.json +++ b/Regression/Checksum/benchmarks_json/subcyclingMR.json @@ -1,7 +1,7 @@ { "beam": { "particle_cpu": 0.0, - "particle_id": 21474836440000.0, + "particle_id": 150005000.0, "particle_momentum_x": 3.026788506525083e-19, "particle_momentum_y": 0.0, "particle_momentum_z": 6.301932614657182e-17, @@ -43,7 +43,7 @@ }, "plasma_e": { "particle_cpu": 0.0, - "particle_id": 22972039003792.0, + "particle_id": 3730265090.0, "particle_momentum_x": 1.1529420490113797e-18, "particle_momentum_y": 0.0, "particle_momentum_z": 1.537431384936693e-18, @@ -53,7 +53,7 @@ }, "plasma_p": { "particle_cpu": 0.0, - "particle_id": 23092453563072.0, + "particle_id": 3856236480.0, "particle_momentum_x": 1.5163562656560444e-18, "particle_momentum_y": 0.0, "particle_momentum_z": 2.652916405237197e-18, diff --git a/Regression/Checksum/checksum.py b/Regression/Checksum/checksum.py index df60bbe77d2..446fac35221 100644 --- a/Regression/Checksum/checksum.py +++ b/Regression/Checksum/checksum.py @@ -1,3 +1,11 @@ +""" + Copyright 2020 + + This file is part of WarpX. + + License: BSD-3-Clause-LBNL + """ + from benchmark import Benchmark import yt import re diff --git a/Regression/Checksum/checksumAPI.py b/Regression/Checksum/checksumAPI.py index 577fb890991..946494a6970 100755 --- a/Regression/Checksum/checksumAPI.py +++ b/Regression/Checksum/checksumAPI.py @@ -1,5 +1,13 @@ #! /usr/bin/env python3 +""" + Copyright 2020 + + This file is part of WarpX. + + License: BSD-3-Clause-LBNL + """ + from checksum import Checksum from benchmark import Benchmark import argparse diff --git a/Regression/Checksum/config.py b/Regression/Checksum/config.py index b7759170f67..fb9dbb223c8 100644 --- a/Regression/Checksum/config.py +++ b/Regression/Checksum/config.py @@ -1,3 +1,11 @@ +""" + Copyright 2020 + + This file is part of WarpX. + + License: BSD-3-Clause-LBNL + """ + import os benchmark_location = os.path.split(__file__)[0] + '/benchmarks_json' diff --git a/Regression/WarpX-tests.ini b/Regression/WarpX-tests.ini index acfe16e7656..3fadf586cd6 100644 --- a/Regression/WarpX-tests.ini +++ b/Regression/WarpX-tests.ini @@ -56,7 +56,7 @@ branch = development [extra-PICSAR] dir = /home/regtester/AMReX_RegTesting/picsar/ -branch = QED +branch = development # individual problems follow @@ -288,7 +288,7 @@ buildDir = . inputFile = Examples/Tests/Langmuir/inputs_3d_rt runtime_params = electrons.ux=0.01 electrons.xmax=0.e-6 diag1.fields_to_plot=Ex jx diag1.electrons.variables=w ux dim = 2 -addToCompileString = +addToCompileString = USE_OPENPMD=FALSE restartTest = 0 useMPI = 0 numprocs = 1 @@ -697,6 +697,25 @@ analysisRoutine = Examples/Tests/Langmuir/analysis_langmuir_multi_rz.py analysisOutputImage = Langmuir_multi_rz_psatd_analysis.png tolerance = 1.e-14 +[Langmuir_multi_rz_psatd_current_correction] +buildDir = . +inputFile = Examples/Tests/Langmuir/inputs_2d_multi_rz_rt +runtime_params = diag1.electrons.variables=w ux uy uz diag1.ions.variables=w ux uy uz diag1.dump_rz_modes=0 algo.current_deposition=direct warpx.do_dive_cleaning=0 amr.max_grid_size=128 psatd.periodic_single_box_fft=1 psatd.current_correction=1 diag1.fields_to_plot=jx jz Ex Ez By rho divE +dim = 2 +addToCompileString = USE_RZ=TRUE USE_PSATD=TRUE BLAS_LIB=-lblas LAPACK_LIB=-llapack +restartTest = 0 +useMPI = 1 +numprocs = 1 +useOMP = 1 +numthreads = 2 +compileTest = 0 +doVis = 0 +compareParticles = 1 +particleTypes = electrons ions +analysisRoutine = Examples/Tests/Langmuir/analysis_langmuir_multi_rz.py +analysisOutputImage = Langmuir_multi_rz_psatd_analysis.png +tolerance = 1.e-14 + [Python_Langmuir_rz_multimode] buildDir = . inputFile = Examples/Tests/Langmuir/PICMI_inputs_langmuir_rz_multimode_analyze.py @@ -1036,9 +1055,9 @@ compareParticles = 0 analysisRoutine = Examples/Tests/radiation_reaction/test_const_B_analytical/analysis_classicalRR.py tolerance = 1.e-14 -[qed_breit_wheeler_tau_init] +[qed_breit_wheeler_2d] buildDir = . -inputFile = Examples/Modules/qed/breit_wheeler/inputs_2d_tau_init +inputFile = Examples/Modules/qed/breit_wheeler/inputs_2d runtime_params = dim = 2 addToCompileString = QED=TRUE @@ -1050,12 +1069,12 @@ numthreads = 2 compileTest = 0 doVis = 0 compareParticles = 0 -analysisRoutine = Examples/Modules/qed/breit_wheeler/analysis_2d_tau_init.py +analysisRoutine = Examples/Modules/qed/breit_wheeler/analysis.py tolerance = 1.e-14 -[qed_breit_wheeler_opt_depth_evolution] +[qed_breit_wheeler_3d] buildDir = . -inputFile = Examples/Modules/qed/breit_wheeler/inputs_3d_optical_depth_evolution +inputFile = Examples/Modules/qed/breit_wheeler/inputs_3d runtime_params = dim = 3 addToCompileString = QED=TRUE @@ -1067,12 +1086,12 @@ numthreads = 1 compileTest = 0 doVis = 0 compareParticles = 0 -analysisRoutine = Examples/Modules/qed/breit_wheeler/analysis_3d_optical_depth_evolution.py +analysisRoutine = Examples/Modules/qed/breit_wheeler/analysis.py tolerance = 1.e-14 -[qed_quantum_sync_tau_init] +[qed_quantum_sync_2d] buildDir = . -inputFile = Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init +inputFile = Examples/Modules/qed/quantum_synchrotron/inputs_2d runtime_params = dim = 2 addToCompileString = QED=TRUE @@ -1084,8 +1103,25 @@ numthreads = 2 compileTest = 0 doVis = 0 compareParticles = 0 -analysisRoutine = Examples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py -tolerance = 2.e-15 +analysisRoutine = Examples/Modules/qed/quantum_synchrotron/analysis.py +tolerance = 1.e-14 + +[qed_quantum_sync_3d] +buildDir = . +inputFile = Examples/Modules/qed/quantum_synchrotron/inputs_3d +runtime_params = +dim = 3 +addToCompileString = QED=TRUE +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 2 +compileTest = 0 +doVis = 0 +compareParticles = 0 +analysisRoutine = Examples/Modules/qed/quantum_synchrotron/analysis.py +tolerance = 1.e-14 [qed_schwinger1] buildDir = . @@ -1272,6 +1308,22 @@ doVis = 0 analysisRoutine = Examples/analysis_default_regression.py tolerance = 1.e-14 +[LaserIonAcc2d] +buildDir = . +inputFile = Examples/Physics_applications/laser_ion/inputs +runtime_params = warpx.do_dynamic_scheduling=0 warpx.serialize_ics=1 amr.n_cell=384 512 max_step=100 +dim = 2 +addToCompileString = +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 2 +compileTest = 0 +doVis = 0 +analysisRoutine = Examples/analysis_default_regression.py +tolerance = 1.e-14 + [momentum-conserving-gather] buildDir = . inputFile = Examples/Physics_applications/plasma_acceleration/inputs_2d @@ -1415,7 +1467,7 @@ inputFile = Examples/Modules/laser_injection_from_file/analysis.py aux1File = Examples/Modules/laser_injection_from_file/inputs.2d_test_txye customRunCmd = ./analysis.py dim = 2 -addToCompileString = +addToCompileString = USE_OPENPMD=FALSE restartTest = 0 useMPI = 0 useOMP = 1 @@ -1490,6 +1542,7 @@ numthreads = 2 compileTest = 0 doVis = 0 compareParticles = 0 +aux1File = Tools/PostProcessing/read_raw_data.py analysisRoutine = Examples/Tests/reduced_diags/analysis_reduced_diags.py tolerance = 1e-12 @@ -1545,6 +1598,60 @@ particleTypes = electrons ions analysisRoutine = Examples/Tests/galilean/analysis_2d.py tolerance = 1.e-14 +[galilean_2d_psatd_current_correction] +buildDir = . +inputFile = Examples/Tests/galilean/inputs_2d +runtime_params = warpx.use_filter=0 psatd.periodic_single_box_fft=1 psatd.update_with_rho=0 psatd.current_correction=1 diag1.fields_to_plot=Ex Ey Ez Bx By Bz jx jy jz rho divE +dim = 2 +addToCompileString = USE_PSATD=TRUE +restartTest = 0 +useMPI = 1 +numprocs = 1 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 1 +particleTypes = electrons ions +analysisRoutine = Examples/Tests/galilean/analysis_2d.py +tolerance = 1.e-14 + +[galilean_rz_psatd] +buildDir = . +inputFile = Examples/Tests/galilean/inputs_rz +runtime_params = +dim = 2 +addToCompileString = USE_RZ=TRUE USE_PSATD=TRUE BLAS_LIB=-lblas LAPACK_LIB=-llapack +restartTest = 0 +useMPI = 1 +numprocs = 1 +useOMP = 1 +numthreads = 2 +compileTest = 0 +doVis = 0 +compareParticles = 1 +particleTypes = electrons ions +analysisRoutine = Examples/Tests/galilean/analysis_2d.py +tolerance = 1.e-14 + +[galilean_rz_psatd_current_correction] +buildDir = . +inputFile = Examples/Tests/galilean/inputs_rz +runtime_params = warpx.use_kspace_filter=0 psatd.periodic_single_box_fft=1 psatd.current_correction=1 +dim = 2 +addToCompileString = USE_RZ=TRUE USE_PSATD=TRUE BLAS_LIB=-lblas LAPACK_LIB=-llapack +restartTest = 0 +useMPI = 1 +numprocs = 1 +useOMP = 1 +numthreads = 2 +compileTest = 0 +doVis = 0 +compareParticles = 1 +particleTypes = electrons ions +analysisRoutine = Examples/Tests/galilean/analysis_2d.py +tolerance = 1.e-14 + [galilean_3d_psatd] buildDir = . inputFile = Examples/Tests/galilean/inputs_3d @@ -1563,6 +1670,24 @@ particleTypes = electrons ions analysisRoutine = Examples/Tests/galilean/analysis_3d.py tolerance = 1.e-14 +[galilean_3d_psatd_current_correction] +buildDir = . +inputFile = Examples/Tests/galilean/inputs_3d +runtime_params = warpx.use_filter=0 psatd.periodic_single_box_fft=1 psatd.update_with_rho=0 psatd.current_correction=1 diag1.fields_to_plot=Ex Ey Ez Bx By Bz jx jy jz rho divE +dim = 3 +addToCompileString = USE_PSATD=TRUE +restartTest = 0 +useMPI = 1 +numprocs = 1 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 1 +particleTypes = electrons ions +analysisRoutine = Examples/Tests/galilean/analysis_3d.py +tolerance = 1.e-14 + [averaged_galilean_2d_psatd] buildDir = . inputFile = Examples/Tests/averaged_galilean/inputs_avg_2d @@ -1628,3 +1753,19 @@ doVis = 0 compareParticles = 0 analysisRoutine = Examples/Tests/initial_distribution/analysis_distribution.py aux1File = Tools/PostProcessing/read_raw_data.py + +[leveling_thinning] +buildDir = . +inputFile = Examples/Modules/resampling/inputs_leveling_thinning +runtime_params = +dim = 2 +addToCompileString = +restartTest = 0 +useMPI = 1 +numprocs = 4 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 0 +analysisRoutine = Examples/Modules/resampling/analysis_leveling_thinning.py diff --git a/Source/BoundaryConditions/PML.H b/Source/BoundaryConditions/PML.H index e24934414da..a1be76d9472 100644 --- a/Source/BoundaryConditions/PML.H +++ b/Source/BoundaryConditions/PML.H @@ -21,7 +21,7 @@ #include -struct Sigma : amrex::Gpu::ManagedVector +struct Sigma : amrex::Gpu::DeviceVector { int lo() const { return m_lo; } int hi() const { return m_hi; } @@ -56,8 +56,8 @@ namespace amrex { public: FabFactory (const BoxArray& grid_ba, const Real* dx, int ncell, int delta) : m_grids(grid_ba), m_dx(dx), m_ncell(ncell), m_delta(delta) {} - virtual SigmaBox* create (const Box& box, int ncomps, - const FabInfo& info, int box_index) const final + virtual SigmaBox* create (const Box& box, int /*ncomps*/, + const FabInfo& /*info*/, int /*box_index*/) const final { return new SigmaBox(box, m_grids, m_dx, m_ncell, m_delta); } virtual void destroy (SigmaBox* fab) const final { delete fab; diff --git a/Source/BoundaryConditions/PML.cpp b/Source/BoundaryConditions/PML.cpp index 35d8f3f781f..202867bdbb8 100644 --- a/Source/BoundaryConditions/PML.cpp +++ b/Source/BoundaryConditions/PML.cpp @@ -8,8 +8,9 @@ */ #include "BoundaryConditions/PML.H" #include "BoundaryConditions/PMLComponent.H" -#include "WarpX.H" #include "Utils/WarpXConst.H" +#include "Utils/WarpXAlgorithmSelection.H" +#include "WarpX.H" #include #include @@ -35,21 +36,25 @@ namespace int slo = sigma.m_lo; int sslo = sigma_star.m_lo; - for (int i = olo; i <= ohi+1; ++i) + const int N = ohi+1-olo+1; + Real* p_sigma = sigma.data(); + Real* p_sigma_cumsum = sigma_cumsum.data(); + Real* p_sigma_star = sigma_star.data(); + Real* p_sigma_star_cumsum = sigma_star_cumsum.data(); + amrex::ParallelFor(N, [=] AMREX_GPU_DEVICE (int i) noexcept { + i += olo; Real offset = static_cast(glo-i); - sigma[i-slo] = fac*(offset*offset); + p_sigma[i-slo] = fac*(offset*offset); // sigma_cumsum is the analytical integral of sigma function at same points than sigma - sigma_cumsum[i-slo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; - } - - for (int i = olo; i <= ohi; ++i) - { - Real offset = static_cast(glo-i) - 0.5; - sigma_star[i-sslo] = fac*(offset*offset); - // sigma_star_cumsum is the analytical integral of sigma function at same points than sigma_star - sigma_star_cumsum[i-sslo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; - } + p_sigma_cumsum[i-slo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; + if (i <= ohi) { + offset = static_cast(glo-i) - 0.5; + p_sigma_star[i-sslo] = fac*(offset*offset); + // sigma_star_cumsum is the analytical integral of sigma function at same points than sigma_star + p_sigma_star_cumsum[i-sslo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; + } + }); } static void FillHi (int idim, Sigma& sigma, Sigma& sigma_cumsum, @@ -61,18 +66,24 @@ namespace int ohi = overlap.bigEnd(idim); int slo = sigma.m_lo; int sslo = sigma_star.m_lo; - for (int i = olo; i <= ohi+1; ++i) + + const int N = ohi+1-olo+1; + Real* p_sigma = sigma.data(); + Real* p_sigma_cumsum = sigma_cumsum.data(); + Real* p_sigma_star = sigma_star.data(); + Real* p_sigma_star_cumsum = sigma_star_cumsum.data(); + amrex::ParallelFor(N, [=] AMREX_GPU_DEVICE (int i) noexcept { + i += olo; Real offset = static_cast(i-ghi-1); - sigma[i-slo] = fac*(offset*offset); - sigma_cumsum[i-slo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; - } - for (int i = olo; i <= ohi; ++i) - { - Real offset = static_cast(i-ghi) - 0.5; - sigma_star[i-sslo] = fac*(offset*offset); - sigma_star_cumsum[i-sslo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; - } + p_sigma[i-slo] = fac*(offset*offset); + p_sigma_cumsum[i-slo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; + if (i <= ohi) { + offset = static_cast(i-ghi) - 0.5; + p_sigma_star[i-sslo] = fac*(offset*offset); + p_sigma_star_cumsum[i-sslo] = (fac*(offset*offset*offset)/3.)/PhysConst::c; + } + }); } static void FillZero (int idim, Sigma& sigma, Sigma& sigma_cumsum, @@ -83,10 +94,22 @@ namespace int ohi = overlap.bigEnd(idim); int slo = sigma.m_lo; int sslo = sigma_star.m_lo; - std::fill(sigma.begin()+(olo-slo), sigma.begin()+(ohi+2-slo), 0.0); - std::fill(sigma_cumsum.begin()+(olo-slo), sigma_cumsum.begin()+(ohi+2-slo), 0.0); - std::fill(sigma_star.begin()+(olo-sslo), sigma_star.begin()+(ohi+1-sslo), 0.0); - std::fill(sigma_star_cumsum.begin()+(olo-sslo), sigma_star_cumsum.begin()+(ohi+1-sslo), 0.0); + + const int N = ohi+1-olo+1; + Real* p_sigma = sigma.data(); + Real* p_sigma_cumsum = sigma_cumsum.data(); + Real* p_sigma_star = sigma_star.data(); + Real* p_sigma_star_cumsum = sigma_star_cumsum.data(); + amrex::ParallelFor(N, [=] AMREX_GPU_DEVICE (int i) noexcept + { + i += olo; + p_sigma[i-slo] = Real(0.0); + p_sigma_cumsum[i-slo] = Real(0.0); + if (i <= ohi) { + p_sigma_star[i-sslo] = Real(0.0); + p_sigma_star_cumsum[i-sslo] = Real(0.0); + } + }); } } @@ -102,29 +125,29 @@ SigmaBox::SigmaBox (const Box& box, const BoxArray& grids, const Real* dx, int n { sigma [idim].resize(sz[idim]+1); sigma_cumsum [idim].resize(sz[idim]+1); - sigma_star [idim].resize(sz[idim]); - sigma_star_cumsum [idim].resize(sz[idim]); + sigma_star [idim].resize(sz[idim]+1); + sigma_star_cumsum [idim].resize(sz[idim]+1); sigma_fac [idim].resize(sz[idim]+1); sigma_cumsum_fac [idim].resize(sz[idim]+1); - sigma_star_fac [idim].resize(sz[idim]); - sigma_star_cumsum_fac[idim].resize(sz[idim]); + sigma_star_fac [idim].resize(sz[idim]+1); + sigma_star_cumsum_fac[idim].resize(sz[idim]+1); sigma [idim].m_lo = lo[idim]; sigma [idim].m_hi = hi[idim]+1; sigma_cumsum [idim].m_lo = lo[idim]; sigma_cumsum [idim].m_hi = hi[idim]+1; sigma_star [idim].m_lo = lo[idim]; - sigma_star [idim].m_hi = hi[idim]; + sigma_star [idim].m_hi = hi[idim]+1; sigma_star_cumsum [idim].m_lo = lo[idim]; - sigma_star_cumsum [idim].m_hi = hi[idim]; + sigma_star_cumsum [idim].m_hi = hi[idim]+1; sigma_fac [idim].m_lo = lo[idim]; sigma_fac [idim].m_hi = hi[idim]+1; sigma_cumsum_fac [idim].m_lo = lo[idim]; sigma_cumsum_fac [idim].m_hi = hi[idim]+1; sigma_star_fac [idim].m_lo = lo[idim]; - sigma_star_fac [idim].m_hi = hi[idim]; + sigma_star_fac [idim].m_hi = hi[idim]+1; sigma_star_cumsum_fac[idim].m_lo = lo[idim]; - sigma_star_cumsum_fac[idim].m_hi = hi[idim]; + sigma_star_cumsum_fac[idim].m_hi = hi[idim]+1; } Array fac; @@ -299,33 +322,67 @@ SigmaBox::SigmaBox (const Box& box, const BoxArray& grids, const Real* dx, int n amrex::Abort("SigmaBox::SigmaBox(): direct_faces.size() > 1, Box gaps not wide enough?\n"); } } + + amrex::Gpu::synchronize(); } void -SigmaBox::ComputePMLFactorsB (const Real* dx, Real dt) +SigmaBox::ComputePMLFactorsB (const Real* a_dx, Real dt) { - for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) + GpuArray p_sigma_star_fac; + GpuArray p_sigma_star_cumsum_fac; + GpuArray p_sigma_star; + GpuArray p_sigma_star_cumsum; + GpuArray N; + GpuArray dx; + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + p_sigma_star_fac[idim] = sigma_star_fac[idim].data(); + p_sigma_star_cumsum_fac[idim] = sigma_star_cumsum_fac[idim].data(); + p_sigma_star[idim] = sigma_star[idim].data(); + p_sigma_star_cumsum[idim] = sigma_star_cumsum[idim].data(); + N[idim] = sigma_star[idim].size(); + dx[idim] = a_dx[idim]; + } + amrex::ParallelFor(amrex::max(AMREX_D_DECL(N[0],N[1],N[2])), + [=] AMREX_GPU_DEVICE (int i) noexcept { - for (int i = 0, N = sigma_star[idim].size(); i < N; ++i) - { - sigma_star_fac[idim][i] = std::exp(-sigma_star[idim][i]*dt); - sigma_star_cumsum_fac[idim][i] = std::exp(-sigma_star_cumsum[idim][i]*dx[idim]); + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + if (i < N[idim]) { + p_sigma_star_fac[idim][i] = std::exp(-p_sigma_star[idim][i]*dt); + p_sigma_star_cumsum_fac[idim][i] = std::exp(-p_sigma_star_cumsum[idim][i]*dx[idim]); + } } - } + }); } void -SigmaBox::ComputePMLFactorsE (const Real* dx, Real dt) +SigmaBox::ComputePMLFactorsE (const Real* a_dx, Real dt) { - for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) + GpuArray p_sigma_fac; + GpuArray p_sigma_cumsum_fac; + GpuArray p_sigma; + GpuArray p_sigma_cumsum; + GpuArray N; + GpuArray dx; + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + p_sigma_fac[idim] = sigma_fac[idim].data(); + p_sigma_cumsum_fac[idim] = sigma_cumsum_fac[idim].data(); + p_sigma[idim] = sigma[idim].data(); + p_sigma_cumsum[idim] = sigma_cumsum[idim].data(); + N[idim] = sigma[idim].size(); + dx[idim] = a_dx[idim]; + } + amrex::ParallelFor(amrex::max(AMREX_D_DECL(N[0],N[1],N[2])), + [=] AMREX_GPU_DEVICE (int i) noexcept { - for (int i = 0, N = sigma[idim].size(); i < N; ++i) - { - sigma_fac[idim][i] = std::exp(-sigma[idim][i]*dt); - sigma_cumsum_fac[idim][i] = std::exp(-sigma_cumsum[idim][i]*dx[idim]); + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + if (i < N[idim]) { + p_sigma_fac[idim][i] = std::exp(-p_sigma[idim][i]*dt); + p_sigma_cumsum_fac[idim][i] = std::exp(-p_sigma_cumsum[idim][i]*dx[idim]); + } } - } + }); } MultiSigmaBox::MultiSigmaBox (const BoxArray& ba, const DistributionMapping& dm, @@ -415,7 +472,7 @@ PML::PML (const BoxArray& grid_ba, const DistributionMapping& /*grid_dm*/, IntVect nge = IntVect(AMREX_D_DECL(2, 2, 2)); IntVect ngb = IntVect(AMREX_D_DECL(2, 2, 2)); int ngf_int = (do_moving_window) ? 2 : 0; - if (WarpX::maxwell_solver_id == 1) ngf_int = std::max( ngf_int, 1 ); + if (WarpX::maxwell_solver_id == MaxwellSolverAlgo::CKC) ngf_int = std::max( ngf_int, 1 ); IntVect ngf = IntVect(AMREX_D_DECL(ngf_int, ngf_int, ngf_int)); #ifdef WARPX_USE_PSATD // Increase the number of guard cells, in order to fit the extent @@ -971,22 +1028,22 @@ PML::CheckPoint (const std::string& dir) const { if (pml_E_fp[0]) { - VisMF::Write(*pml_E_fp[0], dir+"_Ex_fp"); - VisMF::Write(*pml_E_fp[1], dir+"_Ey_fp"); - VisMF::Write(*pml_E_fp[2], dir+"_Ez_fp"); - VisMF::Write(*pml_B_fp[0], dir+"_Bx_fp"); - VisMF::Write(*pml_B_fp[1], dir+"_By_fp"); - VisMF::Write(*pml_B_fp[2], dir+"_Bz_fp"); + VisMF::AsyncWrite(*pml_E_fp[0], dir+"_Ex_fp"); + VisMF::AsyncWrite(*pml_E_fp[1], dir+"_Ey_fp"); + VisMF::AsyncWrite(*pml_E_fp[2], dir+"_Ez_fp"); + VisMF::AsyncWrite(*pml_B_fp[0], dir+"_Bx_fp"); + VisMF::AsyncWrite(*pml_B_fp[1], dir+"_By_fp"); + VisMF::AsyncWrite(*pml_B_fp[2], dir+"_Bz_fp"); } if (pml_E_cp[0]) { - VisMF::Write(*pml_E_cp[0], dir+"_Ex_cp"); - VisMF::Write(*pml_E_cp[1], dir+"_Ey_cp"); - VisMF::Write(*pml_E_cp[2], dir+"_Ez_cp"); - VisMF::Write(*pml_B_cp[0], dir+"_Bx_cp"); - VisMF::Write(*pml_B_cp[1], dir+"_By_cp"); - VisMF::Write(*pml_B_cp[2], dir+"_Bz_cp"); + VisMF::AsyncWrite(*pml_E_cp[0], dir+"_Ex_cp"); + VisMF::AsyncWrite(*pml_E_cp[1], dir+"_Ey_cp"); + VisMF::AsyncWrite(*pml_E_cp[2], dir+"_Ez_cp"); + VisMF::AsyncWrite(*pml_B_cp[0], dir+"_Bx_cp"); + VisMF::AsyncWrite(*pml_B_cp[1], dir+"_By_cp"); + VisMF::AsyncWrite(*pml_B_cp[2], dir+"_Bz_cp"); } } diff --git a/Source/BoundaryConditions/PML_current.H b/Source/BoundaryConditions/PML_current.H index 1d0249d5621..9687282e3e2 100644 --- a/Source/BoundaryConditions/PML_current.H +++ b/Source/BoundaryConditions/PML_current.H @@ -8,6 +8,7 @@ #ifndef PML_CURRENT_H_ #define PML_CURRENT_H_ +#include #include AMREX_GPU_HOST_DEVICE AMREX_INLINE @@ -33,6 +34,7 @@ void push_ex_pml_current (int j, int k, int l, Ex(j,k,l,1) = Ex(j,k,l,1) - mu_c2_dt * alpha_xz * jx(j,k,l); #else Ex(j,k,l,1) = Ex(j,k,l,1) - mu_c2_dt * jx(j,k,l); + amrex::ignore_unused(sigjy, sigjz, ylo, zlo); #endif } @@ -60,6 +62,7 @@ void push_ey_pml_current (int j, int k, int l, #else Ey(j,k,l,0) = Ey(j,k,l,0) - 0.5 * mu_c2_dt * jy(j,k,l); Ey(j,k,l,1) = Ey(j,k,l,1) - 0.5 * mu_c2_dt * jy(j,k,l); + amrex::ignore_unused(sigjx, sigjz, xlo, zlo); #endif } @@ -86,6 +89,7 @@ void push_ez_pml_current (int j, int k, int l, Ez(j,k,l,1) = Ez(j,k,l,1) - mu_c2_dt * alpha_zy * jz(j,k,l); #else Ez(j,k,l,0) = Ez(j,k,l,0) - mu_c2_dt * jz(j,k,l); + amrex::ignore_unused(sigjx, sigjy, xlo, ylo); #endif } @@ -101,6 +105,7 @@ void damp_jx_pml (int j, int k, int l, jx(j,k,l) = jx(j,k,l) * sigsjx[j-xlo] * sigjy[k-ylo] * sigjz[l-zlo]; #else jx(j,k,l) = jx(j,k,l) * sigsjx[j-xlo] * sigjz[k-zlo]; + amrex::ignore_unused(sigjy, ylo); #endif } @@ -116,6 +121,7 @@ void damp_jy_pml (int j, int k, int l, jy(j,k,l) = jy(j,k,l) * sigjx[j-xlo] * sigsjy[k-ylo] * sigjz[l-zlo]; #else jy(j,k,l) = jy(j,k,l) * sigjx[j-xlo] * sigjz[k-zlo]; + amrex::ignore_unused(sigsjy, ylo); #endif } @@ -131,6 +137,7 @@ void damp_jz_pml (int j, int k, int l, jz(j,k,l) = jz(j,k,l) * sigjx[j-xlo] * sigjy[k-ylo] * sigsjz[l-zlo]; #else jz(j,k,l) = jz(j,k,l) * sigjx[j-xlo] * sigsjz[k-zlo]; + amrex::ignore_unused(sigjy, ylo); #endif } diff --git a/Source/BoundaryConditions/WarpX_PML_kernels.H b/Source/BoundaryConditions/WarpX_PML_kernels.H index 75db5ba1df1..e9117532266 100644 --- a/Source/BoundaryConditions/WarpX_PML_kernels.H +++ b/Source/BoundaryConditions/WarpX_PML_kernels.H @@ -8,6 +8,7 @@ #ifndef WARPX_PML_KERNELS_H_ #define WARPX_PML_KERNELS_H_ +#include #include using namespace amrex; @@ -24,6 +25,7 @@ void warpx_damp_pml_ex (int i, int j, int k, Array4 const& Ex, Ex(i,j,k,1) *= sigma_fac_z[k-zlo]; #else Ex(i,j,k,1) *= sigma_fac_z[j-zlo]; + amrex::ignore_unused(sigma_fac_y, ylo); #endif Ex(i,j,k,2) *= sigma_star_fac_x[i-xlo]; } @@ -40,6 +42,7 @@ void warpx_damp_pml_ey (int i, int j, int k, Array4 const& Ey, Ey(i,j,k,2) *= sigma_star_fac_y[j-ylo]; #else Ey(i,j,k,0) *= sigma_fac_z[j-zlo]; + amrex::ignore_unused(sigma_star_fac_y, ylo); #endif Ey(i,j,k,1) *= sigma_fac_x[i-xlo]; } @@ -57,6 +60,7 @@ void warpx_damp_pml_ez (int i, int j, int k, Array4 const& Ez, Ez(i,j,k,2) *= sigma_star_fac_z[k-zlo]; #else Ez(i,j,k,2) *= sigma_star_fac_z[j-zlo]; + amrex::ignore_unused(sigma_fac_y, ylo); #endif } @@ -72,6 +76,7 @@ void warpx_damp_pml_bx (int i, int j, int k, Array4 const& Bx, Bx(i,j,k,1) *= sigma_star_fac_z[k-zlo]; #else Bx(i,j,k,1) *= sigma_star_fac_z[j-zlo]; + amrex::ignore_unused(sigma_star_fac_y, ylo); #endif } @@ -98,6 +103,8 @@ void warpx_damp_pml_bz (int i, int j, int k, Array4 const& Bz, Bz(i,j,k,0) *= sigma_star_fac_x[i-xlo]; #if (AMREX_SPACEDIM == 3) Bz(i,j,k,1) *= sigma_star_fac_y[j-ylo]; +#else + amrex::ignore_unused(sigma_star_fac_y, ylo); #endif } @@ -114,6 +121,7 @@ void warpx_damp_pml_F (int i, int j, int k, Array4 const& F_fab, F_fab(i,j,k,2) *= sigma_fac_z[k-zlo]; #else F_fab(i,j,k,2) *= sigma_fac_z[j-zlo]; + amrex::ignore_unused(sigma_fac_y, ylo); #endif } diff --git a/Source/Diagnostics/BTDiagnostics.H b/Source/Diagnostics/BTDiagnostics.H index fe1b766cbf7..7844c481514 100644 --- a/Source/Diagnostics/BTDiagnostics.H +++ b/Source/Diagnostics/BTDiagnostics.H @@ -13,48 +13,90 @@ public: private: /** Read relevant parameters for BTD */ void ReadParameters (); - /** Flush m_mf_output and particles to file. */ - // Currently this is an empty function. When OpenPMD/Plotfile is supported - // this function can be moved to the base class Diagnostics. - void Flush (int i_buffer) override {} + /** \brief Flush m_mf_output and particles to file. + * Currently, a temporary customized output format for the buffer + * data is implemented and called in this function. + * After implementing OpenPMD and plotfile formats, this function + * can be defined and implemented in Diagnostics.H, such that, + * the function call to flush out buffer data for + * FullDiagnostics and BTDiagnostics is the same */ + void Flush (int i_buffer) override; /** whether to write output files at this time step * The data is flushed when the buffer is full and/or - * when the simulation ends or when forced. + * when the simulation ends or when forced. * \param[in] step current time step * \param[in] force_flush if true, return true for any step * The return bool is used to determine when to write buffer data out */ bool DoDump (int step, int i_buffer, bool force_flush=false) override; + /** whether to compute the back-transformed data and store buffer in this timestep + * The field-data is back-transformed from boosted-frame to lab-frame + * at every time step within the PIC loop. Back-transformation is not performed + * at initialization. + * \param[in] step current time step, return true for any step >= 0 + * \param[in] force_flush if true, return true for any step + */ bool DoComputeAndPack (int step, bool force_flush=false) override; /** Initialize Data required to compute back-transformed diagnostics */ void DerivedInitData () override; /** Initialize functors that store pointers to the fields requested by the user. - * \param[in] lev level on which the vector of unique_ptrs to field functors is initialized. + * Additionally, the cell-center functors that stores pointers to all fields, + * namely, Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, and rho is also initialized. + * \param[in] lev level on which the vector of unique_ptrs to field functors + * is initialized. */ void InitializeFieldFunctors (int lev) override; - /** This function initialized and allocates particle buffers for all the snapshots. - * The particle containers required for this must be added to populate this function. + /** This function allocates and initializes particle buffers for all the snapshots. + * This is currently an empty function: + * The particle containers required for this must be added to populate this function. */ void InitializeParticleBuffer () override {} - /** Compute the cell-centered data for all user-requested fields and store in + /** The cell-centered data for all fields, namely, + * Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, and rho is computed and stored in * the multi-level cell-centered multifab, m_mf_cc. This MultiFab extends - * over the entire domain and is coarsened using the user-defined crse_ratio + * over the entire domain and is coarsened using the user-defined crse_ratio. + * For every lab-frame buffer, the data stored in this cell-centered MultiFab + * is sliced, back-transformed, and stored in the output multifab, m_mf_output. */ void PrepareFieldDataForOutput () override; + + /** whether z-slice that corresponds to the buffer, i_buffer, is within the + * boosted-domain and lab-frame domains at level, lev. + * \param[in] i_buffer index of the buffer for which the z-coordinates are checked + * Return true if the z co-ordinates stored in + * m_current_z_boost and m_current_z_lab for buffer, i_buffer, + * are within the bounds of the boosted-frame and lab-frame domain. + */ + bool GetZSliceInDomainFlag (const int i_buffer, const int lev); + /** Initialize buffer domain, buffer box and lab-frame parameters such as * m_t_lab, and z-positions for the i^th snapshot, i_buffer, and level, lev. * \param[in] i_buffer i^th snapshot or buffer - * \param[in] lev level + * \param[in] lev level for which the field buffer data is initialized. */ void InitializeFieldBufferData ( int i_buffer , int lev) override; - /** Whether to compute back-tranformed values for field-data */ + /** Whether to compute back-tranformed values for field-data. + * default value is true. + */ bool m_do_back_transformed_fields = true; - /** Whether to compute back-tranformed values for particle-data */ + /** Whether to compute back-tranformed values for particle-data + * default value is true. + */ bool m_do_back_transformed_particles = true; - // boost parameters + /** m_gamma_boost, is a copy of warpx.gamma_boost + * That is, the Lorentz factor of the boosted frame in which the simulation is run. + * The direction for Lorentz transformation is assumed to be along + * ``warpx.boost_direction``, which is the same as the moving window direction. + * Currently, back-transformed diagnostics only works if the boost-direction and + * moving window direction are along the z-direction in Cartesian co-ordinates. + */ amrex::Real m_gamma_boost; amrex::Real m_beta_boost; + /** m_moving_window_dir is a copy of warpx.moving_window_dir + * Currently, back-transformed diagnostics only works if moving window is + * in z-direction for both 2D and 3D simulations in the Cartesian frame of reference. + */ int m_moving_window_dir; /** Number of back-transformed snapshots in the lab-frame requested by the user */ @@ -71,70 +113,142 @@ private: /** max grid size used to generate BoxArray to define output MultiFabs */ int m_max_box_size = 256; - /** Time in lab-frame of the back-transformed snapshot */ + /** Vector of lab-frame time corresponding to each snapshot */ amrex::Vector m_t_lab; - /** Physical domain with lab-frame co-ordinates of the back-transformed snapshot */ + /** Vector of lab-frame physical domain corresponding to the boosted-frame simulation + * domain at lab-frame time corresponding to each snapshot*/ + amrex::Vector m_prob_domain_lab; + /** Vector of user-defined physical region for diagnostics in lab-frame + * for each back-transformed snapshot */ amrex::Vector m_buffer_domain_lab; - /** Number of cells in the lab-frame for all back-transformed snapshots */ + /** Vector of number of cells in the lab-frame for each back-transformed snapshot */ amrex::Vector m_buffer_ncells_lab; - /** Box-dimension in boosted-frame index space for each back-transformed snapshot */ + /** Vector of Box-dimension in boosted-frame index space + * for each back-transformed snapshot */ amrex::Vector m_buffer_box; - /** Lab-frame z co-ordinate of the back-transformed snapshot at the current timestep */ + /** Vector of lab-frame z co-ordinate of each back-transformed snapshot + * at the current timestep */ amrex::Vector m_current_z_lab; - /** Boosted-frame z co-ordinate corresponding to the back-transformed - snapshot at the current timestep */ + /** Vector of boosted-frame z co-ordinate corresponding to each back-transformed + snapshot at the current timestep */ amrex::Vector m_current_z_boost; - /** Number of back-transformed z-slices in the output buffer multifab */ + /** Vector of counters tracking number of back-transformed z-slices filled + * in the output buffer multifab, m_mf_output, for each snapshot. + * When the buffer counter for a snapshot is equal to the maximum number + * of buffers (m_buffer_size), i.e., when the buffer is full, the data + * stored in the buffer multifab is flushed out and the counter is reset is zero. + */ amrex::Vector m_buffer_counter; - /** Multi-level cell-centered multifab with all field-data components - which will be sliced and transformed. The same multifab will be used - for all snapshots. - */ + /** Multi-level cell-centered multifab with all field-data components, namely, + * Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, and rho. + * This cell-centered data extending over the entire domain + * will be used by all snapshots to obtain lab-frame data at the respective + * z slice location. + */ amrex::Vector > m_cell_centered_data; + /** Name of the output directories where the lab-frame data + * is dumped out in a custom format. + * This is a TMP variable, which may not be required after including + * OpenPMD and plotfile formats. + */ std::vector m_file_name; /** Vector of pointers to compute cell-centered data, per level, per component * using the coarsening-ratio provided by the user. */ amrex::Vector< amrex::Vector > > m_cell_center_functors; - /** Customized function to write back-transformed data */ + /** Customized (temporary) function to flush out back-transformed data */ void TMP_writeMetaData (); - /** Customized functions to write back-transformed data */ + /** Customized (temporary) functions creating lab-frame directories + * to flush out back-transformed data + * \param[in] i_buffer, index of the buffer data that is being flushed out + * \param[in] lev, level of the buffer data. + * (Currently only single-level data is supported.) + */ void TMP_createLabFrameDirectories (int i_buffer, int lev); + /** Customized (temporary) functions creating header files + * to flush out back-transformed data for the ith buffer + * \param[in] i_buffer, ith buffer storing data for the ith snapshot + */ + void TMP_writeLabFrameHeader (int i_buffer); + /** Customized (temporary) output format for flushing out lab-frame data stored in + * the output buffer, m_mf_output. + * \param[in] i_buffer, ith buffer for which the lab-farme data is flushed out. + */ + void TMP_FlushLabFrameData ( int i_buffer ); /** Define the cell-centered multi-component MultiFab at level, lev. + * This function is called when the buffer counter for a snapshot is zero, + * or when the buffer is empty. Checked using `buffer_empty(i_buffer)` * \param[in] lev level at which the cell-centered MultiFab is defined. */ void DefineCellCenteredMultiFab(int lev); - /** Returns z-position in the boosted-frame - * \param[in] lab-frame time of the snapshot - * \param[in] boosted-frame time at level, lev + /** Define the cell-centered multi-component output buffer MultiFab for + * snapshot, i_buffer, at level, lev. + * + * \param[in] i_buffer buffer-id of the back-transformed snapshot + * \param[in] lev, level at which the output buffer MultiFab is defined + */ + void DefineFieldBufferMultiFab (const int i_buffer, const int lev); + /** Compute and return z-position in the boosted-frame at the current timestep + * \param[in] t_lab, lab-frame time of the snapshot + * \param[in] t_boost, boosted-frame time at level, lev */ amrex::Real UpdateCurrentZBoostCoordinate(amrex::Real t_lab, amrex::Real t_boost) { amrex::Real current_z_boost = (t_lab / m_gamma_boost - t_boost) * PhysConst::c / m_beta_boost; return current_z_boost; } - /** Returns z-position in the lab-frame - * \param[in] lab-frame time of the snapshot - * \param[in] boosted-frame time at level, lev + /** Compute and return z-position in the lab-frame at the current timestep + * \param[in] t_lab, lab-frame time of the snapshot + * \param[in] t_boost, boosted-frame time at level, lev */ amrex::Real UpdateCurrentZLabCoordinate(amrex::Real t_lab, amrex::Real t_boost) { amrex::Real current_z_lab = (t_lab - t_boost / m_gamma_boost ) * PhysConst::c / m_beta_boost; return current_z_lab; } - /** Returns cell-size of the grid in z-direction in the lab-frame at level, lev - * \param[in] dt in boosted-frame at level, lev - * \param[in] ref_ratio, refinement ratio at level, lev - */ + /** Compute and return cell-size in z-dimension in the lab-frame at level, lev + * \param[in] dt, timestep in boosted-frame at level, lev + * \param[in] ref_ratio, refinement ratio in the z-direction at level, lev-1. + * The ref-ratio in the z-direction for single-level diagnostics is 1. */ amrex::Real dz_lab (amrex::Real dt, amrex::Real ref_ratio); + /** Compute k-index corresponding to current lab-frame z co-ordinate (m_current_z_lab) + * for the ith buffer, i_buffer, and at level, lev. + * \param[in] i_buffer, ith buffer + * \param[in] lev, level at which the lab-frame z-index is computed + */ + int k_index_zlab (int i_buffer, int lev); /** whether field buffer is full * \param[in] i_buffer buffer id for which the buffer size is checked. - * returns bool = true is buffer is full + * returns bool = true is buffer is full, that is, + when buffer counter is equal to m_buffer_size */ bool buffer_full (int i_buffer) { return ( m_buffer_counter[i_buffer] == m_buffer_size ); } + + /** whether field buffer is empty. + * \param[in] i_buffer buffer id for which the buffer size is checked. + * returns bool = true is buffer is empty i.e., when buffer counter is zero. + */ + bool buffer_empty (int i_buffer) { + return ( m_buffer_counter[i_buffer] == 0) ; + } + + /** Reset buffer counter to zero. + * \param[in] i_buffer, ith buffer for which the bounter is set to zero. + */ + void ResetBufferCounter(int i_buffer) { + m_buffer_counter[i_buffer] = 0; + } + /** Vector of field-data stored in the cell-centered multifab, m_cell_centered_data. + * All the fields are stored regardless of the specific fields to plot selected + * by the user. + */ + amrex::Vector< std::string > m_cellcenter_varnames = {"Ex", "Ey", "Ez", + "Bx", "By", "Bz", + "jx", "jy", "jz", "rho"}; + }; #endif // WARPX_BTDIAGNOSTICS_H_ diff --git a/Source/Diagnostics/BTDiagnostics.cpp b/Source/Diagnostics/BTDiagnostics.cpp index 069b410aba1..d57af3ec0a2 100644 --- a/Source/Diagnostics/BTDiagnostics.cpp +++ b/Source/Diagnostics/BTDiagnostics.cpp @@ -4,6 +4,7 @@ #include "ComputeDiagFunctors/ComputeDiagFunctor.H" #include "ComputeDiagFunctors/CellCenterFunctor.H" #include "ComputeDiagFunctors/BackTransformFunctor.H" +#include "ComputeDiagFunctors/RhoFunctor.H" #include "Utils/CoarsenIO.H" #include #include @@ -19,7 +20,6 @@ BTDiagnostics::BTDiagnostics (int i, std::string name) void BTDiagnostics::DerivedInitData () { - // storing BTD related member variables auto & warpx = WarpX::GetInstance(); m_gamma_boost = WarpX::gamma_boost; m_beta_boost = std::sqrt( 1._rt - 1._rt/( m_gamma_boost * m_gamma_boost) ); @@ -33,6 +33,8 @@ void BTDiagnostics::DerivedInitData () // allocate vector of m_t_lab with m_num_buffers; m_t_lab.resize(m_num_buffers); + // allocate vector of RealBost of the simulation domain in lab-frame + m_prob_domain_lab.resize(m_num_buffers); // allocate vector of RealBox of the diag domain m_buffer_domain_lab.resize(m_num_buffers); // define box correctly (one for all snapshots) @@ -62,6 +64,7 @@ void BTDiagnostics::DerivedInitData () DefineCellCenteredMultiFab(lev); } + } void @@ -69,8 +72,6 @@ BTDiagnostics::ReadParameters () { BaseReadParameters(); auto & warpx = WarpX::GetInstance(); - // Read list of back-transform diag parameters requested by the user // - amrex::Print() << " in read parameters for BTD \n"; AMREX_ALWAYS_ASSERT_WITH_MESSAGE( ( warpx.do_back_transformed_diagnostics==true), "the do_back_transformed_diagnostics flag must be set to true for BTDiagnostics"); @@ -80,11 +81,17 @@ BTDiagnostics::ReadParameters () "The back transformed diagnostics currently only works if the boost is in the z-direction"); AMREX_ALWAYS_ASSERT_WITH_MESSAGE( warpx.do_moving_window, "The moving window should be on if using the boosted frame diagnostic."); - AMREX_ALWAYS_ASSERT_WITH_MESSAGE( warpx.moving_window_dir == 2, + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( warpx.moving_window_dir == AMREX_SPACEDIM-1, "The boosted frame diagnostic currently only works if the moving window is in the z direction."); AMREX_ALWAYS_ASSERT_WITH_MESSAGE( m_format == "plotfile" || m_format == "openpmd", ".format must be plotfile or openpmd for back transformed diagnostics"); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + m_crse_ratio == amrex::IntVect(1), + "Only support for coarsening ratio of 1 in all directions is included for BTD\n" + ); + + // Read list of back-transform diag parameters requested by the user // amrex::ParmParse pp(m_diag_name); m_file_prefix = "diags/lab_frame_data/" + m_diag_name; @@ -105,7 +112,8 @@ BTDiagnostics::ReadParameters () } AMREX_ALWAYS_ASSERT_WITH_MESSAGE(snapshot_interval_is_specified, "For back-transformed diagnostics, user should specify either dz_snapshots_lab or dt_snapshots_lab"); - + // For BTD, we always need rho to perform Lorentz Transform of current-density + if (WarpXUtilStr::is_in(m_cellcenter_varnames, "rho")) warpx.setplot_rho(true); } void @@ -141,8 +149,10 @@ BTDiagnostics::TMP_writeMetaData () bool BTDiagnostics::DoDump (int step, int i_buffer, bool force_flush) { - // check if buffer is full (m_buffer_counter[i_buffer] == m_) or if force_flush == true - if ( buffer_full(i_buffer) || force_flush) { + // Return true if buffer is full or if force_flush == true + // Return false if timestep < 0, i.e., at initialization when step == -1. + if (step < 0 ) return false; + else if ( buffer_full(i_buffer) || force_flush) { return true; } return false; @@ -150,20 +160,35 @@ BTDiagnostics::DoDump (int step, int i_buffer, bool force_flush) bool -BTDiagnostics::DoComputeAndPack (int step, bool force_flush) +BTDiagnostics::DoComputeAndPack (int step, bool /*force_flush*/) { // always set to true for BTDiagnostics since back-transform buffers are potentially - // computed and packed every timstep. - return true; + // computed and packed every timstep, except at initialization when step == -1. + return (step>=0); } void BTDiagnostics::InitializeFieldBufferData ( int i_buffer , int lev) { auto & warpx = WarpX::GetInstance(); - // 1. Lab-frame time for the i^th snapshot + // Lab-frame time for the i^th snapshot m_t_lab.at(i_buffer) = i_buffer * m_dt_snapshots_lab; - // 2. Define domain in boosted frame at level, lev + + + // Compute lab-frame co-ordinates that correspond to the simulation domain + // at level, lev, and time, m_t_lab[i_buffer] for each ith buffer. + m_prob_domain_lab[i_buffer] = warpx.Geom(lev).ProbDomain(); + amrex::Real zmin_prob_domain_lab = m_prob_domain_lab[i_buffer].lo(m_moving_window_dir) + / ( (1.0_rt + m_beta_boost) * m_gamma_boost); + amrex::Real zmax_prob_domain_lab = m_prob_domain_lab[i_buffer].hi(m_moving_window_dir) + / ( (1.0_rt + m_beta_boost) * m_gamma_boost); + m_prob_domain_lab[i_buffer].setLo(m_moving_window_dir, zmin_prob_domain_lab + + warpx.moving_window_v * m_t_lab[i_buffer] ); + m_prob_domain_lab[i_buffer].setHi(m_moving_window_dir, zmax_prob_domain_lab + + warpx.moving_window_v * m_t_lab[i_buffer] ); + + + // Define buffer domain in boosted frame at level, lev, with user-defined lo and hi amrex::RealBox diag_dom; for (int idim = 0; idim < AMREX_SPACEDIM; ++idim ) { // Setting lo-coordinate for the diag domain by taking the max of user-defined @@ -173,10 +198,10 @@ BTDiagnostics::InitializeFieldBufferData ( int i_buffer , int lev) // hi-cordinate and hi-coordinate of the simulation domain at level, lev diag_dom.setHi(idim, std::min(m_hi[idim],warpx.Geom(lev).ProbHi(idim)) ); } - // 3. Initializing the m_buffer_box for the i^th snapshot. - // At initialization, the Box has the same index space as the boosted-frame - // As time-progresses, the z-dimension indices will be modified based on - // current_z_lab + // Initializing the m_buffer_box for the i^th snapshot. + // At initialization, the Box has the same index space as the boosted-frame + // As time-progresses, the z-dimension indices will be modified based on + // current_z_lab amrex::IntVect lo(0); amrex::IntVect hi(-1); for (int idim=0; idim < AMREX_SPACEDIM; ++idim) { @@ -202,24 +227,24 @@ BTDiagnostics::InitializeFieldBufferData ( int i_buffer , int lev) } } amrex::Box diag_box( lo, hi ); - // The box is not coarsened yet. Should this be coarsened here or when - // the buffer multifab is initialized? m_buffer_box[i_buffer] = diag_box; - // 4. Define buffer_domain in lab-frame for the i^th snapshot. - // Replace z-dimension with lab-frame co-ordinates. - amrex::Real zmin_lab = diag_dom.lo(m_moving_window_dir) - / ( (1.0_rt + m_beta_boost) * m_gamma_boost); - amrex::Real zmax_lab = diag_dom.hi(m_moving_window_dir) - / ( (1.0_rt + m_beta_boost) * m_gamma_boost); + // Define buffer_domain in lab-frame for the i^th snapshot. + // Replace z-dimension with lab-frame co-ordinates. + amrex::Real zmin_buffer_lab = diag_dom.lo(m_moving_window_dir) + / ( (1.0_rt + m_beta_boost) * m_gamma_boost); + amrex::Real zmax_buffer_lab = diag_dom.hi(m_moving_window_dir) + / ( (1.0_rt + m_beta_boost) * m_gamma_boost); - m_buffer_domain_lab[i_buffer] = warpx.Geom(lev).ProbDomain(); - m_buffer_domain_lab[i_buffer].setLo(m_moving_window_dir, zmin_lab + warpx.moving_window_v * m_t_lab[i_buffer]); - m_buffer_domain_lab[i_buffer].setHi(m_moving_window_dir, zmin_lab + warpx.moving_window_v * m_t_lab[i_buffer]); + m_buffer_domain_lab[i_buffer] = diag_dom; + m_buffer_domain_lab[i_buffer].setLo(m_moving_window_dir, + zmin_buffer_lab + warpx.moving_window_v * m_t_lab[i_buffer]); + m_buffer_domain_lab[i_buffer].setHi(m_moving_window_dir, + zmax_buffer_lab + warpx.moving_window_v * m_t_lab[i_buffer]); - // 5. Initialize buffer counter and z-positions of the i^th snapshot in - // boosted-frame and lab-frame + // Initialize buffer counter and z-positions of the i^th snapshot in + // boosted-frame and lab-frame m_buffer_counter[i_buffer] = 0; m_current_z_lab[i_buffer] = 0.0_rt; m_current_z_boost[i_buffer] = 0.0_rt; @@ -230,16 +255,18 @@ BTDiagnostics::InitializeFieldBufferData ( int i_buffer , int lev) warpx.gett_new(lev) ); - // 6. Compute ncells_lab required for writing Header file and potentially to generate - // Back-Transform geometry to ensure compatibility with plotfiles // + // Compute number of cells in lab-frame required for writing Header file + // and potentially to generate Back-Transform geometry to ensure + // compatibility with plotfiles. // For the z-dimension, number of cells in the lab-frame is // computed using the coarsened cell-size in the lab-frame obtained using - // the ref_ratio at level, lev. - amrex::IntVect ref_ratio = WarpX::RefRatio(lev); + // the ref_ratio at level, lev-1. + amrex::IntVect ref_ratio = amrex::IntVect(1); + if (lev > 0 ) ref_ratio = WarpX::RefRatio(lev-1); // Number of lab-frame cells in z-direction at level, lev const int num_zcells_lab = static_cast( floor ( - ( zmax_lab - zmin_lab) - / dz_lab(warpx.getdt(lev), ref_ratio[AMREX_SPACEDIM-1] ) ) ); + ( zmax_buffer_lab - zmin_buffer_lab) + / dz_lab(warpx.getdt(lev), ref_ratio[m_moving_window_dir]) ) ); // Take the max of 0 and num_zcells_lab int Nz_lab = std::max( 0, num_zcells_lab ); // Number of lab-frame cells in x-direction at level, lev @@ -259,11 +286,9 @@ BTDiagnostics::InitializeFieldBufferData ( int i_buffer , int lev) int Ny_lab = std::max( 0, num_ycells_lab ); m_buffer_ncells_lab[i_buffer] = {Nx_lab, Ny_lab, Nz_lab}; #else - int Ny_lab = 0; m_buffer_ncells_lab[i_buffer] = {Nx_lab, Nz_lab}; #endif - - // 7. Call funtion to create directories for customized output format + // Call funtion to create directories for customized output format TMP_createLabFrameDirectories(i_buffer, lev); } @@ -278,7 +303,8 @@ BTDiagnostics::DefineCellCenteredMultiFab(int lev) ba.coarsen(m_crse_ratio); amrex::DistributionMapping dmap = warpx.DistributionMap(lev); int ngrow = 1; - m_cell_centered_data[lev].reset( new amrex::MultiFab(ba, dmap, m_varnames.size(), ngrow) ); + m_cell_centered_data[lev].reset( new amrex::MultiFab(ba, dmap, + m_cellcenter_varnames.size(), ngrow) ); } @@ -290,50 +316,48 @@ BTDiagnostics::InitializeFieldFunctors (int lev) // This ensures that when domain is load-balanced, the functors point // to the correct field-data pointers m_all_field_functors[lev].clear(); - // For back-transformed data, all the components are cell-centered and stored in a single multifab. Therefore, size of functors at all levels is 1. + // For back-transformed data, all the components are cell-centered and stored + // in a single multifab, m_cell_centered_data. + // Therefore, size of functors at all levels is 1. int num_BT_functors = 1; m_all_field_functors[lev].resize(num_BT_functors); m_cell_center_functors[lev].clear(); - m_cell_center_functors[lev].resize( m_varnames.size() ); - // 1. create an object of class BackTransformFunctor + m_cell_center_functors[lev].resize( m_cellcenter_varnames.size() ); + // Create an object of class BackTransformFunctor for (int i = 0; i < num_BT_functors; ++i) { // coarsening ratio is not provided since the source MultiFab, m_cell_centered_data // is coarsened based on the user-defined m_crse_ratio m_all_field_functors[lev][i] = std::make_unique( - m_cell_centered_data[lev].get(), lev, m_varnames.size() ); + m_cell_centered_data[lev].get(), lev, + m_varnames.size(), m_num_buffers, m_varnames); } - // 2. Define all cell-centered functors required to compute cell-centere data - // Fill vector of cell-center functors for all components - // Only E,B, j, and rho are included in the cell-center functors for BackTransform Diags + // Define all cell-centered functors required to compute cell-centere data + // Fill vector of cell-center functors for all field-components, namely, + // Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, and rho are included in the + // cell-center functors for BackTransform Diags for (int comp=0, n=m_cell_center_functors[lev].size(); comp(warpx.get_pointer_Efield_aux(lev, 0), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "Ey" ){ + } else if ( m_cellcenter_varnames[comp] == "Ey" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_Efield_aux(lev, 1), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "Ez" ){ + } else if ( m_cellcenter_varnames[comp] == "Ez" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_Efield_aux(lev, 2), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "Bx" ){ + } else if ( m_cellcenter_varnames[comp] == "Bx" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_Bfield_aux(lev, 0), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "By" ){ + } else if ( m_cellcenter_varnames[comp] == "By" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_Bfield_aux(lev, 1), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "Bz" ){ + } else if ( m_cellcenter_varnames[comp] == "Bz" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_Bfield_aux(lev, 2), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "jx" ){ + } else if ( m_cellcenter_varnames[comp] == "jx" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_current_fp(lev, 0), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "jy" ){ + } else if ( m_cellcenter_varnames[comp] == "jy" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_current_fp(lev, 1), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "jz" ){ + } else if ( m_cellcenter_varnames[comp] == "jz" ){ m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_current_fp(lev, 2), lev, m_crse_ratio); - } else if ( m_varnames[comp] == "rho" ){ - // rho_new is stored in component 1 of rho_fp when using PSATD -#ifdef WARPX_USE_PSATD - amrex::MultiFab* rho_new = new amrex::MultiFab(*warpx.get_pointer_rho_fp(lev), amrex::make_alias, 1, 1); - m_cell_center_functors[lev][comp] = std::make_unique(rho_new, lev, m_crse_ratio); -#else - m_cell_center_functors[lev][comp] = std::make_unique(warpx.get_pointer_rho_fp(lev), lev, m_crse_ratio); -#endif + } else if ( m_cellcenter_varnames[comp] == "rho" ){ + m_cell_center_functors[lev][comp] = std::make_unique(lev, m_crse_ratio); } } @@ -344,9 +368,18 @@ BTDiagnostics::InitializeFieldFunctors (int lev) void BTDiagnostics::TMP_createLabFrameDirectories(int i_buffer, int lev) { - // This function will include relevant code from class BackTransformedDiagnostics - // to create lab-frame directories. Note that here we will also add level, lev - // if required. + // This is a creates lab-frame directories for writing out customized BTD output. + if (amrex::ParallelDescriptor::IOProcessor()) + { + if ( !amrex::UtilCreateDirectory (m_file_name[i_buffer], 0755) ) + amrex::CreateDirectoryFailed(m_file_name[i_buffer]); + + const std::string &fullpath = amrex::LevelFullPath(lev, m_file_name[i_buffer]); + if ( !amrex::UtilCreateDirectory(fullpath, 0755) ) + amrex::CreateDirectoryFailed(fullpath); + } + amrex::ParallelDescriptor::Barrier(); + TMP_writeLabFrameHeader(i_buffer); } @@ -367,7 +400,7 @@ BTDiagnostics::PrepareFieldDataForOutput () icomp_dst += m_cell_center_functors[lev][icomp]->nComp(); } // Check that the proper number of user-requested components are cell-centered - AMREX_ALWAYS_ASSERT( icomp_dst == m_varnames.size() ); + AMREX_ALWAYS_ASSERT( icomp_dst == m_cellcenter_varnames.size() ); // fill boundary call is required to average_down (flatten) data to // the coarsest level. m_cell_centered_data[lev]->FillBoundary(warpx.Geom(lev).periodicity() ); @@ -375,13 +408,191 @@ BTDiagnostics::PrepareFieldDataForOutput () // Flattening out MF over levels for (int lev = warpx.finestLevel(); lev > 0; --lev) { - CoarsenIO::Coarsen( *m_cell_centered_data[lev-1], *m_cell_centered_data[lev], 0, 0, m_varnames.size(), 0, WarpX::RefRatio(lev-1) ); + CoarsenIO::Coarsen( *m_cell_centered_data[lev-1], *m_cell_centered_data[lev], 0, 0, + m_cellcenter_varnames.size(), 0, WarpX::RefRatio(lev-1) ); } + + int num_BT_functors = 1; + + for (int lev = 0; lev < nlev_output; ++lev) + { + for (int i = 0; i < num_BT_functors; ++i) + { + for (int i_buffer = 0; i_buffer < m_num_buffers; ++i_buffer ) + { + // Update z-boost and z-lab positions + m_current_z_boost[i_buffer] = UpdateCurrentZBoostCoordinate(m_t_lab[i_buffer], + warpx.gett_new(lev) ); + m_current_z_lab[i_buffer] = UpdateCurrentZLabCoordinate(m_t_lab[i_buffer], + warpx.gett_new(lev) ); + bool ZSliceInDomain = GetZSliceInDomainFlag (i_buffer, lev); + // Initialize and define field buffer multifab if buffer is empty + if (ZSliceInDomain) { + if ( buffer_empty(i_buffer) ) DefineFieldBufferMultiFab(i_buffer, lev); + } + m_all_field_functors[lev][i]->PrepareFunctorData ( + i_buffer, ZSliceInDomain, + m_current_z_boost[i_buffer], + m_buffer_box[i_buffer], + k_index_zlab(i_buffer, lev) ); + + if (ZSliceInDomain) ++m_buffer_counter[i_buffer]; + } + } + } + } amrex::Real -BTDiagnostics::dz_lab (amrex::Real dt, amrex::Real ref_ratio) -{ +BTDiagnostics::dz_lab (amrex::Real dt, amrex::Real ref_ratio){ return PhysConst::c * dt * 1._rt/m_beta_boost * 1._rt/m_gamma_boost * 1._rt/ref_ratio; } + + +int +BTDiagnostics::k_index_zlab (int i_buffer, int lev) +{ + auto & warpx = WarpX::GetInstance(); + amrex::Real prob_domain_zmin_lab = m_prob_domain_lab[i_buffer].lo( m_moving_window_dir ); + amrex::IntVect ref_ratio = amrex::IntVect(1); + if (lev > 0 ) ref_ratio = WarpX::RefRatio(lev-1); + int k_lab = static_cast( ( + ( m_current_z_lab[i_buffer] - prob_domain_zmin_lab ) + / dz_lab( warpx.getdt(lev), ref_ratio[m_moving_window_dir] ) + ) ); + return k_lab; +} + + + +void +BTDiagnostics::DefineFieldBufferMultiFab (const int i_buffer, const int lev) +{ + if ( m_do_back_transformed_fields ) { + + const int k_lab = k_index_zlab (i_buffer, lev); + m_buffer_box[i_buffer].setSmall( m_moving_window_dir, k_lab - m_buffer_size + 1); + m_buffer_box[i_buffer].setBig( m_moving_window_dir, k_lab); + + amrex::BoxArray buffer_ba( m_buffer_box[i_buffer] ); + buffer_ba.maxSize(m_max_box_size); + + // Generate a new distribution map for the back-transformed buffer multifab + amrex::DistributionMapping buffer_dmap(buffer_ba); + // Number of guard cells for the output buffer is zero. + // Unlike FullDiagnostics, "m_format == sensei" option is not included here. + int ngrow = 0; + m_mf_output[i_buffer][lev] = amrex::MultiFab ( buffer_ba, buffer_dmap, + m_varnames.size(), ngrow ) ; + } +} + +bool +BTDiagnostics::GetZSliceInDomainFlag (const int i_buffer, const int lev) +{ + auto & warpx = WarpX::GetInstance(); + const amrex::RealBox& boost_domain = warpx.Geom(lev).ProbDomain(); + + amrex::Real buffer_zmin_lab = m_buffer_domain_lab[i_buffer].lo( m_moving_window_dir ); + amrex::Real buffer_zmax_lab = m_buffer_domain_lab[i_buffer].hi( m_moving_window_dir ); + + if ( ( m_current_z_boost[i_buffer] < boost_domain.lo(m_moving_window_dir) ) or + ( m_current_z_boost[i_buffer] > boost_domain.hi(m_moving_window_dir) ) or + ( m_current_z_lab[i_buffer] < buffer_zmin_lab ) or + ( m_current_z_lab[i_buffer] > buffer_zmax_lab ) ) + { + // the slice is not in the boosted domain or lab-frame domain + return false; + } + + return true; +} + +void +BTDiagnostics::TMP_writeLabFrameHeader (int i_buffer) +{ + if (amrex::ParallelDescriptor::IOProcessor() ) + { + amrex::VisMF::IO_Buffer io_buffer(amrex::VisMF::IO_Buffer_Size); + std::ofstream HeaderFile; + HeaderFile.rdbuf()->pubsetbuf(io_buffer.dataPtr(), io_buffer.size()); + std::string HeaderFileName(m_file_name[i_buffer] + "/Header"); + HeaderFile.open(HeaderFileName.c_str(), std::ofstream::out | + std::ofstream::trunc | + std::ofstream::binary ); + + if ( !HeaderFile.good() ) amrex::FileOpenFailed( HeaderFileName ); + + HeaderFile.precision(17); + + HeaderFile << m_t_lab[i_buffer] << "\n"; + // Write buffer number of cells + HeaderFile << m_buffer_ncells_lab[i_buffer][0] << ' ' +#if ( AMREX_SPACEDIM==3 ) + << m_buffer_ncells_lab[i_buffer][1] << ' ' +#endif + << m_buffer_ncells_lab[i_buffer][m_moving_window_dir] << "\n"; + // Write physical boundary of the buffer + // Lower bound + HeaderFile << m_buffer_domain_lab[i_buffer].lo(0) << ' ' +#if ( AMREX_SPACEDIM == 3 ) + << m_buffer_domain_lab[i_buffer].lo(1) << ' ' +#endif + << m_buffer_domain_lab[i_buffer].lo(m_moving_window_dir) << "\n"; + // Higher bound + HeaderFile << m_buffer_domain_lab[i_buffer].hi(0) << ' ' +#if ( AMREX_SPACEDIM == 3 ) + << m_buffer_domain_lab[i_buffer].hi(1) << ' ' +#endif + << m_buffer_domain_lab[i_buffer].hi(m_moving_window_dir) << "\n"; + // List of fields to flush to file + for (int i = 0; i < m_varnames.size(); ++i) + { + HeaderFile << m_varnames[i] << ' '; + } + HeaderFile << "\n"; + } +} + +void +BTDiagnostics::Flush (int i_buffer) +{ + TMP_FlushLabFrameData (i_buffer); + // Reset the buffer counter to zero after flushing out data stored in the buffer. + ResetBufferCounter(i_buffer); +} + +void +BTDiagnostics::TMP_FlushLabFrameData ( int i_buffer ) +{ + // customized output format for writing data stored in buffers to the disk. + amrex::VisMF::Header::Version current_version = amrex::VisMF::GetHeaderVersion(); + amrex::VisMF::SetHeaderVersion(amrex::VisMF::Header::NoFabHeader_v1); + + if ( ! buffer_empty(i_buffer) ) { + for ( int lev = 0; lev < nlev_output; ++lev) { + const int k_lab = k_index_zlab (i_buffer, lev); + const amrex::BoxArray& ba = m_mf_output[i_buffer][lev].boxArray(); + const int hi = ba[0].bigEnd(m_moving_window_dir); + const int lo = hi - m_buffer_counter[i_buffer] + 1; + + amrex::Box buffer_box = m_buffer_box[i_buffer]; + buffer_box.setSmall(m_moving_window_dir, lo); + buffer_box.setBig(m_moving_window_dir, hi); + amrex::BoxArray buffer_ba(buffer_box); + buffer_ba.maxSize(m_max_box_size); + amrex::DistributionMapping buffer_dm(buffer_ba); + + amrex::MultiFab tmp (buffer_ba, buffer_dm, m_varnames.size(), 0); + tmp.copy(m_mf_output[i_buffer][lev], 0, 0, m_varnames.size()); + + std::stringstream ss; + ss << m_file_name[i_buffer] << "/Level_0/" + << amrex::Concatenate("buffer", k_lab, 5); + amrex::VisMF::Write(tmp, ss.str()); + + } + } + amrex::VisMF::SetHeaderVersion(current_version); +} diff --git a/Source/Diagnostics/BackTransformedDiagnostic.H b/Source/Diagnostics/BackTransformedDiagnostic.H index 8776dbb1c6a..36441829f78 100644 --- a/Source/Diagnostics/BackTransformedDiagnostic.H +++ b/Source/Diagnostics/BackTransformedDiagnostic.H @@ -87,8 +87,8 @@ class LabFrameDiag { /// For the reduced diagnostic, the data is selectively copied if the /// extent of the z_lab multifab intersects with the user-defined sub-domain /// for the reduced diagnostic (i.e., a 1D, 2D, or 3D region of the domain). - virtual void AddDataToBuffer(amrex::MultiFab& tmp_slice_ptr, int i_lab, - amrex::Gpu::ManagedDeviceVector map_actual_fields_to_dump){} + virtual void AddDataToBuffer(amrex::MultiFab& /*tmp_slice_ptr*/, int /*i_lab*/, + amrex::Vector const& /*map_actual_fields_to_dump*/){} /// Back-transformed lab-frame particles is copied from /// tmp_particle_buffer to particles_buffer. @@ -97,8 +97,12 @@ class LabFrameDiag { /// copied if their position in within the user-defined /// sub-domain +/- 1 cell size width for the reduced slice diagnostic. virtual void AddPartDataToParticleBuffer( - amrex::Vector tmp_particle_buffer, - int nSpeciesBoostedFrame) {} + amrex::Vector const& /*tmp_particle_buffer*/, + int /*nSpeciesBoostedFrame*/) {} + + // The destructor should also be a virtual function, so that + // a pointer to subclass of `LabFrameDiag` actually calls the subclass's destructor. + virtual ~LabFrameDiag() = default; }; /** \brief @@ -120,9 +124,9 @@ class LabFrameSnapShot : public LabFrameDiag { amrex::RealBox diag_domain_lab, amrex::Box diag_box, int file_num_in); void AddDataToBuffer( amrex::MultiFab& tmp_slice, int k_lab, - amrex::Gpu::ManagedDeviceVector map_actual_fields_to_dump) override; + amrex::Vector const& map_actual_fields_to_dump) override; void AddPartDataToParticleBuffer( - amrex::Vector tmp_particle_buffer, + amrex::Vector const& tmp_particle_buffer, int nSpeciesBoostedFrame) override; }; @@ -147,9 +151,9 @@ class LabFrameSlice : public LabFrameDiag { amrex::Box diag_box, int file_num_in, amrex::Real particle_slice_dx_lab); void AddDataToBuffer( amrex::MultiFab& tmp_slice_ptr, int i_lab, - amrex::Gpu::ManagedDeviceVector map_actual_fields_to_dump) override; + amrex::Vector const& map_actual_fields_to_dump) override; void AddPartDataToParticleBuffer( - amrex::Vector tmp_particle_buffer, + amrex::Vector const& tmp_particle_buffer, int nSpeciesBoostedFrame) override; }; @@ -260,7 +264,7 @@ private: // maps field index in data_buffer_[i] -> cell_centered_data for // snapshots i. By default, all fields in cell_centered_data are dumped. // Needs to be amrex::Vector because used in a ParallelFor kernel. - amrex::Gpu::ManagedDeviceVector map_actual_fields_to_dump; + amrex::Vector map_actual_fields_to_dump; // Name of fields to dump. By default, all fields in cell_centered_data. // Needed for file headers only. std::vector m_mesh_field_names = {"Ex", "Ey", "Ez", diff --git a/Source/Diagnostics/BackTransformedDiagnostic.cpp b/Source/Diagnostics/BackTransformedDiagnostic.cpp index 8a4ede367ed..5a251ad1333 100644 --- a/Source/Diagnostics/BackTransformedDiagnostic.cpp +++ b/Source/Diagnostics/BackTransformedDiagnostic.cpp @@ -694,7 +694,7 @@ void BackTransformedDiagnostic::Flush(const Geometry& /*geom*/) VisMF::Header::Version current_version = VisMF::GetHeaderVersion(); VisMF::SetHeaderVersion(amrex::VisMF::Header::NoFabHeader_v1); - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); const std::vector species_names = mypc.GetSpeciesNames(); // Loop over BFD snapshots @@ -1187,7 +1187,7 @@ createLabFrameDirectories() { ParallelDescriptor::Barrier(); if (WarpX::do_back_transformed_particles){ - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); const std::vector species_names = mypc.GetSpeciesNames(); // Loop over species to be dumped to BFD for (int j = 0; j < mypc.nSpeciesBackTransformedDiagnostics(); ++j) @@ -1216,7 +1216,7 @@ createLabFrameDirectories() { CreateDirectoryFailed(fullpath); } - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); const std::vector species_names = mypc.GetSpeciesNames(); const std::string particles_prefix = "particle"; @@ -1320,17 +1320,26 @@ LabFrameSlice(Real t_lab_in, Real t_boost, Real inv_gamma_boost_in, void LabFrameSnapShot:: AddDataToBuffer( MultiFab& tmp, int k_lab, - amrex::Gpu::ManagedDeviceVector map_actual_fields_to_dump) + amrex::Vector const& map_actual_fields_to_dump) { const int ncomp_to_dump = map_actual_fields_to_dump.size(); MultiFab& buf = *m_data_buffer_; +#ifdef AMREX_USE_GPU + Gpu::DeviceVector d_map_actual_fields_to_dump(ncomp_to_dump); + Gpu::copyAsync(Gpu::hostToDevice, + map_actual_fields_to_dump.begin(), map_actual_fields_to_dump.end(), + d_map_actual_fields_to_dump.begin()); + Gpu::synchronize(); + int const* field_map_ptr = d_map_actual_fields_to_dump.dataPtr(); +#else + int const* field_map_ptr = map_actual_fields_to_dump.dataPtr(); +#endif for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) { Array4 tmp_arr = tmp[mfi].array(); Array4 buf_arr = buf[mfi].array(); // For 3D runs, tmp is a 2D (x,y) multifab that contains only // slice to write to file const Box& bx = mfi.tilebox(); - const auto field_map_ptr = map_actual_fields_to_dump.dataPtr(); ParallelFor(bx, ncomp_to_dump, [=] AMREX_GPU_DEVICE(int i, int j, int k, int n) { @@ -1349,10 +1358,20 @@ AddDataToBuffer( MultiFab& tmp, int k_lab, void LabFrameSlice:: AddDataToBuffer( MultiFab& tmp, int k_lab, - amrex::Gpu::ManagedDeviceVector map_actual_fields_to_dump) + amrex::Vector const& map_actual_fields_to_dump) { const int ncomp_to_dump = map_actual_fields_to_dump.size(); MultiFab& buf = *m_data_buffer_; +#ifdef AMREX_USE_GPU + Gpu::DeviceVector d_map_actual_fields_to_dump(ncomp_to_dump); + Gpu::copyAsync(Gpu::hostToDevice, + map_actual_fields_to_dump.begin(), map_actual_fields_to_dump.end(), + d_map_actual_fields_to_dump.begin()); + Gpu::synchronize(); + int const* field_map_ptr = d_map_actual_fields_to_dump.dataPtr(); +#else + int const* field_map_ptr = map_actual_fields_to_dump.dataPtr(); +#endif for (MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) { Box& bx = m_buff_box_; @@ -1361,7 +1380,6 @@ AddDataToBuffer( MultiFab& tmp, int k_lab, bx.setBig(AMREX_SPACEDIM-1,bx_bf.bigEnd(AMREX_SPACEDIM-1)); Array4 tmp_arr = tmp[mfi].array(); Array4 buf_arr = buf[mfi].array(); - const auto field_map_ptr = map_actual_fields_to_dump.dataPtr(); ParallelFor(bx, ncomp_to_dump, [=] AMREX_GPU_DEVICE(int i, int j, int k, int n) { @@ -1380,7 +1398,7 @@ AddDataToBuffer( MultiFab& tmp, int k_lab, void LabFrameSnapShot:: AddPartDataToParticleBuffer( - Vector tmp_particle_buffer, + Vector const& tmp_particle_buffer, int nspeciesBoostedFrame) { for (int isp = 0; isp < nspeciesBoostedFrame; ++isp) { auto np = tmp_particle_buffer[isp].GetRealData(DiagIdx::w).size(); @@ -1442,7 +1460,7 @@ AddPartDataToParticleBuffer( void LabFrameSlice:: AddPartDataToParticleBuffer( - Vector tmp_particle_buffer, + Vector const& tmp_particle_buffer, int nSpeciesBackTransformedDiagnostics) { @@ -1468,8 +1486,8 @@ AddPartDataToParticleBuffer( // temporary arrays to store copy_flag and copy_index // for particles that cross the reduced domain for diagnostics. - amrex::Gpu::ManagedDeviceVector FlagForPartCopy(np); - amrex::Gpu::ManagedDeviceVector IndexForPartCopy(np); + amrex::Gpu::DeviceVector FlagForPartCopy(np); + amrex::Gpu::DeviceVector IndexForPartCopy(np); int* const AMREX_RESTRICT Flag = FlagForPartCopy.dataPtr(); int* const AMREX_RESTRICT IndexLocation = IndexForPartCopy.dataPtr(); @@ -1503,8 +1521,7 @@ AddPartDataToParticleBuffer( // Call exclusive scan to obtain location indices using // flag values. These location indices are used to copy data // from src to dst when the copy-flag is set to 1. - amrex::Gpu::exclusive_scan(Flag,Flag+np,IndexLocation); - const int copy_size = IndexLocation[np-1] + Flag[np-1]; + const int copy_size = amrex::Scan::ExclusiveSum(np, Flag, IndexLocation); const int init_size = m_particles_buffer_[isp].GetRealData(DiagIdx::w).size(); const int total_reducedDiag_size = copy_size + init_size; diff --git a/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.H index d6fb4d80a26..6dd0d5e31c8 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.H @@ -5,42 +5,103 @@ /** * \brief Functor to back-transform cell-centered data and store result in mf_out + * + * The cell-centered data is a ten-component multifab with field-data averaged-down + * from the finest to coarsest level, and stored as single-level data. + * For every i^th buffer, a z-slice corresponding to the z-boost location of the + * slice at the current timestep is extracted. This slice containing field-data + * in the boosted-frame is Lorentz-transformed to the lab-frame. The user-requested + * lab-frame field data is then stored in mf_dst. */ class BackTransformFunctor final : public ComputeDiagFunctor { public: - // This class will handle the slicing of the cell-centered field-data (stored as mf_src) - // It will Lorentz-Transform the sliced data from boosted-frame at z=zboost - // to lab-frame at z=zlab and add the lab-frame slice to the destination - // multifab that will be provided in the operator () function, for all ncomponents - // starting from icomp_dst. - // The operator() function will also need a few boosted-frame parameters, such as, - // current_z_boost of the corresponding snapshot, to generate appropriate slices - // in boosted-frame and lab-frame.i - // Unlike cell-center functor, coarsening ratio is not provided to this functor, - // since the data is coarsened when preparing fields in m_mf_cc (mf_src). - /** Constructor description + * * \param[in] mf_src cell-centered multifab containing all user-requested fields in boosted-frame * \param[in] lev level of multifab. * \param[in] ncom number of components of mf_src to Lorentz-Transform and store in destination multifab. */ + BackTransformFunctor ( const amrex::MultiFab * const mf_src, const int lev, + const int ncomp, const int num_buffers, + amrex::Vector< std::string > varnames, + const amrex::IntVect crse_ratio= amrex::IntVect(1)); - // initializing crse_ratio with 1, to accurately initialize ComputeDiagFunctor - - BackTransformFunctor( const amrex::MultiFab * const mf_src, const int lev, - const int ncomp, const amrex::IntVect crse_ratio= amrex::IntVect(1)); + /** \brief Lorentz-transform mf_src for the ith buffer and write the result in mf_dst. + * + * The source multifab, is a ten-component cell-centered multifab storing + * field-data in the boosted-frame. An z-slice is generated + * at the z-boost location for the ith buffer, stored in m_current_z_boost[i_buffer]. + * The data is then lorentz-transformed in-place using LorenzTransformZ (). + * The user-requested fields are then copied to mf_dst. + * + * \param[out] mf_dst output MuliFab where the back-transformed data is written + * \param[in] dcomp first component of mf_dst in which the back-transformed + * lab-frame data for the user-request fields is written. + * \param[in] i_buffer buffer index for which the data is transformed. + */ + void operator ()(amrex::MultiFab& mf_dst, int dcomp, const int i_buffer) const override; - void operator()(amrex::MultiFab& mf_dst, int dcomp) const override {} + /** \brief Prepare data required to back-transform fields for lab-frame snapshot, i_buffer + * + * \param[in] i_buffer, index of the snapshot + * \param[in] ZSliceInDomain if the z-slice at current_z_boost is within + * the boosted-frame and lab-frame domain. + * The fields are sliced and back-transformed only if this value is true. + * \param[in] buffer_box, Box with index-space in lab-frame for the ith buffer + * \param[in] k_index_zlab, k-index in the lab-frame corresponding to the + * current z co-ordinate in the lab-frame for the ith buffer. + */ + void PrepareFunctorData ( int i_buffer, bool ZSliceInDomain, + amrex::Real current_z_boost, + amrex::Box buffer_box, const int k_index_zlab ) override; + /** Allocate and initialize member variables and arrays required to back-transform + * field-data from boosted-frame to lab-frame. + */ + void InitData () override; + /** \brief In-place Lorentz-transform of MultiFab, data, from boosted-frame to the + * lab-frame for all fields, Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, and rho. + * + * \param[in] data z-slice field-data MultiFab to be back-transformed in-place + * from boosted-frame to lab-frame + * \param[in] gamma_boost The Lorentz factor of the boosted-frame in which + the simulation is run + * \param[in] beta_boost The ratio of boost velocity to the speed of light + */ + void LorentzTransformZ (amrex::MultiFab& data, amrex::Real gamma_boost, + amrex::Real beta_boost) const; private: /** pointer to source multifab (cell-centered multi-component multifab) */ amrex::MultiFab const * const m_mf_src = nullptr; /** level at which m_mf_src is defined */ int const m_lev; + /** Number of buffers or snapshots */ + int const m_num_buffers; + /** Vector of amrex::Box with index-space in the lab-frame */ + amrex::Vector m_buffer_box; + /** Vector of current z co-ordinate in the boosted-frame for each buffer */ + amrex::Vector m_current_z_boost; + /** Vector of 0s and 1s stored to check if back-transformation is to be performed + * for the ith buffer. The value is set 0 (false) or 1 (true) using the boolean + * ZSliceInDomain in PrepareFunctorData(). + */ + amrex::Vector m_perform_backtransform; + /** Vector of k-index correspoding to the current lab-frame z co-ordinate for each buffer */ + amrex::Vector m_k_index_zlab; + /** Vector of user-defined field names to be stored in the output multifab */ + amrex::Vector< std::string > m_varnames; + + /** max grid size used to generate BoxArray to define output MultiFabs */ + int m_max_box_size = 256; + /** Indices that map user-defined fields to plot to the fields + * stored in the cell-centered MultiFab, m_mf_src. + * The cell-centered MultiFab stores Ex, Ey, Ez, Bx, By, Bz, jx, jy, jz, and rho. + */ + amrex::Vector m_map_varnames; }; #endif diff --git a/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.cpp index 2d8651efe4b..298a74c2bf9 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/BackTransformFunctor.cpp @@ -1,8 +1,201 @@ #include "BackTransformFunctor.H" +#include "WarpX.H" +#include +#include +using namespace amrex; -BackTransformFunctor::BackTransformFunctor(amrex::MultiFab const * mf_src, int lev, - const int ncomp, const amrex::IntVect crse_ratio) - : ComputeDiagFunctor(ncomp, crse_ratio), m_mf_src(mf_src), m_lev(lev) +BackTransformFunctor::BackTransformFunctor (amrex::MultiFab const * mf_src, int lev, + const int ncomp, const int num_buffers, + amrex::Vector< std::string > varnames, + const amrex::IntVect crse_ratio) + : ComputeDiagFunctor(ncomp, crse_ratio), m_mf_src(mf_src), m_lev(lev), m_num_buffers(num_buffers), m_varnames(varnames) { - // Get the right slice of each field in the CC MultiFab, transform it and store it in the output. + InitData(); +} + +void +BackTransformFunctor::operator ()(amrex::MultiFab& mf_dst, int /*dcomp*/, const int i_buffer) const +{ + // Perform back-transformation only if z slice is within the domain stored as 0/1 + // in m_perform_backtransform[i_buffer] + if ( m_perform_backtransform[i_buffer] == 1) { + auto& warpx = WarpX::GetInstance(); + auto geom = warpx.Geom(m_lev); + amrex::Real gamma_boost = warpx.gamma_boost; + int moving_window_dir = warpx.moving_window_dir; + amrex::Real beta_boost = std::sqrt( 1._rt - 1._rt/( gamma_boost * gamma_boost) ); + bool interpolate = true; + std::unique_ptr< amrex::MultiFab > slice = nullptr; + int scomp = 0; + // Generate slice of the cell-centered multifab containing boosted-frame field-data + // at current z-boost location for the ith buffer + slice = amrex::get_slice_data (moving_window_dir, m_current_z_boost[i_buffer], + *m_mf_src, geom, scomp, m_mf_src->nComp(), interpolate); + // Perform in-place Lorentz-transform of all the fields stored in the slice. + LorentzTransformZ( *slice, gamma_boost, beta_boost); + + // Create a 2D box for the slice in the boosted frame + amrex::Real dx = geom.CellSize(moving_window_dir); + // index corresponding to z_boost location in the boost-frame + int i_boost = static_cast ( ( m_current_z_boost[i_buffer] + - geom.ProbLo(moving_window_dir) ) / dx ); + // z-Slice at i_boost with x,y indices same as buffer_box + amrex::Box slice_box = m_buffer_box[i_buffer]; + slice_box.setSmall(moving_window_dir, i_boost); + slice_box.setBig(moving_window_dir, i_boost); + + // Make it a BoxArray + amrex::BoxArray slice_ba(slice_box); + slice_ba.maxSize( m_max_box_size ); + // Define MultiFab with the distribution map of the destination multifab and + // containing all ten components that were in the slice generated from m_mf_src. + std::unique_ptr< amrex::MultiFab > tmp_slice_ptr = nullptr; + tmp_slice_ptr.reset( new MultiFab ( slice_ba, mf_dst.DistributionMap(), + slice->nComp(), 0) ); + // Parallel copy the lab-frame data from "slice" MultiFab with + // ncomp=10 and boosted-frame dmap to "tmp_slice_ptr" MultiFab with + // ncomp=10 and dmap of the destination Multifab, which will store the final data + tmp_slice_ptr->copy( *slice, 0, 0, slice->nComp() ); + // Now we will cherry pick only the user-defined fields from + // tmp_slice_ptr to dst_mf + const int k_lab = m_k_index_zlab[i_buffer]; + const int ncomp_dst = mf_dst.nComp(); + amrex::MultiFab& tmp = *tmp_slice_ptr; +#ifdef AMREX_USE_GPU + Gpu::DeviceVector d_map_varnames(m_map_varnames.size()); + Gpu::copyAsync(Gpu::hostToDevice, + m_map_varnames.begin(), m_map_varnames.end(), + d_map_varnames.begin()); + Gpu::synchronize(); + int const* field_map_ptr = d_map_varnames.dataPtr(); +#else + int const* field_map_ptr = m_map_varnames.dataPtr(); +#endif + for (amrex::MFIter mfi(tmp, TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + // Box spanning the user-defined index-space for diagnostic, mf_dst + amrex::Box bx = m_buffer_box[i_buffer]; + // Box of tmp_slice_ptr spanning full domain in x,y and z_boost + // of the respective buffer , i_buffer + const Box& tbx = mfi.tilebox(); + // Modify bx, such that the z-index is equal to the sliced tmp_mf + bx.setSmall( moving_window_dir, tbx.smallEnd( moving_window_dir ) ); + bx.setBig( moving_window_dir, tbx.bigEnd( moving_window_dir ) ); + amrex::Array4 src_arr = tmp[mfi].array(); + amrex::Array4 dst_arr = mf_dst[mfi].array(); + amrex::ParallelFor( bx, ncomp_dst, + [=] AMREX_GPU_DEVICE(int i, int j, int k, int n) + { + const int icomp = field_map_ptr[n]; +#if (AMREX_SPACEDIM == 3) + dst_arr(i, j, k_lab, n) = src_arr(i, j, k, icomp); +#else + dst_arr(i, k_lab, k, n) = src_arr(i, j, k, icomp); +#endif + } ); + } + + // Reset the temporary MultiFabs generated + slice.reset(new MultiFab); + slice.reset(nullptr); + tmp_slice_ptr.reset(new MultiFab); + tmp_slice_ptr.reset(nullptr); + + } + +} + +void +BackTransformFunctor::PrepareFunctorData (int i_buffer, + bool ZSliceInDomain, amrex::Real current_z_boost, + amrex::Box buffer_box, const int k_index_zlab ) +{ + m_buffer_box[i_buffer] = buffer_box; + m_current_z_boost[i_buffer] = current_z_boost; + m_k_index_zlab[i_buffer] = k_index_zlab; + m_perform_backtransform[i_buffer] = 0; + if (ZSliceInDomain) m_perform_backtransform[i_buffer] = 1; +} + +void +BackTransformFunctor::InitData () +{ + + m_buffer_box.resize( m_num_buffers ); + m_current_z_boost.resize( m_num_buffers ); + m_perform_backtransform.resize( m_num_buffers ); + m_k_index_zlab.resize( m_num_buffers ); + m_map_varnames.resize( m_varnames.size() ); + + std::map m_possible_fields_to_dump = { + {"Ex", 0}, + {"Ey", 1}, + {"Ez", 2}, + {"Bx", 3}, + {"By", 4}, + {"Bz", 5}, + {"jx", 6}, + {"jy", 7}, + {"jz", 8}, + {"rho", 9} + }; + + for (int i = 0; i < m_varnames.size(); ++i) + { + m_map_varnames[i] = m_possible_fields_to_dump[ m_varnames[i] ] ; + } + +} + +void +BackTransformFunctor::LorentzTransformZ (amrex::MultiFab& data, amrex::Real gamma_boost, + amrex::Real beta_boost) const +{ +#ifdef _OPENMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (amrex::MFIter mfi(data, TilingIfNotGPU()); mfi.isValid(); ++mfi) { + const amrex::Box& tbx = mfi.tilebox(); + amrex::Array4< amrex::Real > arr = data[mfi].array(); + amrex::Real clight = PhysConst::c; + amrex::Real inv_clight = 1.0_rt/clight; + // arr(x,y,z,comp) has ten-components namely, + // Ex Ey Ez Bx By Bz jx jy jz rho in that order. + amrex::ParallelFor( tbx, + [=] AMREX_GPU_DEVICE (int i, int j, int k) + { + // Back-transform the transverse electric and magnetic fields. + // Note that the z-components, Ez, Bz, are not changed by the transform. + amrex::Real e_lab, b_lab, j_lab, rho_lab; + // Transform Ex_boost (ncomp=0) & By_boost (ncomp=4) to lab-frame + e_lab = gamma_boost * ( arr(i, j, k, 0) + + beta_boost * clight * arr(i, j, k, 4) ); + b_lab = gamma_boost * ( arr(i, j, k, 4) + + beta_boost * inv_clight * arr(i, j, k, 0) ); + // Store lab-frame data in-place + arr(i, j, k, 0) = e_lab; + arr(i, j, k, 4) = b_lab; + + // Transform Ey_boost (ncomp=1) & Bx_boost (ncomp=3) to lab-frame + e_lab = gamma_boost * ( arr(i, j, k, 1) + - beta_boost * clight * arr(i, j, k, 3) ); + b_lab = gamma_boost * ( arr(i, j, k, 3) + - beta_boost * inv_clight * arr(i, j, k, 1) ); + // Store lab-frame data in-place + arr(i, j, k, 1) = e_lab; + arr(i, j, k, 3) = b_lab; + + // Transform charge density (ncomp=9) + // and z-component of current density (ncomp=8) + j_lab = gamma_boost * ( arr(i, j, k, 8) + + beta_boost * clight * arr(i, j, k, 9) ); + rho_lab = gamma_boost * ( arr(i, j, k, 9) + + beta_boost * inv_clight * arr(i, j, k, 8) ); + // Store lab-frame jz and rho in-place + arr(i, j, k, 8) = j_lab; + arr(i, j, k, 9) = rho_lab; + } + ); + } + } diff --git a/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.H index cb4cd7a414d..809cc47bb38 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.H @@ -31,7 +31,7 @@ public: * \param[in] dcomp first component of mf_dst in which cell-centered * data is stored */ - virtual void operator()(amrex::MultiFab& mf_dst, int dcomp) const override; + virtual void operator()(amrex::MultiFab& mf_dst, int dcomp, const int /*i_buffer=0*/) const override; private: /** pointer to source multifab (can be multi-component) */ amrex::MultiFab const * const m_mf_src = nullptr; diff --git a/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.cpp index a821861bb00..ea98b2f06f7 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/CellCenterFunctor.cpp @@ -1,6 +1,8 @@ #include "CellCenterFunctor.H" #include "Utils/CoarsenIO.H" +#include + CellCenterFunctor::CellCenterFunctor(amrex::MultiFab const * mf_src, int lev, amrex::IntVect crse_ratio, bool convertRZmodes2cartesian, int ncomp) @@ -9,7 +11,7 @@ CellCenterFunctor::CellCenterFunctor(amrex::MultiFab const * mf_src, int lev, {} void -CellCenterFunctor::operator()(amrex::MultiFab& mf_dst, int dcomp) const +CellCenterFunctor::operator()(amrex::MultiFab& mf_dst, int dcomp, const int /*i_buffer*/) const { #ifdef WARPX_DIM_RZ if (m_convertRZmodes2cartesian) { @@ -33,6 +35,7 @@ CellCenterFunctor::operator()(amrex::MultiFab& mf_dst, int dcomp) const #else // In cartesian geometry, coarsen and interpolate from simulation MultiFab, m_mf_src, // to output diagnostic MultiFab, mf_dst. - CoarsenIO::Coarsen( mf_dst, *m_mf_src, dcomp, 0, nComp(), 0, m_crse_ratio); + CoarsenIO::Coarsen( mf_dst, *m_mf_src, dcomp, 0, nComp(), mf_dst.nGrow(0), m_crse_ratio); + amrex::ignore_unused(m_lev, m_convertRZmodes2cartesian); #endif } diff --git a/Source/Diagnostics/ComputeDiagFunctors/ComputeDiagFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/ComputeDiagFunctor.H index 718c39f864f..f1f2d587c74 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/ComputeDiagFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/ComputeDiagFunctor.H @@ -1,6 +1,7 @@ #ifndef WARPX_COMPUTEDIAGFUNCTOR_H_ #define WARPX_COMPUTEDIAGFUNCTOR_H_ +#include #include /** @@ -22,10 +23,20 @@ public: * \param[in] crse_ratio for interpolating field values from simulation MultiFab, mf_src, to the output diagnostic MultiFab, mf_dst. */ - virtual void operator() (amrex::MultiFab& mf_dst, int dcomp) const = 0; + virtual void operator() (amrex::MultiFab& mf_dst, int dcomp, const int i_buffer = 0) const = 0; /** Number of component from the input multifab to write to the output * multifab */ int nComp () const { return m_ncomp; } + + virtual void PrepareFunctorData ( int i_buffer, bool ZSliceInDomain, + amrex::Real current_z_boost, + amrex::Box buffer_box, const int k_index_zlab) { + amrex::ignore_unused(i_buffer, + ZSliceInDomain, + current_z_boost, buffer_box, + k_index_zlab); + } + virtual void InitData() {} private: /** Number of components of mf_dst that this functor updates. */ int m_ncomp; diff --git a/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.H index f3716665cf9..52b9a1dbd05 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.H @@ -25,7 +25,7 @@ public: * \param[in] dcomp first component of mf_dst in which cell-centered * data is stored */ - virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp) const override; + virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer*/) const override; private: /** Vector of pointer to source multifab Bx, By, Bz */ std::array m_arr_mf_src; diff --git a/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.cpp index 3b2889b83d1..512d801483a 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/DivBFunctor.cpp @@ -6,7 +6,7 @@ DivBFunctor::DivBFunctor(const std::array arr_m {} void -DivBFunctor::operator()(amrex::MultiFab& mf_dst, int dcomp) const +DivBFunctor::operator()(amrex::MultiFab& mf_dst, int dcomp, const int /*i_buffer*/) const { auto& warpx = WarpX::GetInstance(); // Guard cell is set to 1 for generality. However, for a cell-centered diff --git a/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.H index adb6584c225..503737f84e9 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.H @@ -26,7 +26,7 @@ public: * \param[in] dcomp first component of mf_dst in which cell-centered * data is stored */ - virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp) const override; + virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer=0*/) const override; private: /** Vector of pointer to source multifab Bx, By, Bz */ std::array m_arr_mf_src; diff --git a/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.cpp index adf78b3291a..26104bdf32d 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/DivEFunctor.cpp @@ -1,26 +1,35 @@ #include "DivEFunctor.H" #include "Utils/CoarsenIO.H" +#include + DivEFunctor::DivEFunctor(const std::array arr_mf_src, const int lev, const amrex::IntVect crse_ratio, bool convertRZmodes2cartesian, const int ncomp) : ComputeDiagFunctor(ncomp, crse_ratio), m_arr_mf_src(arr_mf_src), m_lev(lev), m_convertRZmodes2cartesian(convertRZmodes2cartesian) -{} +{ + amrex::ignore_unused(m_arr_mf_src); +} void -DivEFunctor::operator()(amrex::MultiFab& mf_dst, const int dcomp) const +DivEFunctor::operator()(amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer*/) const { auto& warpx = WarpX::GetInstance(); // Guard cell is set to 1 for generality. However, for a cell-centered // output Multifab, mf_dst, the guard-cell data is not needed especially considering // the operations performend in the CoarsenAndInterpolate function. constexpr int ng = 1; +#if (defined WARPX_DIM_RZ) && (defined WARPX_USE_PSATD) + // For RZ spectral, all quantities are cell centered. + amrex::IntVect cell_type = amrex::IntVect::TheCellVector(); +#else // For staggered and nodal calculations, divE is computed on the nodes. // The temporary divE MultiFab is generated to comply with the location of divE. - const amrex::BoxArray& ba = amrex::convert(warpx.boxArray(m_lev), - amrex::IntVect::TheUnitVector()); - amrex::MultiFab divE(ba, warpx.DistributionMap(m_lev), 2*warpx.n_rz_azimuthal_modes-1, ng); + amrex::IntVect cell_type = amrex::IntVect::TheNodeVector(); +#endif + const amrex::BoxArray& ba = amrex::convert(warpx.boxArray(m_lev), cell_type); + amrex::MultiFab divE(ba, warpx.DistributionMap(m_lev), 2*warpx.n_rz_azimuthal_modes-1, ng ); warpx.ComputeDivE(divE, m_lev); #ifdef WARPX_DIM_RZ @@ -45,5 +54,6 @@ DivEFunctor::operator()(amrex::MultiFab& mf_dst, const int dcomp) const // In cartesian geometry, coarsen and interpolate from simulation MultiFab, divE, // to output diagnostic MultiFab, mf_dst. CoarsenIO::Coarsen( mf_dst, divE, dcomp, 0, nComp(), 0, m_crse_ratio); + amrex::ignore_unused(m_convertRZmodes2cartesian); #endif } diff --git a/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.H index 636656c5aef..ac76c0db15b 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.H @@ -27,7 +27,7 @@ public: * \param[in] dcomp first component of mf_dst in which cell-centered * data is stored */ - virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp) const override; + virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer=0*/) const override; private: int const m_lev; /**< level on which mf_src is defined */ }; diff --git a/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.cpp index 232a2348f2c..31acacc9bce 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/PartPerCellFunctor.cpp @@ -14,7 +14,7 @@ PartPerCellFunctor::PartPerCellFunctor(const amrex::MultiFab* mf_src, const int } void -PartPerCellFunctor::operator()(amrex::MultiFab& mf_dst, const int dcomp) const +PartPerCellFunctor::operator()(amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer*/) const { auto& warpx = WarpX::GetInstance(); // Guard cell is set to 1 for generality. However, for a cell-centered diff --git a/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.H index d2eaad1b1ef..5c5d4b84ac8 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.H @@ -27,7 +27,7 @@ public: * \param[in] dcomp first component of mf_dst in which cell-centered * data is stored */ - virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp) const override; + virtual void operator()(amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer=0*/) const override; private: int const m_lev; /**< level on which mf_src is defined */ }; diff --git a/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.cpp index b61748a18e6..9f4b0adb88a 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/PartPerGridFunctor.cpp @@ -11,7 +11,7 @@ PartPerGridFunctor::PartPerGridFunctor(const amrex::MultiFab * const mf_src, con } void -PartPerGridFunctor::operator()(amrex::MultiFab& mf_dst, const int dcomp) const +PartPerGridFunctor::operator()(amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer*/) const { auto& warpx = WarpX::GetInstance(); const amrex::Vector& npart_in_grid = warpx.GetPartContainer().NumberOfParticlesInGrid(m_lev); diff --git a/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.H b/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.H index ed31b845b6f..b6af4a41a45 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.H +++ b/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.H @@ -18,12 +18,17 @@ public: * \param[in] lev level of MultiFab * \param[in] crse_ratio coarsening ratio for interpolation of field values * from simulation MultiFabs to the output MultiFab mf_dst + * \param[in] species_index Index of species to dump rho per species. This argument + * is optional and defaults to -1 (dump total rho) * \param[in] convertRZmodes2cartesian if true, all RZ modes are averaged into one component * \param[in] ncomp optional number of component of source MultiFab mf_src * to be cell-centered in output MultiFab mf_dst */ - RhoFunctor ( const int lev, const amrex::IntVect crse_ratio, - bool convertRZmodes2cartesian=true, const int ncomp=1 ); + RhoFunctor (const int lev, + const amrex::IntVect crse_ratio, + const int species_index = -1, + bool convertRZmodes2cartesian = true, + const int ncomp = 1); /** * \brief Compute rho directly into mf_dst @@ -31,13 +36,16 @@ public: * \param[out] mf_dst output MultiFab where the result is written * \param[in] dcomp first component of mf_dst in which cell-centered data are stored */ - virtual void operator() ( amrex::MultiFab& mf_dst, const int dcomp ) const override; + virtual void operator() ( amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer=0*/ ) const override; private: // Level on which source MultiFab mf_src is defined in RZ geometry int const m_lev; + // Species index to dump rho per species + const int m_species_index; + // Whether to average all modes into one component in RZ geometry bool m_convertRZmodes2cartesian; }; diff --git a/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp index c5739ef21a1..c8ccb2deda7 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp @@ -2,20 +2,35 @@ #include "RhoFunctor.H" #include "Utils/CoarsenIO.H" -RhoFunctor::RhoFunctor ( const int lev, const amrex::IntVect crse_ratio, - bool convertRZmodes2cartesian, const int ncomp ) - : ComputeDiagFunctor(ncomp, crse_ratio), m_lev(lev), +#include + +RhoFunctor::RhoFunctor (const int lev, + const amrex::IntVect crse_ratio, + const int species_index, + bool convertRZmodes2cartesian, + const int ncomp) + : ComputeDiagFunctor(ncomp, crse_ratio), + m_lev(lev), + m_species_index(species_index), m_convertRZmodes2cartesian(convertRZmodes2cartesian) {} void -RhoFunctor::operator() ( amrex::MultiFab& mf_dst, const int dcomp ) const +RhoFunctor::operator() ( amrex::MultiFab& mf_dst, const int dcomp, const int /*i_buffer*/ ) const { auto& warpx = WarpX::GetInstance(); - auto& mypc = warpx.GetPartContainer(); + std::unique_ptr rho; - // Deposit charge density - std::unique_ptr rho = mypc.GetChargeDensity( m_lev ); + // Dump total rho + if (m_species_index == -1) { + auto& mypc = warpx.GetPartContainer(); + rho = mypc.GetChargeDensity(m_lev); + } + // Dump rho per species + else { + auto& mypc = warpx.GetPartContainer().GetParticleContainer(m_species_index); + rho = mypc.GetChargeDensity(m_lev); + } #ifdef WARPX_DIM_RZ if (m_convertRZmodes2cartesian) { @@ -38,6 +53,7 @@ RhoFunctor::operator() ( amrex::MultiFab& mf_dst, const int dcomp ) const #else // In Cartesian geometry, coarsen and interpolate from temporary MultiFab rho // to output diagnostic MultiFab mf_dst - CoarsenIO::Coarsen( mf_dst, *rho, dcomp, 0, nComp(), 0, m_crse_ratio ); + CoarsenIO::Coarsen( mf_dst, *rho, dcomp, 0, nComp(), mf_dst.nGrow(0), m_crse_ratio ); + amrex::ignore_unused(m_convertRZmodes2cartesian); #endif } diff --git a/Source/Diagnostics/Diagnostics.H b/Source/Diagnostics/Diagnostics.H index 4f7e57772ce..3f6aa13d873 100644 --- a/Source/Diagnostics/Diagnostics.H +++ b/Source/Diagnostics/Diagnostics.H @@ -92,6 +92,8 @@ protected: back-transform diagnostics) to be processed for diagnostics. */ virtual void PrepareFieldDataForOutput () {} + /** Shift the diag-domain boundary for moving window and galilean simulations */ + virtual void MovingWindowAndGalileanDomainShift () {} /** Name of diagnostics: runtime parameter given in the input file. */ std::string m_diag_name; /** Prefix for output directories */ @@ -116,21 +118,31 @@ protected: * The second vector is loops over the total number of levels. */ amrex::Vector< amrex::Vector< amrex::MultiFab > > m_mf_output; + + /** Geometry that defines the domain attributes corresponding to output multifab. + * Specifically, the user-defined physical co-ordinates for the diagnostics + * is used to construct the geometry information for each MultiFab at + * the respective levels. This geometry will be used to write out + * plotfile data using the WriteToFile() function + */ + amrex::Vector< amrex::Vector > m_geom_output; // a particle buffer here? int nlev; /**< number of levels to output */ int nmax_lev; /**< max_level to allocate output multifab and vector of field functors. */ /** Number of levels to be output*/ int nlev_output; - /** Name of species to write to file */ - std::vector< std::string > m_species_names; + /** Names of species to write to output */ + std::vector< std::string > m_output_species_names; + /** Names of all species in the simulation */ + std::vector< std::string > m_all_species_names; /** Each element of this vector handles output for 1 species */ - amrex::Vector< ParticleDiag > m_all_species; + amrex::Vector< ParticleDiag > m_output_species; /** Vector of (pointers to) functors to compute output fields, per level, * per component. This allows for simple operations (averaging to * cell-center for standard EB fields) as well as more involved operations * (back-transformed diagnostics, filtering, reconstructing cartesian * fields in cylindrical). */ - amrex::Vector< amrex::Vector > > m_all_field_functors; + amrex::Vector< amrex::Vector > > m_all_field_functors; /** Coarsening ratio such that, fields are averaged to the coarsened grid. * The ratio should render the grid to be coarsenable (as defined by AMReX). */ amrex::IntVect m_crse_ratio = amrex::IntVect(1); @@ -140,6 +152,8 @@ protected: amrex::Vector< amrex::Real> m_hi; /** Number of output buffers. The value is set to 1 for all FullDiagnostics */ int m_num_buffers; + /** Array of species indices that dump rho per species */ + amrex::Vector m_rho_per_species_index; }; #endif // WARPX_DIAGNOSTICS_H_ diff --git a/Source/Diagnostics/Diagnostics.cpp b/Source/Diagnostics/Diagnostics.cpp index 484ea88c935..7c1de1d68f5 100644 --- a/Source/Diagnostics/Diagnostics.cpp +++ b/Source/Diagnostics/Diagnostics.cpp @@ -13,7 +13,9 @@ #endif #include "WarpX.H" #include "Utils/WarpXUtil.H" - +#include +#include +using namespace amrex::literals; Diagnostics::Diagnostics (int i, std::string name) : m_diag_name(name), m_diag_index(i) @@ -29,26 +31,31 @@ bool Diagnostics::BaseReadParameters () { auto & warpx = WarpX::GetInstance(); - // Read list of fields requested by the user. + amrex::ParmParse pp(m_diag_name); m_file_prefix = "diags/" + m_diag_name; pp.query("file_prefix", m_file_prefix); pp.query("format", m_format); + + // Query list of grid fields to write to output bool varnames_specified = pp.queryarr("fields_to_plot", m_varnames); if (!varnames_specified){ m_varnames = {"Ex", "Ey", "Ez", "Bx", "By", "Bz", "jx", "jy", "jz"}; } + // If user requests rho with back-transformed diagnostics, we set plot_rho=true // and compute rho at each iteration if (WarpXUtilStr::is_in(m_varnames, "rho") && WarpX::do_back_transformed_diagnostics) { warpx.setplot_rho(true); } + // Sanity check if user requests to plot F if (WarpXUtilStr::is_in(m_varnames, "F")){ AMREX_ALWAYS_ASSERT_WITH_MESSAGE( warpx.do_dive_cleaning, "plot F only works if warpx.do_dive_cleaning = 1"); } + // If user requests to plot proc_number for a serial run, // delete proc_number from fields_to_plot if (amrex::ParallelDescriptor::NProcs() == 1){ @@ -74,6 +81,22 @@ Diagnostics::BaseReadParameters () m_hi[idim] = warpx.Geom(0).ProbHi(idim); } } + // For a moving window simulation, the user-defined m_lo and m_hi must be converted. + if (warpx.do_moving_window) { +#if (AMREX_SPACEDIM == 3) + amrex::Vector dim_map {0, 1, 2}; +#else + amrex::Vector dim_map {0, 2}; +#endif + if (warpx.boost_direction[ dim_map[warpx.moving_window_dir] ] == 1) { + // Convert user-defined lo and hi for diagnostics to account for boosted-frame + // simulations with moving window + amrex::Real convert_factor = 1._rt/(warpx.gamma_boost * (1._rt - warpx.beta_boost) ); + // Assuming that the window travels with speed c + m_lo[warpx.moving_window_dir] *= convert_factor; + m_hi[warpx.moving_window_dir] *= convert_factor; + } + } // Initialize cr_ratio with default value of 1 for each dimension. amrex::Vector cr_ratio(AMREX_SPACEDIM, 1); @@ -85,7 +108,41 @@ Diagnostics::BaseReadParameters () } } - bool species_specified = pp.queryarr("species", m_species_names); + // Names of species to write to output + bool species_specified = pp.queryarr("species", m_output_species_names); + + // Names of all species in the simulation + m_all_species_names = warpx.GetPartContainer().GetSpeciesNames(); + + // Auxiliary variables + std::string species; + bool species_name_is_wrong; + // Loop over all fields stored in m_varnames + for (const auto& var : m_varnames) { + // Check if m_varnames contains a string of the form rho_ + if (var.rfind("rho_", 0) == 0) { + // Extract species name from the string rho_ + species = var.substr(var.find("rho_") + 4); + // Boolean used to check if species name was misspelled + species_name_is_wrong = true; + // Loop over all species + for (int i = 0, n = int(m_all_species_names.size()); i < n; i++) { + // Check if species name extracted from the string rho_ + // matches any of the species in the simulation + if (species == m_all_species_names[i]) { + // Store species index: will be used in RhoFunctor to dump + // rho for this species + m_rho_per_species_index.push_back(i); + species_name_is_wrong = false; + } + } + // If species name was misspelled, abort with error message + if (species_name_is_wrong) { + amrex::Abort("Input error: string " + var + " in " + m_diag_name + + ".fields_to_plot does not match any species"); + } + } + } bool checkpoint_compatibility = false; if (m_format == "checkpoint"){ @@ -121,6 +178,32 @@ Diagnostics::InitData () // When particle buffers, m_particle_buffers are included, they will be initialized here InitializeParticleBuffer(); + amrex::ParmParse pp(m_diag_name); + amrex::Vector dummy_val(AMREX_SPACEDIM); + if ( pp.queryarr("diag_lo", dummy_val) || pp.queryarr("diag_hi", dummy_val) ) { + // set geometry filter for particle-diags to true when the diagnostic domain-extent + // is specified by the user + for (int i = 0; i < m_output_species.size(); ++i) { + m_output_species[i].m_do_geom_filter = true; + } + // Disabling particle-io for reduced domain diagnostics by reducing + // the particle-diag vector to zero. + // This is a temporary fix until particle_buffer is supported in diagnostics. + m_output_species.clear(); + amrex::Print() << " WARNING: For full diagnostics on a reduced domain, particle io is not supported, yet! Therefore, particle-io is disabled for this diag " << m_diag_name << "\n"; + } + + // default for writing species output is 1 + int write_species = 1; + pp.query("write_species", write_species); + if (write_species == 0) { + if (m_format == "checkpoint"){ + amrex::Abort("For checkpoint format, write_species flag must be 1."); + } + // if user-defined value for write_species == 0, then clear species vector + m_output_species.clear(); + m_output_species_names.clear(); + } } @@ -136,6 +219,14 @@ Diagnostics::InitBaseData () nmax_lev = warpx.maxLevel() + 1; m_all_field_functors.resize( nmax_lev ); + // For restart, move the m_lo and m_hi of the diag consistent with the + // current moving_window location + if (warpx.do_moving_window) { + int moving_dir = warpx.moving_window_dir; + int shift_num_base = static_cast((warpx.getmoving_window_x() - m_lo[moving_dir]) / warpx.Geom(0).CellSize(moving_dir) ); + m_lo[moving_dir] += shift_num_base * warpx.Geom(0).CellSize(moving_dir); + m_hi[moving_dir] += shift_num_base * warpx.Geom(0).CellSize(moving_dir); + } // Construct Flush class. if (m_format == "plotfile"){ m_flush_format = new FlushFormatPlotfile; @@ -167,6 +258,12 @@ Diagnostics::InitBaseData () for (int i = 0; i < m_num_buffers; ++i) { m_mf_output[i].resize( nmax_lev ); } + + // allocate vector of geometry objects corresponding to each output multifab. + m_geom_output.resize( m_num_buffers ); + for (int i = 0; i < m_num_buffers; ++i) { + m_geom_output[i].resize( nmax_lev ); + } } void @@ -182,7 +279,7 @@ Diagnostics::ComputeAndPack () // Call all functors in m_all_field_functors[lev]. Each of them computes // a diagnostics and writes in one or more components of the output // multifab m_mf_output[lev]. - m_all_field_functors[lev][icomp]->operator()(m_mf_output[i_buffer][lev], icomp_dst); + m_all_field_functors[lev][icomp]->operator()(m_mf_output[i_buffer][lev], icomp_dst, i_buffer); // update the index of the next component to fill icomp_dst += m_all_field_functors[lev][icomp]->nComp(); } @@ -196,6 +293,10 @@ Diagnostics::ComputeAndPack () void Diagnostics::FilterComputePackFlush (int step, bool force_flush) { + WARPX_PROFILE("Diagnostics::FilterComputePackFlush()"); + + MovingWindowAndGalileanDomainShift (); + if ( DoComputeAndPack (step, force_flush) ) { ComputeAndPack(); diff --git a/Source/Diagnostics/FieldIO.H b/Source/Diagnostics/FieldIO.H index cfe58d9272c..146fbbd9f0f 100644 --- a/Source/Diagnostics/FieldIO.H +++ b/Source/Diagnostics/FieldIO.H @@ -9,9 +9,6 @@ #define WARPX_FielIO_H_ #include -#ifdef WARPX_USE_OPENPMD -# include -#endif void AverageAndPackVectorField( amrex::MultiFab& mf_avg, @@ -25,16 +22,10 @@ AverageAndPackScalarField( amrex::MultiFab& mf_avg, const amrex::DistributionMapping& dm, const int dcomp, const int ngrow ); -#ifdef WARPX_USE_OPENPMD -void -setOpenPMDUnit( openPMD::Mesh mesh, const std::string field_name ); - std::vector getReversedVec( const amrex::IntVect& v ); std::vector getReversedVec( const amrex::Real* v ); -#endif // WARPX_USE_OPENPMD - #endif // WARPX_FielIO_H_ diff --git a/Source/Diagnostics/FieldIO.cpp b/Source/Diagnostics/FieldIO.cpp index 3ee88866502..10195eb998b 100644 --- a/Source/Diagnostics/FieldIO.cpp +++ b/Source/Diagnostics/FieldIO.cpp @@ -18,42 +18,9 @@ # include #endif -using namespace amrex; - -#ifdef WARPX_USE_OPENPMD -/** \brief For a given field that is to be written to an openPMD file, - * set the metadata that indicates the physical unit. - */ -void -setOpenPMDUnit( openPMD::Mesh mesh, const std::string field_name ) -{ - if (field_name[0] == 'E'){ // Electric field - mesh.setUnitDimension({ - {openPMD::UnitDimension::L, 1}, - {openPMD::UnitDimension::M, 1}, - {openPMD::UnitDimension::T, -3}, - {openPMD::UnitDimension::I, -1}, - }); - } else if (field_name[0] == 'B'){ // Magnetic field - mesh.setUnitDimension({ - {openPMD::UnitDimension::M, 1}, - {openPMD::UnitDimension::I, -1}, - {openPMD::UnitDimension::T, -2} - }); - } else if (field_name[0] == 'j'){ // current - mesh.setUnitDimension({ - {openPMD::UnitDimension::L, -2}, - {openPMD::UnitDimension::I, 1}, - }); - } else if (field_name.substr(0,3) == "rho"){ // charge density - mesh.setUnitDimension({ - {openPMD::UnitDimension::L, -3}, - {openPMD::UnitDimension::I, 1}, - {openPMD::UnitDimension::T, 1}, - }); - } -} +#include +using namespace amrex; /** \brief * Convert an IntVect to a std::vector @@ -103,8 +70,6 @@ getReversedVec( const Real* v ) return u; } -#endif // WARPX_USE_OPENPMD - #ifdef WARPX_DIM_RZ void ConstructTotalRZVectorField (const std::array< std::unique_ptr, 3 >& vector_total, @@ -197,6 +162,7 @@ AverageAndPackScalarField (MultiFab& mf_avg, scalar_total = new MultiFab(scalar_field, amrex::make_alias, 0, 1); } #else + amrex::ignore_unused(dm); const MultiFab *scalar_total = &scalar_field; #endif diff --git a/Source/Diagnostics/FlushFormats/FlushFormatAscent.cpp b/Source/Diagnostics/FlushFormats/FlushFormatAscent.cpp index b0903b5a49c..e14199b02b2 100644 --- a/Source/Diagnostics/FlushFormats/FlushFormatAscent.cpp +++ b/Source/Diagnostics/FlushFormats/FlushFormatAscent.cpp @@ -1,6 +1,8 @@ #include "FlushFormatAscent.H" #include "WarpX.H" +#include + using namespace amrex; void @@ -11,7 +13,7 @@ FlushFormatAscent::WriteToFile ( const amrex::Vector iteration, const double time, const amrex::Vector& particle_diags, int nlev, const std::string prefix, bool plot_raw_fields, - bool plot_raw_fields_guards, bool plot_raw_rho, bool plot_raw_F) const + bool plot_raw_fields_guards, bool /*plot_raw_rho*/, bool plot_raw_F) const { #ifdef AMREX_USE_ASCENT @@ -39,6 +41,10 @@ FlushFormatAscent::WriteToFile ( ascent.execute(actions); ascent.close(); +#else + amrex::ignore_unused(varnames, mf, geom, iteration, time, + particle_diags, nlev, prefix, plot_raw_fields, + plot_raw_fields_guards, plot_raw_F); #endif // AMREX_USE_ASCENT } diff --git a/Source/Diagnostics/FlushFormats/FlushFormatCheckpoint.cpp b/Source/Diagnostics/FlushFormats/FlushFormatCheckpoint.cpp index d0bc9292cae..f3569666c44 100644 --- a/Source/Diagnostics/FlushFormats/FlushFormatCheckpoint.cpp +++ b/Source/Diagnostics/FlushFormats/FlushFormatCheckpoint.cpp @@ -8,19 +8,19 @@ using namespace amrex; namespace { - const std::string level_prefix {"Level_"}; + const std::string default_level_prefix {"Level_"}; } void FlushFormatCheckpoint::WriteToFile ( - const amrex::Vector varnames, - const amrex::Vector& mf, - amrex::Vector& geom, - const amrex::Vector iteration, const double time, + const amrex::Vector /*varnames*/, + const amrex::Vector& /*mf*/, + amrex::Vector& /*geom*/, + const amrex::Vector iteration, const double /*time*/, const amrex::Vector& particle_diags, int nlev, const std::string prefix, - bool plot_raw_fields, - bool plot_raw_fields_guards, - bool plot_raw_rho, bool plot_raw_F) const + bool /*plot_raw_fields*/, + bool /*plot_raw_fields_guards*/, + bool /*plot_raw_rho*/, bool /*plot_raw_F*/) const { WARPX_PROFILE("FlushFormatCheckpoint::WriteToFile()"); @@ -34,7 +34,7 @@ FlushFormatCheckpoint::WriteToFile ( amrex::Print() << " Writing checkpoint " << checkpointname << "\n"; // const int nlevels = finestLevel()+1; - amrex::PreBuildDirectorHierarchy(checkpointname, level_prefix, nlev, true); + amrex::PreBuildDirectorHierarchy(checkpointname, default_level_prefix, nlev, true); WriteWarpXHeader(checkpointname, particle_diags); @@ -43,55 +43,55 @@ FlushFormatCheckpoint::WriteToFile ( for (int lev = 0; lev < nlev; ++lev) { VisMF::Write(warpx.getEfield_fp(lev, 0), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Ex_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Ex_fp")); VisMF::Write(warpx.getEfield_fp(lev, 1), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Ey_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Ey_fp")); VisMF::Write(warpx.getEfield_fp(lev, 2), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Ez_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Ez_fp")); VisMF::Write(warpx.getBfield_fp(lev, 0), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Bx_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Bx_fp")); VisMF::Write(warpx.getBfield_fp(lev, 1), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "By_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "By_fp")); VisMF::Write(warpx.getBfield_fp(lev, 2), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Bz_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Bz_fp")); if (warpx.getis_synchronized()) { // Need to save j if synchronized because after restart we need j to evolve E by dt/2. VisMF::Write(warpx.getcurrent_fp(lev, 0), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "jx_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "jx_fp")); VisMF::Write(warpx.getcurrent_fp(lev, 1), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "jy_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "jy_fp")); VisMF::Write(warpx.getcurrent_fp(lev, 2), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "jz_fp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "jz_fp")); } if (lev > 0) { VisMF::Write(warpx.getEfield_cp(lev, 0), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Ex_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Ex_cp")); VisMF::Write(warpx.getEfield_cp(lev, 1), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Ey_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Ey_cp")); VisMF::Write(warpx.getEfield_cp(lev, 2), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Ez_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Ez_cp")); VisMF::Write(warpx.getBfield_cp(lev, 0), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Bx_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Bx_cp")); VisMF::Write(warpx.getBfield_cp(lev, 1), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "By_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "By_cp")); VisMF::Write(warpx.getBfield_cp(lev, 2), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "Bz_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "Bz_cp")); if (warpx.getis_synchronized()) { // Need to save j if synchronized because after restart we need j to evolve E by dt/2. VisMF::Write(warpx.getcurrent_cp(lev, 0), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "jx_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "jx_cp")); VisMF::Write(warpx.getcurrent_cp(lev, 1), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "jy_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "jy_cp")); VisMF::Write(warpx.getcurrent_cp(lev, 2), - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "jz_cp")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "jz_cp")); } } if (warpx.DoPML() && warpx.GetPML(lev)) { warpx.GetPML(lev)->CheckPoint( - amrex::MultiFabFileFullPrefix(lev, checkpointname, level_prefix, "pml")); + amrex::MultiFabFileFullPrefix(lev, checkpointname, default_level_prefix, "pml")); } } diff --git a/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.H b/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.H index 5d6197d6694..baf8728dcf2 100644 --- a/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.H +++ b/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.H @@ -22,7 +22,7 @@ public: const amrex::Vector& mf, amrex::Vector& geom, const amrex::Vector iteration, const double time, - const amrex::Vector& particle_diags, int nlev, const std::string prefix, + const amrex::Vector& particle_diags, int /*nlev*/, const std::string prefix, bool plot_raw_fields, bool plot_raw_fields_guards, bool plot_raw_rho, bool plot_raw_F) const override final; diff --git a/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp b/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp index 9f9f2b96a22..20e3bb311cd 100644 --- a/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp +++ b/Source/Diagnostics/FlushFormats/FlushFormatOpenPMD.cpp @@ -46,6 +46,9 @@ FlushFormatOpenPMD::WriteToFile ( // particles: all (reside only on locally finest level) m_OpenPMDPlotWriter->WriteOpenPMDParticles(particle_diags); + + // signal that no further updates will be written to this iteration + m_OpenPMDPlotWriter->CloseStep(); } FlushFormatOpenPMD::~FlushFormatOpenPMD (){ diff --git a/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp b/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp index 02104edd0ae..72743131a75 100644 --- a/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp +++ b/Source/Diagnostics/FlushFormats/FlushFormatPlotfile.cpp @@ -9,7 +9,7 @@ using namespace amrex; namespace { - const std::string level_prefix {"Level_"}; + const std::string default_level_prefix {"Level_"}; } void @@ -306,17 +306,20 @@ FlushFormatPlotfile::WriteParticles(const std::string& dir, UniformFilter const uniform_filter(particle_diags[i].m_do_uniform_filter, particle_diags[i].m_uniform_stride); ParserFilter const parser_filter(particle_diags[i].m_do_parser_filter, - particle_diags[i].m_particle_filter_parser.get()); - + getParser(particle_diags[i].m_particle_filter_parser)); + GeometryFilter const geometry_filter(particle_diags[i].m_do_geom_filter, + particle_diags[i].m_diag_domain); // real_names contains a list of all particle attributes. // particle_diags[i].plot_flags is 1 or 0, whether quantity is dumped or not. pc->WritePlotFile( dir, particle_diags[i].getSpeciesName(), particle_diags[i].plot_flags, int_flags, real_names, int_names, - [=] AMREX_GPU_HOST_DEVICE (const SuperParticleType& p) + [=] AMREX_GPU_HOST_DEVICE (const SuperParticleType& p, + const amrex::RandomEngine& engine) { - return random_filter(p) * uniform_filter(p) * parser_filter(p); + return random_filter(p,engine) * uniform_filter(p,engine) + * parser_filter(p,engine) * geometry_filter(p,engine); }); // Convert momentum back to WarpX units @@ -458,54 +461,76 @@ FlushFormatPlotfile::WriteAllRawFields( // Auxiliary patch - WriteRawMF( warpx.getEfield(lev, 0), dm, raw_pltname, level_prefix, "Ex_aux", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getEfield(lev, 1), dm, raw_pltname, level_prefix, "Ey_aux", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getEfield(lev, 2), dm, raw_pltname, level_prefix, "Ez_aux", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getBfield(lev, 0), dm, raw_pltname, level_prefix, "Bx_aux", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getBfield(lev, 1), dm, raw_pltname, level_prefix, "By_aux", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getBfield(lev, 2), dm, raw_pltname, level_prefix, "Bz_aux", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getEfield(lev, 0), dm, raw_pltname, default_level_prefix, "Ex_aux", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getEfield(lev, 1), dm, raw_pltname, default_level_prefix, "Ey_aux", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getEfield(lev, 2), dm, raw_pltname, default_level_prefix, "Ez_aux", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getBfield(lev, 0), dm, raw_pltname, default_level_prefix, "Bx_aux", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getBfield(lev, 1), dm, raw_pltname, default_level_prefix, "By_aux", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getBfield(lev, 2), dm, raw_pltname, default_level_prefix, "Bz_aux", lev, plot_raw_fields_guards); // fine patch - WriteRawMF( warpx.getEfield_fp(lev, 0), dm, raw_pltname, level_prefix, "Ex_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getEfield_fp(lev, 1), dm, raw_pltname, level_prefix, "Ey_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getEfield_fp(lev, 2), dm, raw_pltname, level_prefix, "Ez_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getcurrent_fp(lev, 0), dm, raw_pltname, level_prefix, "jx_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getcurrent_fp(lev, 1), dm, raw_pltname, level_prefix, "jy_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getcurrent_fp(lev, 2), dm, raw_pltname, level_prefix, "jz_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getBfield_fp(lev, 0), dm, raw_pltname, level_prefix, "Bx_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getBfield_fp(lev, 1), dm, raw_pltname, level_prefix, "By_fp", lev, plot_raw_fields_guards); - WriteRawMF( warpx.getBfield_fp(lev, 2), dm, raw_pltname, level_prefix, "Bz_fp", lev, plot_raw_fields_guards); - if (plot_raw_F) WriteRawMF( warpx.getF_fp(lev), dm, raw_pltname, level_prefix, "F_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getEfield_fp(lev, 0), dm, raw_pltname, default_level_prefix, "Ex_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getEfield_fp(lev, 1), dm, raw_pltname, default_level_prefix, "Ey_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getEfield_fp(lev, 2), dm, raw_pltname, default_level_prefix, "Ez_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getcurrent_fp(lev, 0), dm, raw_pltname, default_level_prefix, "jx_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getcurrent_fp(lev, 1), dm, raw_pltname, default_level_prefix, "jy_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getcurrent_fp(lev, 2), dm, raw_pltname, default_level_prefix, "jz_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getBfield_fp(lev, 0), dm, raw_pltname, default_level_prefix, "Bx_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getBfield_fp(lev, 1), dm, raw_pltname, default_level_prefix, "By_fp", lev, plot_raw_fields_guards); + WriteRawMF( warpx.getBfield_fp(lev, 2), dm, raw_pltname, default_level_prefix, "Bz_fp", lev, plot_raw_fields_guards); + if (plot_raw_F) { + if (warpx.get_pointer_F_fp(lev) == nullptr) { + amrex::Warning("The user requested to write raw F data, but F_fp was not allocated"); + } else { + WriteRawMF(warpx.getF_fp(lev), dm, raw_pltname, default_level_prefix, "F_fp", lev, plot_raw_fields_guards); + } + } if (plot_raw_rho) { - // Use the component 1 of `rho_fp`, i.e. rho_new for time synchronization - // If nComp > 1, this is the upper half of the list of components. - MultiFab rho_new(warpx.getF_fp(lev), amrex::make_alias, warpx.getF_fp(lev).nComp()/2, warpx.getF_fp(lev).nComp()/2); - WriteRawMF( rho_new, dm, raw_pltname, level_prefix, "rho_fp", lev, plot_raw_fields_guards); + if (warpx.get_pointer_rho_fp(lev) == nullptr) { + amrex::Warning("The user requested to write raw rho data, but rho_fp was not allocated"); + } else { + // Use the component 1 of `rho_fp`, i.e. rho_new for time synchronization + // If nComp > 1, this is the upper half of the list of components. + MultiFab rho_new(warpx.getrho_fp(lev), amrex::make_alias, warpx.getrho_fp(lev).nComp()/2, warpx.getrho_fp(lev).nComp()/2); + WriteRawMF(rho_new, dm, raw_pltname, default_level_prefix, "rho_fp", lev, plot_raw_fields_guards); + } } // Coarse path - if (lev > 0){ + if (lev > 0) { WriteCoarseVector( "E", warpx.get_pointer_Efield_cp(lev, 0), warpx.get_pointer_Efield_cp(lev, 1), warpx.get_pointer_Efield_cp(lev, 2), warpx.get_pointer_Efield_fp(lev, 0), warpx.get_pointer_Efield_fp(lev, 1), warpx.get_pointer_Efield_fp(lev, 2), - dm, raw_pltname, level_prefix, lev, plot_raw_fields_guards); + dm, raw_pltname, default_level_prefix, lev, plot_raw_fields_guards); WriteCoarseVector( "B", warpx.get_pointer_Bfield_cp(lev, 0), warpx.get_pointer_Bfield_cp(lev, 1), warpx.get_pointer_Bfield_cp(lev, 2), warpx.get_pointer_Bfield_fp(lev, 0), warpx.get_pointer_Bfield_fp(lev, 1), warpx.get_pointer_Bfield_fp(lev, 2), - dm, raw_pltname, level_prefix, lev, plot_raw_fields_guards); + dm, raw_pltname, default_level_prefix, lev, plot_raw_fields_guards); WriteCoarseVector( "j", warpx.get_pointer_current_cp(lev, 0), warpx.get_pointer_current_cp(lev, 1), warpx.get_pointer_current_cp(lev, 2), warpx.get_pointer_current_fp(lev, 0), warpx.get_pointer_current_fp(lev, 1), warpx.get_pointer_current_fp(lev, 2), - dm, raw_pltname, level_prefix, lev, plot_raw_fields_guards); + dm, raw_pltname, default_level_prefix, lev, plot_raw_fields_guards); + if (plot_raw_F) { + if (warpx.get_pointer_F_fp(lev) == nullptr) { + amrex::Warning("The user requested to write raw F data, but F_fp was not allocated"); + } else if (warpx.get_pointer_F_cp(lev) == nullptr) { + amrex::Warning("The user requested to write raw F data, but F_cp was not allocated"); + } else { + WriteCoarseScalar("F", warpx.get_pointer_F_cp(lev), warpx.get_pointer_F_fp(lev), + dm, raw_pltname, default_level_prefix, lev, plot_raw_fields_guards, 0); + } + } + if (plot_raw_rho) { + if (warpx.get_pointer_rho_fp(lev) == nullptr) { + amrex::Warning("The user requested to write raw rho data, but rho_fp was not allocated"); + } else if (warpx.get_pointer_rho_cp(lev) == nullptr) { + amrex::Warning("The user requested to write raw rho data, but rho_cp was not allocated"); + } else { + // Use the component 1 of `rho_cp`, i.e. rho_new for time synchronization + WriteCoarseScalar("rho", warpx.get_pointer_rho_cp(lev), warpx.get_pointer_rho_fp(lev), + dm, raw_pltname, default_level_prefix, lev, plot_raw_fields_guards, 1); + } + } } - if (plot_raw_F) WriteCoarseScalar( - "F", warpx.get_pointer_F_cp(lev), warpx.get_pointer_F_fp(lev), - dm, raw_pltname, level_prefix, lev, - plot_raw_fields_guards, 0); - if (plot_raw_rho) WriteCoarseScalar( - "rho", warpx.get_pointer_rho_cp(lev), warpx.get_pointer_rho_fp(lev), - dm, raw_pltname, level_prefix, lev, - plot_raw_fields_guards, 1); - // Use the component 1 of `rho_cp`, i.e. rho_new for time synchronization } } diff --git a/Source/Diagnostics/FlushFormats/FlushFormatSensei.H b/Source/Diagnostics/FlushFormats/FlushFormatSensei.H index 1cf652e6321..eed95371e2f 100644 --- a/Source/Diagnostics/FlushFormats/FlushFormatSensei.H +++ b/Source/Diagnostics/FlushFormats/FlushFormatSensei.H @@ -61,10 +61,10 @@ public: void WriteParticles(const amrex::Vector& particle_diags) const; private: - amrex::AmrMesh * m_amr_mesh; - amrex::AmrMeshInSituBridge * m_insitu_bridge; std::string m_insitu_config; - int m_insitu_pin_mesh; + int m_insitu_pin_mesh = 0; + amrex::AmrMeshInSituBridge * m_insitu_bridge = nullptr; + amrex::AmrMesh * m_amr_mesh = nullptr; }; #endif // WARPX_FLUSHFORMATSENSEI_H_ diff --git a/Source/Diagnostics/FullDiagnostics.H b/Source/Diagnostics/FullDiagnostics.H index 4fdabf9e4d1..ca2d6f4f8d8 100644 --- a/Source/Diagnostics/FullDiagnostics.H +++ b/Source/Diagnostics/FullDiagnostics.H @@ -64,7 +64,10 @@ private: void InitializeParticleBuffer () override; /** Prepare field data to be used for diagnostics */ void PrepareFieldDataForOutput () override; - + /** Update the physical extent of the diagnostic domain for moving window and + * galilean shift simulations + */ + void MovingWindowAndGalileanDomainShift () override; }; #endif // WARPX_FULLDIAGNOSTICS_H_ diff --git a/Source/Diagnostics/FullDiagnostics.cpp b/Source/Diagnostics/FullDiagnostics.cpp index cbbc89e5fa4..d302755df2e 100644 --- a/Source/Diagnostics/FullDiagnostics.cpp +++ b/Source/Diagnostics/FullDiagnostics.cpp @@ -14,11 +14,11 @@ #ifdef WARPX_USE_OPENPMD # include "FlushFormats/FlushFormatOpenPMD.H" #endif +#include #include #include using namespace amrex::literals; - FullDiagnostics::FullDiagnostics (int i, std::string name) : Diagnostics(i, name) { @@ -35,12 +35,11 @@ FullDiagnostics::InitializeParticleBuffer () const MultiParticleContainer& mpc = warpx.GetPartContainer(); // If not specified, dump all species - if (m_species_names.size() == 0) m_species_names = mpc.GetSpeciesNames(); + if (m_output_species_names.size() == 0) m_output_species_names = mpc.GetSpeciesNames(); // Initialize one ParticleDiag per species requested - for (auto const& species : m_species_names){ + for (auto const& species : m_output_species_names){ const int idx = mpc.getSpeciesID(species); - m_all_species.push_back(ParticleDiag(m_diag_name, species, - mpc.GetParticleContainerPtr(idx))); + m_output_species.push_back(ParticleDiag(m_diag_name, species, mpc.GetParticleContainerPtr(idx))); } } @@ -55,14 +54,17 @@ FullDiagnostics::ReadParameters () m_format == "checkpoint" || m_format == "ascent" || m_format == "sensei", ".format must be plotfile or openpmd or checkpoint or ascent or sensei"); - std::string period_string = "0"; - pp.query("period", period_string); - m_intervals = IntervalsParser(period_string); + std::vector period_string_vec = {"0"}; + pp.queryarr("period", period_string_vec); + m_intervals = IntervalsParser(period_string_vec); bool raw_specified = pp.query("plot_raw_fields", m_plot_raw_fields); raw_specified += pp.query("plot_raw_fields_guards", m_plot_raw_fields_guards); + raw_specified += pp.query("plot_raw_rho", m_plot_raw_rho); #ifdef WARPX_DIM_RZ pp.query("dump_rz_modes", m_dump_rz_modes); +#else + amrex::ignore_unused(m_dump_rz_modes); #endif if (m_format == "checkpoint"){ @@ -83,9 +85,10 @@ FullDiagnostics::Flush ( int i_buffer ) // This function should be moved to Diagnostics when plotfiles/openpmd format // is supported for BackTransformed Diagnostics, in BTDiagnostics class. auto & warpx = WarpX::GetInstance(); + m_flush_format->WriteToFile( - m_varnames, m_mf_output[i_buffer], warpx.Geom(), warpx.getistep(), - warpx.gett_new(0), m_all_species, nlev_output, m_file_prefix, + m_varnames, m_mf_output[i_buffer], m_geom_output[i_buffer], warpx.getistep(), + warpx.gett_new(0), m_output_species, nlev_output, m_file_prefix, m_plot_raw_fields, m_plot_raw_fields_guards, m_plot_raw_rho, m_plot_raw_F); FlushRaw(); @@ -96,7 +99,7 @@ FullDiagnostics::FlushRaw () {} bool -FullDiagnostics::DoDump (int step, int i_buffer, bool force_flush) +FullDiagnostics::DoDump (int step, int /*i_buffer*/, bool force_flush) { if (m_already_done) return false; if ( force_flush || (m_intervals.contains(step+1)) ){ @@ -214,6 +217,8 @@ FullDiagnostics::AddRZModesToDiags (int lev) ncomp_from_src += m_all_field_functors[lev][i]->nComp(); } AMREX_ALWAYS_ASSERT( ncomp_from_src == m_varnames.size() ); +#else + amrex::ignore_unused(lev); #endif } @@ -227,6 +232,8 @@ FullDiagnostics::AddRZModesToOutputNames (const std::string& field, int ncomp){ m_varnames.push_back( field + "_" + std::to_string(ic) + "_real" ); m_varnames.push_back( field + "_" + std::to_string(ic) + "_imag" ); } +#else + amrex::ignore_unused(field, ncomp); #endif } @@ -241,13 +248,11 @@ FullDiagnostics::InitializeFieldBufferData (int i_buffer, int lev ) { // Default BoxArray and DistributionMap for initializing the output MultiFab, m_mf_output. amrex::BoxArray ba = warpx.boxArray(lev); amrex::DistributionMapping dmap = warpx.DistributionMap(lev); - // Check if warpx BoxArray is coarsenable. AMREX_ALWAYS_ASSERT_WITH_MESSAGE ( ba.coarsenable(m_crse_ratio), "Invalid coarsening ratio for warpx boxArray. Must be an integer divisor of the blocking factor." ); - // Find if user-defined physical dimensions are different from the simulation domain. for (int idim=0; idim < AMREX_SPACEDIM; ++idim) { // To ensure that the diagnostic lo and hi are within the domain defined at level, lev. @@ -303,10 +308,10 @@ FullDiagnostics::InitializeFieldBufferData (int i_buffer, int lev ) { // Update the physical co-ordinates m_lo and m_hi using the final index values // from the coarsenable, cell-centered BoxArray, ba. for ( int idim = 0; idim < AMREX_SPACEDIM; ++idim) { - m_lo[idim] = warpx.Geom(lev).ProbLo(idim) + warpx.Geom(lev).CellSize(idim)/2.0_rt + - ba.getCellCenteredBox(0).smallEnd(idim) * warpx.Geom(lev).CellSize(idim); - m_hi[idim] = warpx.Geom(lev).ProbLo(idim) + warpx.Geom(lev).CellSize(idim)/2.0_rt + - ba.getCellCenteredBox( ba.size()-1 ).bigEnd(idim) * warpx.Geom(lev).CellSize(idim); + diag_dom.setLo( idim, warpx.Geom(lev).ProbLo(idim) + + ba.getCellCenteredBox(0).smallEnd(idim) * warpx.Geom(lev).CellSize(idim)); + diag_dom.setHi( idim, warpx.Geom(lev).ProbLo(idim) + + (ba.getCellCenteredBox( ba.size()-1 ).bigEnd(idim) + 1) * warpx.Geom(lev).CellSize(idim)); } } @@ -322,6 +327,22 @@ FullDiagnostics::InitializeFieldBufferData (int i_buffer, int lev ) { // The zero is hard-coded since the number of output buffers = 1 for FullDiagnostics m_mf_output[i_buffer][lev] = amrex::MultiFab(ba, dmap, m_varnames.size(), ngrow); + + if (lev == 0) { + // The extent of the domain covered by the diag multifab, m_mf_output + //default non-periodic geometry for diags + amrex::Vector diag_periodicity(AMREX_SPACEDIM,0); + // Box covering the extent of the user-defined diagnostic domain + amrex::Box domain = ba.minimalBox(); + // define geom object + m_geom_output[i_buffer][lev].define( domain, &diag_dom, + amrex::CoordSys::cartesian, + diag_periodicity.data() ); + } else if (lev > 0) { + // Take the geom object of previous level and refine it. + m_geom_output[i_buffer][lev] = amrex::refine( m_geom_output[i_buffer][lev-1], + warpx.RefRatio(lev-1) ); + } } @@ -329,9 +350,13 @@ void FullDiagnostics::InitializeFieldFunctors (int lev) { auto & warpx = WarpX::GetInstance(); + // Clear any pre-existing vector to release stored data. m_all_field_functors[lev].clear(); + // Species index to loop over species that dump rho per species + int i = 0; + m_all_field_functors[lev].resize( m_varnames.size() ); // Fill vector of functors for all components except individual cylindrical modes. for (int comp=0, n=m_all_field_functors[lev].size(); comp(lev, m_crse_ratio); } + } else if ( m_varnames[comp].rfind("rho_", 0) == 0 ){ + // Initialize rho functor to dump rho per species + m_all_field_functors[lev][comp] = std::make_unique(lev, m_crse_ratio, m_rho_per_species_index[i]); + i++; } else if ( m_varnames[comp] == "F" ){ m_all_field_functors[lev][comp] = std::make_unique(warpx.get_pointer_F_fp(lev), lev, m_crse_ratio); } else if ( m_varnames[comp] == "part_per_cell" ){ @@ -377,6 +407,9 @@ FullDiagnostics::InitializeFieldFunctors (int lev) } else if ( m_varnames[comp] == "divE" ){ m_all_field_functors[lev][comp] = std::make_unique(warpx.get_array_Efield_aux(lev), lev, m_crse_ratio); } + else { + amrex::Abort("Error: " + m_varnames[comp] + " is not a known field output type"); + } } AddRZModesToDiags( lev ); } @@ -394,4 +427,65 @@ FullDiagnostics::PrepareFieldDataForOutput () warpx.FillBoundaryAux(warpx.getngUpdateAux()); #endif warpx.UpdateAuxilaryData(); + + // Update the RealBox used for the geometry filter in particle diags + for (int i = 0; i < m_output_species.size(); ++i) { + m_output_species[i].m_diag_domain = m_geom_output[0][0].ProbDomain(); + } +} + +void +FullDiagnostics::MovingWindowAndGalileanDomainShift () +{ + auto & warpx = WarpX::GetInstance(); + + // Account for galilean shift + amrex::Real new_lo[AMREX_SPACEDIM]; + amrex::Real new_hi[AMREX_SPACEDIM]; + const amrex::Real* current_lo = m_geom_output[0][0].ProbLo(); + const amrex::Real* current_hi = m_geom_output[0][0].ProbHi(); + +#if (AMREX_SPACEDIM == 3 ) + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + new_lo[idim] = current_lo[idim] + warpx.m_galilean_shift[idim]; + new_hi[idim] = current_hi[idim] + warpx.m_galilean_shift[idim]; + } +#elif (AMREX_SPACEDIM == 2 ) + { + new_lo[0] = current_lo[0] + warpx.m_galilean_shift[0]; + new_hi[0] = current_hi[0] + warpx.m_galilean_shift[0]; + new_lo[1] = current_lo[1] + warpx.m_galilean_shift[2]; + new_hi[1] = current_hi[1] + warpx.m_galilean_shift[2]; + } +#endif + // Update RealBox of geometry with galilean-shifted boundary + for (int lev = 0; lev < nmax_lev; ++lev) { + m_geom_output[0][lev].ProbDomain( amrex::RealBox(new_lo, new_hi) ); + } + + // For Moving Window Shift + if (warpx.do_moving_window) { + int moving_dir = warpx.moving_window_dir; + amrex::Real moving_window_x = warpx.getmoving_window_x(); + // Get the updated lo and hi of the geom domain + const amrex::Real* cur_lo = m_geom_output[0][0].ProbLo(); + const amrex::Real* cur_hi = m_geom_output[0][0].ProbHi(); + const amrex::Real* geom_dx = m_geom_output[0][0].CellSize(); + int num_shift_base = static_cast((moving_window_x - cur_lo[moving_dir]) + / geom_dx[moving_dir]); + // Update the diagnostic geom domain. Note that this is done only for the + // base level 0 because m_geom_output[0][lev] share the same static RealBox + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + new_lo[idim] = cur_lo[idim]; + new_hi[idim] = cur_hi[idim]; + } + new_lo[moving_dir] = cur_lo[moving_dir] + num_shift_base*geom_dx[moving_dir]; + new_hi[moving_dir] = cur_hi[moving_dir] + num_shift_base*geom_dx[moving_dir]; + // Update RealBox of geometry with shifted domain geometry for moving-window + for (int lev = 0; lev < nmax_lev; ++lev) { + m_geom_output[0][lev].ProbDomain( amrex::RealBox(new_lo, new_hi) ); + } + } + + } diff --git a/Source/Diagnostics/MultiDiagnostics.cpp b/Source/Diagnostics/MultiDiagnostics.cpp index e67586d17d2..d9300187ee8 100644 --- a/Source/Diagnostics/MultiDiagnostics.cpp +++ b/Source/Diagnostics/MultiDiagnostics.cpp @@ -43,9 +43,13 @@ MultiDiagnostics::ReadParameters () { ParmParse pp("diagnostics"); - pp.queryarr("diags_names", diags_names); - ndiags = diags_names.size(); - Print()<<"ndiags "<> m_particle_filter_parser; + amrex::RealBox m_diag_domain; private: std::string m_diag_name; diff --git a/Source/Diagnostics/ParticleDiag/ParticleDiag.cpp b/Source/Diagnostics/ParticleDiag/ParticleDiag.cpp index b06817f91f4..9a9fe205587 100644 --- a/Source/Diagnostics/ParticleDiag/ParticleDiag.cpp +++ b/Source/Diagnostics/ParticleDiag/ParticleDiag.cpp @@ -32,19 +32,25 @@ ParticleDiag::ParticleDiag(std::string diag_name, std::string name, WarpXParticl // If not none, set plot_flags values to 1 for elements in variables. if (variables[0] != "none"){ for (const auto& var : variables){ - // Return error if var not in PIdx. - WarpXUtilMsg::AlwaysAssert( - ParticleStringNames::to_index.count(var), - "ERROR: variables argument '" + var + - "' not in ParticleStringNames" - ); - plot_flags[ParticleStringNames::to_index.at(var)] = 1; + // The string "rho" is needed to dump rho per species, which is generated + // on the fly from existing species variables. Hence, "rho" is not part + // of the species' PIdx variables. + if (var != "rho") { + // Return error if var not in PIdx. + WarpXUtilMsg::AlwaysAssert( + ParticleStringNames::to_index.count(var), + "ERROR: variables argument '" + var + + "' not in ParticleStringNames" + ); + plot_flags[ParticleStringNames::to_index.at(var)] = 1; + } } } #ifdef WARPX_DIM_RZ // Always write out theta, whether or not it's requested, // to be consistent with always writing out r and z. + // TODO: openPMD does a reconstruction to Cartesian, so we can now skip force-writing this plot_flags[ParticleStringNames::to_index.at("theta")] = 1; #endif diff --git a/Source/Diagnostics/ParticleIO.cpp b/Source/Diagnostics/ParticleIO.cpp index 1dfc588e9f0..dacb159406b 100644 --- a/Source/Diagnostics/ParticleIO.cpp +++ b/Source/Diagnostics/ParticleIO.cpp @@ -74,14 +74,6 @@ WarpXParticleContainer::WriteHeader (std::ostream& os) const os << charge << " " << mass << "\n"; } -void -MultiParticleContainer::Checkpoint (const std::string& dir) const -{ - for (unsigned i = 0, n = species_names.size(); i < n; ++i) { - allcontainers[i]->Checkpoint(dir, species_names[i]); - } -} - void MultiParticleContainer::Restart (const std::string& dir) { @@ -116,7 +108,7 @@ MultiParticleContainer::WriteHeader (std::ostream& os) const void PhysicalParticleContainer::ConvertUnits(ConvertDirection convert_direction) { - WARPX_PROFILE("PPC::ConvertUnits()"); + WARPX_PROFILE("PhysicalParticleContainer::ConvertUnits()"); // Compute conversion factor auto factor = 1_rt; diff --git a/Source/Diagnostics/ReducedDiags/BeamRelevant.H b/Source/Diagnostics/ReducedDiags/BeamRelevant.H index fa733ae6615..bf775f25006 100644 --- a/Source/Diagnostics/ReducedDiags/BeamRelevant.H +++ b/Source/Diagnostics/ReducedDiags/BeamRelevant.H @@ -25,7 +25,7 @@ public: /// name of beam species std::string m_beam_name; - /** This funciton computes beam relevant quantites. + /** This function computes beam relevant quantites. * \param [in] step current time step */ virtual void ComputeDiags(int step) override final; diff --git a/Source/Diagnostics/ReducedDiags/BeamRelevant.cpp b/Source/Diagnostics/ReducedDiags/BeamRelevant.cpp index 809f6e6bdac..178ee239ffb 100644 --- a/Source/Diagnostics/ReducedDiags/BeamRelevant.cpp +++ b/Source/Diagnostics/ReducedDiags/BeamRelevant.cpp @@ -23,18 +23,12 @@ BeamRelevant::BeamRelevant (std::string rd_name) : ReducedDiags{rd_name} { -#if (defined WARPX_DIM_RZ) - // RZ coordinate is not working - AMREX_ALWAYS_ASSERT_WITH_MESSAGE(false, - "BeamRelevant reduced diagnostics does not work for RZ coordinate."); -#endif - // read beam name ParmParse pp(rd_name); pp.get("species",m_beam_name); // resize data array -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D || defined WARPX_DIM_RZ) // 0, 1, 2: mean x,y,z // 3, 4, 5: mean px,py,pz // 6: gamma @@ -44,7 +38,7 @@ BeamRelevant::BeamRelevant (std::string rd_name) // 14,15,16: emittance x,y,z // 17: charge m_data.resize(18,0.0); -#elif (AMREX_SPACEDIM == 2) +#elif (defined WARPX_DIM_XZ) // 0, 1: mean x,z // 2, 3, 4: mean px,py,pz // 5: gamma @@ -61,11 +55,10 @@ BeamRelevant::BeamRelevant (std::string rd_name) if ( m_IsNotRestart ) { // open file - std::ofstream ofs; - ofs.open(m_path + m_rd_name + "." + m_extension, - std::ofstream::out | std::ofstream::app); + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; // write header row -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D || defined WARPX_DIM_RZ) ofs << "#"; ofs << "[1]step()"; ofs << m_sep; ofs << "[2]time(s)"; ofs << m_sep; @@ -87,7 +80,7 @@ BeamRelevant::BeamRelevant (std::string rd_name) ofs << "[18]emittance_y(m)"; ofs << m_sep; ofs << "[19]emittance_z(m)"; ofs << m_sep; ofs << "[20]charge(C)"; ofs << std::endl; -#elif (AMREX_SPACEDIM == 2) +#elif (defined WARPX_DIM_XZ) ofs << "#"; ofs << "[1]step()"; ofs << m_sep; ofs << "[2]time(s)"; ofs << m_sep; @@ -120,10 +113,10 @@ void BeamRelevant::ComputeDiags (int step) { // Judge if the diags should be done - if ( (step+1) % m_freq != 0 ) { return; } + if (!m_intervals.contains(step+1)) { return; } // get MultiParticleContainer class object - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); // get number of species int const nSpecies = mypc.nSpecies(); @@ -135,9 +128,9 @@ void BeamRelevant::ComputeDiags (int step) Real constexpr inv_c2 = 1.0 / (PhysConst::c * PhysConst::c); // If 2D-XZ, p.pos(1) is z, rather than p.pos(2). -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D) int const index_z = 2; -#elif (AMREX_SPACEDIM == 2) +#elif (defined WARPX_DIM_XZ || defined WARPX_DIM_RZ) int const index_z = 1; #endif @@ -167,20 +160,34 @@ void BeamRelevant::ComputeDiags (int step) if (w_sum < std::numeric_limits::min() ) { - for (int i = 0; i < m_data.size(); ++i) { m_data[i] = 0.0; } + for (int i = 0; i < static_cast(m_data.size()); ++i){ + m_data[i] = 0.0; + } return; } +#if (defined WARPX_DIM_RZ) + // x mean + Real x_mean = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { return p.pos(0)*std::cos(p.rdata(PIdx::theta)) * p.rdata(PIdx::w); }); +#else // x mean Real x_mean = ReduceSum( myspc, [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real { return p.pos(0) * p.rdata(PIdx::w); }); +#endif -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D) // y mean Real y_mean = ReduceSum( myspc, [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real { return p.pos(1) * p.rdata(PIdx::w); }); +#elif (defined WARPX_DIM_RZ) + // y mean + Real y_mean = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { return p.pos(0)*std::sin(p.rdata(PIdx::theta)) * p.rdata(PIdx::w); }); #endif // z mean @@ -216,7 +223,7 @@ void BeamRelevant::ComputeDiags (int step) // reduced sum over mpi ranks ParallelDescriptor::ReduceRealSum(x_mean); x_mean /= w_sum; -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D || defined WARPX_DIM_RZ) ParallelDescriptor::ReduceRealSum(y_mean); y_mean /= w_sum; #endif ParallelDescriptor::ReduceRealSum(z_mean); z_mean /= w_sum; @@ -225,6 +232,16 @@ void BeamRelevant::ComputeDiags (int step) ParallelDescriptor::ReduceRealSum(uz_mean); uz_mean /= w_sum; ParallelDescriptor::ReduceRealSum(gm_mean); gm_mean /= w_sum; +#if (defined WARPX_DIM_RZ) + // x mean square + Real x_ms = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { + Real const x = p.pos(0)*std::cos(p.rdata(PIdx::theta)); + Real const a = (x-x_mean) * (x-x_mean); + return a * p.rdata(PIdx::w); + }); +#else // x mean square Real x_ms = ReduceSum( myspc, [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real @@ -232,8 +249,9 @@ void BeamRelevant::ComputeDiags (int step) Real const a = (p.pos(0)-x_mean) * (p.pos(0)-x_mean); return a * p.rdata(PIdx::w); }); +#endif -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D) // y mean square Real y_ms = ReduceSum( myspc, [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real @@ -241,6 +259,15 @@ void BeamRelevant::ComputeDiags (int step) Real const a = (p.pos(1)-y_mean) * (p.pos(1)-y_mean); return a * p.rdata(PIdx::w); }); +#elif (defined WARPX_DIM_RZ) + // y mean square + Real y_ms = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { + Real const y = p.pos(0)*std::sin(p.rdata(PIdx::theta)); + Real const a = (y-y_mean) * (y-y_mean); + return a * p.rdata(PIdx::w); + }); #endif // z mean square @@ -291,6 +318,16 @@ void BeamRelevant::ComputeDiags (int step) return a * p.rdata(PIdx::w); }); +#if (defined WARPX_DIM_RZ) + // x times ux + Real xux = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { + Real const x = p.pos(0)*std::cos(p.rdata(PIdx::theta)); + Real const a = (x-x_mean) * (p.rdata(PIdx::ux)-ux_mean); + return a * p.rdata(PIdx::w); + }); +#else // x times ux Real xux = ReduceSum( myspc, [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real @@ -298,8 +335,9 @@ void BeamRelevant::ComputeDiags (int step) Real const a = (p.pos(0)-x_mean) * (p.rdata(PIdx::ux)-ux_mean); return a * p.rdata(PIdx::w); }); +#endif -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D) // y times uy Real yuy = ReduceSum( myspc, [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real @@ -307,6 +345,15 @@ void BeamRelevant::ComputeDiags (int step) Real const a = (p.pos(1)-y_mean) * (p.rdata(PIdx::uy)-uy_mean); return a * p.rdata(PIdx::w); }); +#elif (defined WARPX_DIM_RZ) + // y times uy + Real yuy = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) -> Real + { + Real const y = p.pos(0)*std::sin(p.rdata(PIdx::theta)); + Real const a = (y-y_mean) * (p.rdata(PIdx::uy)-uy_mean); + return a * p.rdata(PIdx::w); + }); #endif // z times uz @@ -329,7 +376,7 @@ void BeamRelevant::ComputeDiags (int step) ParallelDescriptor::ReduceRealSum ( x_ms, ParallelDescriptor::IOProcessorNumber()); x_ms /= w_sum; -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D || defined WARPX_DIM_RZ) ParallelDescriptor::ReduceRealSum ( y_ms, ParallelDescriptor::IOProcessorNumber()); y_ms /= w_sum; @@ -352,7 +399,7 @@ void BeamRelevant::ComputeDiags (int step) ParallelDescriptor::ReduceRealSum ( xux, ParallelDescriptor::IOProcessorNumber()); xux /= w_sum; -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D || defined WARPX_DIM_RZ) ParallelDescriptor::ReduceRealSum ( yuy, ParallelDescriptor::IOProcessorNumber()); yuy /= w_sum; @@ -364,7 +411,7 @@ void BeamRelevant::ComputeDiags (int step) ( charge, ParallelDescriptor::IOProcessorNumber()); // save data -#if (AMREX_SPACEDIM == 3) +#if (defined WARPX_DIM_3D || defined WARPX_DIM_RZ) m_data[0] = x_mean; m_data[1] = y_mean; m_data[2] = z_mean; @@ -383,7 +430,7 @@ void BeamRelevant::ComputeDiags (int step) m_data[15] = std::sqrt(y_ms*uy_ms-yuy*yuy) / PhysConst::c; m_data[16] = std::sqrt(z_ms*uz_ms-zuz*zuz) / PhysConst::c; m_data[17] = charge; -#elif (AMREX_SPACEDIM == 2) +#elif (defined WARPX_DIM_XZ) m_data[0] = x_mean; m_data[1] = z_mean; m_data[2] = ux_mean * m; diff --git a/Source/Diagnostics/ReducedDiags/CMakeLists.txt b/Source/Diagnostics/ReducedDiags/CMakeLists.txt index 559cac5fb8b..4ae5ab6a290 100644 --- a/Source/Diagnostics/ReducedDiags/CMakeLists.txt +++ b/Source/Diagnostics/ReducedDiags/CMakeLists.txt @@ -7,4 +7,6 @@ target_sources(WarpX ParticleEnergy.cpp ParticleHistogram.cpp ReducedDiags.cpp + FieldMaximum.cpp + ParticleNumber.cpp ) diff --git a/Source/Diagnostics/ReducedDiags/FieldEnergy.H b/Source/Diagnostics/ReducedDiags/FieldEnergy.H index 82fa4b6c4eb..71398290c02 100644 --- a/Source/Diagnostics/ReducedDiags/FieldEnergy.H +++ b/Source/Diagnostics/ReducedDiags/FieldEnergy.H @@ -23,7 +23,7 @@ public: * @param[in] rd_name reduced diags names */ FieldEnergy(std::string rd_name); - /** This funciton computes the field energy (EF). + /** This function computes the field energy (EF). * EF = E eps / 2 + B / mu / 2, * where E is the electric field, * B is the magnetic field, diff --git a/Source/Diagnostics/ReducedDiags/FieldEnergy.cpp b/Source/Diagnostics/ReducedDiags/FieldEnergy.cpp index cefd55c8579..892ea9c7c21 100644 --- a/Source/Diagnostics/ReducedDiags/FieldEnergy.cpp +++ b/Source/Diagnostics/ReducedDiags/FieldEnergy.cpp @@ -35,32 +35,35 @@ FieldEnergy::FieldEnergy (std::string rd_name) pp.query("max_level", nLevel); nLevel += 1; + constexpr int noutputs = 3; // total energy, E-field energy and B-field energy // resize data array - m_data.resize(3*nLevel,0.0); + m_data.resize(noutputs*nLevel,0.0); if (ParallelDescriptor::IOProcessor()) { if ( m_IsNotRestart ) { // open file - std::ofstream ofs; - ofs.open(m_path + m_rd_name + "." + m_extension, - std::ofstream::out | std::ofstream::app); + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; // write header row ofs << "#"; ofs << "[1]step()"; ofs << m_sep; ofs << "[2]time(s)"; + constexpr int shift_total = 3; + constexpr int shift_E = 4; + constexpr int shift_B = 5; for (int lev = 0; lev < nLevel; ++lev) { ofs << m_sep; - ofs << "[" + std::to_string(3+3*lev) + "]"; + ofs << "[" + std::to_string(shift_total+noutputs*lev) + "]"; ofs << "total_lev"+std::to_string(lev)+"(J)"; ofs << m_sep; - ofs << "[" + std::to_string(4+3*lev) + "]"; + ofs << "[" + std::to_string(shift_E+noutputs*lev) + "]"; ofs << "E_lev"+std::to_string(lev)+"(J)"; ofs << m_sep; - ofs << "[" + std::to_string(5+3*lev) + "]"; + ofs << "[" + std::to_string(shift_B+noutputs*lev) + "]"; ofs << "B_lev"+std::to_string(lev)+"(J)"; } ofs << std::endl; @@ -77,13 +80,13 @@ void FieldEnergy::ComputeDiags (int step) { // Judge if the diags should be done - if ( (step+1) % m_freq != 0 ) { return; } + if (!m_intervals.contains(step+1)) { return; } // get WarpX class object auto & warpx = WarpX::GetInstance(); // get number of level - auto nLevel = warpx.finestLevel() + 1; + const auto nLevel = warpx.finestLevel() + 1; // loop over refinement levels for (int lev = 0; lev < nLevel; ++lev) @@ -99,7 +102,6 @@ void FieldEnergy::ComputeDiags (int step) // get cell size Geometry const & geom = warpx.Geom(lev); - auto domain_box = geom.Domain(); #if (AMREX_SPACEDIM == 2) auto dV = geom.CellSize(0) * geom.CellSize(1); #elif (AMREX_SPACEDIM == 3) @@ -107,21 +109,27 @@ void FieldEnergy::ComputeDiags (int step) #endif // compute E squared - Real tmpx = Ex.norm2(0,geom.periodicity()); - Real tmpy = Ey.norm2(0,geom.periodicity()); - Real tmpz = Ez.norm2(0,geom.periodicity()); - Real Es = tmpx*tmpx + tmpy*tmpy + tmpz*tmpz; + Real const tmpEx = Ex.norm2(0,geom.periodicity()); + Real const tmpEy = Ey.norm2(0,geom.periodicity()); + Real const tmpEz = Ez.norm2(0,geom.periodicity()); + Real const Es = tmpEx*tmpEx + tmpEy*tmpEy + tmpEz*tmpEz; // compute B squared - tmpx = Bx.norm2(0,geom.periodicity()); - tmpy = By.norm2(0,geom.periodicity()); - tmpz = Bz.norm2(0,geom.periodicity()); - Real Bs = tmpx*tmpx + tmpy*tmpy + tmpz*tmpz; + Real const tmpBx = Bx.norm2(0,geom.periodicity()); + Real const tmpBy = By.norm2(0,geom.periodicity()); + Real const tmpBz = Bz.norm2(0,geom.periodicity()); + Real const Bs = tmpBx*tmpBx + tmpBy*tmpBy + tmpBz*tmpBz; + + constexpr int noutputs = 3; // total energy, E-field energy and B-field energy + constexpr int index_total = 0; + constexpr int index_E = 1; + constexpr int index_B = 2; // save data - m_data[lev*3+1] = 0.5 * Es * PhysConst::ep0 * dV; - m_data[lev*3+2] = 0.5 * Bs / PhysConst::mu0 * dV; - m_data[lev*3+0] = m_data[lev*3+1] + m_data[lev*3+2]; + m_data[lev*noutputs+index_E] = 0.5_rt * Es * PhysConst::ep0 * dV; + m_data[lev*noutputs+index_B] = 0.5_rt * Bs / PhysConst::mu0 * dV; + m_data[lev*noutputs+index_total] = m_data[lev*noutputs+index_E] + + m_data[lev*noutputs+index_B]; } // end loop over refinement levels diff --git a/Source/Diagnostics/ReducedDiags/FieldMaximum.H b/Source/Diagnostics/ReducedDiags/FieldMaximum.H new file mode 100644 index 00000000000..31953e2801a --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/FieldMaximum.H @@ -0,0 +1,30 @@ +/* Copyright 2020 Neil Zaim, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_FIELDMAXIMUM_H_ +#define WARPX_DIAGNOSTICS_REDUCEDDIAGS_FIELDMAXIMUM_H_ + +#include "ReducedDiags.H" + +/** + * This class mainly contains a function that computes the maximum value of each component + * of the EM field and of the vector norm of the E and B fields. + */ +class FieldMaximum : public ReducedDiags +{ +public: + + /** constructor + * @param[in] rd_name reduced diags names */ + FieldMaximum(std::string rd_name); + + /** This function computes the maximum value of Ex, Ey, Ez, |E|, Bx, By, Bz and |B| */ + virtual void ComputeDiags(int step) override final; + +}; + +#endif // WARPX_DIAGNOSTICS_REDUCEDDIAGS_FIELDMAXIMUM_H_ diff --git a/Source/Diagnostics/ReducedDiags/FieldMaximum.cpp b/Source/Diagnostics/ReducedDiags/FieldMaximum.cpp new file mode 100644 index 00000000000..2fee0546cf3 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/FieldMaximum.cpp @@ -0,0 +1,251 @@ +/* Copyright 2020 Neil Zaim, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "FieldMaximum.H" +#include "WarpX.H" +#include "Utils/CoarsenIO.H" + +using namespace amrex; + +// constructor +FieldMaximum::FieldMaximum (std::string rd_name) +: ReducedDiags{rd_name} +{ + + // RZ coordinate is not working +#if (defined WARPX_DIM_RZ) + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(false, + "FieldMaximum reduced diagnostics does not work for RZ coordinate."); +#endif + + // read number of levels + int nLevel = 0; + ParmParse pp("amr"); + pp.query("max_level", nLevel); + nLevel += 1; + + constexpr int noutputs = 8; // total energy, E-field energy and B-field energy + // resize data array + m_data.resize(noutputs*nLevel,0.0); // max of Ex,Ey,Ez,|E|,Bx,By,Bz and |B| + + if (ParallelDescriptor::IOProcessor()) + { + if ( m_IsNotRestart ) + { + // open file + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; + // write header row + ofs << "#"; + ofs << "[1]step()"; + ofs << m_sep; + ofs << "[2]time(s)"; + constexpr int shift_Ex = 3; + constexpr int shift_Ey = 4; + constexpr int shift_Ez = 5; + constexpr int shift_absE = 6; + constexpr int shift_Bx = 7; + constexpr int shift_By = 8; + constexpr int shift_Bz = 9; + constexpr int shift_absB = 10; + for (int lev = 0; lev < nLevel; ++lev) + { + ofs << m_sep; + ofs << "[" + std::to_string(shift_Ex+noutputs*lev) + "]"; + ofs << "max_Ex_lev"+std::to_string(lev)+" (V/m)"; + ofs << m_sep; + ofs << "[" + std::to_string(shift_Ey+noutputs*lev) + "]"; + ofs << "max_Ey_lev"+std::to_string(lev)+" (V/m)"; + ofs << m_sep; + ofs << "[" + std::to_string(shift_Ez+noutputs*lev) + "]"; + ofs << "max_Ez_lev"+std::to_string(lev)+" (V/m)"; + ofs << m_sep; + ofs << "[" + std::to_string(shift_absE+noutputs*lev) + "]"; + ofs << "max_|E|_lev"+std::to_string(lev)+" (V/m)"; + ofs << m_sep; + ofs << "[" + std::to_string(shift_Bx+noutputs*lev) + "]"; + ofs << "max_Bx_lev"+std::to_string(lev)+" (T)"; + ofs << m_sep; + ofs << "[" + std::to_string(shift_By+noutputs*lev) + "]"; + ofs << "max_By_lev"+std::to_string(lev)+" (T)"; + ofs << m_sep; + ofs << "[" + std::to_string(shift_Bz+noutputs*lev) + "]"; + ofs << "max_Bz_lev"+std::to_string(lev)+" (T)"; + ofs << m_sep; + ofs << "[" + std::to_string(shift_absB+noutputs*lev) + "]"; + ofs << "max_|B|_lev"+std::to_string(lev)+" (T)"; + } + ofs << std::endl; + // close file + ofs.close(); + } + } + +} +// end constructor + +// function that computes maximum field values +void FieldMaximum::ComputeDiags (int step) +{ + // Judge if the diags should be done + if (!m_intervals.contains(step+1)) { return; } + + // get WarpX class object + auto & warpx = WarpX::GetInstance(); + + // get number of level + const auto nLevel = warpx.finestLevel() + 1; + + // loop over refinement levels + for (int lev = 0; lev < nLevel; ++lev) + { + + // get MultiFab data at lev + const MultiFab & Ex = warpx.getEfield(lev,0); + const MultiFab & Ey = warpx.getEfield(lev,1); + const MultiFab & Ez = warpx.getEfield(lev,2); + const MultiFab & Bx = warpx.getBfield(lev,0); + const MultiFab & By = warpx.getBfield(lev,1); + const MultiFab & Bz = warpx.getBfield(lev,2); + + constexpr int noutputs = 8; // max of Ex,Ey,Ez,|E|,Bx,By,Bz and |B| + constexpr int index_Ex = 0; + constexpr int index_Ey = 1; + constexpr int index_Ez = 2; + constexpr int index_absE = 3; + constexpr int index_Bx = 4; + constexpr int index_By = 5; + constexpr int index_Bz = 6; + constexpr int index_absB = 7; + + // get Maximums of E field components + m_data[lev*noutputs+index_Ex] = Ex.norm0(); + m_data[lev*noutputs+index_Ey] = Ey.norm0(); + m_data[lev*noutputs+index_Ez] = Ez.norm0(); + + // get Maximums of B field components + m_data[lev*noutputs+index_Bx] = Bx.norm0(); + m_data[lev*noutputs+index_By] = By.norm0(); + m_data[lev*noutputs+index_Bz] = Bz.norm0(); + + // General preparation of interpolation and reduction to compute the maximum value of + // |E| and |B| + const GpuArray cellCenteredtype{0,0,0}; + const GpuArray reduction_coarsening_ratio{1,1,1}; + constexpr int reduction_comp = 0; + + // Prepare reduction for maximum value of |E| + ReduceOps reduceE_op; + ReduceData reduceE_data(reduceE_op); + using ReduceTuple = typename decltype(reduceE_data)::Type; + + // Prepare interpolation of E field components to cell center + GpuArray Extype; + GpuArray Eytype; + GpuArray Eztype; + for (int i = 0; i < AMREX_SPACEDIM; ++i){ + Extype[i] = Ex.ixType()[i]; + Eytype[i] = Ey.ixType()[i]; + Eztype[i] = Ez.ixType()[i]; + } +#if (AMREX_SPACEDIM == 2) + Extype[2] = 0; + Eytype[2] = 0; + Eztype[2] = 0; +#endif + + // MFIter loop to interpolate E field components to cell center and get maximum value + // of |E| +#ifdef _OPENMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for ( MFIter mfi(Ex, TilingIfNotGPU()); mfi.isValid(); ++mfi ) + { + // Make the box cell centered to avoid including ghost cells in the calculation + const Box& box = enclosedCells(mfi.nodaltilebox()); + const auto& arrEx = Ex[mfi].array(); + const auto& arrEy = Ey[mfi].array(); + const auto& arrEz = Ez[mfi].array(); + reduceE_op.eval(box, reduceE_data, + [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple + { + const Real Ex_interp = CoarsenIO::Interp(arrEx, Extype, cellCenteredtype, + reduction_coarsening_ratio, i, j, k, reduction_comp); + const Real Ey_interp = CoarsenIO::Interp(arrEy, Eytype, cellCenteredtype, + reduction_coarsening_ratio, i, j, k, reduction_comp); + const Real Ez_interp = CoarsenIO::Interp(arrEz, Eztype, cellCenteredtype, + reduction_coarsening_ratio, i, j, k, reduction_comp); + return Ex_interp*Ex_interp + Ey_interp*Ey_interp + Ez_interp*Ez_interp; + }); + } + + Real hv_E = amrex::get<0>(reduceE_data.value()); // highest value of |E|**2 + // MPI reduce + ParallelDescriptor::ReduceRealMax(hv_E); + + m_data[lev*noutputs+index_absE] = std::sqrt(hv_E); + + // Prepare reduction for maximum value of |B| + ReduceOps reduceB_op; + ReduceData reduceB_data(reduceB_op); + using ReduceTuple = typename decltype(reduceB_data)::Type; + + // Prepare interpolation of B field components to cell center + GpuArray Bxtype; + GpuArray Bytype; + GpuArray Bztype; + for (int i = 0; i < AMREX_SPACEDIM; ++i){ + Bxtype[i] = Bx.ixType()[i]; + Bytype[i] = By.ixType()[i]; + Bztype[i] = Bz.ixType()[i]; + } +#if (AMREX_SPACEDIM == 2) + Bxtype[2] = 0; + Bytype[2] = 0; + Bztype[2] = 0; +#endif + + // MFIter loop to interpolate B field components to cell center and get maximum value + // of |B| +#ifdef _OPENMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for ( MFIter mfi(Ex, TilingIfNotGPU()); mfi.isValid(); ++mfi ) + { + // Make the box cell centered to avoid including ghost cells in the calculation + const Box& box = enclosedCells(mfi.nodaltilebox()); + const auto& arrBx = Bx[mfi].array(); + const auto& arrBy = By[mfi].array(); + const auto& arrBz = Bz[mfi].array(); + + reduceB_op.eval(box, reduceB_data, + [=] AMREX_GPU_DEVICE (int i, int j, int k) -> ReduceTuple + { + const Real Bx_interp = CoarsenIO::Interp(arrBx, Bxtype, cellCenteredtype, + reduction_coarsening_ratio, i, j, k, reduction_comp); + const Real By_interp = CoarsenIO::Interp(arrBy, Bytype, cellCenteredtype, + reduction_coarsening_ratio, i, j, k, reduction_comp); + const Real Bz_interp = CoarsenIO::Interp(arrBz, Bztype, cellCenteredtype, + reduction_coarsening_ratio, i, j, k, reduction_comp); + return Bx_interp*Bx_interp + By_interp*By_interp + Bz_interp*Bz_interp; + }); + } + + Real hv_B = amrex::get<0>(reduceB_data.value()); // highest value of |B|**2 + // MPI reduce + ParallelDescriptor::ReduceRealMax(hv_B); + + m_data[lev*noutputs+index_absB] = std::sqrt(hv_B); + } + // end loop over refinement levels + + /* m_data now contains up-to-date values for: + * [max(Ex),max(Ey),max(Ez),max(|E|), + * max(Bx),max(By),max(Bz),max(|B|)] */ + +} +// end void FieldMaximum::ComputeDiags diff --git a/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.H b/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.H index a70f16e5c4c..3aeb10ad3a3 100644 --- a/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.H +++ b/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.H @@ -51,7 +51,7 @@ public: * @param[in] rd_name reduced diags names */ LoadBalanceCosts(std::string rd_name); - /** This funciton updates the costs, given the current distribution mapping, + /** This function updates the costs, given the current distribution mapping, * according to the number of particles and cells on the box */ virtual void ComputeDiags(int step) override final; diff --git a/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.cpp b/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.cpp index 3d6932eac97..671355188c4 100644 --- a/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.cpp +++ b/Source/Diagnostics/ReducedDiags/LoadBalanceCosts.cpp @@ -29,7 +29,7 @@ void LoadBalanceCosts::ComputeDiags (int step) // judge if the diags should be done // costs is initialized only if we're doing load balance - if ( ((step+1) % m_freq != 0) || + if (!m_intervals.contains(step+1) || !warpx.get_load_balance_intervals().isActivated() ) { return; } // get number of boxes over all levels @@ -183,9 +183,8 @@ void LoadBalanceCosts::ComputeDiags (int step) void LoadBalanceCosts::WriteToFile (int step) const { // open file - std::ofstream ofs; - ofs.open(m_path + m_rd_name + "." + m_extension, - std::ofstream::out | std::ofstream::app); + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; // write step ofs << step+1 << m_sep; @@ -197,7 +196,7 @@ void LoadBalanceCosts::WriteToFile (int step) const ofs << WarpX::GetInstance().gett_new(0); // loop over data size and write - for (int i = 0; i < m_data.size(); ++i) + for (int i = 0; i < static_cast(m_data.size()); ++i) { ofs << m_sep << m_data[i]; if ((i - m_nDataFields + 1)%m_nDataFields == 0) @@ -206,7 +205,7 @@ void LoadBalanceCosts::WriteToFile (int step) const int ind_rank = i - m_nDataFields + 2; // index for the rank corresponding to current box // m_data --> rank --> hostname - ofs << m_sep << m_data_string[m_data[ind_rank]]; + ofs << m_sep << m_data_string[static_cast(m_data[ind_rank])]; } } // end loop over data size @@ -224,7 +223,7 @@ void LoadBalanceCosts::WriteToFile (int step) const if (!ParallelDescriptor::IOProcessor()) return; // final step is a special case, fill jagged array with NaN - if (step == (warpx.maxStep() - (warpx.maxStep()%m_freq) - 1 )) + if (m_intervals.nextContains(step+1) > warpx.maxStep()) { // open tmp file to copy data std::string fileTmpName = m_path + m_rd_name + ".tmp." + m_extension; diff --git a/Source/Diagnostics/ReducedDiags/Make.package b/Source/Diagnostics/ReducedDiags/Make.package index 3be9858a8d0..0ea185454ee 100644 --- a/Source/Diagnostics/ReducedDiags/Make.package +++ b/Source/Diagnostics/ReducedDiags/Make.package @@ -5,5 +5,7 @@ CEXE_sources += FieldEnergy.cpp CEXE_sources += BeamRelevant.cpp CEXE_sources += LoadBalanceCosts.cpp CEXE_sources += ParticleHistogram.cpp +CEXE_sources += FieldMaximum.cpp +CEXE_sources += ParticleNumber.cpp VPATH_LOCATIONS += $(WARPX_HOME)/Source/Diagnostics/ReducedDiags diff --git a/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp index fe4ac71eaa9..a6bb16e57b1 100644 --- a/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp +++ b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp @@ -10,6 +10,8 @@ #include "BeamRelevant.H" #include "ParticleEnergy.H" #include "FieldEnergy.H" +#include "FieldMaximum.H" +#include "ParticleNumber.H" #include "MultiReducedDiags.H" #include @@ -34,7 +36,7 @@ MultiReducedDiags::MultiReducedDiags () m_multi_rd.resize(m_rd_names.size()); // loop over all reduced diags - for (int i_rd = 0; i_rd < m_rd_names.size(); ++i_rd) + for (int i_rd = 0; i_rd < static_cast(m_rd_names.size()); ++i_rd) { ParmParse pp_rd(m_rd_names[i_rd]); @@ -54,6 +56,11 @@ MultiReducedDiags::MultiReducedDiags () m_multi_rd[i_rd].reset ( new FieldEnergy(m_rd_names[i_rd])); } + else if (rd_type.compare("FieldMaximum") == 0) + { + m_multi_rd[i_rd].reset + ( new FieldMaximum(m_rd_names[i_rd])); + } else if (rd_type.compare("BeamRelevant") == 0) { m_multi_rd[i_rd].reset @@ -69,6 +76,11 @@ MultiReducedDiags::MultiReducedDiags () m_multi_rd[i_rd].reset ( new ParticleHistogram(m_rd_names[i_rd])); } + else if (rd_type.compare("ParticleNumber") == 0) + { + m_multi_rd[i_rd].reset + ( new ParticleNumber(m_rd_names[i_rd])); + } else { Abort("No matching reduced diagnostics type found."); } // end if match diags @@ -83,7 +95,7 @@ MultiReducedDiags::MultiReducedDiags () void MultiReducedDiags::ComputeDiags (int step) { // loop over all reduced diags - for (int i_rd = 0; i_rd < m_rd_names.size(); ++i_rd) + for (int i_rd = 0; i_rd < static_cast(m_rd_names.size()); ++i_rd) { m_multi_rd[i_rd] -> ComputeDiags(step); } @@ -91,7 +103,7 @@ void MultiReducedDiags::ComputeDiags (int step) } // end void MultiReducedDiags::ComputeDiags -// funciton to write data +// function to write data void MultiReducedDiags::WriteToFile (int step) { @@ -99,11 +111,10 @@ void MultiReducedDiags::WriteToFile (int step) if ( !ParallelDescriptor::IOProcessor() ) { return; } // loop over all reduced diags - for (int i_rd = 0; i_rd < m_rd_names.size(); ++i_rd) + for (int i_rd = 0; i_rd < static_cast(m_rd_names.size()); ++i_rd) { - // Judge if the diags should be done - if ( (step+1) % m_multi_rd[i_rd]->m_freq != 0 ) { continue; } + if (!m_multi_rd[i_rd]->m_intervals.contains(step+1)) { continue; } // call the write to file function m_multi_rd[i_rd]->WriteToFile(step); diff --git a/Source/Diagnostics/ReducedDiags/ParticleEnergy.H b/Source/Diagnostics/ReducedDiags/ParticleEnergy.H index d7c60a24e41..fbbf197a574 100644 --- a/Source/Diagnostics/ReducedDiags/ParticleEnergy.H +++ b/Source/Diagnostics/ReducedDiags/ParticleEnergy.H @@ -24,7 +24,7 @@ public: * @param[in] rd_name reduced diags names */ ParticleEnergy(std::string rd_name); - /** This funciton computes the particle relativistic + /** This function computes the particle relativistic * kinetic energy (EP). * \param [in] step current time step * EP = sqrt( p^2 c^2 + m^2 c^4 ) - m c^2, diff --git a/Source/Diagnostics/ReducedDiags/ParticleEnergy.cpp b/Source/Diagnostics/ReducedDiags/ParticleEnergy.cpp index 1b767c57f58..9d2b3eac2cf 100644 --- a/Source/Diagnostics/ReducedDiags/ParticleEnergy.cpp +++ b/Source/Diagnostics/ReducedDiags/ParticleEnergy.cpp @@ -27,25 +27,24 @@ ParticleEnergy::ParticleEnergy (std::string rd_name) auto & warpx = WarpX::GetInstance(); // get MultiParticleContainer class object - auto & mypc = warpx.GetPartContainer(); + const auto & mypc = warpx.GetPartContainer(); // get number of species (int) - auto nSpecies = mypc.nSpecies(); + const auto nSpecies = mypc.nSpecies(); // resize data array m_data.resize(2*nSpecies+2,0.0); // get species names (std::vector) - auto species_names = mypc.GetSpeciesNames(); + const auto species_names = mypc.GetSpeciesNames(); if (ParallelDescriptor::IOProcessor()) { if ( m_IsNotRestart ) { // open file - std::ofstream ofs; - ofs.open(m_path + m_rd_name + "." + m_extension, - std::ofstream::out | std::ofstream::app); + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; // write header row ofs << "#"; ofs << "[1]step()"; @@ -82,16 +81,16 @@ void ParticleEnergy::ComputeDiags (int step) { // Judge if the diags should be done - if ( (step+1) % m_freq != 0 ) { return; } + if (!m_intervals.contains(step+1)) { return; } // get MultiParticleContainer class object - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); // get number of species (int) - auto nSpecies = mypc.nSpecies(); + const auto nSpecies = mypc.nSpecies(); // get species names (std::vector) - auto species_names = mypc.GetSpeciesNames(); + const auto species_names = mypc.GetSpeciesNames(); // speed of light squared auto c2 = PhysConst::c * PhysConst::c; @@ -100,14 +99,14 @@ void ParticleEnergy::ComputeDiags (int step) for (int i_s = 0; i_s < nSpecies; ++i_s) { // get WarpXParticleContainer class object - auto & myspc = mypc.GetParticleContainer(i_s); + const auto & myspc = mypc.GetParticleContainer(i_s); // get mass (Real) auto m = myspc.getMass(); using PType = typename WarpXParticleContainer::SuperParticleType; - // Use amex::ReduceSum to compute the sum of energies of all particles + // Use amrex::ReduceSum to compute the sum of energies of all particles // held by the current MPI rank, for this species. This involves a loop over all // boxes held by this MPI rank. Real Etot = 0.0_rt; diff --git a/Source/Diagnostics/ReducedDiags/ParticleHistogram.cpp b/Source/Diagnostics/ReducedDiags/ParticleHistogram.cpp index 78793eb4545..85624c6a19b 100644 --- a/Source/Diagnostics/ReducedDiags/ParticleHistogram.cpp +++ b/Source/Diagnostics/ReducedDiags/ParticleHistogram.cpp @@ -65,7 +65,7 @@ ParticleHistogram::ParticleHistogram (std::string rd_name) } // get MultiParticleContainer class object - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); // get species names (std::vector) auto const species_names = mypc.GetSpeciesNames(); // select species @@ -88,9 +88,8 @@ ParticleHistogram::ParticleHistogram (std::string rd_name) if ( m_IsNotRestart ) { // open file - std::ofstream ofs; - ofs.open(m_path + m_rd_name + "." + m_extension, - std::ofstream::out | std::ofstream::app); + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; // write header row ofs << "#"; ofs << "[1]step()"; @@ -118,7 +117,7 @@ void ParticleHistogram::ComputeDiags (int step) { // Judge if the diags should be done - if ( (step+1) % m_freq != 0 ) return; + if (!m_intervals.contains(step+1)) return; // get WarpX class object auto & warpx = WarpX::GetInstance(); @@ -127,7 +126,7 @@ void ParticleHistogram::ComputeDiags (int step) auto const t = warpx.gett_new(0); // get MultiParticleContainer class object - auto & mypc = warpx.GetPartContainer(); + const auto & mypc = warpx.GetPartContainer(); // get WarpXParticleContainer class object auto const & myspc = mypc.GetParticleContainer(m_selected_species_id); @@ -135,7 +134,7 @@ void ParticleHistogram::ComputeDiags (int step) using PType = typename WarpXParticleContainer::SuperParticleType; // get parser - ParserWrapper *fun_partparser = m_parser.get(); + HostDeviceParser fun_partparser = getParser(m_parser); // declare local variables Real const bin_min = m_bin_min; @@ -156,7 +155,7 @@ void ParticleHistogram::ComputeDiags (int step) auto const ux = p.rdata(PIdx::ux)/PhysConst::c; auto const uy = p.rdata(PIdx::uy)/PhysConst::c; auto const uz = p.rdata(PIdx::uz)/PhysConst::c; - auto const f = (*fun_partparser)(t,x,y,z,ux,uy,uz); + auto const f = fun_partparser(t,x,y,z,ux,uy,uz); auto const f1 = bin_min + bin_size*i; auto const f2 = bin_min + bin_size*(i+1); if ( f > f1 && f < f2 ) { diff --git a/Source/Diagnostics/ReducedDiags/ParticleNumber.H b/Source/Diagnostics/ReducedDiags/ParticleNumber.H new file mode 100644 index 00000000000..5606026ac82 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ParticleNumber.H @@ -0,0 +1,32 @@ +/* Copyright 2019-2020 Neil Zaim, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_PARTICLENUMBER_H_ +#define WARPX_DIAGNOSTICS_REDUCEDDIAGS_PARTICLENUMBER_H_ + +#include "ReducedDiags.H" + +/** + * This class mainly contains a function that computes the total number of macroparticles of each + * species. + */ +class ParticleNumber : public ReducedDiags +{ +public: + + /** constructor + * @param[in] rd_name reduced diags names */ + ParticleNumber(std::string rd_name); + + /** This function computes the total number of macroparticles of each species. + * @param [in] step current time step + */ + virtual void ComputeDiags(int step) override final; + +}; + +#endif // WARPX_DIAGNOSTICS_REDUCEDDIAGS_PARTICLENUMBER_H_ diff --git a/Source/Diagnostics/ReducedDiags/ParticleNumber.cpp b/Source/Diagnostics/ReducedDiags/ParticleNumber.cpp new file mode 100644 index 00000000000..fff202c7ca7 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ParticleNumber.cpp @@ -0,0 +1,102 @@ +/* Copyright 2019-2020 Neil Zaim, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "ParticleNumber.H" +#include "WarpX.H" + +// constructor +ParticleNumber::ParticleNumber (std::string rd_name) +: ReducedDiags{rd_name} +{ + // get WarpX class object + auto & warpx = WarpX::GetInstance(); + + // get MultiParticleContainer class object + const auto & mypc = warpx.GetPartContainer(); + + // get number of species (int) + const auto nSpecies = mypc.nSpecies(); + + // resize data array to nSpecies+1 + // (number of particles of each species + total number of particles) + m_data.resize(nSpecies+1, amrex::Real(0.)); + + // get species names (std::vector) + const auto species_names = mypc.GetSpeciesNames(); + + if (amrex::ParallelDescriptor::IOProcessor()) + { + if ( m_IsNotRestart ) + { + // open file + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; + // write header row + ofs << "#"; + ofs << "[1]step()"; + ofs << m_sep; + ofs << "[2]time(s)"; + ofs << m_sep; + ofs << "[3]total()"; + constexpr int shift_first_species = 4; // Column number of first species in output file + for (int i = 0; i < nSpecies; ++i) + { + ofs << m_sep; + ofs << "[" + std::to_string(shift_first_species+i) + "]"; + ofs << species_names[i]+"()"; + } + ofs << std::endl; + // close file + ofs.close(); + } + } + +} +// end constructor + +// function that computes total number of macroparticles +void ParticleNumber::ComputeDiags (int step) +{ + + // Judge if the diags should be done + if (!m_intervals.contains(step+1)) { return; } + + // get MultiParticleContainer class object + const auto & mypc = WarpX::GetInstance().GetPartContainer(); + + // get number of species (int) + const auto nSpecies = mypc.nSpecies(); + + // Index of total number of particles (all species) in m_data + constexpr int idx_total = 0; + // Index of first species in m_data + constexpr int idx_first_species = 1; + + // Initialize total number of particles (all species) to 0 + m_data[idx_total] = amrex::Real(0.); + + // loop over species + for (int i_s = 0; i_s < nSpecies; ++i_s) + { + // get WarpXParticleContainer class object + const auto & myspc = mypc.GetParticleContainer(i_s); + + // Save result for this species + m_data[idx_first_species + i_s] = myspc.TotalNumberOfParticles(); + // Increase total number of particles (all species) + m_data[idx_total] += m_data[idx_first_species + i_s]; + } + // end loop over species + + /* m_data now contains up-to-date values for: + * [total number of macroparticles (all species), + * total number of macroparticles (species 1), + * ..., + * total number of macroparticles (species n)] */ + +} +// end void ParticleNumber::ComputeDiags diff --git a/Source/Diagnostics/ReducedDiags/ReducedDiags.H b/Source/Diagnostics/ReducedDiags/ReducedDiags.H index 150ec8de039..3552d9ccac5 100644 --- a/Source/Diagnostics/ReducedDiags/ReducedDiags.H +++ b/Source/Diagnostics/ReducedDiags/ReducedDiags.H @@ -8,6 +8,8 @@ #ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_REDUCEDDIAGS_H_ #define WARPX_DIAGNOSTICS_REDUCEDDIAGS_REDUCEDDIAGS_H_ +#include "Utils/IntervalsParser.H" + #include #include @@ -33,7 +35,7 @@ public: std::string m_rd_name; /// output frequency - int m_freq = 1; + IntervalsParser m_intervals; /// check if it is a restart run int m_IsNotRestart = 1; diff --git a/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp b/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp index cb9448870e3..9f757079d06 100644 --- a/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp +++ b/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp @@ -29,26 +29,30 @@ ReducedDiags::ReducedDiags (std::string rd_name) // read extension pp.query("extension", m_extension); - // creater folder - if (!UtilCreateDirectory(m_path, 0755)) - { CreateDirectoryFailed(m_path); } - // check if it is a restart run std::string restart_chkfile = ""; ParmParse pp_amr("amr"); pp_amr.query("restart", restart_chkfile); m_IsNotRestart = restart_chkfile.empty(); - // replace / create output file - if ( m_IsNotRestart ) // not a restart + if (ParallelDescriptor::IOProcessor()) { - std::ofstream ofs; - ofs.open(m_path+m_rd_name+"."+m_extension, std::ios::trunc); - ofs.close(); + // create folder + if (!UtilCreateDirectory(m_path, 0755)) + { CreateDirectoryFailed(m_path); } + + // replace / create output file + if ( m_IsNotRestart ) // not a restart + { + std::ofstream ofs{m_path+m_rd_name+"."+m_extension, std::ios::trunc}; + ofs.close(); + } } // read reduced diags frequency - pp.query("frequency", m_freq); + std::vector intervals_string_vec = {"1"}; + pp.queryarr("frequency", intervals_string_vec); + m_intervals = IntervalsParser(intervals_string_vec); // read separator pp.query("separator", m_sep); @@ -61,9 +65,8 @@ void ReducedDiags::WriteToFile (int step) const { // open file - std::ofstream ofs; - ofs.open(m_path + m_rd_name + "." + m_extension, - std::ofstream::out | std::ofstream::app); + std::ofstream ofs{m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app}; // write step ofs << step+1; @@ -77,7 +80,7 @@ void ReducedDiags::WriteToFile (int step) const ofs << WarpX::GetInstance().gett_new(0); // loop over data size and write - for (int i = 0; i < m_data.size(); ++i) + for (int i = 0; i < static_cast(m_data.size()); ++i) { ofs << m_sep; ofs << m_data[i]; diff --git a/Source/Diagnostics/WarpXIO.cpp b/Source/Diagnostics/WarpXIO.cpp index c9d8edc1ed8..42261caf68f 100644 --- a/Source/Diagnostics/WarpXIO.cpp +++ b/Source/Diagnostics/WarpXIO.cpp @@ -242,7 +242,7 @@ WarpX::InitFromCheckpoint () std::unique_ptr WarpX::GetCellCenteredData() { - WARPX_PROFILE("WarpX::GetCellCenteredData"); + WARPX_PROFILE("WarpX::GetCellCenteredData()"); const int ng = 1; const int nc = 10; diff --git a/Source/Diagnostics/WarpXOpenPMD.H b/Source/Diagnostics/WarpXOpenPMD.H index 78a5bddea12..a37a7e7abed 100644 --- a/Source/Diagnostics/WarpXOpenPMD.H +++ b/Source/Diagnostics/WarpXOpenPMD.H @@ -89,16 +89,22 @@ public: * @param filetype file backend, e.g. "bp" or "h5" * @param fieldPMLdirections PML field solver, @see WarpX::getPMLdirections() */ - WarpXOpenPMDPlot(bool oneFilePerTS, std::string filetype, std::vector fieldPMLdirections); + WarpXOpenPMDPlot (bool oneFilePerTS, std::string filetype, std::vector fieldPMLdirections); - ~WarpXOpenPMDPlot(); + ~WarpXOpenPMDPlot (); /** Set Iteration Step for the series * * @note If an iteration has been written, then it will give a warning * */ - void SetStep(int ts, const std::string& filePrefix); + void SetStep (int ts, const std::string& filePrefix); + + /** Close the step + * + * Signal that no further updates will be written for the step. + */ + void CloseStep (); void WriteOpenPMDParticles (const amrex::Vector& particle_diags); @@ -110,7 +116,7 @@ public: private: - void Init(openPMD::AccessType accessType, const std::string& filePrefix); + void Init(openPMD::Access access, const std::string& filePrefix); /** This function sets up the entries for storing the particle positions, global IDs, and constant records (charge, mass) * diff --git a/Source/Diagnostics/WarpXOpenPMD.cpp b/Source/Diagnostics/WarpXOpenPMD.cpp index 8aba0ba9624..d7072ac56bc 100644 --- a/Source/Diagnostics/WarpXOpenPMD.cpp +++ b/Source/Diagnostics/WarpXOpenPMD.cpp @@ -6,6 +6,7 @@ */ #include "WarpXOpenPMD.H" #include "FieldIO.H" // for getReversedVec +#include "Utils/RelativeCellPosition.H" #include "Utils/WarpXAlgorithmSelection.H" #include "Utils/WarpXUtil.H" @@ -51,17 +52,13 @@ namespace detail #if defined(WARPX_DIM_XZ) vs const positionComponents{"x", "z"}; #elif defined(WARPX_DIM_RZ) - // note: this will change when we back-transform - // z,r,theta on the fly to cartesian coordinates - vs const positionComponents{"r", "z"}; - // TODO: transform to x,y,z // note: although we internally store particle positions // for AMReX in r,z and a theta attribute, we // actually need them for algorithms (e.g. push) // and I/O in Cartesian. // Other attributes like momentum are consequently // stored in x,y,z internally. - // vs const positionComponents{"x", "y", "z"}; + vs const positionComponents{"x", "y", "z"}; #elif (AMREX_SPACEDIM==3) vs const positionComponents{"x", "y", "z"}; #else @@ -71,11 +68,16 @@ namespace detail } /** Return the axis (index) names of a mesh + * + * This will be returned in C order. This is inverse of the Fortran order + * of the index labels for the AMReX FArrayBox. */ inline std::vector< std::string > getFieldAxisLabels() { using vs = std::vector< std::string >; + + // Fortran order of the index labels for the AMReX FArrayBox #if defined(WARPX_DIM_XZ) vs const axisLabels{"x", "z"}; #elif defined(WARPX_DIM_RZ) @@ -88,7 +90,9 @@ namespace detail #else # error Unknown WarpX dimensionality. #endif - return axisLabels; + + // revert to C order (fastest varying index last) + return {axisLabels.rbegin(), axisLabels.rend()}; } /** Return the component names of a mesh @@ -114,7 +118,7 @@ namespace detail * @param record_name name of the openPMD record * @return map with base quantities and power scaling */ - std::map< openPMD::UnitDimension, double > + inline std::map< openPMD::UnitDimension, double > getUnitDimension( std::string const & record_name ) { @@ -149,6 +153,39 @@ namespace detail }; else return {}; } + + /** \brief For a given field that is to be written to an openPMD file, + * set the metadata that indicates the physical unit. + */ + inline void + setOpenPMDUnit( openPMD::Mesh mesh, const std::string field_name ) + { + if (field_name[0] == 'E'){ // Electric field + mesh.setUnitDimension({ + {openPMD::UnitDimension::L, 1}, + {openPMD::UnitDimension::M, 1}, + {openPMD::UnitDimension::T, -3}, + {openPMD::UnitDimension::I, -1}, + }); + } else if (field_name[0] == 'B'){ // Magnetic field + mesh.setUnitDimension({ + {openPMD::UnitDimension::M, 1}, + {openPMD::UnitDimension::I, -1}, + {openPMD::UnitDimension::T, -2} + }); + } else if (field_name[0] == 'j'){ // current + mesh.setUnitDimension({ + {openPMD::UnitDimension::L, -2}, + {openPMD::UnitDimension::I, 1}, + }); + } else if (field_name.substr(0,3) == "rho"){ // charge density + mesh.setUnitDimension({ + {openPMD::UnitDimension::L, -3}, + {openPMD::UnitDimension::I, 1}, + {openPMD::UnitDimension::T, 1}, + }); + } + } #endif // WARPX_USE_OPENPMD } @@ -198,7 +235,7 @@ void WarpXOpenPMDPlot::GetFileName(std::string& filename) } -void WarpXOpenPMDPlot::SetStep(int ts, const std::string& filePrefix) +void WarpXOpenPMDPlot::SetStep (int ts, const std::string& filePrefix) { AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ts >= 0 , "openPMD iterations are unsigned"); @@ -211,12 +248,18 @@ void WarpXOpenPMDPlot::SetStep(int ts, const std::string& filePrefix) } m_CurrentStep = ts; - Init(openPMD::AccessType::CREATE, filePrefix); + Init(openPMD::Access::CREATE, filePrefix); + +} +void WarpXOpenPMDPlot::CloseStep () +{ + if (m_Series) + m_Series->iterations[m_CurrentStep].close(); } void -WarpXOpenPMDPlot::Init(openPMD::AccessType accessType, const std::string& filePrefix) +WarpXOpenPMDPlot::Init (openPMD::Access access, const std::string& filePrefix) { // either for the next ts file, // or init a single file for all ts @@ -231,7 +274,7 @@ WarpXOpenPMDPlot::Init(openPMD::AccessType accessType, const std::string& filePr { #if defined(AMREX_USE_MPI) m_Series = std::make_unique( - filename, accessType, + filename, access, amrex::ParallelDescriptor::Communicator() ); m_MPISize = amrex::ParallelDescriptor::NProcs(); @@ -242,7 +285,7 @@ WarpXOpenPMDPlot::Init(openPMD::AccessType accessType, const std::string& filePr } else { - m_Series = std::make_unique(filename, accessType); + m_Series = std::make_unique(filename, access); m_MPISize = 1; m_MPIRank = 1; } @@ -390,17 +433,54 @@ WarpXOpenPMDPlot::DumpToFile (WarpXParticleContainer* pc, { // Save positions auto const positionComponents = detail::getParticlePositionComponentLabels(); +#if defined(WARPX_DIM_RZ) + { + std::shared_ptr z( + new amrex::ParticleReal[numParticleOnTile], + [](amrex::ParticleReal const *p) { delete[] p; } + ); + for (auto i = 0; i < numParticleOnTile; i++) + z.get()[i] = aos[i].pos(1); // {0: "r", 1: "z"} + std::string const positionComponent = "z"; + currSpecies["position"]["z"].storeChunk(z, {offset}, {numParticleOnTile64}); + } + + // reconstruct x and y from polar coordinates r, theta + auto const& soa = pti.GetStructOfArrays(); + amrex::ParticleReal const* theta = soa.GetRealData(PIdx::theta).dataPtr(); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(theta != nullptr, "openPMD: invalid theta pointer."); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(int(soa.GetRealData(PIdx::theta).size()) == numParticleOnTile, + "openPMD: theta and tile size do not match"); + { + std::shared_ptr< amrex::ParticleReal > x( + new amrex::ParticleReal[numParticleOnTile], + [](amrex::ParticleReal const *p){ delete[] p; } + ); + std::shared_ptr< amrex::ParticleReal > y( + new amrex::ParticleReal[numParticleOnTile], + [](amrex::ParticleReal const *p){ delete[] p; } + ); + for (auto i=0; i curr( new amrex::ParticleReal[numParticleOnTile], [](amrex::ParticleReal const *p){ delete[] p; } ); for (auto i=0; i ids( @@ -412,7 +492,7 @@ WarpXOpenPMDPlot::DumpToFile (WarpXParticleContainer* pc, } auto const scalar = openPMD::RecordComponent::SCALAR; currSpecies["id"][scalar].storeChunk(ids, {offset}, {numParticleOnTile64}); - } + } // save "extra" particle properties in AoS and SoA SaveRealProperty(pti, currSpecies, @@ -508,7 +588,7 @@ WarpXOpenPMDPlot::SaveRealProperty(WarpXParIter& pti, ); for( auto kk=0; kk{AMREX_D_DECL(0.5, 0.5, 0.5)} ); + + auto relative_cell_pos = utils::getRelativeCellPosition( mf ); // AMReX Fortran index order + std::reverse( relative_cell_pos.begin(), relative_cell_pos.end() ); // now in C order + mesh_comp.setPosition( relative_cell_pos ); // Loop through the multifab, and store each box as a chunk, // in the openPMD file. diff --git a/Source/Diagnostics/requirements.txt b/Source/Diagnostics/requirements.txt index c8826d0541b..89d14e60480 100644 --- a/Source/Diagnostics/requirements.txt +++ b/Source/Diagnostics/requirements.txt @@ -5,4 +5,4 @@ # License: BSD-3-Clause-LBNL # keep this entry for GitHub's dependency graph -openPMD-api>=0.11.0 +openPMD-api>=0.12.0 diff --git a/Source/Evolve/WarpXEvolve.cpp b/Source/Evolve/WarpXEvolve.cpp index 15e28dccaf2..3581ecd5efd 100644 --- a/Source/Evolve/WarpXEvolve.cpp +++ b/Source/Evolve/WarpXEvolve.cpp @@ -278,15 +278,16 @@ WarpX::Evolve (int numsteps) int num_moved = MoveWindow(move_j); #ifdef PULSAR - if (!rho_fp[0]) { - amrex::Print() << " no rho -- compute rho! \n"; - } - else { - amrex::Print() << " rho is computed \n"; - } - mypc->PulsarParticleRemoval(); - mypc->PulsarParticleInjection(); + if (!rho_fp[0]) { + amrex::Print() << " no rho -- compute rho! \n"; + } + else { + amrex::Print() << " rho is computed \n"; + } + mypc->PulsarParticleRemoval(); + mypc->PulsarParticleInjection(); #endif + mypc->ApplyBoundaryConditions(); // Electrostatic solver: particles can move by an arbitrary number of cells if( do_electrostatic ) @@ -298,7 +299,7 @@ WarpX::Evolve (int numsteps) // only move by one or two cells per time step if (max_level == 0) { int num_redistribute_ghost = num_moved; - if ((v_galilean[0]!=0) or (v_galilean[1]!=0) or (v_galilean[2]!=0)) { + if ((m_v_galilean[0]!=0) or (m_v_galilean[1]!=0) or (m_v_galilean[2]!=0)) { // Galilean algorithm ; particles can move by up to 2 cells num_redistribute_ghost += 2; } else { @@ -312,6 +313,7 @@ WarpX::Evolve (int numsteps) } } + if (sort_intervals.contains(step+1)) { amrex::Print() << "re-sorting particles \n"; mypc->SortParticlesByBin(sort_bin_size); @@ -336,7 +338,6 @@ WarpX::Evolve (int numsteps) reduced_diags->ComputeDiags(step); reduced_diags->WriteToFile(step); } - multi_diags->FilterComputePackFlush( step ); if (cur_time >= stop_time - 1.e-3*dt[0]) { @@ -378,7 +379,7 @@ WarpX::OneStep_nosub (Real cur_time) // product species. doFieldIonization(); - mypc->doCoulombCollisions(); + mypc->doCoulombCollisions( cur_time ); #ifdef WARPX_QED mypc->doQEDSchwinger(); #endif @@ -413,6 +414,10 @@ WarpX::OneStep_nosub (Real cur_time) doQEDEvents(); #endif + // +1 is necessary here because value of step seen by user (first step is 1) is different than + // value of step in code (first step is 0) + mypc->doResampling(istep[0]+1); + // Synchronize J and rho SyncCurrent(); SyncRho(); @@ -523,6 +528,10 @@ WarpX::OneStep_sub1 (Real curtime) doQEDEvents(); #endif + // +1 is necessary here because value of step seen by user (first step is 1) is different than + // value of step in code (first step is 0) + mypc->doResampling(istep[0]+1); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(finest_level == 1, "Must have exactly two levels"); const int fine_lev = 1; const int coarse_lev = 0; diff --git a/Source/FieldSolver/ElectrostaticSolver.cpp b/Source/FieldSolver/ElectrostaticSolver.cpp index 676d89b1f5c..25f672f908c 100644 --- a/Source/FieldSolver/ElectrostaticSolver.cpp +++ b/Source/FieldSolver/ElectrostaticSolver.cpp @@ -47,11 +47,12 @@ WarpX::AddSpaceChargeField (WarpXParticleContainer& pc) const int num_levels = max_level + 1; Vector > rho(num_levels); Vector > phi(num_levels); - const int ng = WarpX::nox; + // Use number of guard cells used for local deposition of rho + const int ng = guard_cells.ng_depos_rho.max(); for (int lev = 0; lev <= max_level; lev++) { BoxArray nba = boxArray(lev); nba.surroundingNodes(); - rho[lev].reset(new MultiFab(nba, dmap[lev], 1, ng)); // Make ng big enough/use rho from sim + rho[lev].reset(new MultiFab(nba, dmap[lev], 1, ng)); phi[lev].reset(new MultiFab(nba, dmap[lev], 1, 1)); phi[lev]->setVal(0.); } diff --git a/Source/FieldSolver/FiniteDifferenceSolver/ComputeDivE.cpp b/Source/FieldSolver/FiniteDifferenceSolver/ComputeDivE.cpp index 05b4f9fce55..a6376e03c77 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/ComputeDivE.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/ComputeDivE.cpp @@ -137,7 +137,7 @@ void FiniteDifferenceSolver::ComputeDivECylindrical ( // Loop over the cells and update the fields amrex::ParallelFor(tdive, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + i*dr; // r on a nodal grid (F is nodal in r) if (r != 0) { // Off-axis, regular equations divE(i, j, 0, 0) = @@ -149,7 +149,7 @@ void FiniteDifferenceSolver::ComputeDivECylindrical ( + m * Et( i, j, 0, 2*m )/r + T_Algo::DownwardDz(Ez, coefs_z, n_coefs_z, i, j, 0, 2*m-1); // Real part divE(i, j, 0, 2*m ) = - T_Algo::DownwardDrr_over_r(Er, r, dr, coefs_r, n_coefs_r, i, j, 0, 2*m-1) + T_Algo::DownwardDrr_over_r(Er, r, dr, coefs_r, n_coefs_r, i, j, 0, 2*m) - m * Et( i, j, 0, 2*m-1 )/r + T_Algo::DownwardDz(Ez, coefs_z, n_coefs_z, i, j, 0, 2*m ); // Imaginary part } diff --git a/Source/FieldSolver/FiniteDifferenceSolver/EvolveB.cpp b/Source/FieldSolver/FiniteDifferenceSolver/EvolveB.cpp index 138e7eb9a93..9afec55e05d 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/EvolveB.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/EvolveB.cpp @@ -154,7 +154,7 @@ void FiniteDifferenceSolver::EvolveBCylindrical ( // Loop over the cells and update the fields amrex::ParallelFor(tbr, tbt, tbz, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + i*dr; // r on nodal point (Br is nodal in r) if (r != 0) { // Off-axis, regular Maxwell equations Br(i, j, 0, 0) += dt * T_Algo::UpwardDz(Et, coefs_z, n_coefs_z, i, j, 0, 0); // Mode m=0 @@ -187,7 +187,7 @@ void FiniteDifferenceSolver::EvolveBCylindrical ( } }, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Bt(i, j, 0, 0) += dt*( T_Algo::UpwardDr(Ez, coefs_r, n_coefs_r, i, j, 0, 0) - T_Algo::UpwardDz(Er, coefs_z, n_coefs_z, i, j, 0, 0)); // Mode m=0 @@ -201,7 +201,7 @@ void FiniteDifferenceSolver::EvolveBCylindrical ( } }, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + (i + 0.5)*dr; // r on a cell-centered grid (Bz is cell-centered in r) Bz(i, j, 0, 0) += dt*( - T_Algo::UpwardDrr_over_r(Et, r, dr, coefs_r, n_coefs_r, i, j, 0, 0)); for (int m=1 ; m +#include using namespace amrex; @@ -30,6 +31,7 @@ void FiniteDifferenceSolver::EvolveBPML ( // Select algorithm (The choice of algorithm is a runtime option, // but we compile code for each algorithm, using templates) #ifdef WARPX_DIM_RZ + amrex::ignore_unused(Bfield, Efield, dt); amrex::Abort("PML are not implemented in cylindrical geometry."); #else if (m_do_nodal) { diff --git a/Source/FieldSolver/FiniteDifferenceSolver/EvolveE.cpp b/Source/FieldSolver/FiniteDifferenceSolver/EvolveE.cpp index 09aa9df4885..fc43fe209fe 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/EvolveE.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/EvolveE.cpp @@ -202,7 +202,7 @@ void FiniteDifferenceSolver::EvolveECylindrical ( // Loop over the cells and update the fields amrex::ParallelFor(ter, tet, tez, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + (i + 0.5)*dr; // r on cell-centered point (Er is cell-centered in r) Er(i, j, 0, 0) += c2 * dt*( - T_Algo::DownwardDz(Bt, coefs_z, n_coefs_z, i, j, 0, 0) @@ -219,7 +219,7 @@ void FiniteDifferenceSolver::EvolveECylindrical ( } }, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + i*dr; // r on a nodal grid (Et is nodal in r) if (r != 0) { // Off-axis, regular Maxwell equations Et(i, j, 0, 0) += c2 * dt*( @@ -260,7 +260,7 @@ void FiniteDifferenceSolver::EvolveECylindrical ( } }, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + i*dr; // r on a nodal grid (Ez is nodal in r) if (r != 0) { // Off-axis, regular Maxwell equations Ez(i, j, 0, 0) += c2 * dt*( @@ -302,14 +302,14 @@ void FiniteDifferenceSolver::EvolveECylindrical ( // Loop over the cells and update the fields amrex::ParallelFor(ter, tet, tez, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Er(i, j, 0, 0) += c2 * dt * T_Algo::UpwardDr(F, coefs_r, n_coefs_r, i, j, 0, 0); for (int m=1; m +#include using namespace amrex; @@ -36,6 +37,7 @@ void FiniteDifferenceSolver::EvolveEPML ( // Select algorithm (The choice of algorithm is a runtime option, // but we compile code for each algorithm, using templates) #ifdef WARPX_DIM_RZ + amrex::ignore_unused(Efield, Bfield, Jfield, Ffield, sigba, dt, pml_has_particles); amrex::Abort("PML are not implemented in cylindrical geometry."); #else if (m_do_nodal) { diff --git a/Source/FieldSolver/FiniteDifferenceSolver/EvolveF.cpp b/Source/FieldSolver/FiniteDifferenceSolver/EvolveF.cpp index 20560f0384f..6627d2ef62d 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/EvolveF.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/EvolveF.cpp @@ -162,7 +162,7 @@ void FiniteDifferenceSolver::EvolveFCylindrical ( // Loop over the cells and update the fields amrex::ParallelFor(tf, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/){ Real const r = rmin + i*dr; // r on a nodal grid (F is nodal in r) if (r != 0) { // Off-axis, regular equations F(i, j, 0, 0) += dt * ( diff --git a/Source/FieldSolver/FiniteDifferenceSolver/EvolveFPML.cpp b/Source/FieldSolver/FiniteDifferenceSolver/EvolveFPML.cpp index b36c50c354e..ba7a90483f0 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/EvolveFPML.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/EvolveFPML.cpp @@ -16,6 +16,7 @@ #endif #include "BoundaryConditions/PMLComponent.H" #include +#include using namespace amrex; @@ -30,6 +31,7 @@ void FiniteDifferenceSolver::EvolveFPML ( // Select algorithm (The choice of algorithm is a runtime option, // but we compile code for each algorithm, using templates) #ifdef WARPX_DIM_RZ + amrex::ignore_unused(Ffield, Efield, dt); amrex::Abort("PML are not implemented in cylindrical geometry."); #else if (m_do_nodal) { diff --git a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianCKCAlgorithm.H b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianCKCAlgorithm.H index d5b567bc15f..b688110b79e 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianCKCAlgorithm.H +++ b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianCKCAlgorithm.H @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -26,9 +27,9 @@ struct CartesianCKCAlgorithm { static void InitializeStencilCoefficients ( std::array& cell_size, - amrex::Gpu::ManagedVector& stencil_coefs_x, - amrex::Gpu::ManagedVector& stencil_coefs_y, - amrex::Gpu::ManagedVector& stencil_coefs_z ) { + amrex::Vector& stencil_coefs_x, + amrex::Vector& stencil_coefs_y, + amrex::Vector& stencil_coefs_z ) { using namespace amrex; @@ -112,7 +113,7 @@ struct CartesianCKCAlgorithm { AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real UpwardDx ( amrex::Array4 const& F, - amrex::Real const * const coefs_x, int const n_coefs_x, + amrex::Real const * const coefs_x, int const /*n_coefs_x*/, int const i, int const j, int const k, int const ncomp=0 ) { amrex::Real const alphax = coefs_x[1]; @@ -142,10 +143,11 @@ struct CartesianCKCAlgorithm { /** * Perform derivative along x on a nodal grid, from a cell-centered field `F` */ + template< typename T_Field> AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real DownwardDx ( - amrex::Array4 const& F, - amrex::Real const * const coefs_x, int const n_coefs_x, + T_Field const& F, + amrex::Real const * const coefs_x, int const /*n_coefs_x*/, int const i, int const j, int const k, int const ncomp=0 ) { amrex::Real const inv_dx = coefs_x[0]; @@ -175,25 +177,38 @@ struct CartesianCKCAlgorithm { + F(i-1,j+1,k+1,ncomp) - F(i-1,j ,k+1,ncomp) + F(i+1,j+1,k-1,ncomp) - F(i+1,j ,k-1,ncomp) + F(i-1,j+1,k-1,ncomp) - F(i-1,j ,k-1,ncomp)); + amrex::ignore_unused(n_coefs_y); #elif (defined WARPX_DIM_XZ) - return 0._rt; // 2D Cartesian: derivative along y is 0 + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); + return 0._rt; // 2D Cartesian: derivative along y is 0 +#else + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); #endif } /** * Perform derivative along y on a nodal grid, from a cell-centered field `F` */ + template< typename T_Field> AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real DownwardDy ( - amrex::Array4 const& F, + T_Field const& F, amrex::Real const * const coefs_y, int const n_coefs_y, int const i, int const j, int const k, int const ncomp=0 ) { using namespace amrex; #if defined WARPX_DIM_3D - Real const inv_dy = coefs_y[0]; + amrex::Real const inv_dy = coefs_y[0]; return inv_dy*( F(i,j,k,ncomp) - F(i,j-1,k,ncomp) ); + amrex::ignore_unused(n_coefs_y); #elif (defined WARPX_DIM_XZ) + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); return 0._rt; // 2D Cartesian: derivative along y is 0 +#else + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); #endif } @@ -206,6 +221,9 @@ struct CartesianCKCAlgorithm { int const i, int const j, int const k, int const ncomp=0 ) { using namespace amrex; + + amrex::ignore_unused(n_coefs_z); + Real const alphaz = coefs_z[1]; Real const betazx = coefs_z[2]; #if defined WARPX_DIM_3D @@ -222,6 +240,7 @@ struct CartesianCKCAlgorithm { + F(i-1,j+1,k+1,ncomp) - F(i-1,j+1,k ,ncomp) + F(i+1,j-1,k+1,ncomp) - F(i+1,j-1,k ,ncomp) + F(i-1,j-1,k+1,ncomp) - F(i-1,j-1,k ,ncomp)); + amrex::ignore_unused(n_coefs_z); #elif (defined WARPX_DIM_XZ) return alphaz * (F(i ,j+1,k ,ncomp) - F(i ,j ,k ,ncomp)) + betazx * (F(i+1,j+1,k ,ncomp) - F(i+1,j ,k ,ncomp) @@ -231,11 +250,12 @@ struct CartesianCKCAlgorithm { /** * Perform derivative along z on a nodal grid, from a cell-centered field `F` */ + template< typename T_Field> AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real DownwardDz ( - amrex::Array4 const& F, - amrex::Real const * const coefs_z, int const n_coefs_z, - int const i, int const j, int const k, int const ncomp=0 ) { + T_Field const& F, + amrex::Real const * const coefs_z, int const /*n_coefs_z*/, + int const i, int const j, int const k, int const ncomp=0) { amrex::Real const inv_dz = coefs_z[0]; #if defined WARPX_DIM_3D diff --git a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianNodalAlgorithm.H b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianNodalAlgorithm.H index b0fe4d5620b..5bd7e88bc2b 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianNodalAlgorithm.H +++ b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianNodalAlgorithm.H @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -26,9 +27,9 @@ struct CartesianNodalAlgorithm { static void InitializeStencilCoefficients ( std::array& cell_size, - amrex::Gpu::ManagedVector& stencil_coefs_x, - amrex::Gpu::ManagedVector& stencil_coefs_y, - amrex::Gpu::ManagedVector& stencil_coefs_z ) { + amrex::Vector& stencil_coefs_x, + amrex::Vector& stencil_coefs_y, + amrex::Vector& stencil_coefs_z ) { using namespace amrex; @@ -61,7 +62,7 @@ struct CartesianNodalAlgorithm { AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real UpwardDx ( amrex::Array4 const& F, - amrex::Real const * const coefs_x, int const n_coefs_x, + amrex::Real const * const coefs_x, int const /*n_coefs_x*/, int const i, int const j, int const k, int const ncomp=0 ) { using namespace amrex; @@ -90,7 +91,7 @@ struct CartesianNodalAlgorithm { AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real UpwardDy ( amrex::Array4 const& F, - amrex::Real const * const coefs_y, int const n_coefs_y, + amrex::Real const * const coefs_y, int const /*n_coefs_y*/, int const i, int const j, int const k, int const ncomp=0 ) { using namespace amrex; @@ -98,6 +99,7 @@ struct CartesianNodalAlgorithm { Real const inv_dy = coefs_y[0]; return 0.5_rt*inv_dy*( F(i,j+1,k,ncomp) - F(i,j-1,k,ncomp) ); #elif (defined WARPX_DIM_XZ) + ignore_unused(i, j, k, coefs_y, ncomp, F); return 0._rt; // 2D Cartesian: derivative along y is 0 #endif } @@ -123,7 +125,7 @@ struct CartesianNodalAlgorithm { AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real UpwardDz ( amrex::Array4 const& F, - amrex::Real const * const coefs_z, int const n_coefs_z, + amrex::Real const * const coefs_z, int const /*n_coefs_z*/, int const i, int const j, int const k, int const ncomp=0 ) { using namespace amrex; diff --git a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianYeeAlgorithm.H b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianYeeAlgorithm.H index 29e3d710e71..2ec6a3d223f 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianYeeAlgorithm.H +++ b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CartesianYeeAlgorithm.H @@ -9,7 +9,9 @@ #define WARPX_FINITE_DIFFERENCE_ALGORITHM_CARTESIAN_YEE_H_ #include "Utils/WarpXConst.H" +#include "FieldAccessorFunctors.H" +#include #include #include #include @@ -26,9 +28,9 @@ struct CartesianYeeAlgorithm { static void InitializeStencilCoefficients ( std::array& cell_size, - amrex::Gpu::ManagedVector& stencil_coefs_x, - amrex::Gpu::ManagedVector& stencil_coefs_y, - amrex::Gpu::ManagedVector& stencil_coefs_z ) { + amrex::Vector& stencil_coefs_x, + amrex::Vector& stencil_coefs_y, + amrex::Vector& stencil_coefs_z ) { using namespace amrex; // Store the inverse cell size along each direction in the coefficients @@ -58,7 +60,7 @@ struct CartesianYeeAlgorithm { AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real UpwardDx ( amrex::Array4 const& F, - amrex::Real const * const coefs_x, int const n_coefs_x, + amrex::Real const * const coefs_x, int const /*n_coefs_x*/, int const i, int const j, int const k, int const ncomp=0 ) { amrex::Real const inv_dx = coefs_x[0]; @@ -67,10 +69,11 @@ struct CartesianYeeAlgorithm { /** * Perform derivative along x on a nodal grid, from a cell-centered field `F`*/ + template< typename T_Field> AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real DownwardDx ( - amrex::Array4 const& F, - amrex::Real const * const coefs_x, int const n_coefs_x, + T_Field const& F, + amrex::Real const * const coefs_x, int const /*n_coefs_x*/, int const i, int const j, int const k, int const ncomp=0 ) { amrex::Real const inv_dx = coefs_x[0]; @@ -89,16 +92,23 @@ struct CartesianYeeAlgorithm { #if defined WARPX_DIM_3D Real const inv_dy = coefs_y[0]; return inv_dy*( F(i,j+1,k,ncomp) - F(i,j,k,ncomp) ); + amrex::ignore_unused(n_coefs_y); #elif (defined WARPX_DIM_XZ) + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); return 0._rt; // 2D Cartesian: derivative along y is 0 +#else + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); #endif } /** * Perform derivative along y on a nodal grid, from a cell-centered field `F`*/ + template< typename T_Field> AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real DownwardDy ( - amrex::Array4 const& F, + T_Field const& F, amrex::Real const * const coefs_y, int const n_coefs_y, int const i, int const j, int const k, int const ncomp=0 ) { @@ -106,8 +116,14 @@ struct CartesianYeeAlgorithm { #if defined WARPX_DIM_3D Real const inv_dy = coefs_y[0]; return inv_dy*( F(i,j,k,ncomp) - F(i,j-1,k,ncomp) ); + amrex::ignore_unused(n_coefs_y); #elif (defined WARPX_DIM_XZ) + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); return 0._rt; // 2D Cartesian: derivative along y is 0 +#else + amrex::ignore_unused(F, coefs_y, n_coefs_y, + i, j, k, ncomp); #endif } @@ -116,7 +132,7 @@ struct CartesianYeeAlgorithm { AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real UpwardDz ( amrex::Array4 const& F, - amrex::Real const * const coefs_z, int const n_coefs_z, + amrex::Real const * const coefs_z, int const /*n_coefs_z*/, int const i, int const j, int const k, int const ncomp=0 ) { using namespace amrex; @@ -130,10 +146,11 @@ struct CartesianYeeAlgorithm { /** * Perform derivative along z on a nodal grid, from a cell-centered field `F`*/ + template< typename T_Field> AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE static amrex::Real DownwardDz ( - amrex::Array4 const& F, - amrex::Real const * const coefs_z, int const n_coefs_z, + T_Field const& F, + amrex::Real const * const coefs_z, int const /*n_coefs_z*/, int const i, int const j, int const k, int const ncomp=0 ) { using namespace amrex; diff --git a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CylindricalYeeAlgorithm.H b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CylindricalYeeAlgorithm.H index 4e01041b274..6196598b34e 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CylindricalYeeAlgorithm.H +++ b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/CylindricalYeeAlgorithm.H @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -26,8 +27,8 @@ struct CylindricalYeeAlgorithm { static void InitializeStencilCoefficients ( std::array& cell_size, - amrex::Gpu::ManagedVector& stencil_coefs_r, - amrex::Gpu::ManagedVector& stencil_coefs_z ) { + amrex::Vector& stencil_coefs_r, + amrex::Vector& stencil_coefs_z ) { using namespace amrex; // Store the inverse cell size along each direction in the coefficients @@ -76,6 +77,8 @@ struct CylindricalYeeAlgorithm { int const i, int const j, int const k, int const comp ) { using namespace amrex; + ignore_unused(n_coefs_r); + Real const inv_dr = coefs_r[0]; return 1._rt/r * inv_dr*( (r+0.5_rt*dr)*F(i+1,j,k,comp) - (r-0.5_rt*dr)*F(i,j,k,comp) ); }; @@ -92,6 +95,8 @@ struct CylindricalYeeAlgorithm { int const i, int const j, int const k, int const comp ) { using namespace amrex; + ignore_unused(n_coefs_r); + Real const inv_dr = coefs_r[0]; return 1._rt/r * inv_dr*( (r+0.5_rt*dr)*F(i,j,k,comp) - (r-0.5_rt*dr)*F(i-1,j,k,comp) ); }; @@ -105,6 +110,8 @@ struct CylindricalYeeAlgorithm { int const i, int const j, int const k, int const comp ) { using namespace amrex; + ignore_unused(n_coefs_r); + Real const inv_dr = coefs_r[0]; return inv_dr*( F(i+1,j,k,comp) - F(i,j,k,comp) ); }; @@ -118,6 +125,8 @@ struct CylindricalYeeAlgorithm { int const i, int const j, int const k, int const comp ) { using namespace amrex; + ignore_unused(n_coefs_r); + Real const inv_dr = coefs_r[0]; return inv_dr*( F(i,j,k,comp) - F(i-1,j,k,comp) ); }; @@ -130,6 +139,8 @@ struct CylindricalYeeAlgorithm { amrex::Real const * const coefs_z, int const n_coefs_z, int const i, int const j, int const k, int const comp ) { + amrex::ignore_unused(n_coefs_z); + amrex::Real const inv_dz = coefs_z[0]; return inv_dz*( F(i,j+1,k,comp) - F(i,j,k,comp) ); }; @@ -142,6 +153,8 @@ struct CylindricalYeeAlgorithm { amrex::Real const * const coefs_z, int const n_coefs_z, int const i, int const j, int const k, int const comp ) { + amrex::ignore_unused(n_coefs_z); + amrex::Real const inv_dz = coefs_z[0]; return inv_dz*( F(i,j,k,comp) - F(i,j-1,k,comp) ); }; diff --git a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/FieldAccessorFunctors.H b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/FieldAccessorFunctors.H new file mode 100644 index 00000000000..3ba6de3355f --- /dev/null +++ b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceAlgorithms/FieldAccessorFunctors.H @@ -0,0 +1,40 @@ +#ifndef WARPX_FIELD_ACCESSOR_FUNCTORS_H +#define WARPX_FIELD_ACCESSOR_FUNCTORS_H +#include "WarpX.H" +/** + * \brief Functor that returns the division of the source m_field Array4 value + by macroparameter, m_parameter value at the respective (i,j,k,ncomp). + */ +struct FieldAccessorMacroscopic +{ + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + FieldAccessorMacroscopic ( amrex::Array4 const a_field, + amrex::Array4 const a_parameter ) + : m_field(a_field), m_parameter(a_parameter) {} + + /** + * \brief return field value at (i,j,k,ncomp) scaled by (1/m_parameters(i,j,k,ncomp)) + * \param[in] i index along x of the Array4, m_field and m_parameter. + * \param[in] j index along y of the Array4, m_field and m_parameter. + * \param[in] k index along z of the Array4, m_field and m_parameter. + * \param[in] ncomp index along fourth component of the Array4, containing field-data + to be returned after diving with zero-th component + of m_paramter. + * + * \return m_field/m_paramter at (i,j,k,ncomp) + */ + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + amrex::Real operator() (int const i, int const j, + int const k, int const ncomp) const noexcept + { + return ( m_field(i, j, k, ncomp) / m_parameter(i, j, k, 0) ) ; + } +private: + /** Array4 of the source field to be scaled and returned by the operator() */ + amrex::Array4 const m_field; + /** Array4 of the macroscopic parameter used to divide m_field in the operator() */ + amrex::Array4 const m_parameter; +}; + + +#endif diff --git a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.H b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.H index 31f8008bff6..0c37e3fa8e9 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.H +++ b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.H @@ -96,12 +96,12 @@ class FiniteDifferenceSolver #ifdef WARPX_DIM_RZ amrex::Real m_dr, m_rmin; amrex::Real m_nmodes; - amrex::Gpu::ManagedVector m_stencil_coefs_r; - amrex::Gpu::ManagedVector m_stencil_coefs_z; + amrex::Gpu::DeviceVector m_stencil_coefs_r; + amrex::Gpu::DeviceVector m_stencil_coefs_z; #else - amrex::Gpu::ManagedVector m_stencil_coefs_x; - amrex::Gpu::ManagedVector m_stencil_coefs_y; - amrex::Gpu::ManagedVector m_stencil_coefs_z; + amrex::Gpu::DeviceVector m_stencil_coefs_x; + amrex::Gpu::DeviceVector m_stencil_coefs_y; + amrex::Gpu::DeviceVector m_stencil_coefs_z; #endif public: @@ -164,7 +164,7 @@ class FiniteDifferenceSolver const std::array,3>& Efield, amrex::MultiFab& divE ); - template< typename T_Algo > + template< typename T_Algo, typename T_MacroAlgo > void MacroscopicEvolveECartesian ( std::array< std::unique_ptr< amrex::MultiFab>, 3>& Efield, std::array< std::unique_ptr< amrex::MultiFab>, 3> const &Bfield, diff --git a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.cpp b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.cpp index 08ff5e18f60..fcab20510aa 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/FiniteDifferenceSolver.cpp @@ -33,26 +33,56 @@ FiniteDifferenceSolver::FiniteDifferenceSolver ( m_rmin = WarpX::GetInstance().Geom(0).ProbLo(0); if (fdtd_algo == MaxwellSolverAlgo::Yee) { + amrex::Vector stencil_coefs_r, stencil_coefs_z; CylindricalYeeAlgorithm::InitializeStencilCoefficients( cell_size, - m_stencil_coefs_r, m_stencil_coefs_z ); + stencil_coefs_r, stencil_coefs_z ); + m_stencil_coefs_r.resize(stencil_coefs_r.size()); + m_stencil_coefs_z.resize(stencil_coefs_z.size()); + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, + stencil_coefs_r.begin(), stencil_coefs_r.end(), + m_stencil_coefs_r.begin()); + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, + stencil_coefs_z.begin(), stencil_coefs_z.end(), + m_stencil_coefs_z.begin()); + amrex::Gpu::synchronize(); + } else { + amrex::Abort("Unknown algorithm"); + } #else + amrex::Vector stencil_coefs_x, stencil_coefs_y, stencil_coefs_z; + if (do_nodal) { CartesianNodalAlgorithm::InitializeStencilCoefficients( cell_size, - m_stencil_coefs_x, m_stencil_coefs_y, m_stencil_coefs_z ); + stencil_coefs_x, stencil_coefs_y, stencil_coefs_z ); } else if (fdtd_algo == MaxwellSolverAlgo::Yee) { CartesianYeeAlgorithm::InitializeStencilCoefficients( cell_size, - m_stencil_coefs_x, m_stencil_coefs_y, m_stencil_coefs_z ); + stencil_coefs_x, stencil_coefs_y, stencil_coefs_z ); } else if (fdtd_algo == MaxwellSolverAlgo::CKC) { CartesianCKCAlgorithm::InitializeStencilCoefficients( cell_size, - m_stencil_coefs_x, m_stencil_coefs_y, m_stencil_coefs_z ); + stencil_coefs_x, stencil_coefs_y, stencil_coefs_z ); -#endif } else { amrex::Abort("Unknown algorithm"); } + + m_stencil_coefs_x.resize(stencil_coefs_x.size()); + m_stencil_coefs_y.resize(stencil_coefs_y.size()); + m_stencil_coefs_z.resize(stencil_coefs_z.size()); + + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, + stencil_coefs_x.begin(), stencil_coefs_x.end(), + m_stencil_coefs_x.begin()); + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, + stencil_coefs_y.begin(), stencil_coefs_y.end(), + m_stencil_coefs_y.begin()); + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, + stencil_coefs_z.begin(), stencil_coefs_z.end(), + m_stencil_coefs_z.begin()); + amrex::Gpu::synchronize(); +#endif } diff --git a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicEvolveE.cpp b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicEvolveE.cpp index b7f2e26da8d..8ed0f5927d8 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicEvolveE.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicEvolveE.cpp @@ -5,10 +5,14 @@ #else # include "FiniteDifferenceAlgorithms/CartesianYeeAlgorithm.H" # include "FiniteDifferenceAlgorithms/CartesianCKCAlgorithm.H" +# include "FiniteDifferenceAlgorithms/FieldAccessorFunctors.H" #endif #include "Utils/WarpXConst.H" -#include +#include "Utils/CoarsenIO.H" #include +#include +#include + using namespace amrex; @@ -21,6 +25,7 @@ void FiniteDifferenceSolver::MacroscopicEvolveE ( // Select algorithm (The choice of algorithm is a runtime option, // but we compile code for each algorithm, using templates) #ifdef WARPX_DIM_RZ + amrex::ignore_unused(Efield, Bfield, Jfield, dt, macroscopic_properties); amrex::Abort("currently macro E-push does not work for RZ"); #else if (m_do_nodal) { @@ -28,15 +33,35 @@ void FiniteDifferenceSolver::MacroscopicEvolveE ( } else if (m_fdtd_algo == MaxwellSolverAlgo::Yee) { - MacroscopicEvolveECartesian ( Efield, Bfield, Jfield, dt, - macroscopic_properties ); + if (WarpX::macroscopic_solver_algo == MacroscopicSolverAlgo::LaxWendroff) { + + MacroscopicEvolveECartesian + ( Efield, Bfield, Jfield, dt, macroscopic_properties ); + + } + if (WarpX::macroscopic_solver_algo == MacroscopicSolverAlgo::BackwardEuler) { + + MacroscopicEvolveECartesian + ( Efield, Bfield, Jfield, dt, macroscopic_properties ); + + } } else if (m_fdtd_algo == MaxwellSolverAlgo::CKC) { // Note : EvolveE is the same for CKC and Yee. // In the templated Yee and CKC calls, the core operations for EvolveE is the same. - MacroscopicEvolveECartesian ( Efield, Bfield, Jfield, dt, - macroscopic_properties ); + if (WarpX::macroscopic_solver_algo == MacroscopicSolverAlgo::LaxWendroff) { + + MacroscopicEvolveECartesian + ( Efield, Bfield, Jfield, dt, macroscopic_properties ); + + } else if (WarpX::macroscopic_solver_algo == MacroscopicSolverAlgo::BackwardEuler) { + + MacroscopicEvolveECartesian + ( Efield, Bfield, Jfield, dt, macroscopic_properties ); + + } + } else { amrex::Abort("Unknown algorithm"); } @@ -47,34 +72,26 @@ void FiniteDifferenceSolver::MacroscopicEvolveE ( #ifndef WARPX_DIM_RZ -template +template void FiniteDifferenceSolver::MacroscopicEvolveECartesian ( std::array< std::unique_ptr, 3 >& Efield, std::array< std::unique_ptr, 3 > const& Bfield, std::array< std::unique_ptr, 3 > const& Jfield, amrex::Real const dt, std::unique_ptr const& macroscopic_properties ) { - const int ¯oscopic_solver_algo = WarpX::macroscopic_solver_algo; - Real sigma = macroscopic_properties->sigma(); - Real const mu = macroscopic_properties->mu(); - Real const epsilon = macroscopic_properties->epsilon(); - - Real alpha = 0._rt; - Real beta = 0._rt; - Real fac1 = 0._rt; - Real inv_fac = 0._rt; - - if (macroscopic_solver_algo == MacroscopicSolverAlgo::BackwardEuler) { - fac1 = sigma * dt / epsilon; - inv_fac = 1._rt / ( 1._rt + fac1); - alpha = inv_fac; - beta = dt * inv_fac / epsilon; - } else if (macroscopic_solver_algo == MacroscopicSolverAlgo::LaxWendroff) { - fac1 = 0.5_rt * sigma * dt / epsilon; - inv_fac = 1._rt / ( 1._rt + fac1); - alpha = (1.0_rt - fac1) * inv_fac; - beta = dt * inv_fac / epsilon; - } + auto& sigma_mf = macroscopic_properties->getsigma_mf(); + auto& epsilon_mf = macroscopic_properties->getepsilon_mf(); + auto& mu_mf = macroscopic_properties->getmu_mf(); + + // Index type required for calling CoarsenIO::Interp to interpolate macroscopic + // properties from their respective staggering to the Ex, Ey, Ez locations + amrex::GpuArray const& sigma_stag = macroscopic_properties->sigma_IndexType; + amrex::GpuArray const& epsilon_stag = macroscopic_properties->epsilon_IndexType; + amrex::GpuArray const& Ex_stag = macroscopic_properties->Ex_IndexType; + amrex::GpuArray const& Ey_stag = macroscopic_properties->Ey_IndexType; + amrex::GpuArray const& Ez_stag = macroscopic_properties->Ez_IndexType; + amrex::GpuArray const& macro_cr = macroscopic_properties->macro_cr_ratio; + // Loop through the grids, and over the tiles within each grid #ifdef _OPENMP @@ -89,6 +106,14 @@ void FiniteDifferenceSolver::MacroscopicEvolveECartesian ( Array4 const& Bx = Bfield[0]->array(mfi); Array4 const& By = Bfield[1]->array(mfi); Array4 const& Bz = Bfield[2]->array(mfi); + Array4 const& jx = Jfield[0]->array(mfi); + Array4 const& jy = Jfield[1]->array(mfi); + Array4 const& jz = Jfield[2]->array(mfi); + + // material prop // + Array4 const& sigma_arr = sigma_mf.array(mfi); + Array4 const& eps_arr = epsilon_mf.array(mfi); + Array4 const& mu_arr = mu_mf.array(mfi); // Extract stencil coefficients Real const * const AMREX_RESTRICT coefs_x = m_stencil_coefs_x.dataPtr(); @@ -98,55 +123,62 @@ void FiniteDifferenceSolver::MacroscopicEvolveECartesian ( Real const * const AMREX_RESTRICT coefs_z = m_stencil_coefs_z.dataPtr(); int const n_coefs_z = m_stencil_coefs_z.size(); + FieldAccessorMacroscopic const Hx(Bx, mu_arr); + FieldAccessorMacroscopic const Hy(By, mu_arr); + FieldAccessorMacroscopic const Hz(Bz, mu_arr); + // Extract tileboxes for which to loop Box const& tex = mfi.tilebox(Efield[0]->ixType().toIntVect()); Box const& tey = mfi.tilebox(Efield[1]->ixType().toIntVect()); Box const& tez = mfi.tilebox(Efield[2]->ixType().toIntVect()); - - + // starting component to interpolate macro properties to Ex, Ey, Ez locations + const int scomp = 0; // Loop over the cells and update the fields amrex::ParallelFor(tex, tey, tez, - [=] AMREX_GPU_DEVICE (int i, int j, int k){ - Ex(i, j, k) = alpha * Ex(i, j, k) + (beta/mu) - * ( - T_Algo::DownwardDz(By, coefs_z, n_coefs_z, i, j, k) - + T_Algo::DownwardDy(Bz, coefs_y, n_coefs_y, i, j, k)); + //// Interpolate conductivity, sigma, to Ex position on the grid + amrex::Real const sigma_interp = CoarsenIO::Interp( sigma_arr, sigma_stag, + Ex_stag, macro_cr, i, j, k, scomp); + // Interpolated permittivity, epsilon, to Ex position on the grid + amrex::Real const epsilon_interp = CoarsenIO::Interp( eps_arr, epsilon_stag, + Ex_stag, macro_cr, i, j, k, scomp); + amrex::Real alpha = T_MacroAlgo::alpha( sigma_interp, epsilon_interp, dt); + amrex::Real beta = T_MacroAlgo::beta( sigma_interp, epsilon_interp, dt); + Ex(i, j, k) = alpha * Ex(i, j, k) + + beta * ( - T_Algo::DownwardDz(Hy, coefs_z, n_coefs_z, i, j, k,0) + + T_Algo::DownwardDy(Hz, coefs_y, n_coefs_y, i, j, k,0) + ) - beta * jx(i, j, k); }, [=] AMREX_GPU_DEVICE (int i, int j, int k){ - Ey(i, j, k) = alpha * Ey(i, j, k) + (beta/mu) - * ( - T_Algo::DownwardDx(Bz, coefs_x, n_coefs_x, i, j, k) - + T_Algo::DownwardDz(Bx, coefs_z, n_coefs_z, i, j, k)); + amrex::Real const sigma_interp = CoarsenIO::Interp( sigma_arr, sigma_stag, + Ey_stag, macro_cr, i, j, k, scomp); + amrex::Real const epsilon_interp = CoarsenIO::Interp( eps_arr, epsilon_stag, + Ey_stag, macro_cr, i, j, k, scomp); + amrex::Real alpha = T_MacroAlgo::alpha( sigma_interp, epsilon_interp, dt); + amrex::Real beta = T_MacroAlgo::beta( sigma_interp, epsilon_interp, dt); + + Ey(i, j, k) = alpha * Ey(i, j, k) + + beta * ( - T_Algo::DownwardDx(Hz, coefs_x, n_coefs_x, i, j, k,0) + + T_Algo::DownwardDz(Hx, coefs_z, n_coefs_z, i, j, k,0) + ) - beta * jy(i, j, k); }, [=] AMREX_GPU_DEVICE (int i, int j, int k){ - Ez(i, j, k) = alpha * Ez(i, j, k) + (beta/mu) - * ( - T_Algo::DownwardDy(Bx, coefs_y, n_coefs_y, i, j, k) - + T_Algo::DownwardDx(By, coefs_x, n_coefs_x, i, j, k)); + amrex::Real const sigma_interp = CoarsenIO::Interp( sigma_arr, sigma_stag, + Ez_stag, macro_cr, i, j, k, scomp); + amrex::Real const epsilon_interp = CoarsenIO::Interp( eps_arr, epsilon_stag, + Ez_stag, macro_cr, i, j, k, scomp); + amrex::Real alpha = T_MacroAlgo::alpha( sigma_interp, epsilon_interp, dt); + amrex::Real beta = T_MacroAlgo::beta( sigma_interp, epsilon_interp, dt); + + Ez(i, j, k) = alpha * Ez(i, j, k) + + beta * ( - T_Algo::DownwardDy(Hx, coefs_y, n_coefs_y, i, j, k,0) + + T_Algo::DownwardDx(Hy, coefs_x, n_coefs_x, i, j, k,0) + ) - beta * jz(i, j, k); } - ); - - // update E using J, if source currents are specified. - if (Jfield[0]) { - Array4 const& jx = Jfield[0]->array(mfi); - Array4 const& jy = Jfield[1]->array(mfi); - Array4 const& jz = Jfield[2]->array(mfi); - - amrex::ParallelFor(tex, tey, tez, - [=] AMREX_GPU_DEVICE (int i, int j, int k) { - Ex(i, j, k) += -beta * jx(i, j, k); - }, - [=] AMREX_GPU_DEVICE (int i, int j, int k) { - Ey(i, j, k) += -beta * jy(i, j, k); - }, - [=] AMREX_GPU_DEVICE (int i, int j, int k) { - Ez(i, j, k) += -beta * jz(i, j, k); - } - ); - } } - } #endif // corresponds to ifndef WARPX_DIM_RZ diff --git a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.H b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.H index 673eae76e4a..7dfa2ff6219 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.H +++ b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.H @@ -2,11 +2,14 @@ #define WARPX_MACROSCOPICPROPERTIES_H_ +#include "Parser/WarpXParserWrapper.H" +#include "Utils/WarpXConst.H" #include + /** - * \brief This class contains the macroscopic parameters of the medium needed to + * \brief This class contains the macroscopic properties of the medium needed to * evaluate macroscopic Maxwell equation. */ class @@ -14,22 +17,130 @@ MacroscopicProperties { public: MacroscopicProperties (); // constructor - /** \brief Read user-defined macroscopic properties. Called in constructor. */ + /** Read user-defined macroscopic properties. Called in constructor. */ void ReadParameters (); - /** return Real, sigma (conductivity) of the medium. */ - amrex::Real sigma () const noexcept {return m_sigma;} - /** return Real, epsilon (permittivity) of the medium. */ - amrex::Real epsilon () const noexcept {return m_epsilon;} - /** return Real, mu (permeability) of the medium. */ - amrex::Real mu () const noexcept {return m_mu;} + /** Initialize multifabs storing macroscopic multifabs */ + void InitData (); + + /** return MultiFab, sigma (conductivity) of the medium. */ + amrex::MultiFab& getsigma_mf () {return (*m_sigma_mf);} + /** return MultiFab, epsilon (permittivity) of the medium. */ + amrex::MultiFab& getepsilon_mf () {return (*m_eps_mf);} + /** return MultiFab, mu (permeability) of the medium. */ + amrex::MultiFab& getmu_mf () {return (*m_mu_mf);} + + /** Initializes the Multifabs storing macroscopic properties + * with user-defined functions(x,y,z). + */ + void InitializeMacroMultiFabUsingParser (amrex::MultiFab *macro_mf, + HostDeviceParser<3> const& macro_parser, int lev); + /** Gpu Vector with index type of the conductivity multifab */ + amrex::GpuArray sigma_IndexType; + /** Gpu Vector with index type of the permittivity multifab */ + amrex::GpuArray epsilon_IndexType; + /** Gpu Vector with index type of the permeability multifab */ + amrex::GpuArray mu_IndexType; + /** Gpu Vector with index type of the Ex multifab */ + amrex::GpuArray Ex_IndexType; + /** Gpu Vector with index type of the Ey multifab */ + amrex::GpuArray Ey_IndexType; + /** Gpu Vector with index type of the Ez multifab */ + amrex::GpuArray Ez_IndexType; + /** Gpu Vector with index type of coarsening ratio with default value (1,1,1) */ + amrex::GpuArray macro_cr_ratio; private: /** Conductivity, sigma, of the medium */ - amrex::Real m_sigma; + amrex::Real m_sigma = 0.0; /** Permittivity, epsilon, of the medium */ - amrex::Real m_epsilon; + amrex::Real m_epsilon = PhysConst::ep0; /** Permeability, mu, of the medium */ - amrex::Real m_mu; + amrex::Real m_mu = PhysConst::mu0; + /** Multifab for m_sigma */ + std::unique_ptr m_sigma_mf; + /** Multifab for m_epsilon */ + std::unique_ptr m_eps_mf; + /** Multifab for m_mu */ + std::unique_ptr m_mu_mf; + /** Stores initialization type for conductivity : constant or parser */ + std::string m_sigma_s = "constant"; + /** Stores initialization type for permittivity : constant or parser */ + std::string m_epsilon_s = "constant"; + /** Stores initialization type for permeability : constant or parser */ + std::string m_mu_s = "constant"; + + /** string for storing parser function */ + std::string m_str_sigma_function; + std::string m_str_epsilon_function; + std::string m_str_mu_function; + /** Parser Wrappers */ + std::unique_ptr > m_sigma_parser; + std::unique_ptr > m_epsilon_parser; + std::unique_ptr > m_mu_parser; +}; + +/** + * \brief + * This struct contains only static functions to compute the co-efficients for the + * Lax-Wendroff scheme of macroscopic Maxwells equations using + * macroscopic properties, namely, conductivity (sigma), permittivity (epsilon). + * Permeability of the material, mu, is used as (beta/mu) for the E-update + * defined in MacroscopicEvolveECartesian(). + */ +struct LaxWendroffAlgo { + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + static amrex::Real alpha (amrex::Real const sigma, + amrex::Real const epsilon, + amrex::Real dt) { + using namespace amrex; + amrex::Real fac1 = 0.5_rt * sigma * dt / epsilon; + amrex::Real alpha = (1._rt - fac1)/(1._rt + fac1); + return alpha; + } + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + static amrex::Real beta (amrex::Real const sigma, + amrex::Real const epsilon, + amrex::Real dt) { + using namespace amrex; + amrex::Real fac1 = 0.5_rt * sigma * dt / epsilon; + amrex::Real beta = dt / ( epsilon * (1._rt + fac1) ); + return beta; + } + +}; + +/** + * \brief + * This struct contains only static functions to compute the co-efficients for the + * BackwardEuler scheme of macroscopic Maxwells equations using + * macroscopic properties, namely, conductivity (sigma) and permittivity (epsilon). + * Permeability of the material, mu, is used as (beta/mu) for the E-update + * defined in MacroscopicEvolveECartesian(). + */ +struct BackwardEulerAlgo { + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + static amrex::Real alpha (amrex::Real const sigma, + amrex::Real const epsilon, + amrex::Real dt) { + using namespace amrex; + amrex::Real fac1 = sigma * dt / epsilon; + amrex::Real alpha = (1._rt)/(1._rt + fac1); + return alpha; + } + + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + static amrex::Real beta (amrex::Real const sigma, + amrex::Real const epsilon, + amrex::Real dt) { + using namespace amrex; + amrex::Real fac1 = sigma * dt / epsilon; + amrex::Real beta = dt / ( epsilon * (1._rt + fac1) ); + return beta; + } + }; #endif // WARPX_MACROSCOPIC_PROPERTIES_H_ diff --git a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp index 7bb1911fd78..a368dc02682 100644 --- a/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp +++ b/Source/FieldSolver/FiniteDifferenceSolver/MacroscopicProperties/MacroscopicProperties.cpp @@ -1,5 +1,6 @@ #include "MacroscopicProperties.H" #include +#include "WarpX.H" using namespace amrex; @@ -12,10 +13,185 @@ void MacroscopicProperties::ReadParameters () { ParmParse pp("macroscopic"); - // Since macroscopic maxwell solve is turned on, user must define sigma, mu, and epsilon // - pp.get("sigma", m_sigma); - pp.get("mu", m_mu); - pp.get("epsilon", m_epsilon); + // Since macroscopic maxwell solve is turned on, + // user-defined sigma, mu, and epsilon are queried. + // The vacuum values are used as default for the macroscopic parameters + // with a warning message to the user to indicate that no value was specified. + + // Query input for material conductivity, sigma. + bool sigma_specified = false; + if (pp.query("sigma", m_sigma)) { + m_sigma_s = "constant"; + sigma_specified = true; + } + if (pp.query("sigma_function(x,y,z)", m_str_sigma_function) ) { + m_sigma_s = "parse_sigma_function"; + sigma_specified = true; + } + if (!sigma_specified) { + amrex::Print() << "WARNING: Material conductivity is not specified. Using default vacuum value of " << m_sigma << " in the simulation\n"; + } + // initialization of sigma (conductivity) with parser + if (m_sigma_s == "parse_sigma_function") { + Store_parserString(pp, "sigma_function(x,y,z)", m_str_sigma_function); + m_sigma_parser.reset(new ParserWrapper<3>( + makeParser(m_str_sigma_function,{"x","y","z"}) ) ); + } + + bool epsilon_specified = false; + if (pp.query("epsilon", m_epsilon)) { + m_epsilon_s = "constant"; + epsilon_specified = true; + } + if (pp.query("epsilon_function(x,y,z)", m_str_epsilon_function) ) { + m_epsilon_s = "parse_epsilon_function"; + epsilon_specified = true; + } + if (!epsilon_specified) { + amrex::Print() << "WARNING: Material permittivity is not specified. Using default vacuum value of " << m_epsilon << " in the simulation\n"; + } + + // initialization of epsilon (permittivity) with parser + if (m_epsilon_s == "parse_epsilon_function") { + Store_parserString(pp, "epsilon_function(x,y,z)", m_str_epsilon_function); + m_epsilon_parser.reset(new ParserWrapper<3>( + makeParser(m_str_epsilon_function,{"x","y","z"}) ) ); + } + + // Query input for material permittivity, epsilon. + bool mu_specified = false; + if (pp.query("mu", m_mu)) { + m_mu_s = "constant"; + mu_specified = true; + } + if (pp.query("mu_function(x,y,z)", m_str_mu_function) ) { + m_mu_s = "parse_mu_function"; + mu_specified = true; + } + if (!mu_specified) { + amrex::Print() << "WARNING: Material permittivity is not specified. Using default vacuum value of " << m_mu << " in the simulation\n"; + } + + // initialization of mu (permeability) with parser + if (m_mu_s == "parse_mu_function") { + Store_parserString(pp, "mu_function(x,y,z)", m_str_mu_function); + m_mu_parser.reset(new ParserWrapper<3>( + makeParser(m_str_mu_function,{"x","y","z"}) ) ); + } + +} + +void +MacroscopicProperties::InitData () +{ + amrex::Print() << "we are in init data of macro \n"; + auto & warpx = WarpX::GetInstance(); + + // Get BoxArray and DistributionMap of warpx instant. + int lev = 0; + BoxArray ba = warpx.boxArray(lev); + DistributionMapping dmap = warpx.DistributionMap(lev); + int ng = 3; + // Define material property multifabs using ba and dmap from WarpX instance + // sigma is cell-centered MultiFab + m_sigma_mf = std::make_unique(amrex::convert(ba,IntVect::TheUnitVector()), dmap, 1, ng); + // epsilon is cell-centered MultiFab + m_eps_mf = std::make_unique(amrex::convert(ba,IntVect::TheUnitVector()), dmap, 1, ng); + // mu is cell-centered MultiFab + m_mu_mf = std::make_unique(amrex::convert(ba,IntVect::TheUnitVector()), dmap, 1, ng); + // Initialize sigma + if (m_sigma_s == "constant") { + + m_sigma_mf->setVal(m_sigma); + + } else if (m_sigma_s == "parse_sigma_function") { + + InitializeMacroMultiFabUsingParser(m_sigma_mf.get(), getParser(m_sigma_parser), lev); + } + // Initialize epsilon + if (m_epsilon_s == "constant") { + + m_eps_mf->setVal(m_epsilon); + + } else if (m_epsilon_s == "parse_epsilon_function") { + + InitializeMacroMultiFabUsingParser(m_eps_mf.get(), getParser(m_epsilon_parser), lev); + + } + // Initialize mu + if (m_mu_s == "constant") { + + m_mu_mf->setVal(m_mu); + + } else if (m_mu_s == "parse_mu_function") { + + InitializeMacroMultiFabUsingParser(m_mu_mf.get(), getParser(m_mu_parser), lev); + + } + + + IntVect sigma_stag = m_sigma_mf->ixType().toIntVect(); + IntVect epsilon_stag = m_eps_mf->ixType().toIntVect(); + IntVect mu_stag = m_mu_mf->ixType().toIntVect(); + IntVect Ex_stag = warpx.getEfield_fp(0,0).ixType().toIntVect(); + IntVect Ey_stag = warpx.getEfield_fp(0,1).ixType().toIntVect(); + IntVect Ez_stag = warpx.getEfield_fp(0,2).ixType().toIntVect(); + + for ( int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + sigma_IndexType[idim] = sigma_stag[idim]; + epsilon_IndexType[idim] = epsilon_stag[idim]; + mu_IndexType[idim] = mu_stag[idim]; + Ex_IndexType[idim] = Ex_stag[idim]; + Ey_IndexType[idim] = Ey_stag[idim]; + Ez_IndexType[idim] = Ez_stag[idim]; + macro_cr_ratio[idim] = 1; + } +#if (AMREX_SPACEDIM==2) + sigma_IndexType[2] = 0; + epsilon_IndexType[2] = 0; + mu_IndexType[2] = 0; + Ex_IndexType[2] = 0; + Ey_IndexType[2] = 0; + Ez_IndexType[2] = 0; + macro_cr_ratio[2] = 1; +#endif + + +} + +void +MacroscopicProperties::InitializeMacroMultiFabUsingParser ( + MultiFab *macro_mf, HostDeviceParser<3> const& macro_parser, + int lev) +{ + auto& warpx = WarpX::GetInstance(); + const auto dx_lev = warpx.Geom(lev).CellSizeArray(); + const RealBox& real_box = warpx.Geom(lev).ProbDomain(); + IntVect iv = macro_mf->ixType().toIntVect(); + IntVect grown_iv = iv ; + for ( MFIter mfi(*macro_mf, TilingIfNotGPU()); mfi.isValid(); ++mfi ) { + // Initialize ghost cells in addition to valid cells + + const Box& tb = mfi.growntilebox(grown_iv); + auto const& macro_fab = macro_mf->array(mfi); + amrex::ParallelFor (tb, + [=] AMREX_GPU_DEVICE (int i, int j, int k) { + // Shift x, y, z position based on index type + Real fac_x = (1._rt - iv[0]) * dx_lev[0] * 0.5_rt; + Real x = i * dx_lev[0] + real_box.lo(0) + fac_x; + + Real fac_y = (1._rt - iv[1]) * dx_lev[1] * 0.5_rt; + Real y = j * dx_lev[1] + real_box.lo(1) + fac_y; + + Real fac_z = (1._rt - iv[2]) * dx_lev[2] * 0.5_rt; + Real z = k * dx_lev[2] + real_box.lo(2) + fac_z; + + // initialize the macroparameter + macro_fab(i,j,k) = macro_parser(x,y,z); + }); + + } + } diff --git a/Source/FieldSolver/SpectralSolver/AnyFFT.H b/Source/FieldSolver/SpectralSolver/AnyFFT.H index a02f86343ce..1a551c16701 100644 --- a/Source/FieldSolver/SpectralSolver/AnyFFT.H +++ b/Source/FieldSolver/SpectralSolver/AnyFFT.H @@ -1,8 +1,17 @@ +/* Copyright 2019-2020 + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + #ifndef ANYFFT_H_ #define ANYFFT_H_ -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) # include +#elif defined(AMREX_USE_HIP) +# include #else # include #endif @@ -20,12 +29,18 @@ namespace AnyFFT // First, define library-dependent types (complex, FFT plan) /** Complex type for FFT, depends on FFT library */ -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) # ifdef AMREX_USE_FLOAT using Complex = cuComplex; # else using Complex = cuDoubleComplex; # endif +#elif defined(AMREX_USE_HIP) +# ifdef AMREX_USE_FLOAT + using Complex = float2; +# else + using Complex = double2; +# endif #else # ifdef AMREX_USE_FLOAT using Complex = fftwf_complex; @@ -37,8 +52,10 @@ namespace AnyFFT /** Library-dependent FFT plans type, which holds one fft plan per box * (plans are only initialized for the boxes that are owned by the local MPI rank). */ -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) using VendorFFTPlan = cufftHandle; +#elif defined(AMREX_USE_HIP) + using VendorFFTPlan = rocfft_plan; #else # ifdef AMREX_USE_FLOAT using VendorFFTPlan = fftwf_plan; diff --git a/Source/FieldSolver/SpectralSolver/Make.package b/Source/FieldSolver/SpectralSolver/Make.package index 04ae352be52..8be3a6812ea 100644 --- a/Source/FieldSolver/SpectralSolver/Make.package +++ b/Source/FieldSolver/SpectralSolver/Make.package @@ -3,6 +3,8 @@ CEXE_sources += SpectralFieldData.cpp CEXE_sources += SpectralKSpace.cpp ifeq ($(USE_CUDA),TRUE) CEXE_sources += WrapCuFFT.cpp +else ifeq ($(USE_HIP),TRUE) + CEXE_sources += WrapRocFFT.cpp else CEXE_sources += WrapFFTW.cpp endif diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.H index 45a35098b4b..9d3facf3fb3 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.H @@ -26,6 +26,22 @@ class AvgGalileanAlgorithm : public SpectralBaseAlgorithm const amrex::Array& v_galilean, const amrex::Real dt); + /** + * \brief Virtual function for current correction in Fourier space + * ( Vay et al, 2013). + * This function overrides the virtual function \c CurrentCorrection in the + * base class \c SpectralBaseAlgorithm and cannot be overridden by further + * derived classes. + * + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + * \param[in] rho Unique pointer to \c MultiFab storing the charge density + */ + virtual void CurrentCorrection (SpectralFieldData& field_data, + std::array,3>& current, + const std::unique_ptr& rho) override final; + /** * \brief Virtual function for Vay current deposition in Fourier space * ( Vay et al, 2013). diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp index 0ba6030311a..ea381235e47 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/AvgGalileanAlgorithm.cpp @@ -225,7 +225,7 @@ void AvgGalileanAlgorithm::InitializeSpectralCoefficients( }); } -}; +} /* Advance the E and B field in spectral space (stored in `f`) * over one time step */ @@ -249,10 +249,8 @@ AvgGalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ Array4 Theta2_arr = Theta2_coef[mfi].array(); Array4 Psi1_arr = Psi1_coef[mfi].array(); Array4 Psi2_arr = Psi2_coef[mfi].array(); - Array4 Psi3_arr = Psi3_coef[mfi].array(); Array4 A1_arr = A1_coef[mfi].array(); - Array4 A2_arr = A2_coef[mfi].array(); Array4 Rhonew_arr = Rhonew_coef[mfi].array(); Array4 Rhoold_arr = Rhoold_coef[mfi].array(); Array4 Jcoef_arr =Jcoef_coef[mfi].array(); @@ -284,12 +282,6 @@ AvgGalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ const Complex rho_old = fields(i,j,k,Idx::rho_old); const Complex rho_new = fields(i,j,k,Idx::rho_new); - const Complex Ex_avg = fields(i,j,k,Idx::Ex_avg); - const Complex Ey_avg= fields(i,j,k,Idx::Ey_avg); - const Complex Ez_avg = fields(i,j,k,Idx::Ez_avg); - const Complex Bx_avg = fields(i,j,k,Idx::Bx_avg); - const Complex By_avg = fields(i,j,k,Idx::By_avg); - const Complex Bz_avg = fields(i,j,k,Idx::Bz_avg); // k vector values, and coefficients const Real kx = modified_kx_arr[i]; @@ -315,14 +307,11 @@ AvgGalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ const Complex Psi1 = Psi1_arr(i,j,k); const Complex Psi2 = Psi2_arr(i,j,k); - const Complex Psi3 = Psi3_arr(i,j,k); const Complex A1 = A1_arr(i,j,k); - const Complex A2 = A2_arr(i,j,k); const Complex CRhoold= Rhoold_arr(i,j,k); const Complex CRhonew= Rhonew_arr(i,j,k); const Complex Jcoef = Jcoef_arr(i,j,k); - //Update E (see the original Galilean article) fields(i,j,k,Idx::Ex) = T2*C*Ex_old + T2*S_ck*c2*I*(ky*Bz_old - kz*By_old) @@ -333,6 +322,7 @@ AvgGalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ fields(i,j,k,Idx::Ez) = T2*C*Ez_old + T2*S_ck*c2*I*(kx*By_old - ky*Bx_old) + X4*Jz - I*(X2*rho_new - T2*X3*rho_old)*kz; + // Update B (see the original Galilean article) // Note: here X1 is T2*x1/(ep0*c*c*k_norm*k_norm), where // x1 has the same definition as in the original paper @@ -346,7 +336,7 @@ AvgGalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ - T2*S_ck*I*(kx*Ey_old - ky*Ex_old) + X1*I*(kx*Jy - ky*Jx); -//Update the averaged E,B fields in time on the interval [(n-1/2)dx, (n+1/2)dx] + //Update the averaged E,B fields in time on the interval [(n-1/2)dx, (n+1/2)dx] fields(i,j,k,Idx::Ex_avg) = Psi1*Ex_old - Psi2*c2*I*(ky*Bz_old - kz*By_old) + Jcoef*Jx + ( CRhonew * rho_new + CRhoold*rho_old )*kx; @@ -368,11 +358,19 @@ AvgGalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ + A1*I*(kx*Jy - ky*Jx)*inv_ep0; }); } -}; +} + +void +AvgGalileanAlgorithm::CurrentCorrection (SpectralFieldData& /*field_data*/, + std::array,3>& /*current*/, + const std::unique_ptr& /*rho*/) +{ + amrex::Abort("Current correction not implemented for averaged Galilean PSATD"); +} void -AvgGalileanAlgorithm::VayDeposition (SpectralFieldData& field_data, - std::array,3>& current) +AvgGalileanAlgorithm::VayDeposition (SpectralFieldData& /*field_data*/, + std::array,3>& /*current*/) { amrex::Abort("Vay deposition not implemented for averaged Galilean PSATD"); } diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt index c5ebf1c3e80..a050bd5c2a0 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/CMakeLists.txt @@ -12,5 +12,6 @@ if(WarpX_DIMS STREQUAL RZ) PRIVATE SpectralBaseAlgorithmRZ.cpp PsatdAlgorithmRZ.cpp + GalileanPsatdAlgorithmRZ.cpp ) endif() diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.H index 799eca8b1c4..83ae83d6992 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.H @@ -10,21 +10,37 @@ class GalileanAlgorithm : public SpectralBaseAlgorithm { public: - GalileanAlgorithm(const SpectralKSpace& spectral_kspace, - const amrex::DistributionMapping& dm, - const int norder_x, const int norder_y, - const int norder_z, const bool nodal, - const amrex::Array& v_galilean, - const amrex::Real dt); + GalileanAlgorithm (const SpectralKSpace& spectral_kspace, + const amrex::DistributionMapping& dm, + const int norder_x, const int norder_y, + const int norder_z, const bool nodal, + const amrex::Array& v_galilean, + const amrex::Real dt, + const bool update_with_rho); // Redefine update equation from base class - virtual void pushSpectralFields(SpectralFieldData& f) const override final; - virtual int getRequiredNumberOfFields() const override final { + virtual void pushSpectralFields (SpectralFieldData& f) const override final; + virtual int getRequiredNumberOfFields () const override final { return SpectralFieldIndex::n_fields; }; - void InitializeSpectralCoefficients(const SpectralKSpace& spectral_kspace, - const amrex::DistributionMapping& dm, - const amrex::Array& v_galilean, - const amrex::Real dt); + void InitializeSpectralCoefficients (const SpectralKSpace& spectral_kspace, + const amrex::DistributionMapping& dm, + const amrex::Real dt); + + /** + * \brief Virtual function for current correction in Fourier space + * ( Vay et al, 2013). + * This function overrides the virtual function \c CurrentCorrection in the + * base class \c SpectralBaseAlgorithm and cannot be overridden by further + * derived classes. + * + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + * \param[in] rho Unique pointer to \c MultiFab storing the charge density + */ + virtual void CurrentCorrection (SpectralFieldData& field_data, + std::array,3>& current, + const std::unique_ptr& rho) override final; /** * \brief Virtual function for Vay current deposition in Fourier space @@ -43,6 +59,9 @@ class GalileanAlgorithm : public SpectralBaseAlgorithm private: SpectralRealCoefficients C_coef, S_ck_coef; SpectralComplexCoefficients Theta2_coef, X1_coef, X2_coef, X3_coef, X4_coef; + amrex::Array m_v_galilean; + amrex::Real m_dt; + bool m_update_with_rho; }; #endif // WARPX_USE_PSATD #endif // WARPX_GALILEAN_ALGORITHM_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp index e606b323274..0757d17cdac 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanAlgorithm.cpp @@ -14,10 +14,13 @@ GalileanAlgorithm::GalileanAlgorithm(const SpectralKSpace& spectral_kspace, const int norder_x, const int norder_y, const int norder_z, const bool nodal, const Array& v_galilean, - const Real dt) + const Real dt, + const bool update_with_rho) // Initialize members of base class - : SpectralBaseAlgorithm( spectral_kspace, dm, - norder_x, norder_y, norder_z, nodal ) + : SpectralBaseAlgorithm(spectral_kspace, dm, norder_x, norder_y, norder_z, nodal), + m_v_galilean(v_galilean), + m_dt(dt), + m_update_with_rho(update_with_rho) { const BoxArray& ba = spectral_kspace.spectralspace_ba; @@ -30,14 +33,14 @@ GalileanAlgorithm::GalileanAlgorithm(const SpectralKSpace& spectral_kspace, X4_coef = SpectralComplexCoefficients(ba, dm, 1, 0); Theta2_coef = SpectralComplexCoefficients(ba, dm, 1, 0); - InitializeSpectralCoefficients(spectral_kspace, dm, v_galilean, dt); - -}; + InitializeSpectralCoefficients(spectral_kspace, dm, dt); +} -/* Advance the E and B field in spectral space (stored in `f`) - * over one time step */ +/* Advance the E and B field in spectral space (stored in `f`) over one time step */ void -GalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ +GalileanAlgorithm::pushSpectralFields (SpectralFieldData& f) const +{ + const bool update_with_rho = m_update_with_rho; // Loop over boxes for (MFIter mfi(f.fields); mfi.isValid(); ++mfi){ @@ -46,6 +49,7 @@ GalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ // Extract arrays for the fields to be updated Array4 fields = f.fields[mfi].array(); + // Extract arrays for the coefficients Array4 C_arr = C_coef[mfi].array(); Array4 S_ck_arr = S_ck_coef[mfi].array(); @@ -63,8 +67,7 @@ GalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ const Real* modified_kz_arr = modified_kz_vec[mfi].dataPtr(); // Loop over indices within one box - ParallelFor(bx, - [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { // Record old values of the fields to be updated using Idx = SpectralFieldIndex; @@ -74,13 +77,15 @@ GalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ const Complex Bx_old = fields(i,j,k,Idx::Bx); const Complex By_old = fields(i,j,k,Idx::By); const Complex Bz_old = fields(i,j,k,Idx::Bz); - // Shortcut for the values of J and rho + + // Shortcuts for the values of J and rho const Complex Jx = fields(i,j,k,Idx::Jx); const Complex Jy = fields(i,j,k,Idx::Jy); const Complex Jz = fields(i,j,k,Idx::Jz); const Complex rho_old = fields(i,j,k,Idx::rho_old); const Complex rho_new = fields(i,j,k,Idx::rho_new); - // k vector values, and coefficients + + // k vector values const Real kx = modified_kx_arr[i]; #if (AMREX_SPACEDIM==3) const Real ky = modified_ky_arr[j]; @@ -89,8 +94,12 @@ GalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ constexpr Real ky = 0; const Real kz = modified_kz_arr[j]; #endif - constexpr Real c2 = PhysConst::c*PhysConst::c; - constexpr Complex I = Complex{0,1}; + // Physical constant c**2 and imaginary unit + constexpr Real c2 = PhysConst::c*PhysConst::c; + constexpr Complex I = Complex{0._rt,1._rt}; + + // The definition of these coefficients is explained in more detail + // in the function InitializeSpectralCoefficients below const Real C = C_arr(i,j,k); const Real S_ck = S_ck_arr(i,j,k); const Complex X1 = X1_arr(i,j,k); @@ -99,43 +108,62 @@ GalileanAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ const Complex X4 = X4_arr(i,j,k); const Complex T2 = Theta2_arr(i,j,k); - // Update E (see the original Galilean article) - fields(i,j,k,Idx::Ex) = T2*C*Ex_old - + T2*S_ck*c2*I*(ky*Bz_old - kz*By_old) - + X4*Jx - I*(X2*rho_new - T2*X3*rho_old)*kx; - fields(i,j,k,Idx::Ey) = T2*C*Ey_old - + T2*S_ck*c2*I*(kz*Bx_old - kx*Bz_old) - + X4*Jy - I*(X2*rho_new - T2*X3*rho_old)*ky; - fields(i,j,k,Idx::Ez) = T2*C*Ez_old - + T2*S_ck*c2*I*(kx*By_old - ky*Bx_old) - + X4*Jz - I*(X2*rho_new - T2*X3*rho_old)*kz; - // Update B (see the original Galilean article) - // Note: here X1 is T2*x1/(ep0*c*c*k_norm*k_norm), where - // x1 has the same definition as in the original paper - fields(i,j,k,Idx::Bx) = T2*C*Bx_old - - T2*S_ck*I*(ky*Ez_old - kz*Ey_old) - + X1*I*(ky*Jz - kz*Jy); - fields(i,j,k,Idx::By) = T2*C*By_old - - T2*S_ck*I*(kz*Ex_old - kx*Ez_old) - + X1*I*(kz*Jx - kx*Jz); - fields(i,j,k,Idx::Bz) = T2*C*Bz_old - - T2*S_ck*I*(kx*Ey_old - ky*Ex_old) - + X1*I*(kx*Jy - ky*Jx); + // The equations in the following are the update equations for B and E, + // equations (11a) and (11b) of (Lehe et al, PRE 94, 2016), respectively, + // (or their rho-free formulation) + + // Update E (equation (11b) or its rho-free formulation): + if (update_with_rho) { + + // Ex + fields(i,j,k,Idx::Ex) = T2*C*Ex_old + + T2*S_ck*c2*I*(ky*Bz_old - kz*By_old) + + X4*Jx - I*(X2*rho_new - T2*X3*rho_old)*kx; + // Ey + fields(i,j,k,Idx::Ey) = T2*C*Ey_old + + T2*S_ck*c2*I*(kz*Bx_old - kx*Bz_old) + + X4*Jy - I*(X2*rho_new - T2*X3*rho_old)*ky; + // Ez + fields(i,j,k,Idx::Ez) = T2*C*Ez_old + + T2*S_ck*c2*I*(kx*By_old - ky*Bx_old) + + X4*Jz - I*(X2*rho_new - T2*X3*rho_old)*kz; + } else { + + Complex k_dot_J = kx * Jx + ky * Jy + kz * Jz; + Complex k_dot_E = kx * Ex_old + ky * Ey_old + kz * Ez_old; + + // Ex + fields(i,j,k,Idx::Ex) = T2 * C * Ex_old + I * T2 * S_ck * c2 * (ky * Bz_old - kz * By_old) + + X4 * Jx + X2 * k_dot_E * kx + X3 * k_dot_J * kx; + // Ey + fields(i,j,k,Idx::Ey) = T2 * C * Ey_old + I * T2 * S_ck * c2 * (kz * Bx_old - kx * Bz_old) + + X4 * Jy + X2 * k_dot_E * ky + X3 * k_dot_J * ky; + // Ez + fields(i,j,k,Idx::Ez) = T2 * C * Ez_old + I * T2 * S_ck * c2 * (kx * By_old - ky * Bx_old) + + X4 * Jz + X2 * k_dot_E * kz + X3 * k_dot_J * kz; + } + + // Update B (equation (11a) with X1 rescaled by theta/(epsilon_0*c**2*k**2)): + // Bx + fields(i,j,k,Idx::Bx) = T2*C*Bx_old - T2*S_ck*I*(ky*Ez_old - kz*Ey_old) + X1*I*(ky*Jz - kz*Jy); + // By + fields(i,j,k,Idx::By) = T2*C*By_old - T2*S_ck*I*(kz*Ex_old - kx*Ez_old) + X1*I*(kz*Jx - kx*Jz); + // Bz + fields(i,j,k,Idx::Bz) = T2*C*Bz_old - T2*S_ck*I*(kx*Ey_old - ky*Ex_old) + X1*I*(kx*Jy - ky*Jx); }); } -}; - +} -void GalileanAlgorithm::InitializeSpectralCoefficients(const SpectralKSpace& spectral_kspace, - const amrex::DistributionMapping& dm, - const Array& v_galilean, - const amrex::Real dt) +void GalileanAlgorithm::InitializeSpectralCoefficients (const SpectralKSpace& spectral_kspace, + const amrex::DistributionMapping& dm, + const amrex::Real dt) { + const bool update_with_rho = m_update_with_rho; + const BoxArray& ba = spectral_kspace.spectralspace_ba; - // Fill them with the right values: - // Loop over boxes and allocate the corresponding coefficients - // for each box owned by the local MPI proc - for (MFIter mfi(ba, dm); mfi.isValid(); ++mfi){ + + // Loop over boxes and allocate the corresponding coefficients for each box + for (MFIter mfi(ba, dm); mfi.isValid(); ++mfi) { const Box& bx = ba[mfi]; @@ -145,6 +173,7 @@ void GalileanAlgorithm::InitializeSpectralCoefficients(const SpectralKSpace& spe const Real* modified_ky = modified_ky_vec[mfi].dataPtr(); #endif const Real* modified_kz = modified_kz_vec[mfi].dataPtr(); + // Extract arrays for the coefficients Array4 C = C_coef[mfi].array(); Array4 S_ck = S_ck_coef[mfi].array(); @@ -152,17 +181,17 @@ void GalileanAlgorithm::InitializeSpectralCoefficients(const SpectralKSpace& spe Array4 X2 = X2_coef[mfi].array(); Array4 X3 = X3_coef[mfi].array(); Array4 X4 = X4_coef[mfi].array(); - Array4 Theta2 = Theta2_coef[mfi].array(); - // Extract reals (for portability on GPU) - Real vx = v_galilean[0]; + Array4 T2 = Theta2_coef[mfi].array(); + + // Extract Galilean velocity + Real vx = m_v_galilean[0]; #if (AMREX_SPACEDIM==3) - Real vy = v_galilean[1]; + Real vy = m_v_galilean[1]; #endif - Real vz = v_galilean[2]; + Real vz = m_v_galilean[2]; // Loop over indices within one box - ParallelFor(bx, - [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { // Calculate norm of vector const Real k_norm = std::sqrt( @@ -173,80 +202,279 @@ void GalileanAlgorithm::InitializeSpectralCoefficients(const SpectralKSpace& spe #else std::pow(modified_kz[j], 2)); #endif + // Physical constants c, c**2, and epsilon_0, and imaginary unit + constexpr Real c = PhysConst::c; + constexpr Real c2 = c*c; + constexpr Real ep0 = PhysConst::ep0; + constexpr Complex I = Complex{0._rt,1._rt}; - // Calculate coefficients - constexpr Real c = PhysConst::c; - constexpr Real ep0 = PhysConst::ep0; - const Complex I{0.,1.}; - if (k_norm != 0){ - - C(i,j,k) = std::cos(c*k_norm*dt); - S_ck(i,j,k) = std::sin(c*k_norm*dt)/(c*k_norm); + // Auxiliary coefficients used when update_with_rho=false + const Real dt2 = dt * dt; + const Real dt3 = dt * dt2; + Complex X2_old, X3_old; - // Calculate dot product with galilean velocity - const Real kv = modified_kx[i]*vx + + // Calculate dot product of k vector with Galilean velocity + const Real kv = modified_kx[i]*vx + #if (AMREX_SPACEDIM==3) - modified_ky[j]*vy + - modified_kz[k]*vz; + modified_ky[j]*vy + modified_kz[k]*vz; #else - modified_kz[j]*vz; + modified_kz[j]*vz; #endif + // The coefficients in the following refer to the ones given in equations + // (12a)-(12d) of (Lehe et al, PRE 94, 2016), used to update B and E + // (equations (11a) and (11b) of the same reference, respectively) + + if (k_norm != 0.) { + + // Auxiliary coefficients + const Real k2 = k_norm * k_norm; + const Real ck = c * k_norm; + const Real ckdt = ck * dt; + const Complex tmp1 = amrex::exp( I * ckdt); // limit of T2 for nu = 1 + const Complex tmp2 = amrex::exp(- I * ckdt); // limit of T2 for nu = -1 - const Real nu = kv/(k_norm*c); - const Complex theta = amrex::exp( 0.5_rt*I*kv*dt ); - const Complex theta_star = amrex::exp( -0.5_rt*I*kv*dt ); - const Complex e_theta = amrex::exp( I*c*k_norm*dt ); - - Theta2(i,j,k) = theta*theta; - - if ( (nu != 1.) && (nu != 0) ) { - - // Note: the coefficients X1, X2, X3 do not correspond - // exactly to the original Galilean paper, but the - // update equation have been modified accordingly so that - // the expressions/ below (with the update equations) - // are mathematically equivalent to those of the paper. - Complex x1 = 1._rt/(1._rt-nu*nu) * - (theta_star - C(i,j,k)*theta + I*kv*S_ck(i,j,k)*theta); - // x1, above, is identical to the original paper - X1(i,j,k) = theta*x1/(ep0*c*c*k_norm*k_norm); - // The difference betwen X2 and X3 below, and those - // from the original paper is the factor ep0*k_norm*k_norm - X2(i,j,k) = (x1 - theta*(1._rt - C(i,j,k))) - /(theta_star-theta)/(ep0*k_norm*k_norm); - X3(i,j,k) = (x1 - theta_star*(1._rt - C(i,j,k))) - /(theta_star-theta)/(ep0*k_norm*k_norm); - X4(i,j,k) = I*kv*X1(i,j,k) - theta*theta*S_ck(i,j,k)/ep0; + // See equation (12a) + C (i,j,k) = std::cos(ckdt); + S_ck(i,j,k) = std::sin(ckdt) / ck; + + // See equation (12b) + const Real nu = kv / ck; + const Complex theta = amrex::exp( I * 0.5_rt * kv * dt); + const Complex theta_star = amrex::exp(- I * 0.5_rt * kv * dt); + + // This is exp(i*(k \dot v_gal)*dt) + T2(i,j,k) = theta * theta; + + if ( (nu != 1.) && (nu != 0.) ) { + + // x1 is the coefficient chi_1 in equation (12c) + Complex x1 = 1._rt / (1._rt - nu*nu) + * (theta_star - C(i,j,k) * theta + I * kv * S_ck(i,j,k) * theta); + + // X1 multiplies i*(k \times J) in the update equation for B + X1(i,j,k) = theta * x1 / (ep0 * c2 * k2); + + if (update_with_rho) { + // X2 multiplies rho_new in the update equation for E + // X3 multiplies rho_old in the update equation for E + X2(i,j,k) = (x1 - theta * (1._rt - C(i,j,k))) / (theta_star - theta) / (ep0 * k2); + X3(i,j,k) = (x1 - theta_star * (1._rt - C(i,j,k))) / (theta_star - theta) / (ep0 * k2); + } else { + // X2_old is the coefficient chi_2 in equation (12d) + // X3_old is the coefficient chi_3 in equation (12d) + // X2 multiplies (k \dot E) in the update equation for E + // X3 multiplies (k \dot J) in the update equation for E + X2_old = (x1 - theta * (1._rt - C(i,j,k))) / (theta_star - theta); + X3_old = (x1 - theta_star * (1._rt - C(i,j,k))) / (theta_star - theta); + X2(i,j,k) = T2(i,j,k) * (X2_old - X3_old) / k2; + X3(i,j,k) = I * X2_old * (T2(i,j,k) - 1._rt) / (ep0 * k2 * kv); + } + + // X4 multiplies J in the update equation for E + X4(i,j,k) = I * kv * X1(i,j,k) - T2(i,j,k) * S_ck(i,j,k) / ep0; } - if ( nu == 0) { - X1(i,j,k) = (1._rt - C(i,j,k)) / (ep0*c*c*k_norm*k_norm); - X2(i,j,k) = (1._rt - S_ck(i,j,k)/dt) / (ep0*k_norm*k_norm); - X3(i,j,k) = (C(i,j,k) - S_ck(i,j,k)/dt) / (ep0*k_norm*k_norm); - X4(i,j,k) = -S_ck(i,j,k)/ep0; + + // Limits for nu = 0 + if (nu == 0.) { + + // X1 multiplies i*(k \times J) in the update equation for B + X1(i,j,k) = (1._rt - C(i,j,k)) / (ep0 * c2 * k2); + + if (update_with_rho) { + // X2 multiplies rho_new in the update equation for E + // X3 multiplies rho_old in the update equation for E + X2(i,j,k) = (1._rt - S_ck(i,j,k) / dt) / (ep0 * k2); + X3(i,j,k) = (C(i,j,k) - S_ck(i,j,k) / dt) / (ep0 * k2); + } else { + // X2 multiplies (k \dot E) in the update equation for E + // X3 multiplies (k \dot J) in the update equation for E + X2(i,j,k) = (1._rt - C(i,j,k)) / k2; + X3(i,j,k) = (S_ck(i,j,k) / dt - 1._rt) * dt / (ep0 * k2); + } + + // Coefficient multiplying J in update equation for E + X4(i,j,k) = - S_ck(i,j,k) / ep0; } - if ( nu == 1.) { - X1(i,j,k) = (1._rt - e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*c*c*ep0*k_norm*k_norm); - X2(i,j,k) = (3._rt - 4._rt*e_theta + e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*ep0*k_norm*k_norm*(1._rt - e_theta)); - X3(i,j,k) = (3._rt - 2._rt/e_theta - 2._rt*e_theta + e_theta*e_theta - 2._rt*I*c*k_norm*dt) / (4._rt*ep0*(e_theta - 1._rt)*k_norm*k_norm); - X4(i,j,k) = I*(-1._rt + e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*ep0*c*k_norm); + + // Limits for nu = 1 + if (nu == 1.) { + + // X1 multiplies i*(k \times J) in the update equation for B + X1(i,j,k) = (1._rt - tmp1 * tmp1 + 2._rt * I * ckdt) / (4._rt * ep0 * c2 * k2); + + if (update_with_rho) { + // X2 multiplies rho_new in the update equation for E + // X3 multiplies rho_old in the update equation for E + X2(i,j,k) = (- 3._rt + 4._rt * tmp1 - tmp1 * tmp1 - 2._rt * I * ckdt) + / (4._rt * ep0 * k2 * (tmp1 - 1._rt)); + X3(i,j,k) = (3._rt - 2._rt / tmp1 - 2._rt * tmp1 + tmp1 * tmp1 - 2._rt * I * ckdt) + / (4._rt * ep0 * k2 * (tmp1 - 1._rt)); + } else { + // X2 multiplies (k \dot E) in the update equation for E + // X3 multiplies (k \dot J) in the update equation for E + X2(i,j,k) = (1._rt - C(i,j,k)) * tmp1 / k2; + X3(i,j,k) = (2._rt * ckdt - I * tmp1 * tmp1 + 4._rt * I * tmp1 - 3._rt * I) + / (4._rt * ep0 * ck * k2); + } + + // Coefficient multiplying J in update equation for E + X4(i,j,k) = (- I + I * tmp1 * tmp1 - 2._rt * ckdt) / (4._rt * ep0 * ck); } - } else { // Handle k_norm = 0, by using the analytical limit + // Limits for nu = -1 + if (nu == -1.) { + + // X1 multiplies i*(k \times J) in the update equation for B + X1(i,j,k) = (1._rt - tmp2 * tmp2 - 2._rt * I * ckdt) / (4._rt * ep0 * c2 * k2); + + if (update_with_rho) { + // X2 multiplies rho_new in the update equation for E + // X3 multiplies rho_old in the update equation for E + X2(i,j,k) = (- 4._rt + 3._rt * tmp1 + tmp2 - 2._rt * I * ckdt * tmp1) + / (4._rt * ep0 * k2 * (tmp1 - 1._rt)); + X3(i,j,k) = (2._rt - tmp2 - 3._rt * tmp1 + 2._rt * tmp1 * tmp1 - 2._rt * I * ckdt * tmp1) + / (4._rt * ep0 * k2 * (tmp1 - 1._rt)); + } else { + // X2 multiplies (k \dot E) in the update equation for E + // X3 multiplies (k \dot J) in the update equation for E + X2(i,j,k) = (1._rt - C(i,j,k)) * tmp2 / k2; + X3(i,j,k) = (2._rt * ckdt + I * tmp2 * tmp2 - 4._rt * I * tmp2 + 3._rt * I) + / (4._rt * ep0 * ck * k2); + } + + // Coefficient multiplying J in update equation for E + X4(i,j,k) = (I - I * tmp2 * tmp2 - 2._rt * ckdt) / (4._rt * ep0 * ck); + } + } + + // Limits for k = 0 + else { + + // Limits of cos(c*k*dt) and sin(c*k*dt)/(c*k) C(i,j,k) = 1._rt; S_ck(i,j,k) = dt; - X1(i,j,k) = dt*dt/(2._rt * ep0); - X2(i,j,k) = c*c*dt*dt/(6._rt * ep0); - X3(i,j,k) = - c*c*dt*dt/(3._rt * ep0); - X4(i,j,k) = -dt/ep0; - Theta2(i,j,k) = 1._rt; + + // X1 multiplies i*(k \times J) in the update equation for B + X1(i,j,k) = dt2 / (2._rt * ep0); + + if (update_with_rho) { + // X2 multiplies rho_new in the update equation for E + // X3 multiplies rho_old in the update equation for E + X2(i,j,k) = c2 * dt2 / (6._rt * ep0); + X3(i,j,k) = - c2 * dt2 / (3._rt * ep0); + } else { + // X2 multiplies (k \dot E) in the update equation for E + // X3 multiplies (k \dot J) in the update equation for E + X2(i,j,k) = c2 * dt2 * 0.5_rt; + X3(i,j,k) = - c2 * dt3 / (6._rt * ep0); + } + + // Coefficient multiplying J in update equation for E + X4(i,j,k) = -dt / ep0; + + // Limit of exp(I*(k \dot v_gal)*dt) + T2(i,j,k) = 1._rt; } }); } } void -GalileanAlgorithm::VayDeposition (SpectralFieldData& field_data, - std::array,3>& current) +GalileanAlgorithm::CurrentCorrection (SpectralFieldData& field_data, + std::array,3>& current, + const std::unique_ptr& rho) { + // Profiling + BL_PROFILE("GalileanAlgorithm::CurrentCorrection"); + + using Idx = SpectralFieldIndex; + + // Forward Fourier transform of J and rho + field_data.ForwardTransform(*current[0], Idx::Jx, 0); + field_data.ForwardTransform(*current[1], Idx::Jy, 0); + field_data.ForwardTransform(*current[2], Idx::Jz, 0); + field_data.ForwardTransform(*rho, Idx::rho_old, 0); + field_data.ForwardTransform(*rho, Idx::rho_new, 1); + + // Loop over boxes + for (amrex::MFIter mfi(field_data.fields); mfi.isValid(); ++mfi){ + + const amrex::Box& bx = field_data.fields[mfi].box(); + + // Extract arrays for the fields to be updated + amrex::Array4 fields = field_data.fields[mfi].array(); + + // Extract pointers for the k vectors + const amrex::Real* const modified_kx_arr = modified_kx_vec[mfi].dataPtr(); +#if (AMREX_SPACEDIM==3) + const amrex::Real* const modified_ky_arr = modified_ky_vec[mfi].dataPtr(); +#endif + const amrex::Real* const modified_kz_arr = modified_kz_vec[mfi].dataPtr(); + + // Local copy of member variables before GPU loop + const amrex::Real dt = m_dt; + + // Galilean velocity + const amrex::Real vgx = m_v_galilean[0]; + const amrex::Real vgy = m_v_galilean[1]; + const amrex::Real vgz = m_v_galilean[2]; + + // Loop over indices within one box + ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + // Shortcuts for the values of J and rho + const Complex Jx = fields(i,j,k,Idx::Jx); + const Complex Jy = fields(i,j,k,Idx::Jy); + const Complex Jz = fields(i,j,k,Idx::Jz); + const Complex rho_old = fields(i,j,k,Idx::rho_old); + const Complex rho_new = fields(i,j,k,Idx::rho_new); + + // k vector values, and coefficients + const amrex::Real kx = modified_kx_arr[i]; +#if (AMREX_SPACEDIM==3) + const amrex::Real ky = modified_ky_arr[j]; + const amrex::Real kz = modified_kz_arr[k]; +#else + constexpr amrex::Real ky = 0._rt; + const amrex::Real kz = modified_kz_arr[j]; +#endif + constexpr Complex I = Complex{0._rt,1._rt}; + + const amrex::Real k_norm = std::sqrt(kx * kx + ky * ky + kz * kz); + + // Correct J + if (k_norm != 0._rt) + { + const Complex k_dot_J = kx * Jx + ky * Jy + kz * Jz; + const amrex::Real k_dot_vg = kx * vgx + ky * vgy + kz * vgz; + + if ( k_dot_vg != 0._rt ) { + + const Complex rho_old_mod = rho_old * amrex::exp(I * k_dot_vg * dt); + const Complex den = 1._rt - amrex::exp(I * k_dot_vg * dt); + + fields(i,j,k,Idx::Jx) = Jx - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den) * kx / (k_norm * k_norm); + fields(i,j,k,Idx::Jy) = Jy - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den) * ky / (k_norm * k_norm); + fields(i,j,k,Idx::Jz) = Jz - (k_dot_J - k_dot_vg * (rho_new - rho_old_mod) / den) * kz / (k_norm * k_norm); + + } else { + + fields(i,j,k,Idx::Jx) = Jx - (k_dot_J - I * (rho_new - rho_old) / dt) * kx / (k_norm * k_norm); + fields(i,j,k,Idx::Jy) = Jy - (k_dot_J - I * (rho_new - rho_old) / dt) * ky / (k_norm * k_norm); + fields(i,j,k,Idx::Jz) = Jz - (k_dot_J - I * (rho_new - rho_old) / dt) * kz / (k_norm * k_norm); + } + } + }); + } + + // Backward Fourier transform of J + field_data.BackwardTransform(*current[0], Idx::Jx, 0); + field_data.BackwardTransform(*current[1], Idx::Jy, 0); + field_data.BackwardTransform(*current[2], Idx::Jz, 0); +} + +void +GalileanAlgorithm::VayDeposition (SpectralFieldData& /*field_data*/, + std::array,3>& /*current*/) { amrex::Abort("Vay deposition not implemented for Galilean PSATD"); } diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanPsatdAlgorithmRZ.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanPsatdAlgorithmRZ.H new file mode 100644 index 00000000000..3c8d3d60e75 --- /dev/null +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanPsatdAlgorithmRZ.H @@ -0,0 +1,72 @@ +/* Copyright 2019 David Grote + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_GALILEAN_PSATD_ALGORITHM_RZ_H_ +#define WARPX_GALILEAN_PSATD_ALGORITHM_RZ_H_ + +#include "SpectralBaseAlgorithmRZ.H" + +/* \brief Class that updates the field in spectral space + * and stores the coefficients of the corresponding update equation. + */ +class GalileanPsatdAlgorithmRZ : public SpectralBaseAlgorithmRZ +{ + + public: + GalileanPsatdAlgorithmRZ (SpectralKSpaceRZ const & spectral_kspace, + amrex::DistributionMapping const & dm, + int const n_rz_azimuthal_modes, int const norder_z, + bool const nodal, + const amrex::Array& v_galilean, + amrex::Real const dt_step); + // Redefine functions from base class + virtual void pushSpectralFields (SpectralFieldDataRZ & f) override final; + virtual int getRequiredNumberOfFields () const override final { + return SpectralFieldIndex::n_fields; + } + + void InitializeSpectralCoefficients (SpectralFieldDataRZ const & f); + + /** + * \brief Virtual function for current correction in Fourier space + * This function overrides the virtual function \c CurrentCorrection in the + * base class \c SpectralBaseAlgorithmRZ (qualifier \c override) and cannot be + * overridden by further derived classes (qualifier \c final). + * + * \param[in,out] field_data all fields in Fourier space + * \param[in,out] current two-dimensional array of unique pointers to MultiFab + * storing the three components of the current density + * \param[in] rho unique pointer to MultiFab storing the charge density + */ + virtual void CurrentCorrection ( SpectralFieldDataRZ& field_data, + std::array,3>& current, + const std::unique_ptr& rho ) override final; + + /** + * \brief Virtual function for Vay current deposition in Fourier space + * This function overrides the virtual function \c VayDeposition in the + * base class \c SpectralBaseAlgorithmRZ and cannot be overridden by further + * derived classes. + * + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + */ + virtual void VayDeposition (SpectralFieldDataRZ& field_data, + std::array,3>& current) override final; + + private: + bool coefficients_initialized; + // Note that dt and v_galilean are saved to use in InitializeSpectralCoefficients + amrex::Real const m_dt; + amrex::Array m_v_galilean; + + SpectralRealCoefficients C_coef, S_ck_coef; + SpectralComplexCoefficients Theta2_coef, X1_coef, X2_coef, X3_coef, X4_coef; + +}; + +#endif // WARPX_GALILEAN_PSATD_ALGORITHM_RZ_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanPsatdAlgorithmRZ.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanPsatdAlgorithmRZ.cpp new file mode 100644 index 00000000000..dab4b7428ac --- /dev/null +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/GalileanPsatdAlgorithmRZ.cpp @@ -0,0 +1,352 @@ +/* Copyright 2019-2020 David Grote + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "WarpX.H" +#include "GalileanPsatdAlgorithmRZ.H" +#include "Utils/WarpXConst.H" +#include "Utils/WarpXProfilerWrapper.H" + +#include + +using namespace amrex::literals; + + +/* \brief Initialize coefficients for the update equation */ +GalileanPsatdAlgorithmRZ::GalileanPsatdAlgorithmRZ (SpectralKSpaceRZ const & spectral_kspace, + amrex::DistributionMapping const & dm, + int const n_rz_azimuthal_modes, int const norder_z, + bool const nodal, + const amrex::Array& v_galilean, + amrex::Real const dt) + // Initialize members of base class + : SpectralBaseAlgorithmRZ(spectral_kspace, dm, norder_z, nodal), + m_dt(dt), + m_v_galilean(v_galilean) +{ + + // Allocate the arrays of coefficients + amrex::BoxArray const & ba = spectral_kspace.spectralspace_ba; + C_coef = SpectralRealCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + S_ck_coef = SpectralRealCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + X1_coef = SpectralComplexCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + X2_coef = SpectralComplexCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + X3_coef = SpectralComplexCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + X4_coef = SpectralComplexCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + Theta2_coef = SpectralComplexCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + + coefficients_initialized = false; +} + +/* Advance the E and B field in spectral space (stored in `f`) + * over one time step */ +void +GalileanPsatdAlgorithmRZ::pushSpectralFields (SpectralFieldDataRZ & f) +{ + + if (not coefficients_initialized) { + // This is called from here since it needs the kr values + // which can be obtained from the SpectralFieldDataRZ + InitializeSpectralCoefficients(f); + coefficients_initialized = true; + } + + // Loop over boxes + for (amrex::MFIter mfi(f.fields); mfi.isValid(); ++mfi){ + + amrex::Box const & bx = f.fields[mfi].box(); + + // Extract arrays for the fields to be updated + amrex::Array4 const& fields = f.fields[mfi].array(); + // Extract arrays for the coefficients + amrex::Array4 const& C_arr = C_coef[mfi].array(); + amrex::Array4 const& S_ck_arr = S_ck_coef[mfi].array(); + amrex::Array4 const& X1_arr = X1_coef[mfi].array(); + amrex::Array4 const& X2_arr = X2_coef[mfi].array(); + amrex::Array4 const& X3_arr = X3_coef[mfi].array(); + amrex::Array4 const& X4_arr = X4_coef[mfi].array(); + amrex::Array4 const& Theta2_arr = Theta2_coef[mfi].array(); + + // Extract pointers for the k vectors + auto const & kr_modes = f.getKrArray(mfi); + amrex::Real const* kr_arr = kr_modes.dataPtr(); + amrex::Real const* modified_kz_arr = modified_kz_vec[mfi].dataPtr(); + int const nr = bx.length(0); + + // Loop over indices within one box + // Note that k = 0 + int const modes = f.n_rz_azimuthal_modes; + amrex::ParallelFor(bx, modes, + [=] AMREX_GPU_DEVICE(int i, int j, int k, int mode) noexcept + { + + // All of the fields of each mode are grouped together + using Idx = SpectralFieldIndex; + auto const Ep_m = Idx::Ex + Idx::n_fields*mode; + auto const Em_m = Idx::Ey + Idx::n_fields*mode; + auto const Ez_m = Idx::Ez + Idx::n_fields*mode; + auto const Bp_m = Idx::Bx + Idx::n_fields*mode; + auto const Bm_m = Idx::By + Idx::n_fields*mode; + auto const Bz_m = Idx::Bz + Idx::n_fields*mode; + auto const Jp_m = Idx::Jx + Idx::n_fields*mode; + auto const Jm_m = Idx::Jy + Idx::n_fields*mode; + auto const Jz_m = Idx::Jz + Idx::n_fields*mode; + auto const rho_old_m = Idx::rho_old + Idx::n_fields*mode; + auto const rho_new_m = Idx::rho_new + Idx::n_fields*mode; + + // Record old values of the fields to be updated + Complex const Ep_old = fields(i,j,k,Ep_m); + Complex const Em_old = fields(i,j,k,Em_m); + Complex const Ez_old = fields(i,j,k,Ez_m); + Complex const Bp_old = fields(i,j,k,Bp_m); + Complex const Bm_old = fields(i,j,k,Bm_m); + Complex const Bz_old = fields(i,j,k,Bz_m); + // Shortcut for the values of J and rho + Complex const Jp = fields(i,j,k,Jp_m); + Complex const Jm = fields(i,j,k,Jm_m); + Complex const Jz = fields(i,j,k,Jz_m); + Complex const rho_old = fields(i,j,k,rho_old_m); + Complex const rho_new = fields(i,j,k,rho_new_m); + + // k vector values, and coefficients + // The k values for each mode are grouped together + int const ir = i + nr*mode; + amrex::Real const kr = kr_arr[ir]; + amrex::Real const kz = modified_kz_arr[j]; + + constexpr amrex::Real c2 = PhysConst::c*PhysConst::c; + Complex const I = Complex{0._rt,1._rt}; + amrex::Real const C = C_arr(i,j,k,mode); + amrex::Real const S_ck = S_ck_arr(i,j,k,mode); + Complex const X1 = X1_arr(i,j,k,mode); + Complex const X2 = X2_arr(i,j,k,mode); + Complex const X3 = X3_arr(i,j,k,mode); + Complex const X4 = X4_arr(i,j,k,mode); + Complex const T2 = Theta2_arr(i,j,k,mode); + + // Update E (see WarpX online documentation: theory section) + fields(i,j,k,Ep_m) = T2*C*Ep_old + + T2*S_ck*(-c2*I*kr/2._rt*Bz_old + c2*kz*Bp_old) + + X4*Jp + 0.5_rt*kr*(X2*rho_new - T2*X3*rho_old); + fields(i,j,k,Em_m) = T2*C*Em_old + + T2*S_ck*(-c2*I*kr/2._rt*Bz_old - c2*kz*Bm_old) + + X4*Jm - 0.5_rt*kr*(X2*rho_new - T2*X3*rho_old); + fields(i,j,k,Ez_m) = T2*C*Ez_old + + T2*S_ck*(c2*I*kr*Bp_old + c2*I*kr*Bm_old) + + X4*Jz - I*kz*(X2*rho_new - T2*X3*rho_old); + // Update B (see WarpX online documentation: theory section) + // Note: here X1 is T2*x1/(ep0*c*c*k_norm*k_norm), where + // x1 has the same definition as in the original paper + fields(i,j,k,Bp_m) = T2*C*Bp_old + - T2*S_ck*(-I*kr/2._rt*Ez_old + kz*Ep_old) + + X1*(-I*kr/2._rt*Jz + kz*Jp); + fields(i,j,k,Bm_m) = T2*C*Bm_old + - T2*S_ck*(-I*kr/2._rt*Ez_old - kz*Em_old) + + X1*(-I*kr/2._rt*Jz - kz*Jm); + fields(i,j,k,Bz_m) = T2*C*Bz_old + - T2*S_ck*I*(kr*Ep_old + kr*Em_old) + + X1*I*(kr*Jp + kr*Jm); + }); + } +} + +void GalileanPsatdAlgorithmRZ::InitializeSpectralCoefficients (SpectralFieldDataRZ const & f) +{ + + // Fill them with the right values: + // Loop over boxes and allocate the corresponding coefficients + // for each box owned by the local MPI proc + for (amrex::MFIter mfi(f.fields); mfi.isValid(); ++mfi){ + + amrex::Box const & bx = f.fields[mfi].box(); + + // Extract pointers for the k vectors + amrex::Real const* const modified_kz = modified_kz_vec[mfi].dataPtr(); + + // Extract arrays for the coefficients + amrex::Array4 const& C = C_coef[mfi].array(); + amrex::Array4 const& S_ck = S_ck_coef[mfi].array(); + amrex::Array4 const& X1 = X1_coef[mfi].array(); + amrex::Array4 const& X2 = X2_coef[mfi].array(); + amrex::Array4 const& X3 = X3_coef[mfi].array(); + amrex::Array4 const& X4 = X4_coef[mfi].array(); + amrex::Array4 const& Theta2 = Theta2_coef[mfi].array(); + + // Extract real (for portability on GPU) + amrex::Real vz = m_v_galilean[2]; + + auto const & kr_modes = f.getKrArray(mfi); + amrex::Real const* kr_arr = kr_modes.dataPtr(); + int const nr = bx.length(0); + amrex::Real const dt = m_dt; + + // Loop over indices within one box + int const modes = f.n_rz_azimuthal_modes; + amrex::ParallelFor(bx, modes, + [=] AMREX_GPU_DEVICE(int i, int j, int k, int mode) noexcept + { + constexpr amrex::Real c = PhysConst::c; + constexpr amrex::Real ep0 = PhysConst::ep0; + Complex const I = Complex{0._rt,1._rt}; + + // Calculate norm of vector + int const ir = i + nr*mode; + amrex::Real const kr = kr_arr[ir]; + amrex::Real const kz = modified_kz[j]; + amrex::Real const k_norm = std::sqrt(kr*kr + kz*kz); + + // Calculate coefficients + if (k_norm != 0._rt){ + + C(i,j,k,mode) = std::cos(c*k_norm*dt); + S_ck(i,j,k,mode) = std::sin(c*k_norm*dt)/(c*k_norm); + + // Calculate dot product with galilean velocity + amrex::Real const kv = kz*vz; + + amrex::Real const nu = kv/(k_norm*c); + Complex const theta = amrex::exp( 0.5_rt*I*kv*dt ); + Complex const theta_star = amrex::exp( -0.5_rt*I*kv*dt ); + Complex const e_theta = amrex::exp( I*c*k_norm*dt ); + + Theta2(i,j,k,mode) = theta*theta; + + if ( (nu != 1._rt) && (nu != 0._rt) ) { + + // Note: the coefficients X1, X2, X do not correspond + // exactly to the original Galilean paper, but the + // update equation have been modified accordingly so that + // the expressions/ below (with the update equations) + // are mathematically equivalent to those of the paper. + Complex x1 = 1._rt/(1._rt-nu*nu) * + (theta_star - C(i,j,k,mode)*theta + I*kv*S_ck(i,j,k,mode)*theta); + // x1, above, is identical to the original paper + X1(i,j,k,mode) = theta*x1/(ep0*c*c*k_norm*k_norm); + // The difference betwen X2 and X3 below, and those + // from the original paper is the factor ep0*k_norm*k_norm + X2(i,j,k,mode) = (x1 - theta*(1._rt - C(i,j,k,mode))) + /(theta_star-theta)/(ep0*k_norm*k_norm); + X3(i,j,k,mode) = (x1 - theta_star*(1._rt - C(i,j,k,mode))) + /(theta_star-theta)/(ep0*k_norm*k_norm); + X4(i,j,k,mode) = I*kv*X1(i,j,k,mode) - theta*theta*S_ck(i,j,k,mode)/ep0; + + } else if (nu == 0._rt) { + + X1(i,j,k,mode) = (1._rt - C(i,j,k,mode))/(ep0 * c*c * k_norm*k_norm); + X2(i,j,k,mode) = (1._rt - S_ck(i,j,k,mode)/dt)/(ep0 * k_norm*k_norm); + X3(i,j,k,mode) = (C(i,j,k,mode) - S_ck(i,j,k,mode)/dt)/(ep0 * k_norm*k_norm); + X4(i,j,k,mode) = -S_ck(i,j,k,mode)/ep0; + + } else if ( nu == 1._rt) { + X1(i,j,k,mode) = (1._rt - e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*c*c*ep0*k_norm*k_norm); + X2(i,j,k,mode) = (3._rt - 4._rt*e_theta + e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*ep0*k_norm*k_norm*(1._rt - e_theta)); + X3(i,j,k,mode) = (3._rt - 2._rt/e_theta - 2._rt*e_theta + e_theta*e_theta - 2._rt*I*c*k_norm*dt) / (4._rt*ep0*(e_theta - 1._rt)*k_norm*k_norm); + X4(i,j,k,mode) = I*(-1._rt + e_theta*e_theta + 2._rt*I*c*k_norm*dt) / (4._rt*ep0*c*k_norm); + } + + } else { // Handle k_norm = 0, by using the analytical limit + C(i,j,k,mode) = 1._rt; + S_ck(i,j,k,mode) = dt; + X1(i,j,k,mode) = 0.5_rt * dt*dt / ep0; + X2(i,j,k,mode) = c*c * dt*dt / (6._rt*ep0); + X3(i,j,k,mode) = - c*c * dt*dt / (3._rt*ep0); + X4(i,j,k,mode) = -dt/ep0; + Theta2(i,j,k,mode) = 1._rt; + } + }); + } +} + +void +GalileanPsatdAlgorithmRZ::CurrentCorrection (SpectralFieldDataRZ& field_data, + std::array,3>& current, + const std::unique_ptr& rho ) +{ + // Profiling + WARPX_PROFILE( "GalileanPsatdAlgorithmRZ::CurrentCorrection" ); + + using Idx = SpectralFieldIndex; + + // Forward Fourier transform of J and rho + field_data.ForwardTransform( *current[0], Idx::Jx, + *current[1], Idx::Jy); + field_data.ForwardTransform( *current[2], Idx::Jz, 0); + field_data.ForwardTransform( *rho, Idx::rho_old, 0 ); + field_data.ForwardTransform( *rho, Idx::rho_new, 1 ); + + // Loop over boxes + for (amrex::MFIter mfi(field_data.fields); mfi.isValid(); ++mfi){ + + amrex::Box const & bx = field_data.fields[mfi].box(); + + // Extract arrays for the fields to be updated + amrex::Array4 fields = field_data.fields[mfi].array(); + + // Extract pointers for the k vectors + auto const & kr_modes = field_data.getKrArray(mfi); + amrex::Real const* kr_arr = kr_modes.dataPtr(); + amrex::Real const* modified_kz_arr = modified_kz_vec[mfi].dataPtr(); + int const nr = bx.length(0); + + // Local copy of member variables before GPU loop + amrex::Real vz = m_v_galilean[2]; + amrex::Real const dt = m_dt; + + // Loop over indices within one box + int const modes = field_data.n_rz_azimuthal_modes; + ParallelFor(bx, modes, + [=] AMREX_GPU_DEVICE(int i, int j, int k, int mode) noexcept + { + // All of the fields of each mode are grouped together + auto const Jp_m = Idx::Jx + Idx::n_fields*mode; + auto const Jm_m = Idx::Jy + Idx::n_fields*mode; + auto const Jz_m = Idx::Jz + Idx::n_fields*mode; + auto const rho_old_m = Idx::rho_old + Idx::n_fields*mode; + auto const rho_new_m = Idx::rho_new + Idx::n_fields*mode; + + // Shortcuts for the values of J and rho + Complex const Jp = fields(i,j,k,Jp_m); + Complex const Jm = fields(i,j,k,Jm_m); + Complex const Jz = fields(i,j,k,Jz_m); + Complex const rho_old = fields(i,j,k,rho_old_m); + Complex const rho_new = fields(i,j,k,rho_new_m); + + // k vector values, and coefficients + // The k values for each mode are grouped together + int const ir = i + nr*mode; + amrex::Real const kr = kr_arr[ir]; + amrex::Real const kz = modified_kz_arr[j]; + amrex::Real const k_norm2 = kr*kr + kz*kz; + + constexpr Complex I = Complex{0._rt,1._rt}; + + // Correct J + if ( k_norm2 != 0._rt ) + { + Complex const theta2 = amrex::exp(I*kz*vz*dt); + Complex const inv_1_T2 = 1._rt/(kz*vz == 0._rt ? 1._rt : 1._rt - theta2); + Complex const j_corr_coef = (kz == 0._rt ? 1._rt/dt : -I*kz*vz*inv_1_T2); + Complex const F = - (j_corr_coef*(rho_new - rho_old*theta2) + I*kz*Jz + kr*(Jp - Jm))/k_norm2; + + fields(i,j,k,Jp_m) += +0.5_rt*kr*F; + fields(i,j,k,Jm_m) += -0.5_rt*kr*F; + fields(i,j,k,Jz_m) += -I*kz*F; + } + }); + } + + // Backward Fourier transform of J + field_data.BackwardTransform( *current[0], Idx::Jx, + *current[1], Idx::Jy); + field_data.BackwardTransform( *current[2], Idx::Jz, 0 ); + +} + +void +GalileanPsatdAlgorithmRZ::VayDeposition (SpectralFieldDataRZ& /*field_data*/, + std::array,3>& /*current*/) +{ + amrex::Abort("Vay deposition not implemented in RZ geometry"); +} diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package index f316abefadd..57f8c5e3fdc 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/Make.package @@ -7,6 +7,7 @@ CEXE_sources += PMLPsatdAlgorithm.cpp ifeq ($(USE_RZ),TRUE) CEXE_sources += SpectralBaseAlgorithmRZ.cpp CEXE_sources += PsatdAlgorithmRZ.cpp + CEXE_sources += GalileanPsatdAlgorithmRZ.cpp endif VPATH_LOCATIONS += $(WARPX_HOME)/Source/FieldSolver/SpectralSolver/SpectralAlgorithms diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H index 347012d5abd..348282dce14 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.H @@ -34,6 +34,22 @@ class PMLPsatdAlgorithm : public SpectralBaseAlgorithm return SpectralPMLIndex::n_fields; } + /** + * \brief Virtual function for current correction in Fourier space + * ( Vay et al, 2013). + * This function overrides the virtual function \c CurrentCorrection in the + * base class \c SpectralBaseAlgorithm and cannot be overridden by further + * derived classes. + * + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + * \param[in] rho Unique pointer to \c MultiFab storing the charge density + */ + virtual void CurrentCorrection (SpectralFieldData& field_data, + std::array,3>& current, + const std::unique_ptr& rho) override final; + /** * \brief Virtual function for Vay current deposition in Fourier space * ( Vay et al, 2013). diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp index d2f087706f2..e1be557fe29 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PMLPsatdAlgorithm.cpp @@ -103,7 +103,7 @@ PMLPsatdAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ fields(i,j,k,Idx::Bzy) = C*fields(i,j,k,Idx::Bzy) + S_ck*I*ky*Ex_old; }); } -}; +} void PMLPsatdAlgorithm::InitializeSpectralCoefficients ( const SpectralKSpace& spectral_kspace, @@ -153,11 +153,19 @@ void PMLPsatdAlgorithm::InitializeSpectralCoefficients ( } }); } -}; +} + +void +PMLPsatdAlgorithm::CurrentCorrection (SpectralFieldData& /*field_data*/, + std::array,3>& /*current*/, + const std::unique_ptr& /*rho*/) +{ + amrex::Abort("Current correction not implemented for PML PSATD"); +} void -PMLPsatdAlgorithm::VayDeposition (SpectralFieldData& field_data, - std::array,3>& current) +PMLPsatdAlgorithm::VayDeposition (SpectralFieldData& /*field_data*/, + std::array,3>& /*current*/) { amrex::Abort("Vay deposition not implemented for PML PSATD"); } diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H index bfa9283e69c..4bfcd9fc904 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.H @@ -39,19 +39,19 @@ class PsatdAlgorithm : public SpectralBaseAlgorithm /** * \brief Virtual function for current correction in Fourier space - * (equation (19) of https://doi.org/10.1016/j.jcp.2013.03.010). + * ( Vay et al, 2013). * This function overrides the virtual function \c CurrentCorrection in the - * base class \c SpectralBaseAlgorithm (qualifier \c override) and cannot be - * overridden by further derived classes (qualifier \c final). + * base class \c SpectralBaseAlgorithm and cannot be overridden by further + * derived classes. * - * \param[in,out] field_data all fields in Fourier space - * \param[in,out] current three-dimensional array of unique pointers to MultiFab - * storing the three components of the current density - * \param[in] rho unique pointer to MultiFab storing the charge density + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + * \param[in] rho Unique pointer to \c MultiFab storing the charge density */ - virtual void CurrentCorrection ( SpectralFieldData& field_data, - std::array,3>& current, - const std::unique_ptr& rho ) override final; + virtual void CurrentCorrection (SpectralFieldData& field_data, + std::array,3>& current, + const std::unique_ptr& rho) override final; /** * \brief Virtual function for Vay current deposition in Fourier space diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp index 7f9fd3edb36..ce0f466aa6b 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithm.cpp @@ -24,9 +24,9 @@ PsatdAlgorithm::PsatdAlgorithm(const SpectralKSpace& spectral_kspace, const int norder_z, const bool nodal, const Real dt, const bool update_with_rho) // Initialize members of base class - : m_dt( dt ), - m_update_with_rho( update_with_rho ), - SpectralBaseAlgorithm( spectral_kspace, dm, norder_x, norder_y, norder_z, nodal ) + : SpectralBaseAlgorithm(spectral_kspace, dm, norder_x, norder_y, norder_z, nodal), + m_dt(dt), + m_update_with_rho(update_with_rho) { const BoxArray& ba = spectral_kspace.spectralspace_ba; @@ -144,7 +144,7 @@ PsatdAlgorithm::pushSpectralFields(SpectralFieldData& f) const{ fields(i,j,k,Idx::Bz) = C*Bz_old - S_ck*I*(kx*Ey_old-ky*Ex_old) + X1*I*(kx*Jy-ky*Jx); } ); } -}; +} /** * \brief Initialize coefficients for update equations @@ -220,11 +220,11 @@ void PsatdAlgorithm::InitializeSpectralCoefficients(const SpectralKSpace& spectr } void -PsatdAlgorithm::CurrentCorrection( SpectralFieldData& field_data, +PsatdAlgorithm::CurrentCorrection (SpectralFieldData& field_data, std::array,3>& current, - const std::unique_ptr& rho ) { + const std::unique_ptr& rho) { // Profiling - WARPX_PROFILE( "PsatdAlgorithm::CurrentCorrection" ); + WARPX_PROFILE("PsatdAlgorithm::CurrentCorrection()"); using Idx = SpectralFieldIndex; @@ -256,8 +256,6 @@ PsatdAlgorithm::CurrentCorrection( SpectralFieldData& field_data, // Loop over indices within one box ParallelFor( bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { - using Idx = SpectralFieldIndex; - // Shortcuts for the values of J and rho const Complex Jx = fields(i,j,k,Idx::Jx); const Complex Jy = fields(i,j,k,Idx::Jy); @@ -301,7 +299,7 @@ void PsatdAlgorithm::VayDeposition (SpectralFieldData& field_data, std::array,3>& current) { // Profiling - WARPX_PROFILE("PsatdAlgorithm::VayDeposition"); + WARPX_PROFILE("PsatdAlgorithm::VayDeposition()"); using Idx = SpectralFieldIndex; @@ -330,11 +328,11 @@ PsatdAlgorithm::VayDeposition (SpectralFieldData& field_data, // Loop over indices within one box ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { - using Idx = SpectralFieldIndex; - // Shortcuts for the values of D const Complex Dx = fields(i,j,k,Idx::Jx); +#if (AMREX_SPACEDIM==3) const Complex Dy = fields(i,j,k,Idx::Jy); +#endif const Complex Dz = fields(i,j,k,Idx::Jz); // Imaginary unit @@ -346,8 +344,7 @@ PsatdAlgorithm::VayDeposition (SpectralFieldData& field_data, const amrex::Real ky_mod = modified_ky_arr[j]; const amrex::Real kz_mod = modified_kz_arr[k]; #else - constexpr amrex::Real ky_mod = 0._rt; - const amrex::Real kz_mod = modified_kz_arr[j]; + const amrex::Real kz_mod = modified_kz_arr[j]; #endif // Compute Jx diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.H index fc03f95f2bd..f8df96e54cf 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.H @@ -30,19 +30,19 @@ class PsatdAlgorithmRZ : public SpectralBaseAlgorithmRZ /** * \brief Virtual function for current correction in Fourier space - * (equation (19) of https://doi.org/10.1016/j.jcp.2013.03.010). + * ( Vay et al, 2013). * This function overrides the virtual function \c CurrentCorrection in the - * base class \c SpectralBaseAlgorithmRZ (qualifier \c override) and cannot be - * overridden by further derived classes (qualifier \c final). + * base class \c SpectralBaseAlgorithmRZ and cannot be overridden by further + * derived classes. * - * \param[in,out] field_data all fields in Fourier space - * \param[in,out] current two-dimensional array of unique pointers to MultiFab - * storing the three components of the current density - * \param[in] rho unique pointer to MultiFab storing the charge density + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + * \param[in] rho Unique pointer to \c MultiFab storing the charge density */ - virtual void CurrentCorrection ( SpectralFieldDataRZ& field_data, - std::array,3>& current, - const std::unique_ptr& rho ) override final; + virtual void CurrentCorrection (SpectralFieldDataRZ& field_data, + std::array,3>& current, + const std::unique_ptr& rho) override final; /** * \brief Virtual function for Vay current deposition in Fourier space @@ -61,8 +61,8 @@ class PsatdAlgorithmRZ : public SpectralBaseAlgorithmRZ private: bool coefficients_initialized; // Note that dt is saved to use in InitializeSpectralCoefficients - amrex::Real dt; - SpectralCoefficients C_coef, S_ck_coef, X1_coef, X2_coef, X3_coef; + amrex::Real m_dt; + SpectralRealCoefficients C_coef, S_ck_coef, X1_coef, X2_coef, X3_coef; }; #endif // WARPX_PSATD_ALGORITHM_RZ_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.cpp index 35a478f2cb8..7cd23058139 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmRZ.cpp @@ -4,8 +4,10 @@ * * License: BSD-3-Clause-LBNL */ +#include "WarpX.H" #include "PsatdAlgorithmRZ.H" #include "Utils/WarpXConst.H" +#include "Utils/WarpXProfilerWrapper.H" #include @@ -16,20 +18,20 @@ using amrex::operator""_rt; PsatdAlgorithmRZ::PsatdAlgorithmRZ (SpectralKSpaceRZ const & spectral_kspace, amrex::DistributionMapping const & dm, int const n_rz_azimuthal_modes, int const norder_z, - bool const nodal, amrex::Real const dt_step) + bool const nodal, amrex::Real const dt) // Initialize members of base class : SpectralBaseAlgorithmRZ(spectral_kspace, dm, norder_z, nodal), - dt(dt_step) + m_dt(dt) { // Allocate the arrays of coefficients amrex::BoxArray const & ba = spectral_kspace.spectralspace_ba; - C_coef = SpectralCoefficients(ba, dm, n_rz_azimuthal_modes, 0); - S_ck_coef = SpectralCoefficients(ba, dm, n_rz_azimuthal_modes, 0); - X1_coef = SpectralCoefficients(ba, dm, n_rz_azimuthal_modes, 0); - X2_coef = SpectralCoefficients(ba, dm, n_rz_azimuthal_modes, 0); - X3_coef = SpectralCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + C_coef = SpectralRealCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + S_ck_coef = SpectralRealCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + X1_coef = SpectralRealCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + X2_coef = SpectralRealCoefficients(ba, dm, n_rz_azimuthal_modes, 0); + X3_coef = SpectralRealCoefficients(ba, dm, n_rz_azimuthal_modes, 0); coefficients_initialized = false; } @@ -139,7 +141,7 @@ PsatdAlgorithmRZ::pushSpectralFields(SpectralFieldDataRZ & f) + X1*I*(kr*Jp + kr*Jm); }); } -}; +} void PsatdAlgorithmRZ::InitializeSpectralCoefficients (SpectralFieldDataRZ const & f) { @@ -164,7 +166,7 @@ void PsatdAlgorithmRZ::InitializeSpectralCoefficients (SpectralFieldDataRZ const auto const & kr_modes = f.getKrArray(mfi); amrex::Real const* kr_arr = kr_modes.dataPtr(); int const nr = bx.length(0); - amrex::Real const dt_temp = dt; + amrex::Real const dt = m_dt; // Loop over indices within one box int const modes = f.n_rz_azimuthal_modes; @@ -181,17 +183,17 @@ void PsatdAlgorithmRZ::InitializeSpectralCoefficients (SpectralFieldDataRZ const constexpr amrex::Real c = PhysConst::c; constexpr amrex::Real ep0 = PhysConst::ep0; if (k_norm != 0){ - C(i,j,k,mode) = std::cos(c*k_norm*dt_temp); - S_ck(i,j,k,mode) = std::sin(c*k_norm*dt_temp)/(c*k_norm); + C(i,j,k,mode) = std::cos(c*k_norm*dt); + S_ck(i,j,k,mode) = std::sin(c*k_norm*dt)/(c*k_norm); X1(i,j,k,mode) = (1._rt - C(i,j,k,mode))/(ep0 * c*c * k_norm*k_norm); - X2(i,j,k,mode) = (1._rt - S_ck(i,j,k,mode)/dt_temp)/(ep0 * k_norm*k_norm); - X3(i,j,k,mode) = (C(i,j,k,mode) - S_ck(i,j,k,mode)/dt_temp)/(ep0 * k_norm*k_norm); + X2(i,j,k,mode) = (1._rt - S_ck(i,j,k,mode)/dt)/(ep0 * k_norm*k_norm); + X3(i,j,k,mode) = (C(i,j,k,mode) - S_ck(i,j,k,mode)/dt)/(ep0 * k_norm*k_norm); } else { // Handle k_norm = 0, by using the analytical limit C(i,j,k,mode) = 1._rt; - S_ck(i,j,k,mode) = dt_temp; - X1(i,j,k,mode) = 0.5_rt * dt_temp*dt_temp / ep0; - X2(i,j,k,mode) = c*c * dt_temp*dt_temp / (6._rt*ep0); - X3(i,j,k,mode) = - c*c * dt_temp*dt_temp / (3._rt*ep0); + S_ck(i,j,k,mode) = dt; + X1(i,j,k,mode) = 0.5_rt * dt*dt / ep0; + X2(i,j,k,mode) = c*c * dt*dt / (6._rt*ep0); + X3(i,j,k,mode) = - c*c * dt*dt / (3._rt*ep0); } }); } @@ -200,14 +202,86 @@ void PsatdAlgorithmRZ::InitializeSpectralCoefficients (SpectralFieldDataRZ const void PsatdAlgorithmRZ::CurrentCorrection (SpectralFieldDataRZ& field_data, std::array,3>& current, - const std::unique_ptr& rho ) + const std::unique_ptr& rho) { - amrex::Abort("Current correction not implemented in RZ"); + // Profiling + WARPX_PROFILE( "PsatdAlgorithmRZ::CurrentCorrection" ); + + using Idx = SpectralFieldIndex; + + // Forward Fourier transform of J and rho + field_data.ForwardTransform( *current[0], Idx::Jx, + *current[1], Idx::Jy); + field_data.ForwardTransform( *current[2], Idx::Jz, 0); + field_data.ForwardTransform( *rho, Idx::rho_old, 0 ); + field_data.ForwardTransform( *rho, Idx::rho_new, 1 ); + + // Loop over boxes + for (amrex::MFIter mfi(field_data.fields); mfi.isValid(); ++mfi){ + + amrex::Box const & bx = field_data.fields[mfi].box(); + + // Extract arrays for the fields to be updated + amrex::Array4 fields = field_data.fields[mfi].array(); + + // Extract pointers for the k vectors + auto const & kr_modes = field_data.getKrArray(mfi); + amrex::Real const* kr_arr = kr_modes.dataPtr(); + amrex::Real const* modified_kz_arr = modified_kz_vec[mfi].dataPtr(); + int const nr = bx.length(0); + + // Local copy of member variables before GPU loop + amrex::Real const dt = m_dt; + + // Loop over indices within one box + int const modes = field_data.n_rz_azimuthal_modes; + ParallelFor(bx, modes, + [=] AMREX_GPU_DEVICE(int i, int j, int k, int mode) noexcept + { + // All of the fields of each mode are grouped together + auto const Jp_m = Idx::Jx + Idx::n_fields*mode; + auto const Jm_m = Idx::Jy + Idx::n_fields*mode; + auto const Jz_m = Idx::Jz + Idx::n_fields*mode; + auto const rho_old_m = Idx::rho_old + Idx::n_fields*mode; + auto const rho_new_m = Idx::rho_new + Idx::n_fields*mode; + + // Shortcuts for the values of J and rho + Complex const Jp = fields(i,j,k,Jp_m); + Complex const Jm = fields(i,j,k,Jm_m); + Complex const Jz = fields(i,j,k,Jz_m); + Complex const rho_old = fields(i,j,k,rho_old_m); + Complex const rho_new = fields(i,j,k,rho_new_m); + + // k vector values, and coefficients + // The k values for each mode are grouped together + int const ir = i + nr*mode; + amrex::Real const kr = kr_arr[ir]; + amrex::Real const kz = modified_kz_arr[j]; + amrex::Real const k_norm2 = kr*kr + kz*kz; + + constexpr Complex I = Complex{0._rt,1._rt}; + + // Correct J + if ( k_norm2 != 0._rt ) + { + Complex const F = - ((rho_new - rho_old)/dt + I*kz*Jz + kr*(Jp - Jm))/k_norm2; + + fields(i,j,k,Jp_m) += +0.5_rt*kr*F; + fields(i,j,k,Jm_m) += -0.5_rt*kr*F; + fields(i,j,k,Jz_m) += -I*kz*F; + } + }); + } + + // Backward Fourier transform of J + field_data.BackwardTransform( *current[0], Idx::Jx, + *current[1], Idx::Jy); + field_data.BackwardTransform( *current[2], Idx::Jz, 0 ); } void -PsatdAlgorithmRZ::VayDeposition (SpectralFieldDataRZ& field_data, - std::array,3>& current) +PsatdAlgorithmRZ::VayDeposition (SpectralFieldDataRZ& /*field_data*/, + std::array,3>& /*current*/) { amrex::Abort("Vay deposition not implemented in RZ geometry"); } diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H index 2a34d21ab81..3e909145d37 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.H @@ -10,6 +10,7 @@ #include "FieldSolver/SpectralSolver/SpectralKSpace.H" #include "FieldSolver/SpectralSolver/SpectralFieldData.H" +#include #if WARPX_USE_PSATD @@ -29,23 +30,21 @@ class SpectralBaseAlgorithm // The destructor should also be a virtual function, so that // a pointer to subclass of `SpectraBaseAlgorithm` actually // calls the subclass's destructor. - virtual ~SpectralBaseAlgorithm() {}; + virtual ~SpectralBaseAlgorithm() {} /** * \brief Virtual function for current correction in Fourier space - * (equation (19) of https://doi.org/10.1016/j.jcp.2013.03.010). - * This virtual function is not pure: it can be overridden in derived - * classes (e.g. PsatdAlgorithm, PMLPsatdAlgorithm), but a base-class - * implementation is provided (empty implementation in this case). + * ( Vay et al, 2013). + * This virtual function is pure and must be defined in derived classes. * - * \param[in,out] field_data all fields in Fourier space - * \param[in,out] current three-dimensional array of unique pointers to MultiFab - * storing the three components of the current density - * \param[in] rho unique pointer to MultiFab storing the charge density + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + * \param[in] rho Unique pointer to \c MultiFab storing the charge density */ - virtual void CurrentCorrection ( SpectralFieldData& field_data, - std::array,3>& current, - const std::unique_ptr& rho ) {}; + virtual void CurrentCorrection (SpectralFieldData& field_data, + std::array,3>& current, + const std::unique_ptr& rho) = 0; /** * \brief Virtual function for Vay current deposition in Fourier space @@ -86,7 +85,11 @@ class SpectralBaseAlgorithm #else modified_kz_vec(spectral_kspace.getModifiedKComponent(dm,1,norder_z,nodal)) #endif - {}; + { +#if (AMREX_SPACEDIM!=3) + amrex::ignore_unused(norder_y); +#endif + } // Modified finite-order vectors KVectorComponent modified_kx_vec; diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.cpp index 73a0bb9aff2..46e9b50f7f0 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithm.cpp @@ -43,7 +43,6 @@ SpectralBaseAlgorithm::ComputeSpectralDivE ( ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept { - using Idx = SpectralFieldIndex; // Shortcuts for the components of E const Complex Ex = fields(i,j,k,Idx::Ex); const Complex Ey = fields(i,j,k,Idx::Ey); @@ -66,4 +65,4 @@ SpectralBaseAlgorithm::ComputeSpectralDivE ( // Backward Fourier transform field_data.BackwardTransform( divE, Idx::divE, 0 ); -}; +} diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.H index 833a61aec3d..29a8d8e7fe4 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.H @@ -30,19 +30,17 @@ class SpectralBaseAlgorithmRZ /** * \brief Virtual function for current correction in Fourier space - * (equation (19) of https://doi.org/10.1016/j.jcp.2013.03.010). - * This virtual function is not pure: it can be overridden in derived - * classes (e.g. PsatdAlgorithmRZ), but a base-class - * implementation is provided (empty implementation in this case). + * ( Vay et al, 2013). + * This virtual function is pure and must be defined in derived classes. * - * \param[in,out] field_data all fields in Fourier space - * \param[in,out] current two-dimensional array of unique pointers to MultiFab - * storing the three components of the current density - * \param[in] rho unique pointer to MultiFab storing the charge density + * \param[in,out] field_data All fields in Fourier space + * \param[in,out] current Array of unique pointers to \c MultiFab storing + * the three components of the current density + * \param[in] rho Unique pointer to \c MultiFab storing the charge density */ virtual void CurrentCorrection ( SpectralFieldDataRZ& field_data, std::array,3>& current, - const std::unique_ptr& rho ) {}; + const std::unique_ptr& rho ) = 0; /** * \brief Compute spectral divergence of E @@ -65,7 +63,8 @@ class SpectralBaseAlgorithmRZ protected: // Meant to be used in the subclasses - using SpectralCoefficients = amrex::FabArray< amrex::BaseFab >; + using SpectralRealCoefficients = amrex::FabArray< amrex::BaseFab >; + using SpectralComplexCoefficients = amrex::FabArray< amrex::BaseFab >; // Constructor SpectralBaseAlgorithmRZ(SpectralKSpaceRZ const & spectral_kspace, diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.cpp index 679fc5fba28..4609b717707 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/SpectralBaseAlgorithmRZ.cpp @@ -36,8 +36,7 @@ SpectralBaseAlgorithmRZ::ComputeSpectralDivE ( Array4 fields = field_data.fields[mfi].array(); // Extract pointers for the k vectors - auto const & kr = field_data.getKrArray(mfi); - Real const * kr_arr = kr.dataPtr(); + Real const * kr_arr = field_data.getKrArray(mfi).dataPtr(); Real const * modified_kz_arr = modified_kz_vec[mfi].dataPtr(); int const nr = bx.length(0); @@ -46,7 +45,7 @@ SpectralBaseAlgorithmRZ::ComputeSpectralDivE ( // Loop over indices within one box ParallelFor(bx, modes, - [=] AMREX_GPU_DEVICE(int i, int j, int k, int mode) noexcept + [=] AMREX_GPU_DEVICE(int i, int j, int /*k*/, int mode) noexcept { int const ic1 = Idx::Ex + mode*n_fields; int const ic2 = Idx::Ey + mode*n_fields; @@ -71,4 +70,4 @@ SpectralBaseAlgorithmRZ::ComputeSpectralDivE ( // Backward Fourier transform field_data.BackwardTransform( divE, Idx::divE, 0 ); -}; +} diff --git a/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.H b/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.H index 9d3ab70f5f7..3dd3a59cdb7 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.H +++ b/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.H @@ -19,11 +19,11 @@ class SpectralBinomialFilter { public: - // `KFilterArray` holds a one 1D array ("ManagedVector") that + // `KFilterArray` holds a one 1D array ("DeviceVector") that // implements the filter. - using KFilterArray = amrex::Gpu::ManagedVector; + using KFilterArray = amrex::Gpu::DeviceVector; - SpectralBinomialFilter () {}; + SpectralBinomialFilter () {} void InitFilterArray (RealKVector const & kvec, amrex::Real const dels, int const npasses, @@ -35,8 +35,8 @@ class SpectralBinomialFilter amrex::IntVect const filter_npass_each_dir, bool const compensation); - KFilterArray const & getFilterArrayR () {return filter_r;}; - KFilterArray const & getFilterArrayZ () {return filter_z;}; + KFilterArray const & getFilterArrayR () {return filter_r;} + KFilterArray const & getFilterArrayZ () {return filter_z;} protected: diff --git a/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.cpp b/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.cpp index c4ecd0969de..e0ac3e886d4 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralBinomialFilter.cpp @@ -20,19 +20,21 @@ SpectralBinomialFilter::InitFilterArray (HankelTransform::RealVector const & kve bool const compensation, KFilterArray & filter) { - - filter.resize(kvec.size()); - - for (int i=0 ; i < kvec.size() ; i++) { - amrex::Real const ss = std::sin(0.5_rt*kvec[i]*dels); + const int N = kvec.size(); + filter.resize(N); + amrex::Real* p_filter = filter.data(); + amrex::Real const* p_kvec = kvec.data(); + + amrex::ParallelFor(N, [=] AMREX_GPU_DEVICE (int i) noexcept + { + amrex::Real const ss = std::sin(0.5_rt*p_kvec[i]*dels); amrex::Real const ss2 = ss*ss; amrex::Real filt = std::pow(1._rt - ss2, npasses); if (compensation) { filt *= (1._rt + npasses*ss2); } - filter[i] = filt; - } - + p_filter[i] = filt; + }); } /* \brief Initialize the radial and longitudinal filter arrays */ diff --git a/Source/FieldSolver/SpectralSolver/SpectralFieldData.H b/Source/FieldSolver/SpectralSolver/SpectralFieldData.H index f482727444c..4990f9926d6 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralFieldData.H +++ b/Source/FieldSolver/SpectralSolver/SpectralFieldData.H @@ -83,16 +83,6 @@ class SpectralFieldData SpectralShiftFactor yshift_FFTfromCell, yshift_FFTtoCell; #endif -#ifdef AMREX_USE_GPU - /** \brief This method converts a cufftResult - * into the corresponding string - * - * @param[in] err a cufftResult - * @return an std::string - */ - std::string cufftErrorToString (const cufftResult& err); -#endif - bool m_periodic_single_box; }; diff --git a/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.H b/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.H index a2ab2b9cb0a..0dff6da7d44 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.H +++ b/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.H @@ -24,8 +24,10 @@ class SpectralFieldDataRZ // Define the FFTplans type, which holds one fft plan per box // (plans are only initialized for the boxes that are owned by // the local MPI rank) -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) using FFTplans = amrex::LayoutData; +#elif defined(AMREX_USE_HIP) + using FFTplans = amrex::LayoutData; #else using FFTplans = amrex::LayoutData; #endif @@ -34,31 +36,32 @@ class SpectralFieldDataRZ using BinomialFilter = amrex::LayoutData; - SpectralFieldDataRZ(const amrex::BoxArray& realspace_ba, - const SpectralKSpaceRZ& k_space, - const amrex::DistributionMapping& dm, - const int n_field_required, - const int n_modes, - const int lev); - SpectralFieldDataRZ() = default; // Default constructor + SpectralFieldDataRZ (const amrex::BoxArray& realspace_ba, + const SpectralKSpaceRZ& k_space, + const amrex::DistributionMapping& dm, + const int n_field_required, + const int n_modes, + const int lev); + SpectralFieldDataRZ () = default; // Default constructor SpectralFieldDataRZ& operator=(SpectralFieldDataRZ&& field_data) = default; - ~SpectralFieldDataRZ(); - - void ForwardTransform(const amrex::MultiFab& mf, - const int field_index, const int i_comp); - void ForwardTransform(const amrex::MultiFab& mf_r, const int field_index_r, - const amrex::MultiFab& mf_t, const int field_index_t); - void BackwardTransform(amrex::MultiFab& mf, - const int field_index, const int i_comp); - void BackwardTransform(amrex::MultiFab& mf_r, const int field_index_r, - amrex::MultiFab& mf_t, const int field_index_t); - - void FABZForwardTransform(amrex::MFIter const & mfi, - amrex::MultiFab const & tempHTransformedSplit, - int field_index, const bool is_nodal_z); - void FABZBackwardTransform(amrex::MFIter const & mfi, const int field_index, - amrex::MultiFab & tempHTransformedSplit, - const bool is_nodal_z); + ~SpectralFieldDataRZ (); + + void ForwardTransform (const amrex::MultiFab& mf, const int field_index, + const int i_comp=0); + void ForwardTransform (const amrex::MultiFab& mf_r, const int field_index_r, + const amrex::MultiFab& mf_t, const int field_index_t); + void BackwardTransform (amrex::MultiFab& mf, const int field_index, + const int i_comp=0); + void BackwardTransform (amrex::MultiFab& mf_r, const int field_index_r, + amrex::MultiFab& mf_t, const int field_index_t); + + void FABZForwardTransform (amrex::MFIter const & mfi, amrex::Box const & realspace_bx, + amrex::MultiFab const & tempHTransformedSplit, + int field_index, const bool is_nodal_z); + void FABZBackwardTransform (amrex::MFIter const & mfi, amrex::Box const & realspace_bx, + const int field_index, + amrex::MultiFab & tempHTransformedSplit, + const bool is_nodal_z); void InitFilter (amrex::IntVect const & filter_npass_each_dir, bool const compensation, SpectralKSpaceRZ const & k_space); @@ -67,7 +70,7 @@ class SpectralFieldDataRZ void ApplyFilter (int const field_index1, int const field_index2, int const field_index3); // Returns an array that holds the kr for all of the modes - HankelTransform::RealVector const & getKrArray(amrex::MFIter const & mfi) const { + HankelTransform::RealVector const & getKrArray (amrex::MFIter const & mfi) const { return multi_spectral_hankel_transformer[mfi].getKrArray(); } diff --git a/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp b/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp index 21f84fce78d..c98c7983590 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralFieldDataRZ.cpp @@ -53,8 +53,8 @@ SpectralFieldDataRZ::SpectralFieldDataRZ (amrex::BoxArray const & realspace_ba, // Allocate and initialize the FFT plans and Hankel transformer. forward_plan = FFTplans(spectralspace_ba, dm); -#ifndef AMREX_USE_GPU - // The backward plan is not needed with GPU since it would be the same +#ifndef AMREX_USE_CUDA + // The backward plan is not needed with CUDA since it would be the same // as the forward plan anyway. backward_plan = FFTplans(spectralspace_ba, dm); #endif @@ -64,7 +64,7 @@ SpectralFieldDataRZ::SpectralFieldDataRZ (amrex::BoxArray const & realspace_ba, // for each box owned by the local MPI proc. for (amrex::MFIter mfi(spectralspace_ba, dm); mfi.isValid(); ++mfi){ amrex::IntVect grid_size = realspace_ba[mfi].length(); -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) // Create cuFFT plan. // This is alway complex to complex. // This plan is for one azimuthal mode only. @@ -77,13 +77,64 @@ SpectralFieldDataRZ::SpectralFieldDataRZ (amrex::BoxArray const & realspace_ba, int ostride = grid_size[0]; int odist = 1; int batch = grid_size[0]; // number of ffts +# ifdef AMREX_USE_FLOAT + auto cufft_type = CUFFT_C2C; +# else + auto cufft_type = CUFFT_Z2Z; +# endif result = cufftPlanMany(&forward_plan[mfi], 1, fft_length, inembed, istride, idist, - onembed, ostride, odist, CUFFT_Z2Z, batch); + onembed, ostride, odist, cufft_type, batch); if (result != CUFFT_SUCCESS) { - amrex::Print() << " cufftPlanMany failed! \n"; + amrex::AllPrint() << " cufftPlanMany failed! \n"; } // The backward plane is the same as the forward since the direction is passed when executed. +#elif defined(AMREX_USE_HIP) + const std::size_t fft_length[] = {static_cast(grid_size[1])}; + const std::size_t stride[] = {static_cast(grid_size[0])}; + rocfft_plan_description description; + rocfft_status result; + result = rocfft_plan_description_create(&description); + result = rocfft_plan_description_set_data_layout(description, + rocfft_array_type_complex_interleaved, + rocfft_array_type_complex_interleaved, + nullptr, nullptr, + 1, stride, 1, + 1, stride, 1); + + result = rocfft_plan_create(&(forward_plan[mfi]), + rocfft_placement_notinplace, + rocfft_transform_type_complex_forward, +#ifdef AMREX_USE_FLOAT + rocfft_precision_single, +#else + rocfft_precision_double, +#endif + 1, fft_length, + grid_size[0], // number of transforms + description); + if (result != rocfft_status_success) { + amrex::AllPrint() << " rocfft_plan_create failed! \n"; + } + + result = rocfft_plan_create(&(backward_plan[mfi]), + rocfft_placement_notinplace, + rocfft_transform_type_complex_inverse, +#ifdef AMREX_USE_FLOAT + rocfft_precision_single, +#else + rocfft_precision_double, +#endif + 1, fft_length, + grid_size[0], // number of transforms + description); + if (result != rocfft_status_success) { + amrex::AllPrint() << " rocfft_plan_create failed! \n"; + } + result = rocfft_plan_description_destroy(description); + if (result != rocfft_status_success) { + amrex::AllPrint() << " rocfft_plan_description_destroy failed! \n"; + } #else // Create FFTW plans. fftw_iodim dims[1]; @@ -129,10 +180,13 @@ SpectralFieldDataRZ::~SpectralFieldDataRZ() { if (fields.size() > 0){ for (amrex::MFIter mfi(fields); mfi.isValid(); ++mfi){ -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) // Destroy cuFFT plans. cufftDestroy(forward_plan[mfi]); // cufftDestroy(backward_plan[mfi]); // This was never allocated. +#elif defined(AMREX_USE_HIP) + rocfft_plan_destroy(forward_plan[mfi]); + rocfft_plan_destroy(backward_plan[mfi]); #else // Destroy FFTW plans. fftw_destroy_plan(forward_plan[mfi]); @@ -150,14 +204,12 @@ SpectralFieldDataRZ::~SpectralFieldDataRZ() * The input should include the imaginary component of mode 0 * (even though it is all zeros). */ void -SpectralFieldDataRZ::FABZForwardTransform (amrex::MFIter const & mfi, +SpectralFieldDataRZ::FABZForwardTransform (amrex::MFIter const & mfi, amrex::Box const & realspace_bx, amrex::MultiFab const & tempHTransformedSplit, int const field_index, const bool is_nodal_z) { // Copy the split complex to the interleaved complex. - amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); - amrex::Array4 const& split_arr = tempHTransformedSplit[mfi].array(); amrex::Array4 const& complex_arr = tempHTransformed[mfi].array(); @@ -170,7 +222,7 @@ SpectralFieldDataRZ::FABZForwardTransform (amrex::MFIter const & mfi, }); // Perform Fourier transform from `tempHTransformed` to `tmpSpectralField`. -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) // Perform Fast Fourier Transform on GPU using cuFFT. // Make sure that this is done on the same // GPU stream as the above copy. @@ -178,14 +230,39 @@ SpectralFieldDataRZ::FABZForwardTransform (amrex::MFIter const & mfi, cudaStream_t stream = amrex::Gpu::Device::cudaStream(); cufftSetStream(forward_plan[mfi], stream); for (int mode=0 ; mode < n_rz_azimuthal_modes ; mode++) { +# ifdef AMREX_USE_FLOAT + result = cufftExecC2C(forward_plan[mfi], +# else result = cufftExecZ2Z(forward_plan[mfi], - reinterpret_cast(tempHTransformed[mfi].dataPtr(mode)), // cuDoubleComplex *in - reinterpret_cast(tmpSpectralField[mfi].dataPtr(mode)), // cuDoubleComplex *out +# endif + reinterpret_cast(tempHTransformed[mfi].dataPtr(mode)), // Complex *in + reinterpret_cast(tmpSpectralField[mfi].dataPtr(mode)), // Complex *out CUFFT_FORWARD); if (result != CUFFT_SUCCESS) { - amrex::Print() << " forward transform using cufftExecZ2Z failed ! \n"; + amrex::AllPrint() << " forward transform using cufftExecZ2Z failed ! \n"; + } + } +#elif defined(AMREX_USE_HIP) + rocfft_execution_info execinfo = NULL; + rocfft_status result = rocfft_execution_info_create(&execinfo); + std::size_t buffersize = 0; + result = rocfft_plan_get_work_buffer_size(forward_plan[mfi], &buffersize); + void* buffer = amrex::The_Arena()->alloc(buffersize); + result = rocfft_execution_info_set_work_buffer(execinfo, buffer, buffersize); + result = rocfft_execution_info_set_stream(execinfo, amrex::Gpu::gpuStream()); + + for (int mode=0 ; mode < n_rz_azimuthal_modes ; mode++) { + void* in_array[] = {(void*)(tempHTransformed[mfi].dataPtr(mode))}; + void* out_array[] = {(void*)(tmpSpectralField[mfi].dataPtr(mode))}; + result = rocfft_execute(forward_plan[mfi], in_array, out_array, execinfo); + if (result != rocfft_status_success) { + amrex::AllPrint() << " forward transform using rocfft_execute failed ! \n"; } } + + amrex::Gpu::streamSynchronize(); + amrex::The_Arena()->free(buffer); + result = rocfft_execution_info_destroy(execinfo); #else fftw_execute(forward_plan[mfi]); #endif @@ -225,7 +302,8 @@ SpectralFieldDataRZ::FABZForwardTransform (amrex::MFIter const & mfi, * The output includes the imaginary component of mode 0 * (even though it is all zeros). */ void -SpectralFieldDataRZ::FABZBackwardTransform (amrex::MFIter const & mfi, int const field_index, +SpectralFieldDataRZ::FABZBackwardTransform (amrex::MFIter const & mfi, amrex::Box const & realspace_bx, + int const field_index, amrex::MultiFab & tempHTransformedSplit, const bool is_nodal_z) { @@ -253,7 +331,7 @@ SpectralFieldDataRZ::FABZBackwardTransform (amrex::MFIter const & mfi, int const }); // Perform Fourier transform from `tmpSpectralField` to `tempHTransformed`. -#ifdef AMREX_USE_GPU +#if defined(AMREX_USE_CUDA) // Perform Fast Fourier Transform on GPU using cuFFT. // Make sure that this is done on the same // GPU stream as the above copy. @@ -261,21 +339,44 @@ SpectralFieldDataRZ::FABZBackwardTransform (amrex::MFIter const & mfi, int const cudaStream_t stream = amrex::Gpu::Device::cudaStream(); cufftSetStream(forward_plan[mfi], stream); for (int mode=0 ; mode < n_rz_azimuthal_modes ; mode++) { +# ifdef AMREX_USE_FLOAT + result = cufftExecC2C(forward_plan[mfi], +# else result = cufftExecZ2Z(forward_plan[mfi], - reinterpret_cast(tmpSpectralField[mfi].dataPtr(mode)), // cuDoubleComplex *in - reinterpret_cast(tempHTransformed[mfi].dataPtr(mode)), // cuDoubleComplex *out +# endif + reinterpret_cast(tmpSpectralField[mfi].dataPtr(mode)), // Complex *in + reinterpret_cast(tempHTransformed[mfi].dataPtr(mode)), // Complex *out CUFFT_INVERSE); if (result != CUFFT_SUCCESS) { - amrex::Print() << " backwardtransform using cufftExecZ2Z failed ! \n"; + amrex::AllPrint() << " backwardtransform using cufftExecZ2Z failed ! \n"; + } + } +#elif defined(AMREX_USE_HIP) + rocfft_execution_info execinfo = NULL; + rocfft_status result = rocfft_execution_info_create(&execinfo); + std::size_t buffersize = 0; + result = rocfft_plan_get_work_buffer_size(forward_plan[mfi], &buffersize); + void* buffer = amrex::The_Arena()->alloc(buffersize); + result = rocfft_execution_info_set_work_buffer(execinfo, buffer, buffersize); + result = rocfft_execution_info_set_stream(execinfo, amrex::Gpu::gpuStream()); + + for (int mode=0 ; mode < n_rz_azimuthal_modes ; mode++) { + void* in_array[] = {(void*)(tmpSpectralField[mfi].dataPtr(mode))}; + void* out_array[] = {(void*)(tempHTransformed[mfi].dataPtr(mode))}; + result = rocfft_execute(backward_plan[mfi], in_array, out_array, execinfo); + if (result != rocfft_status_success) { + amrex::AllPrint() << " forward transform using rocfft_execute failed ! \n"; } } + + amrex::Gpu::streamSynchronize(); + amrex::The_Arena()->free(buffer); + result = rocfft_execution_info_destroy(execinfo); #else fftw_execute(backward_plan[mfi]); #endif // Copy the interleaved complex to the split complex. - amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); - amrex::Array4 const& split_arr = tempHTransformedSplit[mfi].array(); amrex::Array4 const& complex_arr = tempHTransformed[mfi].array(); @@ -302,6 +403,14 @@ SpectralFieldDataRZ::ForwardTransform (amrex::MultiFab const & field_mf, int con int const ncomp = 2*n_rz_azimuthal_modes - 1; + // Create a copy of the input multifab since the shape of field_mf + // might not be what is needed in transform. + // For example, with periodic_single_box_fft, field_mf will have guard cells but + // the transformed array does not. + // Note that the copy will not include the imaginary part of mode 0 as + // PhysicalToSpectral_Scalar expects. + amrex::MultiFab field_mf_copy(tempHTransformed.boxArray(), field_mf.DistributionMap(), ncomp, 0); + // This will hold the Hankel transformed data, with the real and imaginary parts split. // A full multifab is created so that each GPU stream has its own temp space. amrex::MultiFab tempHTransformedSplit(tempHTransformed.boxArray(), tempHTransformed.DistributionMap(), 2*n_rz_azimuthal_modes, 0); @@ -313,10 +422,11 @@ SpectralFieldDataRZ::ForwardTransform (amrex::MultiFab const & field_mf, int con // tempHTransformedSplit includes the imaginary component of mode 0. // field_mf does not. amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); - amrex::FArrayBox field_comp(field_mf[mfi], amrex::make_alias, i_comp*ncomp, ncomp); - multi_spectral_hankel_transformer[mfi].PhysicalToSpectral_Scalar(realspace_bx, field_comp, tempHTransformedSplit[mfi]); - FABZForwardTransform(mfi, tempHTransformedSplit, field_index, is_nodal_z); + field_mf_copy[mfi].copy(field_mf[mfi], i_comp*ncomp, 0, ncomp); + multi_spectral_hankel_transformer[mfi].PhysicalToSpectral_Scalar(field_mf_copy[mfi], tempHTransformedSplit[mfi]); + + FABZForwardTransform(mfi, realspace_bx, tempHTransformedSplit, field_index, is_nodal_z); } } @@ -335,14 +445,8 @@ SpectralFieldDataRZ::ForwardTransform (amrex::MultiFab const & field_mf_r, int c // Create copies of the input multifabs. The copies will include the imaginary part of mode 0. // Also note that the Hankel transform will overwrite the copies. // Full multifabs are created for the temps so that each GPU stream has its own temp space. - amrex::MultiFab field_mf_r_copy(field_mf_r.boxArray(), field_mf_r.DistributionMap(), 2*n_rz_azimuthal_modes, field_mf_r.nGrowVect()); - amrex::MultiFab field_mf_t_copy(field_mf_t.boxArray(), field_mf_t.DistributionMap(), 2*n_rz_azimuthal_modes, field_mf_t.nGrowVect()); - amrex::MultiFab::Copy(field_mf_r_copy, field_mf_r, 0, 0, 1, field_mf_r.nGrowVect()); // Real part of mode 0 - amrex::MultiFab::Copy(field_mf_t_copy, field_mf_t, 0, 0, 1, field_mf_t.nGrowVect()); // Real part of mode 0 - field_mf_r_copy.setVal(0._rt, 1, 1, field_mf_r.nGrowVect()); // Imaginary part of mode 0 - field_mf_t_copy.setVal(0._rt, 1, 1, field_mf_t.nGrowVect()); // Imaginary part of mode 0 - amrex::MultiFab::Copy(field_mf_r_copy, field_mf_r, 1, 2, 2*n_rz_azimuthal_modes-2, field_mf_r.nGrowVect()); - amrex::MultiFab::Copy(field_mf_t_copy, field_mf_t, 1, 2, 2*n_rz_azimuthal_modes-2, field_mf_t.nGrowVect()); + amrex::MultiFab field_mf_r_copy(tempHTransformed.boxArray(), field_mf_r.DistributionMap(), 2*n_rz_azimuthal_modes, 0); + amrex::MultiFab field_mf_t_copy(tempHTransformed.boxArray(), field_mf_t.DistributionMap(), 2*n_rz_azimuthal_modes, 0); amrex::MultiFab tempHTransformedSplit_p(tempHTransformed.boxArray(), tempHTransformed.DistributionMap(), 2*n_rz_azimuthal_modes, 0); amrex::MultiFab tempHTransformedSplit_m(tempHTransformed.boxArray(), tempHTransformed.DistributionMap(), 2*n_rz_azimuthal_modes, 0); @@ -350,14 +454,22 @@ SpectralFieldDataRZ::ForwardTransform (amrex::MultiFab const & field_mf_r, int c // Loop over boxes. for (amrex::MFIter mfi(field_mf_r); mfi.isValid(); ++mfi){ - // Perform the Hankel transform first. amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); + + field_mf_r_copy[mfi].copy(field_mf_r[mfi], 0, 0, 1); // Real part of mode 0 + field_mf_t_copy[mfi].copy(field_mf_t[mfi], 0, 0, 1); // Real part of mode 0 + field_mf_r_copy[mfi].setVal(0._rt, realspace_bx, 1, 1); // Imaginary part of mode 0 + field_mf_t_copy[mfi].setVal(0._rt, realspace_bx, 1, 1); // Imaginary part of mode 0 + field_mf_r_copy[mfi].copy(field_mf_r[mfi], 1, 2, 2*n_rz_azimuthal_modes-2); + field_mf_t_copy[mfi].copy(field_mf_t[mfi], 1, 2, 2*n_rz_azimuthal_modes-2); + + // Perform the Hankel transform first. multi_spectral_hankel_transformer[mfi].PhysicalToSpectral_Vector(realspace_bx, field_mf_r_copy[mfi], field_mf_t_copy[mfi], tempHTransformedSplit_p[mfi], tempHTransformedSplit_m[mfi]); - FABZForwardTransform(mfi, tempHTransformedSplit_p, field_index_r, is_nodal_z); - FABZForwardTransform(mfi, tempHTransformedSplit_m, field_index_t, is_nodal_z); + FABZForwardTransform(mfi, realspace_bx, tempHTransformedSplit_p, field_index_r, is_nodal_z); + FABZForwardTransform(mfi, realspace_bx, tempHTransformedSplit_m, field_index_t, is_nodal_z); } } @@ -378,20 +490,20 @@ SpectralFieldDataRZ::BackwardTransform (amrex::MultiFab& field_mf, int const fie // Create a temporary to hold the inverse Hankel transform field. // This allows the final result to have a different shape than the transformed field. - amrex::MultiFab field_mf_copy(tempHTransformed.boxArray(), tempHTransformed.DistributionMap(), 2*n_rz_azimuthal_modes, 0); + amrex::MultiFab field_mf_copy(tempHTransformed.boxArray(), tempHTransformed.DistributionMap(), 2*n_rz_azimuthal_modes-1, 0); // Loop over boxes. for (amrex::MFIter mfi(field_mf); mfi.isValid(); ++mfi){ - FABZBackwardTransform(mfi, field_index, tempHTransformedSplit, is_nodal_z); + amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); + + FABZBackwardTransform(mfi, realspace_bx, field_index, tempHTransformedSplit, is_nodal_z); // Perform the Hankel inverse transform last. // tempHTransformedSplit includes the imaginary component of mode 0. // field_mf does not. - amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); - amrex::FArrayBox field_comp(field_mf_copy[mfi], amrex::make_alias, i_comp*ncomp, ncomp); - multi_spectral_hankel_transformer[mfi].SpectralToPhysical_Scalar(realspace_bx, tempHTransformedSplit[mfi], field_comp); - field_mf[mfi].copy(field_comp, 0, i_comp*ncomp, ncomp); + multi_spectral_hankel_transformer[mfi].SpectralToPhysical_Scalar(tempHTransformedSplit[mfi], field_mf_copy[mfi]); + field_mf[mfi].copy(field_mf_copy[mfi], 0, i_comp*ncomp, ncomp); } } @@ -410,19 +522,20 @@ SpectralFieldDataRZ::BackwardTransform (amrex::MultiFab& field_mf_r, int const f amrex::MultiFab tempHTransformedSplit_m(tempHTransformed.boxArray(), tempHTransformed.DistributionMap(), 2*n_rz_azimuthal_modes, 0); // Create copies of the input multifabs. The copies will include the imaginary part of mode 0. - amrex::MultiFab field_mf_r_copy(field_mf_r.boxArray(), field_mf_r.DistributionMap(), 2*n_rz_azimuthal_modes, field_mf_r.nGrowVect()); - amrex::MultiFab field_mf_t_copy(field_mf_t.boxArray(), field_mf_t.DistributionMap(), 2*n_rz_azimuthal_modes, field_mf_t.nGrowVect()); + amrex::MultiFab field_mf_r_copy(tempHTransformed.boxArray(), field_mf_r.DistributionMap(), 2*n_rz_azimuthal_modes, 0); + amrex::MultiFab field_mf_t_copy(tempHTransformed.boxArray(), field_mf_t.DistributionMap(), 2*n_rz_azimuthal_modes, 0); // Loop over boxes. for (amrex::MFIter mfi(field_mf_r); mfi.isValid(); ++mfi){ - FABZBackwardTransform(mfi, field_index_r, tempHTransformedSplit_p, is_nodal_z); - FABZBackwardTransform(mfi, field_index_t, tempHTransformedSplit_m, is_nodal_z); + amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); + + FABZBackwardTransform(mfi, realspace_bx, field_index_r, tempHTransformedSplit_p, is_nodal_z); + FABZBackwardTransform(mfi, realspace_bx, field_index_t, tempHTransformedSplit_m, is_nodal_z); // Perform the Hankel inverse transform last. // tempHTransformedSplit includes the imaginary component of mode 0. // field_mf_[ri] do not. - amrex::Box const& realspace_bx = tempHTransformed[mfi].box(); multi_spectral_hankel_transformer[mfi].SpectralToPhysical_Vector(realspace_bx, tempHTransformedSplit_p[mfi], tempHTransformedSplit_m[mfi], field_mf_r_copy[mfi], field_mf_t_copy[mfi]); diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H index c7a816c614b..a44d192fbd9 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H @@ -51,7 +51,7 @@ void SecantRootFinder(int n, int nitmx, amrex::Real tol, amrex::Real *zeroj, int * abramowitz m. & stegun irene a. * handbook of mathematical functions */ -void GetBesselRoots(int n, int nk, HankelTransform::RealVector& roots, amrex::Vector &ier) { +void GetBesselRoots(int n, int nk, amrex::Vector& roots, amrex::Vector &ier) { amrex::Real zeroj; int ierror, ik, k; @@ -133,6 +133,7 @@ void SecantRootFinder(int n, int nitmx, amrex::Real tol, amrex::Real *zeroj, int c[1] = 0.999_rt; *ier = 0; + p = *zeroj; for (int ntry=0 ; ntry <= 1 ; ntry++) { p0 = c[ntry]*(*zeroj); diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.H b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.H index 46fb2844711..5a87031c40c 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.H +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.H @@ -21,7 +21,7 @@ class HankelTransform { public: - using RealVector = amrex::Gpu::ManagedVector; + using RealVector = amrex::Gpu::DeviceVector; // Constructor HankelTransform(const int hankel_order, @@ -43,8 +43,8 @@ class HankelTransform RealVector m_kr; - RealVector invM; - RealVector M; + RealVector m_invM; + RealVector m_M; }; #endif diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.cpp b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.cpp index c5249d54f18..24e9d7f815a 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/HankelTransform.cpp @@ -25,7 +25,7 @@ HankelTransform::HankelTransform (int const hankel_order, AMREX_ALWAYS_ASSERT_WITH_MESSAGE(hankel_order-1 <= azimuthal_mode && azimuthal_mode <= hankel_order+1, "azimuthal_mode must be either hankel_order-1, hankel_order or hankel_order+1"); - RealVector alphas; + amrex::Vector alphas; amrex::Vector alpha_errors; GetBesselRoots(azimuthal_mode, m_nk, alphas, alpha_errors); @@ -33,13 +33,13 @@ HankelTransform::HankelTransform (int const hankel_order, AMREX_ALWAYS_ASSERT(std::all_of(alpha_errors.begin(), alpha_errors.end(), [](int i) { return i == 0; })); // Calculate the spectral grid - m_kr.resize(m_nk); + amrex::Vector kr(m_nk); for (int ik=0 ; ik < m_nk ; ik++) { - m_kr[ik] = alphas[ik]/rmax; + kr[ik] = alphas[ik]/rmax; } // Calculate the spatial grid (Uniform grid with a half-cell offset) - RealVector rmesh(m_nr); + amrex::Vector rmesh(m_nr); amrex::Real dr = rmax/m_nr; for (int ir=0 ; ir < m_nr ; ir++) { rmesh[ir] = dr*(ir + 0.5_rt); @@ -57,22 +57,22 @@ HankelTransform::HankelTransform (int const hankel_order, p_denom = hankel_order; } - RealVector denom(m_nk); + amrex::Vector denom(m_nk); for (int ik=0 ; ik < m_nk ; ik++) { const amrex::Real jna = jn(p_denom, alphas[ik]); denom[ik] = MathConst::pi*rmax*rmax*jna*jna; } - RealVector num(m_nk*m_nr); + amrex::Vector num(m_nk*m_nr); for (int ir=0 ; ir < m_nr ; ir++) { for (int ik=0 ; ik < m_nk ; ik++) { int const ii = ik + ir*m_nk; - num[ii] = jn(hankel_order, rmesh[ir]*m_kr[ik]); + num[ii] = jn(hankel_order, rmesh[ir]*kr[ik]); } } // Get the inverse matrix - invM.resize(m_nk*m_nr); + amrex::Vector invM(m_nk*m_nr); if (azimuthal_mode > 0) { for (int ir=0 ; ir < m_nr ; ir++) { for (int ik=1 ; ik < m_nk ; ik++) { @@ -107,18 +107,20 @@ HankelTransform::HankelTransform (int const hankel_order, } } + amrex::Vector M; + // Calculate the matrix M by inverting invM if (azimuthal_mode !=0 && hankel_order != azimuthal_mode-1) { // In this case, invM is singular, thus we calculate the pseudo-inverse. // The Moore-Penrose psuedo-inverse is calculated using the SVD method. M.resize(m_nk*m_nr, 0.); - RealVector invMcopy(invM); - RealVector sdiag(m_nk-1, 0.); - RealVector u((m_nk-1)*(m_nk-1), 0.); - RealVector vt((m_nr)*(m_nr), 0.); - RealVector sp((m_nr)*(m_nk-1), 0.); - RealVector temp((m_nr)*(m_nk-1), 0.); + amrex::Vector invMcopy(invM); + amrex::Vector sdiag(m_nk-1, 0.); + amrex::Vector u((m_nk-1)*(m_nk-1), 0.); + amrex::Vector vt((m_nr)*(m_nr), 0.); + amrex::Vector sp((m_nr)*(m_nk-1), 0.); + amrex::Vector temp((m_nr)*(m_nk-1), 0.); // Calculate the singlular-value-decomposition of invM (leaving out the first row). // invM = u*sdiag*vt @@ -169,6 +171,13 @@ HankelTransform::HankelTransform (int const hankel_order, } + m_kr.resize(kr.size()); + m_invM.resize(invM.size()); + m_M.resize(M.size()); + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, kr.begin(), kr.end(), m_kr.begin()); + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, invM.begin(), invM.end(), m_invM.begin()); + amrex::Gpu::copyAsync(amrex::Gpu::hostToDevice, M.begin(), M.end(), m_M.begin()); + amrex::Gpu::synchronize(); } void @@ -193,7 +202,7 @@ HankelTransform::HankelForwardTransform (amrex::FArrayBox const& F, int const F_ // Note that M is flagged to be transposed since it has dimensions (m_nr, m_nk) blas::gemm(blas::Layout::ColMajor, blas::Op::Trans, blas::Op::NoTrans, m_nk, nz, m_nr, 1._rt, - M.dataPtr(), m_nk, + m_M.dataPtr(), m_nk, F.dataPtr(F_icomp)+ngr, nrF, 0._rt, G.dataPtr(G_icomp), m_nk); @@ -202,13 +211,13 @@ HankelTransform::HankelForwardTransform (amrex::FArrayBox const& F, int const F_ // It is not clear if the GPU gemm wasn't build properly, it is cycling data out and back // in to the device, or if it is because gemm is launching its own threads. - amrex::Real const * M_arr = M.dataPtr(); + amrex::Real const * M_arr = m_M.dataPtr(); amrex::Array4 const & F_arr = F.array(); amrex::Array4< amrex::Real> const & G_arr = G.array(); int const nr = m_nr; - ParallelFor(G_box, + amrex::ParallelFor(G_box, [=] AMREX_GPU_DEVICE(int ik, int iz, int inotused) noexcept { G_arr(ik,iz,G_icomp) = 0.; for (int ir=0 ; ir < nr ; ir++) { @@ -240,10 +249,10 @@ HankelTransform::HankelInverseTransform (amrex::FArrayBox const& G, int const G_ #ifndef AMREX_USE_GPU // On CPU, the blas::gemm is significantly faster - // Note that invM is flagged to be transposed since it has dimensions (m_nk, m_nr) + // Note that m_invM is flagged to be transposed since it has dimensions (m_nk, m_nr) blas::gemm(blas::Layout::ColMajor, blas::Op::Trans, blas::Op::NoTrans, m_nr, nz, m_nk, 1._rt, - invM.dataPtr(), m_nr, + m_invM.dataPtr(), m_nr, G.dataPtr(G_icomp), m_nk, 0._rt, F.dataPtr(F_icomp)+ngr, nrF); @@ -252,13 +261,13 @@ HankelTransform::HankelInverseTransform (amrex::FArrayBox const& G, int const G_ // It is not clear if the GPU gemm wasn't build properly, it is cycling data out and back // in to the device, or if it is because gemm is launching its own threads. - amrex::Real const * invM_arr = invM.dataPtr(); + amrex::Real const * invM_arr = m_invM.dataPtr(); amrex::Array4 const & G_arr = G.array(); amrex::Array4< amrex::Real> const & F_arr = F.array(); int const nk = m_nk; - ParallelFor(G_box, + amrex::ParallelFor(G_box, [=] AMREX_GPU_DEVICE(int ir, int iz, int inotused) noexcept { F_arr(ir,iz,F_icomp) = 0.; for (int ik=0 ; ik < nk ; ik++) { diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.H b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.H index d82fb9b8418..e24123f78ed 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.H +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.H @@ -20,48 +20,45 @@ class SpectralHankelTransformer { - public: - SpectralHankelTransformer() {}; + SpectralHankelTransformer () {}; - SpectralHankelTransformer(const int nr, - const int n_rz_azimuthal_modes, - const amrex::Real rmax); + SpectralHankelTransformer (const int nr, + const int n_rz_azimuthal_modes, + const amrex::Real rmax); void - ExtractKrArray(); + ExtractKrArray (); // Returns an array that holds the kr for all of the modes - HankelTransform::RealVector const & getKrArray() const {return m_kr;} + HankelTransform::RealVector const & getKrArray () const {return m_kr;} // Converts a scalar field from the physical to the spectral space void - PhysicalToSpectral_Scalar(amrex::Box const & box, - amrex::FArrayBox const & F_physical, - amrex::FArrayBox & G_spectral); + PhysicalToSpectral_Scalar (amrex::FArrayBox const & F_physical, + amrex::FArrayBox & G_spectral); // Converts a vector field from the physical to the spectral space void - PhysicalToSpectral_Vector(amrex::Box const & box, - amrex::FArrayBox & F_r_physical, - amrex::FArrayBox & F_t_physical, - amrex::FArrayBox & G_p_spectral, - amrex::FArrayBox & G_m_spectral); + PhysicalToSpectral_Vector (amrex::Box const & box, + amrex::FArrayBox & F_r_physical, + amrex::FArrayBox & F_t_physical, + amrex::FArrayBox & G_p_spectral, + amrex::FArrayBox & G_m_spectral); // Converts a scalar field from the spectral to the physical space void - SpectralToPhysical_Scalar(amrex::Box const & box, - amrex::FArrayBox const & G_spectral, - amrex::FArrayBox & F_physical); + SpectralToPhysical_Scalar (amrex::FArrayBox const & G_spectral, + amrex::FArrayBox & F_physical); // Converts a vector field from the spectral to the physical space void - SpectralToPhysical_Vector(amrex::Box const & box, - amrex::FArrayBox const & G_p_spectral, - amrex::FArrayBox const & G_m_spectral, - amrex::FArrayBox & F_r_physical, - amrex::FArrayBox & F_t_physical); + SpectralToPhysical_Vector (amrex::Box const & box, + amrex::FArrayBox const & G_p_spectral, + amrex::FArrayBox const & G_m_spectral, + amrex::FArrayBox & F_r_physical, + amrex::FArrayBox & F_t_physical); private: @@ -72,7 +69,6 @@ class SpectralHankelTransformer amrex::Vector< std::unique_ptr > dht0; amrex::Vector< std::unique_ptr > dhtm; amrex::Vector< std::unique_ptr > dhtp; - }; #endif diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.cpp b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.cpp index 7dd2974187d..7886bd549aa 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/SpectralHankelTransformer.cpp @@ -49,12 +49,12 @@ SpectralHankelTransformer::ExtractKrArray () kr_array[ii] = kr_m_array[ir]; }); } + amrex::Gpu::synchronize(); } /* \brief Converts a scalar field from the physical to the spectral space for all modes */ void -SpectralHankelTransformer::PhysicalToSpectral_Scalar (amrex::Box const & box, - amrex::FArrayBox const & F_physical, +SpectralHankelTransformer::PhysicalToSpectral_Scalar (amrex::FArrayBox const & F_physical, amrex::FArrayBox & G_spectral) { // The Hankel transform is purely real, so the real and imaginary parts of @@ -126,8 +126,7 @@ SpectralHankelTransformer::PhysicalToSpectral_Vector (amrex::Box const & box, /* \brief Converts a scalar field from the spectral to the physical space for all modes */ void -SpectralHankelTransformer::SpectralToPhysical_Scalar (amrex::Box const & box, - amrex::FArrayBox const & G_spectral, +SpectralHankelTransformer::SpectralToPhysical_Scalar (amrex::FArrayBox const & G_spectral, amrex::FArrayBox & F_physical) { // The Hankel inverse transform is purely real, so the real and imaginary parts of diff --git a/Source/FieldSolver/SpectralSolver/SpectralKSpace.H b/Source/FieldSolver/SpectralSolver/SpectralKSpace.H index d41e05a7307..b53fc7c4db7 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralKSpace.H +++ b/Source/FieldSolver/SpectralSolver/SpectralKSpace.H @@ -15,12 +15,12 @@ // `KVectorComponent` and `SpectralShiftFactor` hold one 1D array -// ("ManagedVector") for each box ("LayoutData"). The arrays are +// ("DeviceVector") for each box ("LayoutData"). The arrays are // only allocated if the corresponding box is owned by the local MPI rank. -using RealKVector = amrex::Gpu::ManagedVector; +using RealKVector = amrex::Gpu::DeviceVector; using KVectorComponent = amrex::LayoutData< RealKVector >; using SpectralShiftFactor = amrex::LayoutData< - amrex::Gpu::ManagedVector >; + amrex::Gpu::DeviceVector >; // Indicate the type of correction "shift" factor to apply // when the FFT is performed from/to a cell-centered grid in real space. @@ -39,7 +39,7 @@ class SpectralKSpace { public: amrex::BoxArray spectralspace_ba; - SpectralKSpace() : dx(amrex::RealVect::Zero) {}; + SpectralKSpace() : dx(amrex::RealVect::Zero) {} SpectralKSpace( const amrex::BoxArray& realspace_ba, const amrex::DistributionMapping& dm, const amrex::RealVect realspace_dx ); @@ -61,7 +61,16 @@ class SpectralKSpace amrex::RealVect dx; }; +/** + * \brief Returns an array of coefficients (Fornberg coefficients), corresponding + * to the weight of each point in a finite-difference approximation of a derivative + * (up to order \c n_order). + * + * \param[in] n_order order of the finite-difference approximation + * \param[in] nodal whether the finite-difference approximation is computed + * on a nodal grid or a staggered grid + */ amrex::Vector -getFonbergStencilCoefficients( const int n_order, const bool nodal ); +getFornbergStencilCoefficients(const int n_order, const bool nodal); #endif diff --git a/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp b/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp index 480e915a91e..8e296531ee9 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralKSpace.cpp @@ -75,17 +75,18 @@ SpectralKSpace::getKComponent( const DistributionMapping& dm, const int i_dim, const bool only_positive_k ) const { - // Initialize an empty ManagedVector in each box + // Initialize an empty DeviceVector in each box KVectorComponent k_comp(spectralspace_ba, dm); - // Loop over boxes and allocate the corresponding ManagedVector + // Loop over boxes and allocate the corresponding DeviceVector // for each box owned by the local MPI proc for ( MFIter mfi(spectralspace_ba, dm); mfi.isValid(); ++mfi ){ Box bx = spectralspace_ba[mfi]; - ManagedVector& k = k_comp[mfi]; + DeviceVector& k = k_comp[mfi]; // Allocate k to the right size int N = bx.length( i_dim ); k.resize( N ); + Real* pk = k.data(); // Fill the k vector IntVect fft_size = realspace_ba[mfi].length(); @@ -97,21 +98,24 @@ SpectralKSpace::getKComponent( const DistributionMapping& dm, if (only_positive_k){ // Fill the full axis with positive k values // (typically: first axis, in a real-to-complex FFT) - for (int i=0; i& k = k_vec[i_dim][mfi]; - ManagedVector& shift = shift_factor[mfi]; + const DeviceVector& k = k_vec[i_dim][mfi]; + DeviceVector& shift = shift_factor[mfi]; // Allocate shift coefficients - shift.resize( k.size() ); + const int N = k.size(); + shift.resize(N); + Real const* pk = k.data(); + Complex* pshift = shift.data(); // Fill the shift coefficients Real sign = 0; @@ -149,9 +156,11 @@ SpectralKSpace::getSpectralShiftFactor( const DistributionMapping& dm, case ShiftType::TransformToCellCentered: sign = 1.; } const Complex I{0,1}; - for (int i=0; i& k = k_vec[i_dim][mfi]; - ManagedVector& modified_k = modified_k_comp[mfi]; + const DeviceVector& k = k_vec[i_dim][mfi]; + DeviceVector& modified_k = modified_k_comp[mfi]; // Allocate modified_k to the same size as k - modified_k.resize( k.size() ); + const int N = k.size(); + modified_k.resize(N); // Fill the modified k vector - for (int i=0; i stencil_coef = getFonbergStencilCoefficients(n_order, nodal); + Vector h_stencil_coef = getFornbergStencilCoefficients(n_order, nodal); + DeviceVector d_stencil_coef(h_stencil_coef.size()); + Gpu::copyAsync(Gpu::hostToDevice, h_stencil_coef.begin(), h_stencil_coef.end(), + d_stencil_coef.begin()); + Gpu::synchronize(); + const int nstencil = d_stencil_coef.size(); + Real const* p_stencil_coef = d_stencil_coef.data(); - // Loop over boxes and allocate the corresponding ManagedVector + // Loop over boxes and allocate the corresponding DeviceVector // for each box owned by the local MPI proc for ( MFIter mfi(spectralspace_ba, dm); mfi.isValid(); ++mfi ){ Real delta_x = dx[i_dim]; - const ManagedVector& k = k_vec[i_dim][mfi]; - ManagedVector& modified_k = modified_k_comp[mfi]; + const DeviceVector& k = k_vec[i_dim][mfi]; + DeviceVector& modified_k = modified_k_comp[mfi]; // Allocate modified_k to the same size as k - modified_k.resize( k.size() ); + const int N = k.size(); + modified_k.resize(N); + Real const* p_k = k.data(); + Real * p_modified_k = modified_k.data(); // Fill the modified k vector - for (int i=0; i -getFonbergStencilCoefficients( const int n_order, const bool nodal ) +getFornbergStencilCoefficients(const int n_order, const bool nodal) { - AMREX_ALWAYS_ASSERT_WITH_MESSAGE( n_order%2 == 0, - "n_order should be even."); - const int m = n_order/2; + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(n_order % 2 == 0, "n_order must be even"); + + const int m = n_order / 2; Vector coefs; - coefs.resize( m+1 ); + coefs.resize(m); - // Note: there are closed-form formula for these coefficients, - // but they result in an overflow when evaluated numerically. - // One way to avoid the overflow is to calculate the coefficients - // by recurrence. + // There are closed-form formula for these coefficients, but they result in + // an overflow when evaluated numerically. One way to avoid the overflow is + // to calculate the coefficients by recurrence. - // Coefficients for nodal (a.k.a. centered) finite-difference + // Coefficients for nodal (that is, centered) finite-difference approximation if (nodal == true) { - coefs[0] = -2.; // First coefficient - for (int n=1; n,3>& Efield, amrex::MultiFab& divE ) { algorithm->ComputeSpectralDivE( field_data, Efield, divE ); - }; + } /** * \brief Public interface to call the virtual function \c CurrentCorrection, * defined in the base class SpectralBaseAlgorithm and possibly overridden - * by its derived classes (e.g. PsatdAlgorithm, PMLPsatdAlgorithm), from + * by its derived classes (e.g. PsatdAlgorithm, GalileanAlgorithm), from * objects of class SpectralSolver through the private unique pointer \c algorithm * * \param[in,out] current three-dimensional array of unique pointers to MultiFab @@ -82,7 +82,7 @@ class SpectralSolver void CurrentCorrection ( std::array,3>& current, const std::unique_ptr& rho ) { algorithm->CurrentCorrection( field_data, current, rho ); - }; + } /** * \brief Public interface to call the virtual function \c VayDeposition, diff --git a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp index 7146c7d4ceb..5d2c954ce30 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp @@ -69,7 +69,7 @@ SpectralSolver::SpectralSolver( } else { algorithm = std::unique_ptr( new GalileanAlgorithm( - k_space, dm, norder_x, norder_y, norder_z, nodal, v_galilean, dt ) ); + k_space, dm, norder_x, norder_y, norder_z, nodal, v_galilean, dt, update_with_rho ) ); } } } diff --git a/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.H b/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.H index 7a6f802787a..e79d868f651 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.H +++ b/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.H @@ -25,87 +25,64 @@ class SpectralSolverRZ // underlying classes `SpectralFieldData` and `PsatdAlgorithm` // Constructor - SpectralSolverRZ(amrex::BoxArray const & realspace_ba, - amrex::DistributionMapping const & dm, - int const n_rz_azimuthal_modes, - int const norder_z, bool const nodal, - amrex::RealVect const dx, amrex::Real const dt, - int const lev, - bool const pml=false); + SpectralSolverRZ (amrex::BoxArray const & realspace_ba, + amrex::DistributionMapping const & dm, + int const n_rz_azimuthal_modes, + int const norder_z, bool const nodal, + const amrex::Array& v_galilean, + amrex::RealVect const dx, amrex::Real const dt, + int const lev); /* \brief Transform the component `i_comp` of MultiFab `field_mf` * to spectral space, and store the corresponding result internally * (in the spectral field specified by `field_index`) */ - void ForwardTransform (amrex::MultiFab const & field_mf, - int const field_index, - int const i_comp=0 ) { - BL_PROFILE("SpectralSolverRZ::ForwardTransform"); - field_data.ForwardTransform(field_mf, field_index, i_comp); - }; + void ForwardTransform (amrex::MultiFab const & field_mf, int const field_index, + int const i_comp=0); /* \brief Transform the two MultiFabs `field_mf1` and `field_mf2` * to spectral space, and store the corresponding results internally * (in the spectral field specified by `field_index1` and `field_index2`) */ void ForwardTransform (amrex::MultiFab const & field_mf1, int const field_index1, - amrex::MultiFab const & field_mf2, int const field_index2) { - BL_PROFILE("SpectralSolverRZ::ForwardTransform"); - field_data.ForwardTransform(field_mf1, field_index1, - field_mf2, field_index2); - }; + amrex::MultiFab const & field_mf2, int const field_index2); /* \brief Transform spectral field specified by `field_index` back to * real space, and store it in the component `i_comp` of `field_mf` */ - void BackwardTransform (amrex::MultiFab& field_mf, - int const field_index, - int const i_comp=0) { - BL_PROFILE("SpectralSolverRZ::BackwardTransform"); - field_data.BackwardTransform(field_mf, field_index, i_comp); - }; + void BackwardTransform (amrex::MultiFab& field_mf, int const field_index, + int const i_comp=0); /* \brief Transform spectral fields specified by `field_index1` and `field_index2` * back to real space, and store it in `field_mf1` and `field_mf2`*/ void BackwardTransform (amrex::MultiFab& field_mf1, int const field_index1, - amrex::MultiFab& field_mf2, int const field_index2) { - BL_PROFILE("SpectralSolverRZ::BackwardTransform"); - field_data.BackwardTransform(field_mf1, field_index1, - field_mf2, field_index2); - }; + amrex::MultiFab& field_mf2, int const field_index2); /* \brief Update the fields in spectral space, over one timestep */ - void pushSpectralFields () { - BL_PROFILE("SpectralSolverRZ::pushSpectralFields"); - // Virtual function: the actual function used here depends - // on the sub-class of `SpectralBaseAlgorithm` that was - // initialized in the constructor of `SpectralSolverRZ` - algorithm->pushSpectralFields(field_data); - }; + void pushSpectralFields (); /* \brief Initialize K space filtering arrays */ void InitFilter (amrex::IntVect const & filter_npass_each_dir, bool const compensation) { field_data.InitFilter(filter_npass_each_dir, compensation, k_space); - }; + } /* \brief Apply K space filtering for a scalar */ void ApplyFilter (int const field_index) { field_data.ApplyFilter(field_index); - }; + } /* \brief Apply K space filtering for a vector */ void ApplyFilter (int const field_index1, int const field_index2, int const field_index3) { field_data.ApplyFilter(field_index1, field_index2, field_index3); - }; + } + /** * \brief Public interface to call the member function ComputeSpectralDivE * of the base class SpectralBaseAlgorithmRZ from objects of class SpectralSolverRZ */ - void ComputeSpectralDivE ( const std::array,3>& Efield, - amrex::MultiFab& divE ) { - algorithm->ComputeSpectralDivE( field_data, Efield, divE ); - }; + void ComputeSpectralDivE (const std::array,3>& Efield, + amrex::MultiFab& divE); /** * \brief Public interface to call the virtual function \c CurrentCorrection, @@ -117,10 +94,8 @@ class SpectralSolverRZ * storing the three components of the current density * \param[in] rho unique pointer to MultiFab storing the charge density */ - void CurrentCorrection ( std::array,3>& current, - const std::unique_ptr& rho ) { - algorithm->CurrentCorrection( field_data, current, rho ); - }; + void CurrentCorrection (std::array,3>& current, + const std::unique_ptr& rho); /** * \brief Public interface to call the virtual function \c VayDeposition, @@ -129,12 +104,9 @@ class SpectralSolverRZ * unique pointer \c algorithm. * * \param[in,out] current Array of unique pointers to \c MultiFab storing - * the three components of the current density + * the three components of the current density */ - void VayDeposition (std::array,3>& current) - { - algorithm->VayDeposition(field_data, current); - } + void VayDeposition (std::array,3>& current); private: diff --git a/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.cpp b/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.cpp index 60e981181b0..38851ef50b0 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralSolverRZ.cpp @@ -7,6 +7,9 @@ #include "SpectralKSpaceRZ.H" #include "SpectralSolverRZ.H" #include "SpectralAlgorithms/PsatdAlgorithmRZ.H" +#include "SpectralAlgorithms/GalileanPsatdAlgorithmRZ.H" +#include "WarpX.H" +#include "Utils/WarpXProfilerWrapper.H" /* \brief Initialize the spectral Maxwell solver * @@ -22,16 +25,15 @@ * \param pml Whether the boxes in which the solver is applied are PML boxes * PML is not supported. */ -SpectralSolverRZ::SpectralSolverRZ(amrex::BoxArray const & realspace_ba, - amrex::DistributionMapping const & dm, - int const n_rz_azimuthal_modes, - int const norder_z, bool const nodal, - amrex::RealVect const dx, amrex::Real const dt, - int const lev, - bool const pml ) +SpectralSolverRZ::SpectralSolverRZ (amrex::BoxArray const & realspace_ba, + amrex::DistributionMapping const & dm, + int const n_rz_azimuthal_modes, + int const norder_z, bool const nodal, + const amrex::Array& v_galilean, + amrex::RealVect const dx, amrex::Real const dt, + int const lev) : k_space(realspace_ba, dm, dx) { - // Initialize all structures using the same distribution mapping dm // - The k space object contains info about the size of @@ -41,12 +43,100 @@ SpectralSolverRZ::SpectralSolverRZ(amrex::BoxArray const & realspace_ba, // - Select the algorithm depending on the input parameters // Initialize the corresponding coefficients over k space // PML is not supported. - algorithm = std::unique_ptr( - new PsatdAlgorithmRZ(k_space, dm, n_rz_azimuthal_modes, norder_z, nodal, dt)); + if (v_galilean[2] == 0) { + // v_galilean is 0: use standard PSATD algorithm + algorithm = std::unique_ptr( + new PsatdAlgorithmRZ(k_space, dm, n_rz_azimuthal_modes, norder_z, nodal, dt)); + } else { + // Otherwise: use the Galilean algorithm + algorithm = std::unique_ptr( + new GalileanPsatdAlgorithmRZ(k_space, dm, n_rz_azimuthal_modes, norder_z, nodal, v_galilean, dt)); + } // - Initialize arrays for fields in spectral space + FFT plans field_data = SpectralFieldDataRZ(realspace_ba, k_space, dm, algorithm->getRequiredNumberOfFields(), n_rz_azimuthal_modes, lev); +} + +/* \brief Transform the component `i_comp` of MultiFab `field_mf` + * to spectral space, and store the corresponding result internally + * (in the spectral field specified by `field_index`) */ +void +SpectralSolverRZ::ForwardTransform (amrex::MultiFab const & field_mf, int const field_index, + int const i_comp) { + WARPX_PROFILE("SpectralSolverRZ::ForwardTransform"); + field_data.ForwardTransform(field_mf, field_index, i_comp); +} + +/* \brief Transform the two MultiFabs `field_mf1` and `field_mf2` + * to spectral space, and store the corresponding results internally + * (in the spectral field specified by `field_index1` and `field_index2`) */ +void +SpectralSolverRZ::ForwardTransform (amrex::MultiFab const & field_mf1, int const field_index1, + amrex::MultiFab const & field_mf2, int const field_index2) { + WARPX_PROFILE("SpectralSolverRZ::ForwardTransform"); + field_data.ForwardTransform(field_mf1, field_index1, + field_mf2, field_index2); +} + +/* \brief Transform spectral field specified by `field_index` back to + * real space, and store it in the component `i_comp` of `field_mf` */ +void +SpectralSolverRZ::BackwardTransform (amrex::MultiFab& field_mf, int const field_index, + int const i_comp) { + WARPX_PROFILE("SpectralSolverRZ::BackwardTransform"); + field_data.BackwardTransform(field_mf, field_index, i_comp); +} + +/* \brief Transform spectral fields specified by `field_index1` and `field_index2` + * back to real space, and store it in `field_mf1` and `field_mf2`*/ +void +SpectralSolverRZ::BackwardTransform (amrex::MultiFab& field_mf1, int const field_index1, + amrex::MultiFab& field_mf2, int const field_index2) { + WARPX_PROFILE("SpectralSolverRZ::BackwardTransform"); + field_data.BackwardTransform(field_mf1, field_index1, + field_mf2, field_index2); +} + +/* \brief Update the fields in spectral space, over one timestep */ +void +SpectralSolverRZ::pushSpectralFields () { + WARPX_PROFILE("SpectralSolverRZ::pushSpectralFields"); + // Virtual function: the actual function used here depends + // on the sub-class of `SpectralBaseAlgorithm` that was + // initialized in the constructor of `SpectralSolverRZ` + algorithm->pushSpectralFields(field_data); +} -}; +/** + * \brief Public interface to call the member function ComputeSpectralDivE + * of the base class SpectralBaseAlgorithmRZ from objects of class SpectralSolverRZ + */ +void +SpectralSolverRZ::ComputeSpectralDivE (const std::array,3>& Efield, + amrex::MultiFab& divE) { + algorithm->ComputeSpectralDivE(field_data, Efield, divE); +} + +/** + * \brief Public interface to call the virtual function \c CurrentCorrection, + * defined in the base class SpectralBaseAlgorithmRZ and possibly overridden + * by its derived classes (e.g. PsatdAlgorithmRZ), from + * objects of class SpectralSolverRZ through the private unique pointer \c algorithm + * + * \param[in,out] current two-dimensional array of unique pointers to MultiFab + * storing the three components of the current density + * \param[in] rho unique pointer to MultiFab storing the charge density + */ +void +SpectralSolverRZ::CurrentCorrection (std::array,3>& current, + const std::unique_ptr& rho) { + algorithm->CurrentCorrection(field_data, current, rho); +} + +void +SpectralSolverRZ::VayDeposition (std::array,3>& current) +{ + algorithm->VayDeposition(field_data, current); +} diff --git a/Source/FieldSolver/SpectralSolver/WrapCuFFT.cpp b/Source/FieldSolver/SpectralSolver/WrapCuFFT.cpp index 3793eebe0e5..f612143be81 100644 --- a/Source/FieldSolver/SpectralSolver/WrapCuFFT.cpp +++ b/Source/FieldSolver/SpectralSolver/WrapCuFFT.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + #include "AnyFFT.H" namespace AnyFFT diff --git a/Source/FieldSolver/SpectralSolver/WrapFFTW.cpp b/Source/FieldSolver/SpectralSolver/WrapFFTW.cpp index 2780771efff..b4999380b79 100644 --- a/Source/FieldSolver/SpectralSolver/WrapFFTW.cpp +++ b/Source/FieldSolver/SpectralSolver/WrapFFTW.cpp @@ -1,3 +1,10 @@ +/* Copyright 2019-2020 + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + #include "AnyFFT.H" namespace AnyFFT diff --git a/Source/FieldSolver/SpectralSolver/WrapRocFFT.cpp b/Source/FieldSolver/SpectralSolver/WrapRocFFT.cpp new file mode 100644 index 00000000000..54c96762a63 --- /dev/null +++ b/Source/FieldSolver/SpectralSolver/WrapRocFFT.cpp @@ -0,0 +1,132 @@ +/* Copyright 2019-2020 + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "AnyFFT.H" + +namespace AnyFFT +{ + + std::string rocfftErrorToString (const rocfft_status err); + + namespace { + void assert_rocfft_status (std::string const& name, rocfft_status status) + { + if (status != rocfft_status_success) { + amrex::Abort(name + " failed! Error: " + rocfftErrorToString(status)); + } + } + } + + FFTplan CreatePlan (const amrex::IntVect& real_size, amrex::Real * const real_array, + Complex * const complex_array, const direction dir, const int dim) + { + FFTplan fft_plan; + + const std::size_t lengths[] = {AMREX_D_DECL(std::size_t(real_size[0]), + std::size_t(real_size[1]), + std::size_t(real_size[2]))}; + + // Initialize fft_plan.m_plan with the vendor fft plan. + rocfft_status result = rocfft_plan_create(&(fft_plan.m_plan), + rocfft_placement_notinplace, + (dir == direction::R2C) + ? rocfft_transform_type_real_forward + : rocfft_transform_type_real_inverse, +#ifdef AMREX_USE_FLOAT + rocfft_precision_single, +#else + rocfft_precision_double, +#endif + dim, lengths, + 1, // number of transforms, + nullptr); + assert_rocfft_status("rocfft_plan_create", result); + + // Store meta-data in fft_plan + fft_plan.m_real_array = real_array; + fft_plan.m_complex_array = complex_array; + fft_plan.m_dir = dir; + fft_plan.m_dim = dim; + + return fft_plan; + } + + void DestroyPlan (FFTplan& fft_plan) + { + rocfft_plan_destroy( fft_plan.m_plan ); + } + + void Execute (FFTplan& fft_plan) + { + rocfft_execution_info execinfo = NULL; + rocfft_status result = rocfft_execution_info_create(&execinfo); + assert_rocfft_status("rocfft_execution_info_create", result); + + std::size_t buffersize = 0; + result = rocfft_plan_get_work_buffer_size(fft_plan.m_plan, &buffersize); + assert_rocfft_status("rocfft_plan_get_work_buffer_size", result); + + void* buffer = amrex::The_Arena()->alloc(buffersize); + result = rocfft_execution_info_set_work_buffer(execinfo, buffer, buffersize); + assert_rocfft_status("rocfft_execution_info_set_work_buffer", result); + + result = rocfft_execution_info_set_stream(execinfo, amrex::Gpu::gpuStream()); + assert_rocfft_status("rocfft_execution_info_set_stream", result); + + if (fft_plan.m_dir == direction::R2C) { + result = rocfft_execute(fft_plan.m_plan, + (void**)&(fft_plan.m_real_array), // in + (void**)&(fft_plan.m_complex_array), // out + execinfo); + } else if (fft_plan.m_dir == direction::C2R) { + result = rocfft_execute(fft_plan.m_plan, + (void**)&(fft_plan.m_complex_array), // in + (void**)&(fft_plan.m_real_array), // out + execinfo); + } else { + amrex::Abort("direction must be AnyFFT::direction::R2C or AnyFFT::direction::C2R"); + } + + assert_rocfft_status("rocfft_execute", result); + + amrex::Gpu::streamSynchronize(); + + amrex::The_Arena()->free(buffer); + + result = rocfft_execution_info_destroy(execinfo); + assert_rocfft_status("rocfft_execution_info_destroy", result); + } + + /** \brief This method converts a rocfftResult + * into the corresponding string + * + * @param[in] err a rocfftResult + * @return an std::string + */ + std::string rocfftErrorToString (const rocfft_status err) + { + if (err == rocfft_status_success) { + return std::string("rocfft_status_success"); + } else if (err == rocfft_status_failure) { + return std::string("rocfft_status_failure"); + } else if (err == rocfft_status_invalid_arg_value) { + return std::string("rocfft_status_invalid_arg_value"); + } else if (err == rocfft_status_invalid_dimensions) { + return std::string("rocfft_status_invalid_dimensions"); + } else if (err == rocfft_status_invalid_array_type) { + return std::string("rocfft_status_invalid_array_type"); + } else if (err == rocfft_status_invalid_strides) { + return std::string("rocfft_status_invalid_strides"); + } else if (err == rocfft_status_invalid_distance) { + return std::string("rocfft_status_invalid_distance"); + } else if (err == rocfft_status_invalid_offset) { + return std::string("rocfft_status_invalid_offset"); + } else { + return std::to_string(err) + " (unknown error code)"; + } + } +} diff --git a/Source/FieldSolver/WarpXPushFieldsEM.cpp b/Source/FieldSolver/WarpXPushFieldsEM.cpp index 6c14757ea75..a03026f10a6 100644 --- a/Source/FieldSolver/WarpXPushFieldsEM.cpp +++ b/Source/FieldSolver/WarpXPushFieldsEM.cpp @@ -41,6 +41,10 @@ namespace { std::array,3>& current, std::unique_ptr& rho ) { +#ifdef WARPX_DIM_RZ + amrex::ignore_unused(Efield_avg, Bfield_avg); +#endif + using Idx = SpectralAvgFieldIndex; // Perform forward Fourier transform @@ -135,6 +139,9 @@ WarpX::PushPSATD (int lev, amrex::Real /* dt */) PushPSATDSinglePatch( *spectral_solver_cp[lev], Efield_cp[lev], Bfield_cp[lev], Efield_avg_cp[lev], Bfield_avg_cp[lev], current_cp[lev], rho_cp[lev] ); } + if (use_damp_fields_in_z_guard) { + DampFieldsInGuards( Efield_fp[lev], Bfield_fp[lev] ); + } } #endif @@ -311,8 +318,105 @@ WarpX::MacroscopicEvolveE (int lev, PatchType patch_type, amrex::Real a_dt) { else { amrex::Abort("Macroscopic EvolveE is not implemented for lev > 0, yet."); } - if (do_pml) { - amrex::Abort("Macroscopic EvolveE is not implemented for pml boundary condition, yet"); + if (do_pml && pml[lev]->ok()) { + if (patch_type == PatchType::fine) { + m_fdtd_solver_fp[lev]->EvolveEPML( + pml[lev]->GetE_fp(), pml[lev]->GetB_fp(), + pml[lev]->Getj_fp(), pml[lev]->GetF_fp(), + pml[lev]->GetMultiSigmaBox_fp(), + a_dt, pml_has_particles ); + } else { + m_fdtd_solver_cp[lev]->EvolveEPML( + pml[lev]->GetE_cp(), pml[lev]->GetB_cp(), + pml[lev]->Getj_cp(), pml[lev]->GetF_cp(), + pml[lev]->GetMultiSigmaBox_cp(), + a_dt, pml_has_particles ); + } + } +} + +void +WarpX::DampFieldsInGuards(std::array,3>& Efield, + std::array,3>& Bfield) { + + constexpr int zdir = (AMREX_SPACEDIM - 1); + + for ( amrex::MFIter mfi(*Efield[0], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi ) + { + + amrex::Array4 const& Ex_arr = Efield[0]->array(mfi); + amrex::Array4 const& Ey_arr = Efield[1]->array(mfi); + amrex::Array4 const& Ez_arr = Efield[2]->array(mfi); + amrex::Array4 const& Bx_arr = Bfield[0]->array(mfi); + amrex::Array4 const& By_arr = Bfield[1]->array(mfi); + amrex::Array4 const& Bz_arr = Bfield[2]->array(mfi); + + // Get the tilebox from Efield so that it includes the guard cells. + amrex::Box tilebox = (*Efield[0])[mfi].box(); + int const nz_tile = tilebox.bigEnd(zdir); + + // Box for the whole simulation domain + amrex::Box const& domain = Geom(0).Domain(); + int const nz_domain = domain.bigEnd(zdir); + + if (tilebox.smallEnd(zdir) < 0) { + + // Apply damping factor in guards cells below the lower end of the domain + int const nz_guard = -tilebox.smallEnd(zdir); + + // Set so the box only covers the lower half of the guard cells + tilebox.setBig(zdir, -nz_guard/2-1); + + amrex::ParallelFor(tilebox, Efield[0]->nComp(), + [=] AMREX_GPU_DEVICE (int i, int j, int k, int icomp) + { +#if (AMREX_SPACEDIM == 3) + amrex::Real zcell = static_cast(k + nz_guard); +#else + amrex::Real zcell = static_cast(j + nz_guard); +#endif + amrex::Real phase = MathConst::pi*zcell/nz_guard; + amrex::Real damp_factor = std::pow(std::sin(phase), 2); + + Ex_arr(i,j,k,icomp) *= damp_factor; + Ey_arr(i,j,k,icomp) *= damp_factor; + Ez_arr(i,j,k,icomp) *= damp_factor; + Bx_arr(i,j,k,icomp) *= damp_factor; + By_arr(i,j,k,icomp) *= damp_factor; + Bz_arr(i,j,k,icomp) *= damp_factor; + + }); + + } + else if (nz_tile > nz_domain) { + + // Apply damping factor in guards cells above the upper end of the domain + int nz_guard = nz_tile - nz_domain; + + // Set so the box only covers the upper half of the guard cells + tilebox.setSmall(zdir, nz_domain + nz_guard/2 + 1); + + amrex::ParallelFor(tilebox, Efield[0]->nComp(), + [=] AMREX_GPU_DEVICE (int i, int j, int k, int icomp) + { +#if (AMREX_SPACEDIM == 3) + amrex::Real zcell = static_cast(nz_tile - k); +#else + amrex::Real zcell = static_cast(nz_tile - j); +#endif + amrex::Real phase = MathConst::pi*zcell/nz_guard; + amrex::Real damp_factor = std::pow(std::sin(phase), 2); + + Ex_arr(i,j,k,icomp) *= damp_factor; + Ey_arr(i,j,k,icomp) *= damp_factor; + Ez_arr(i,j,k,icomp) *= damp_factor; + Bx_arr(i,j,k,icomp) *= damp_factor; + By_arr(i,j,k,icomp) *= damp_factor; + Bz_arr(i,j,k,icomp) *= damp_factor; + + }); + + } } } @@ -379,7 +483,7 @@ WarpX::ApplyInverseVolumeScalingToCurrentDensity (MultiFab* Jx, MultiFab* Jy, Mu // Rescale current in r-z mode since the inverse volume factor was not // included in the current deposition. amrex::ParallelFor(tbr, tbt, tbz, - [=] AMREX_GPU_DEVICE (int i, int j, int k) + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/) { // Wrap the current density deposited in the guard cells around // to the cells above the axis. @@ -408,7 +512,7 @@ WarpX::ApplyInverseVolumeScalingToCurrentDensity (MultiFab* Jx, MultiFab* Jy, Mu Jr_arr(i,j,0,2*imode) /= (2.*MathConst::pi*r); } }, - [=] AMREX_GPU_DEVICE (int i, int j, int k) + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/) { // Wrap the current density deposited in the guard cells around // to the cells above the axis. @@ -446,7 +550,7 @@ WarpX::ApplyInverseVolumeScalingToCurrentDensity (MultiFab* Jx, MultiFab* Jy, Mu } } }, - [=] AMREX_GPU_DEVICE (int i, int j, int k) + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/) { // Wrap the current density deposited in the guard cells around // to the cells above the axis. @@ -531,7 +635,7 @@ WarpX::ApplyInverseVolumeScalingToChargeDensity (MultiFab* Rho, int lev) // Note that the loop is also over ncomps, which takes care of the RZ modes, // as well as the old and new rho. amrex::ParallelFor(tb, Rho->nComp(), - [=] AMREX_GPU_DEVICE (int i, int j, int k, int icomp) + [=] AMREX_GPU_DEVICE (int i, int j, int /*k*/, int icomp) { // Wrap the charge density deposited in the guard cells around // to the cells above the axis. diff --git a/Source/FieldSolver/WarpX_FDTD.H b/Source/FieldSolver/WarpX_FDTD.H index a49da2e0895..2ac27227d35 100644 --- a/Source/FieldSolver/WarpX_FDTD.H +++ b/Source/FieldSolver/WarpX_FDTD.H @@ -7,6 +7,7 @@ #ifndef WARPX_FDTD_H_ #define WARPX_FDTD_H_ +#include #include AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE @@ -30,11 +31,13 @@ void warpx_computedivb(int i, int j, int k, int dcomp, #elif defined WARPX_DIM_XZ divB(i,j,0,dcomp) = (Bx(i+1,j ,0) - Bx(i,j,0))*dxinv + (Bz(i ,j+1,0) - Bz(i,j,0))*dzinv; + amrex::ignore_unused(k, By, dyinv); #elif defined WARPX_DIM_RZ const amrex::Real ru = 1. + 0.5/(rmin*dxinv + i + 0.5); const amrex::Real rd = 1. - 0.5/(rmin*dxinv + i + 0.5); divB(i,j,0,dcomp) = (ru*Bx(i+1,j,0) - rd*Bx(i,j,0))*dxinv + (Bz(i,j+1,0) - Bz(i,j,0))*dzinv; + amrex::ignore_unused(k, By, dyinv); #endif } diff --git a/Source/FieldSolver/WarpX_QED_K.H b/Source/FieldSolver/WarpX_QED_K.H index e71f40cfc1d..929d38be123 100644 --- a/Source/FieldSolver/WarpX_QED_K.H +++ b/Source/FieldSolver/WarpX_QED_K.H @@ -10,6 +10,7 @@ #include "Utils/WarpXConst.H" #include +#include #include @@ -345,6 +346,7 @@ const amrex::Real dyi = 1./dy; Ez(j,k,0) = Ez(j,k,0) + 0.5_rt*dt*dEz; + amrex::ignore_unused(l, dy); #endif } diff --git a/Source/Filter/BilinearFilter.cpp b/Source/Filter/BilinearFilter.cpp index fe352e7c0aa..b3c93583d86 100644 --- a/Source/Filter/BilinearFilter.cpp +++ b/Source/Filter/BilinearFilter.cpp @@ -16,10 +16,10 @@ using namespace amrex; namespace { - void compute_stencil(Gpu::ManagedVector &stencil, int npass) + void compute_stencil(Gpu::DeviceVector &stencil, int npass) { - Gpu::ManagedVector old_s(1+npass,0.); - Gpu::ManagedVector new_s(1+npass,0.); + Vector old_s(1+npass,0.); + Vector new_s(1+npass,0.); old_s[0] = 1.; int jmax = 1; @@ -46,8 +46,10 @@ namespace { } // we use old_s here to make sure the stencil // is corrent even when npass = 0 - stencil = old_s; - stencil[0] *= 0.5; // because we will use it twice + old_s[0] *= 0.5; // because we will use it twice + stencil.resize(old_s.size()); + Gpu::copyAsync(Gpu::hostToDevice,old_s.begin(),old_s.end(),stencil.begin()); + amrex::Gpu::synchronize(); } } diff --git a/Source/Filter/Filter.H b/Source/Filter/Filter.H index 4e36a1f1fe3..fdd37d8419f 100644 --- a/Source/Filter/Filter.H +++ b/Source/Filter/Filter.H @@ -37,7 +37,7 @@ public: protected: // Stencil along each direction. // in 2D, stencil_y is not initialized. - amrex::Gpu::ManagedVector stencil_x, stencil_y, stencil_z; + amrex::Gpu::DeviceVector stencil_x, stencil_y, stencil_z; // Length of each stencil. // In 2D, slen = {length(stencil_x), length(stencil_z), 1} amrex::Dim3 slen; diff --git a/Source/Filter/Filter.cpp b/Source/Filter/Filter.cpp index ad9d6d62e9f..e99288ac887 100644 --- a/Source/Filter/Filter.cpp +++ b/Source/Filter/Filter.cpp @@ -27,7 +27,7 @@ using namespace amrex; void Filter::ApplyStencil (MultiFab& dstmf, const MultiFab& srcmf, int scomp, int dcomp, int ncomp) { - WARPX_PROFILE("BilinearFilter::ApplyStencil(MultiFab)"); + WARPX_PROFILE("Filter::ApplyStencil(MultiFab)"); ncomp = std::min(ncomp, srcmf.nComp()); for (MFIter mfi(dstmf); mfi.isValid(); ++mfi) @@ -70,7 +70,7 @@ void Filter::ApplyStencil (FArrayBox& dstfab, const FArrayBox& srcfab, const Box& tbx, int scomp, int dcomp, int ncomp) { - WARPX_PROFILE("BilinearFilter::ApplyStencil(FArrayBox)"); + WARPX_PROFILE("Filter::ApplyStencil(FArrayBox)"); ncomp = std::min(ncomp, srcfab.nComp()); const auto& src = srcfab.array(); const auto& dst = dstfab.array(); @@ -156,7 +156,7 @@ void Filter::DoFilter (const Box& tbx, void Filter::ApplyStencil (amrex::MultiFab& dstmf, const amrex::MultiFab& srcmf, int scomp, int dcomp, int ncomp) { - WARPX_PROFILE("BilinearFilter::ApplyStencil()"); + WARPX_PROFILE("Filter::ApplyStencil(MultiFab)"); ncomp = std::min(ncomp, srcmf.nComp()); #ifdef _OPENMP #pragma omp parallel @@ -192,7 +192,7 @@ void Filter::ApplyStencil (amrex::FArrayBox& dstfab, const amrex::FArrayBox& srcfab, const amrex::Box& tbx, int scomp, int dcomp, int ncomp) { - WARPX_PROFILE("BilinearFilter::ApplyStencil(FArrayBox)"); + WARPX_PROFILE("Filter::ApplyStencil(FArrayBox)"); ncomp = std::min(ncomp, srcfab.nComp()); FArrayBox tmpfab; const Box& gbx = amrex::grow(tbx,stencil_length_each_dir-1); diff --git a/Source/Filter/NCIGodfreyFilter.cpp b/Source/Filter/NCIGodfreyFilter.cpp index 8f4f14fb24c..16a7aaeeb8f 100644 --- a/Source/Filter/NCIGodfreyFilter.cpp +++ b/Source/Filter/NCIGodfreyFilter.cpp @@ -33,14 +33,13 @@ NCIGodfreyFilter::NCIGodfreyFilter(godfrey_coeff_set coeff_set, amrex::Real cdto void NCIGodfreyFilter::ComputeStencils(){ // Sanity checks: filter length shoulz be 5 in z +#if (AMREX_SPACEDIM == 3) AMREX_ALWAYS_ASSERT_WITH_MESSAGE( -#if ( AMREX_SPACEDIM == 3 ) - slen.z==5, + slen.z==5,"ERROR: NCI filter requires 5 points in z"); #else - slen.y==5, + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + slen.y==5,"ERROR: NCI filter requires 5 points in z"); #endif - "ERROR: NCI filter requires 5 points in z"); - // Interpolate coefficients from the table, and store into prestencil. auto index = static_cast(tab_length*m_cdtodz); index = min(index, tab_length-2); @@ -83,28 +82,41 @@ void NCIGodfreyFilter::ComputeStencils(){ } } // Compute stencil_z - stencil_z.resize( 5 ); - stencil_z[0] = (256 + 128*prestencil[0] + 96*prestencil[1] + 80*prestencil[2] + 70*prestencil[3]) / 256; - stencil_z[1] = -( 64*prestencil[0] + 64*prestencil[1] + 60*prestencil[2] + 56*prestencil[3]) / 256; - stencil_z[2] = ( 16*prestencil[1] + 24*prestencil[2] + 28*prestencil[3]) / 256; - stencil_z[3] = -( 4*prestencil[2] + 8*prestencil[3]) / 256; - stencil_z[4] = ( 1*prestencil[3]) / 256; + Vector h_stencil_z(5); + h_stencil_z[0] = (256 + 128*prestencil[0] + 96*prestencil[1] + 80*prestencil[2] + 70*prestencil[3]) / 256; + h_stencil_z[1] = -( 64*prestencil[0] + 64*prestencil[1] + 60*prestencil[2] + 56*prestencil[3]) / 256; + h_stencil_z[2] = ( 16*prestencil[1] + 24*prestencil[2] + 28*prestencil[3]) / 256; + h_stencil_z[3] = -( 4*prestencil[2] + 8*prestencil[3]) / 256; + h_stencil_z[4] = ( 1*prestencil[3]) / 256; - // Compute stencil_x and stencil_y (no filter in these directions, + // Compute h_stencil_x and h_stencil_y (no filter in these directions, // so only 1 coeff, equal to 1) - stencil_x.resize(1); - stencil_x[0] = 1.; + Vector h_stencil_x(1); + h_stencil_x[0] = 1.; #if (AMREX_SPACEDIM == 3) - stencil_y.resize(1); - stencil_y[0] = 1.; + Vector h_stencil_y(1); + h_stencil_y[0] = 1.; #endif // Due to the way Filter::DoFilter() is written, // coefficient 0 has to be /2 - stencil_x[0] /= 2.; + h_stencil_x[0] /= 2.; +#if (AMREX_SPACEDIM == 3) + h_stencil_y[0] /= 2.; +#endif + h_stencil_z[0] /= 2.; + + stencil_x.resize(h_stencil_x.size()); +#if (AMREX_SPACEDIM == 3) + stencil_y.resize(h_stencil_y.size()); +#endif + stencil_z.resize(h_stencil_z.size()); + + Gpu::copyAsync(Gpu::hostToDevice,h_stencil_x.begin(),h_stencil_x.end(),stencil_x.begin()); #if (AMREX_SPACEDIM == 3) - stencil_y[0] /= 2.; + Gpu::copyAsync(Gpu::hostToDevice,h_stencil_y.begin(),h_stencil_y.end(),stencil_y.begin()); #endif - stencil_z[0] /= 2.; + Gpu::copyAsync(Gpu::hostToDevice,h_stencil_z.begin(),h_stencil_z.end(),stencil_z.begin()); + Gpu::synchronize(); } diff --git a/Source/Initialization/CustomDensityProb.H b/Source/Initialization/CustomDensityProb.H index 804b56ce884..cd565151028 100644 --- a/Source/Initialization/CustomDensityProb.H +++ b/Source/Initialization/CustomDensityProb.H @@ -19,15 +19,13 @@ struct InjectorDensityCustom { InjectorDensityCustom (std::string const& species_name) - : p(nullptr) { - // Read parameters for custom density profile from file, and - // store them in managed memory. + // Read parameters for custom density profile from file amrex::ParmParse pp(species_name); std::vector v; + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(v.size() <= 6, + "Too many parameters for InjectorDensityCustom"); pp.getarr("custom_profile_params", v); - p = static_cast - (amrex::The_Managed_Arena()->alloc(sizeof(amrex::Real)*v.size())); for (int i = 0; i < static_cast(v.size()); ++i) { p[i] = v[i]; } @@ -43,13 +41,11 @@ struct InjectorDensityCustom } // Note that we are not allowed to have non-trivial destructor. - // So we rely on clear() to free memory. - void clear () { - amrex::The_Managed_Arena()->free(p); - } + // So we rely on clear() to free memory if needed. + void clear () {} private: - amrex::Real* p; + amrex::GpuArray p; }; #endif diff --git a/Source/Initialization/CustomMomentumProb.H b/Source/Initialization/CustomMomentumProb.H index bff2ce5f020..f406a4706ff 100644 --- a/Source/Initialization/CustomMomentumProb.H +++ b/Source/Initialization/CustomMomentumProb.H @@ -23,7 +23,7 @@ struct InjectorMomentumCustom // Return momentum at given position (illustration: momentum=0). AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real, amrex::Real, amrex::Real) const noexcept + getMomentum (amrex::Real, amrex::Real, amrex::Real, amrex::RandomEngine const&) const noexcept { return {0., 0., 0.}; } diff --git a/Source/Initialization/InjectorDensity.H b/Source/Initialization/InjectorDensity.H index b60f1195960..0a48ca625f4 100644 --- a/Source/Initialization/InjectorDensity.H +++ b/Source/Initialization/InjectorDensity.H @@ -80,7 +80,7 @@ struct InjectorDensityPredefined double n0 = p[5]; double n; double kp = PhysConst::q_e/PhysConst::c - *amrex::Math::sqrt( n0/(PhysConst::m_e*PhysConst::ep0) ); + *std::sqrt( n0/(PhysConst::m_e*PhysConst::ep0) ); // Longitudinal profile, normalized to 1 if ((z-z_start)>=0 and @@ -108,7 +108,7 @@ struct InjectorDensityPredefined private: enum struct Profile { null, parabolic_channel }; Profile profile; - amrex::Real* p; + amrex::GpuArray p; }; // Base struct for density injector. @@ -119,12 +119,8 @@ private: // - InjectorDensityCustom : to generate density from custom profile; // - InjectorDensityPredefined: to generate density from predefined profile; // The choice is made at runtime, depending in the constructor called. -// This mimics virtual functions, except the struct is stored in managed memory -// and member functions are made __host__ __device__ to run on CPU and GPU. -// This struct inherits from amrex::Gpu::Managed to provide new and delete -// operators in managed memory when running on GPU. Nothing special on CPU. +// This mimics virtual functions. struct InjectorDensity - : public amrex::Gpu::Managed { // This constructor stores a InjectorDensityConstant in union object. InjectorDensity (InjectorDensityConstant* t, amrex::Real a_rho) @@ -157,7 +153,7 @@ struct InjectorDensity void operator= (InjectorDensity const&) = delete; void operator= (InjectorDensity &&) = delete; - ~InjectorDensity (); + void clear (); // call getDensity from the object stored in the union // (the union is called Object, and the instance is called object). @@ -214,4 +210,15 @@ private: Object object; }; +// In order for InjectorDensity to be trivially copyable, its destructor +// must be trivial. So we have to rely on a custom deleter for unique_ptr. +struct InjectorDensityDeleter { + void operator () (InjectorDensity* p) const { + if (p) { + p->clear(); + delete p; + } + } +}; + #endif diff --git a/Source/Initialization/InjectorDensity.cpp b/Source/Initialization/InjectorDensity.cpp index 3d52c31667c..49b6852ce93 100644 --- a/Source/Initialization/InjectorDensity.cpp +++ b/Source/Initialization/InjectorDensity.cpp @@ -11,7 +11,7 @@ using namespace amrex; -InjectorDensity::~InjectorDensity () +void InjectorDensity::clear () { switch (type) { @@ -42,11 +42,10 @@ InjectorDensityPredefined::InjectorDensityPredefined ( ParmParse pp(a_species_name); std::vector v; - // Read parameters for the predefined plasma profile, - // and store them in managed memory + // Read parameters for the predefined plasma profile. pp.getarr("predefined_profile_params", v); - p = static_cast - (amrex::The_Managed_Arena()->alloc(sizeof(amrex::Real)*v.size())); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(v.size() <= 6, + "Too many parameters for InjectorDensityPredefined"); for (int i = 0; i < static_cast(v.size()); ++i) { p[i] = v[i]; } @@ -64,8 +63,7 @@ InjectorDensityPredefined::InjectorDensityPredefined ( } // Note that we are not allowed to have non-trivial destructor. -// So we rely on clear() to free memory. +// So we rely on clear() to free memory if needed. void InjectorDensityPredefined::clear () { - amrex::The_Managed_Arena()->free(p); } diff --git a/Source/Initialization/InjectorMomentum.H b/Source/Initialization/InjectorMomentum.H index 2dd2a16254a..49db0e09694 100644 --- a/Source/Initialization/InjectorMomentum.H +++ b/Source/Initialization/InjectorMomentum.H @@ -23,7 +23,8 @@ struct InjectorMomentumConstant AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real, amrex::Real, amrex::Real) const noexcept + getMomentum (amrex::Real, amrex::Real, amrex::Real, + amrex::RandomEngine const&) const noexcept { return amrex::XDim3{m_ux,m_uy,m_uz}; } @@ -52,16 +53,17 @@ struct InjectorMomentumGaussian AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getMomentum (amrex::Real /*x*/, amrex::Real /*y*/, amrex::Real /*z*/, + amrex::RandomEngine const& engine) const noexcept { - return amrex::XDim3{amrex::RandomNormal(m_ux_m, m_ux_th), - amrex::RandomNormal(m_uy_m, m_uy_th), - amrex::RandomNormal(m_uz_m, m_uz_th)}; + return amrex::XDim3{amrex::RandomNormal(m_ux_m, m_ux_th, engine), + amrex::RandomNormal(m_uy_m, m_uy_th, engine), + amrex::RandomNormal(m_uz_m, m_uz_th, engine)}; } AMREX_GPU_HOST_DEVICE amrex::XDim3 - getBulkMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getBulkMomentum (amrex::Real /*x*/, amrex::Real /*y*/, amrex::Real /*z*/) const noexcept { return amrex::XDim3{m_ux_m, m_uy_m, m_uz_m}; } @@ -85,20 +87,21 @@ struct InjectorMomentumBoltzmann AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getMomentum (amrex::Real /*x*/, amrex::Real /*y*/, amrex::Real /*z*/, + amrex::RandomEngine const& engine) const noexcept { amrex::Real x1, x2, gamma; amrex::Real u[3]; - x1 = amrex::Random(); - x2 = amrex::Random(); + x1 = amrex::Random(engine); + x2 = amrex::Random(engine); // Each value of sqrt(-log(x1))*sin(2*pi*x2) is a sample from a Gaussian // distribution with sigma = average velocity / c // using the Box-Mueller Method. u[(dir+1)%3] = vave*std::sqrt(-std::log(x1)) *std::sin(2*M_PI*x2); u[(dir+2)%3] = vave*std::sqrt(-std::log(x1)) *std::cos(2*M_PI*x2); - u[dir] = vave*std::sqrt(-std::log(amrex::Random()))* - std::sin(2*M_PI*amrex::Random()); - gamma = std::pow(u[0],2)+std::pow(u[1],2)+std::pow(u[2],2); + u[dir] = vave*std::sqrt(-std::log(amrex::Random(engine)))* + std::sin(2*M_PI*amrex::Random(engine)); + gamma = u[0]*u[0]+u[1]*u[1]+u[2]*u[2]; gamma = std::sqrt(1+gamma); // The following condition is equtaion 32 in Zenitani 2015 // (Phys. Plasmas 22, 042116) , called the flipping method. It @@ -111,7 +114,7 @@ struct InjectorMomentumBoltzmann // initialize the particle positions and densities in the frame moving // at speed beta, and then perform a Lorentz transform on the positions // and MB sampled velocities to the simulation frame. - x1 = amrex::Random(); + x1 = amrex::Random(engine); if(-beta*u[dir]/gamma > x1) { u[dir] = -u[dir]; @@ -119,7 +122,7 @@ struct InjectorMomentumBoltzmann // This Lorentz transform is equation 17 in Zenitani. // It transforms the integral d3u' -> d3u // where d3u' is the volume element for momentum in the boosted frame. - u[dir] = 1/std::sqrt(1-pow(beta,2))*(u[dir]+gamma*beta); + u[dir] = 1/std::sqrt(1-beta*beta)*(u[dir]+gamma*beta); // Note that if beta = 0 then the flipping method and Lorentz transform // have no effect on the u[dir] direction. return amrex::XDim3 {u[0],u[1],u[2]}; @@ -127,7 +130,7 @@ struct InjectorMomentumBoltzmann AMREX_GPU_HOST_DEVICE amrex::XDim3 - getBulkMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getBulkMomentum (amrex::Real /*x*/, amrex::Real /*y*/, amrex::Real /*z*/) const noexcept { using namespace amrex; Real u[3]; @@ -157,7 +160,8 @@ struct InjectorMomentumJuttner AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getMomentum (amrex::Real /*x*/, amrex::Real /*y*/, amrex::Real /*z*/, + amrex::RandomEngine const& engine) const noexcept { // Sobol method for sampling MJ Speeds, // from Zenitani 2015 (Phys. Plasmas 22, 042116). @@ -171,21 +175,21 @@ struct InjectorMomentumJuttner while(u[dir]-gamma <= x1) { u[dir] = -theta* - std::log(amrex::Random()*amrex::Random()*amrex::Random()); - gamma = std::sqrt(1+std::pow(u[dir],2)); - x1 = theta*std::log(amrex::Random()); + std::log(amrex::Random(engine)*amrex::Random(engine)*amrex::Random(engine)); + gamma = std::sqrt(1+u[dir]*u[dir]); + x1 = theta*std::log(amrex::Random(engine)); } // The following code samples a random unit vector // and multiplies the result by speed u[dir]. - x1 = amrex::Random(); - x2 = amrex::Random(); + x1 = amrex::Random(engine); + x2 = amrex::Random(engine); // Direction dir is an input parameter that sets the boost direction: // 'x' -> d = 0, 'y' -> d = 1, 'z' -> d = 2. u[(dir+1)%3] = 2*u[dir]*std::sqrt(x1*(1-x1))*std::sin(2*M_PI*x2); u[(dir+2)%3] = 2*u[dir]*std::sqrt(x1*(1-x1))*std::cos(2*M_PI*x2); // The value of dir is the boost direction to be transformed. u[dir] = u[dir]*(2*x1-1); - x1 = amrex::Random(); + x1 = amrex::Random(engine); // The following condition is equtaion 32 in Zenitani, called // The flipping method. It transforms the intergral: d3x' -> d3x // where d3x' is the volume element for positions in the boosted frame. @@ -205,7 +209,7 @@ struct InjectorMomentumJuttner // This Lorentz transform is equation 17 in Zenitani. // It transforms the integral d3u' -> d3u // where d3u' is the volume element for momentum in the boosted frame. - u[dir] = 1/std::sqrt(1-pow(beta,2))*(u[dir]+gamma*beta); + u[dir] = 1/std::sqrt(1-beta*beta)*(u[dir]+gamma*beta); // Note that if beta = 0 then the flipping method and Lorentz transform // have no effect on the u[dir] direction. return amrex::XDim3 {u[0],u[1],u[2]}; @@ -213,7 +217,7 @@ struct InjectorMomentumJuttner AMREX_GPU_HOST_DEVICE amrex::XDim3 - getBulkMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getBulkMomentum (amrex::Real /*x*/, amrex::Real /*y*/, amrex::Real /*z*/) const noexcept { using namespace amrex; Real u[3]; @@ -243,7 +247,8 @@ struct InjectorMomentumRadialExpansion AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getMomentum (amrex::Real x, amrex::Real y, amrex::Real z, + amrex::RandomEngine const&) const noexcept { return {x*u_over_r, y*u_over_r, z*u_over_r}; } @@ -270,7 +275,8 @@ struct InjectorMomentumParser AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getMomentum (amrex::Real x, amrex::Real y, amrex::Real z, + amrex::RandomEngine const&) const noexcept { return amrex::XDim3{m_ux_parser(x,y,z),m_uy_parser(x,y,z),m_uz_parser(x,y,z)}; } @@ -293,12 +299,8 @@ struct InjectorMomentumParser // - InjectorMomentumRadialExpansion: to generate radial expansion; // - InjectorMomentumParser : to generate momentum from parser; // The choice is made at runtime, depending in the constructor called. -// This mimics virtual functions, except the struct is stored in managed memory -// and member functions are made __host__ __device__ to run on CPU and GPU. -// This struct inherits from amrex::Gpu::Managed to provide new and delete -// operators in managed memory when running on GPU. Nothing special on CPU. +// This mimics virtual functions. struct InjectorMomentum - : public amrex::Gpu::Managed { // This constructor stores a InjectorMomentumConstant in union object. InjectorMomentum (InjectorMomentumConstant* t, @@ -358,43 +360,44 @@ struct InjectorMomentum void operator= (InjectorMomentum const&) = delete; void operator= (InjectorMomentum &&) = delete; - ~InjectorMomentum (); + void clear (); // call getMomentum from the object stored in the union // (the union is called Object, and the instance is called object). AMREX_GPU_HOST_DEVICE amrex::XDim3 - getMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept + getMomentum (amrex::Real x, amrex::Real y, amrex::Real z, + amrex::RandomEngine const& engine) const noexcept { switch (type) { case Type::parser: { - return object.parser.getMomentum(x,y,z); + return object.parser.getMomentum(x,y,z,engine); } case Type::gaussian: { - return object.gaussian.getMomentum(x,y,z); + return object.gaussian.getMomentum(x,y,z,engine); } case Type::boltzmann: { - return object.boltzmann.getMomentum(x,y,z); + return object.boltzmann.getMomentum(x,y,z,engine); } case Type::juttner: { - return object.juttner.getMomentum(x,y,z); + return object.juttner.getMomentum(x,y,z,engine); } case Type::constant: { - return object.constant.getMomentum(x,y,z); + return object.constant.getMomentum(x,y,z,engine); } case Type::radial_expansion: { - return object.radial_expansion.getMomentum(x,y,z); + return object.radial_expansion.getMomentum(x,y,z,engine); } case Type::custom: { - return object.custom.getMomentum(x,y,z); + return object.custom.getMomentum(x,y,z,engine); } default: { @@ -492,4 +495,15 @@ private: Object object; }; +// In order for InjectorMomentum to be trivially copyable, its destructor +// must be trivial. So we have to rely on a custom deleter for unique_ptr. +struct InjectorMomentumDeleter { + void operator () (InjectorMomentum* p) const { + if (p) { + p->clear(); + delete p; + } + } +}; + #endif diff --git a/Source/Initialization/InjectorMomentum.cpp b/Source/Initialization/InjectorMomentum.cpp index dd9d34f4f40..236fd1b4643 100644 --- a/Source/Initialization/InjectorMomentum.cpp +++ b/Source/Initialization/InjectorMomentum.cpp @@ -11,7 +11,7 @@ using namespace amrex; -InjectorMomentum::~InjectorMomentum () +void InjectorMomentum::clear () { switch (type) { diff --git a/Source/Initialization/InjectorPosition.H b/Source/Initialization/InjectorPosition.H index f35e346c3db..7811937e409 100644 --- a/Source/Initialization/InjectorPosition.H +++ b/Source/Initialization/InjectorPosition.H @@ -18,9 +18,10 @@ struct InjectorPositionRandom { AMREX_GPU_HOST_DEVICE amrex::XDim3 - getPositionUnitBox (int i_part, int ref_fac=1) const noexcept + getPositionUnitBox (int /*i_part*/, int /*ref_fac*/, + amrex::RandomEngine const& engine) const noexcept { - return amrex::XDim3{amrex::Random(), amrex::Random(), amrex::Random()}; + return amrex::XDim3{amrex::Random(engine), amrex::Random(engine), amrex::Random(engine)}; } }; @@ -36,7 +37,8 @@ struct InjectorPositionRegular // is a_ppc*(ref_fac**AMREX_SPACEDIM). AMREX_GPU_HOST_DEVICE amrex::XDim3 - getPositionUnitBox (int const i_part, int const ref_fac=1) const noexcept + getPositionUnitBox (int const i_part, int const ref_fac, + amrex::RandomEngine const&) const noexcept { using namespace amrex; @@ -66,12 +68,8 @@ private: // - InjectorPositionRandom : to generate random distribution; // - InjectorPositionRegular: to generate regular distribution. // The choice is made at runtime, depending in the constructor called. -// This mimics virtual functions, except the struct is stored in managed memory -// and member functions are made __host__ __device__ to run on CPU and GPU. -// This struct inherits from amrex::Gpu::Managed to provide new and delete -// operators in managed memory when running on GPU. Nothing special on CPU. +// This mimics virtual functions. struct InjectorPosition - : public amrex::Gpu::Managed { // This constructor stores a InjectorPositionRandom in union object. InjectorPosition (InjectorPositionRandom* t, @@ -109,17 +107,18 @@ struct InjectorPosition // (the union is called Object, and the instance is called object). AMREX_GPU_HOST_DEVICE amrex::XDim3 - getPositionUnitBox (int const i_part, int const ref_fac=1) const noexcept + getPositionUnitBox (int const i_part, int const ref_fac, + amrex::RandomEngine const& engine) const noexcept { switch (type) { case Type::regular: { - return object.regular.getPositionUnitBox(i_part, ref_fac); + return object.regular.getPositionUnitBox(i_part, ref_fac, engine); } default: { - return object.random.getPositionUnitBox(i_part, ref_fac); + return object.random.getPositionUnitBox(i_part, ref_fac, engine); } }; } diff --git a/Source/Initialization/PlasmaInjector.H b/Source/Initialization/PlasmaInjector.H index 9e2df164f20..f8ef29faf42 100644 --- a/Source/Initialization/PlasmaInjector.H +++ b/Source/Initialization/PlasmaInjector.H @@ -41,6 +41,8 @@ public: PlasmaInjector (int ispecies, const std::string& name); + ~PlasmaInjector (); + // bool: whether the point (x, y, z) is inside the plasma region bool insideBounds (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept; @@ -58,7 +60,7 @@ public: amrex::Real getMass () {return mass;} PhysicalSpecies getPhysicalSpecies() const {return physical_species;} - bool doInjection () const noexcept { return inj_pos != NULL;} + bool doInjection () const noexcept { return h_inj_pos != nullptr;} bool add_single_particle = false; amrex::Vector single_particle_pos; @@ -114,9 +116,14 @@ protected: int species_id; std::string species_name; - std::unique_ptr inj_pos; - std::unique_ptr inj_rho; - std::unique_ptr inj_mom; + std::unique_ptr h_inj_pos; + InjectorPosition* d_inj_pos = nullptr; + + std::unique_ptr h_inj_rho; + InjectorDensity* d_inj_rho = nullptr; + + std::unique_ptr h_inj_mom; + InjectorMomentum* d_inj_mom = nullptr; void parseDensity (amrex::ParmParse& pp); void parseMomentum (amrex::ParmParse& pp); diff --git a/Source/Initialization/PlasmaInjector.cpp b/Source/Initialization/PlasmaInjector.cpp index ca3cc340cf2..3930fd4de7b 100644 --- a/Source/Initialization/PlasmaInjector.cpp +++ b/Source/Initialization/PlasmaInjector.cpp @@ -76,6 +76,13 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name) { ParmParse pp(species_name); + static_assert(std::is_trivially_copyable::value, + "InjectorPosition must be trivially copyable"); + static_assert(std::is_trivially_copyable::value, + "InjectorDensity must be trivially copyable"); + static_assert(std::is_trivially_copyable::value, + "InjectorMomentum must be trivially copyable"); + pp.query("radially_weighted", radially_weighted); AMREX_ALWAYS_ASSERT_WITH_MESSAGE(radially_weighted, "ERROR: Only radially_weighted=true is supported"); @@ -223,8 +230,8 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name) "(Please visit PR#765 for more information.)"); #endif // Construct InjectorPosition with InjectorPositionRandom. - inj_pos.reset(new InjectorPosition((InjectorPositionRandom*)nullptr, - xmin, xmax, ymin, ymax, zmin, zmax)); + h_inj_pos.reset(new InjectorPosition((InjectorPositionRandom*)nullptr, + xmin, xmax, ymin, ymax, zmin, zmax)); parseDensity(pp); parseMomentum(pp); } else if (part_pos_s == "nuniformpercell") { @@ -243,11 +250,11 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name) "n_rz_azimuthal_modes (Please visit PR#765 for more information.)"); #endif // Construct InjectorPosition from InjectorPositionRegular. - inj_pos.reset(new InjectorPosition((InjectorPositionRegular*)nullptr, - xmin, xmax, ymin, ymax, zmin, zmax, - Dim3{num_particles_per_cell_each_dim[0], - num_particles_per_cell_each_dim[1], - num_particles_per_cell_each_dim[2]})); + h_inj_pos.reset(new InjectorPosition((InjectorPositionRegular*)nullptr, + xmin, xmax, ymin, ymax, zmin, zmax, + Dim3{num_particles_per_cell_each_dim[0], + num_particles_per_cell_each_dim[1], + num_particles_per_cell_each_dim[2]})); num_particles_per_cell = num_particles_per_cell_each_dim[0] * num_particles_per_cell_each_dim[1] * num_particles_per_cell_each_dim[2]; @@ -268,7 +275,7 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name) #ifdef WARPX_USE_OPENPMD if (ParallelDescriptor::IOProcessor()) { m_openpmd_input_series = std::make_unique( - str_injection_file, openPMD::AccessType::READ_ONLY); + str_injection_file, openPMD::Access::READ_ONLY); AMREX_ALWAYS_ASSERT_WITH_MESSAGE( m_openpmd_input_series->iterations.size() == 1u, @@ -342,6 +349,53 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name) } else { StringParseAbortMessage("Injection style", part_pos_s); } + + if (h_inj_pos) { +#ifdef AMREX_USE_GPU + d_inj_pos = static_cast + (amrex::The_Arena()->alloc(sizeof(InjectorPosition))); + amrex::Gpu::htod_memcpy_async(d_inj_pos, h_inj_pos.get(), sizeof(InjectorPosition)); +#else + d_inj_pos = h_inj_pos.get(); +#endif + } + + if (h_inj_rho) { +#ifdef AMREX_USE_GPU + d_inj_rho = static_cast + (amrex::The_Arena()->alloc(sizeof(InjectorDensity))); + amrex::Gpu::htod_memcpy_async(d_inj_rho, h_inj_rho.get(), sizeof(InjectorDensity)); +#else + d_inj_rho = h_inj_rho.get(); +#endif + } + + if (h_inj_mom) { +#ifdef AMREX_USE_GPU + d_inj_mom = static_cast + (amrex::The_Arena()->alloc(sizeof(InjectorMomentum))); + amrex::Gpu::htod_memcpy_async(d_inj_mom, h_inj_mom.get(), sizeof(InjectorMomentum)); +#else + d_inj_mom = h_inj_mom.get(); +#endif + } + + amrex::Gpu::synchronize(); +} + +PlasmaInjector::~PlasmaInjector () +{ +#ifdef AMREX_USE_GPU + if (d_inj_pos) { + amrex::The_Arena()->free(d_inj_pos); + } + if (d_inj_rho) { + amrex::The_Arena()->free(d_inj_rho); + } + if (d_inj_mom) { + amrex::The_Arena()->free(d_inj_mom); + } +#endif } // Depending on injection type at runtime, initialize inj_rho @@ -357,18 +411,18 @@ void PlasmaInjector::parseDensity (ParmParse& pp) if (rho_prof_s == "constant") { pp.get("density", density); // Construct InjectorDensity with InjectorDensityConstant. - inj_rho.reset(new InjectorDensity((InjectorDensityConstant*)nullptr, density)); + h_inj_rho.reset(new InjectorDensity((InjectorDensityConstant*)nullptr, density)); } else if (rho_prof_s == "custom") { // Construct InjectorDensity with InjectorDensityCustom. - inj_rho.reset(new InjectorDensity((InjectorDensityCustom*)nullptr, species_name)); + h_inj_rho.reset(new InjectorDensity((InjectorDensityCustom*)nullptr, species_name)); } else if (rho_prof_s == "predefined") { // Construct InjectorDensity with InjectorDensityPredefined. - inj_rho.reset(new InjectorDensity((InjectorDensityPredefined*)nullptr,species_name)); + h_inj_rho.reset(new InjectorDensity((InjectorDensityPredefined*)nullptr,species_name)); } else if (rho_prof_s == "parse_density_function") { Store_parserString(pp, "density_function(x,y,z)", str_density_function); // Construct InjectorDensity with InjectorDensityParser. - inj_rho.reset(new InjectorDensity((InjectorDensityParser*)nullptr, - makeParser(str_density_function,{"x","y","z"}))); + h_inj_rho.reset(new InjectorDensity((InjectorDensityParser*)nullptr, + makeParser(str_density_function,{"x","y","z"}))); } else { //No need for profile definition if external file is used std::string s_inj_style; @@ -399,10 +453,10 @@ void PlasmaInjector::parseMomentum (ParmParse& pp) pp.query("uy", uy); pp.query("uz", uz); // Construct InjectorMomentum with InjectorMomentumConstant. - inj_mom.reset(new InjectorMomentum((InjectorMomentumConstant*)nullptr, ux,uy, uz)); + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumConstant*)nullptr, ux,uy, uz)); } else if (mom_dist_s == "custom") { // Construct InjectorMomentum with InjectorMomentumCustom. - inj_mom.reset(new InjectorMomentum((InjectorMomentumCustom*)nullptr, species_name)); + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumCustom*)nullptr, species_name)); } else if (mom_dist_s == "gaussian") { Real ux_m = 0.; Real uy_m = 0.; @@ -417,8 +471,8 @@ void PlasmaInjector::parseMomentum (ParmParse& pp) pp.query("uy_th", uy_th); pp.query("uz_th", uz_th); // Construct InjectorMomentum with InjectorMomentumGaussian. - inj_mom.reset(new InjectorMomentum((InjectorMomentumGaussian*)nullptr, - ux_m, uy_m, uz_m, ux_th, uy_th, uz_th)); + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumGaussian*)nullptr, + ux_m, uy_m, uz_m, ux_th, uy_th, uz_th)); } else if (mom_dist_s == "maxwell_boltzmann"){ Real beta = 0.; Real theta = 10.; @@ -449,7 +503,7 @@ void PlasmaInjector::parseMomentum (ParmParse& pp) amrex::Abort(direction.c_str()); } // Construct InjectorMomentum with InjectorMomentumBoltzmann. - inj_mom.reset(new InjectorMomentum((InjectorMomentumBoltzmann*)nullptr, theta, beta, dir)); + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumBoltzmann*)nullptr, theta, beta, dir)); } else if (mom_dist_s == "maxwell_juttner"){ Real beta = 0.; Real theta = 10.; @@ -480,13 +534,13 @@ void PlasmaInjector::parseMomentum (ParmParse& pp) amrex::Abort(direction.c_str()); } // Construct InjectorMomentum with InjectorMomentumJuttner. - inj_mom.reset(new InjectorMomentum((InjectorMomentumJuttner*)nullptr, theta, beta, dir)); + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumJuttner*)nullptr, theta, beta, dir)); } else if (mom_dist_s == "radial_expansion") { Real u_over_r = 0.; pp.query("u_over_r", u_over_r); // Construct InjectorMomentum with InjectorMomentumRadialExpansion. - inj_mom.reset(new InjectorMomentum - ((InjectorMomentumRadialExpansion*)nullptr, u_over_r)); + h_inj_mom.reset(new InjectorMomentum + ((InjectorMomentumRadialExpansion*)nullptr, u_over_r)); } else if (mom_dist_s == "parse_momentum_function") { Store_parserString(pp, "momentum_function_ux(x,y,z)", str_momentum_function_ux); @@ -495,10 +549,10 @@ void PlasmaInjector::parseMomentum (ParmParse& pp) Store_parserString(pp, "momentum_function_uz(x,y,z)", str_momentum_function_uz); // Construct InjectorMomentum with InjectorMomentumParser. - inj_mom.reset(new InjectorMomentum((InjectorMomentumParser*)nullptr, - makeParser(str_momentum_function_ux,{"x","y","z"}), - makeParser(str_momentum_function_uy,{"x","y","z"}), - makeParser(str_momentum_function_uz,{"x","y","z"}))); + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumParser*)nullptr, + makeParser(str_momentum_function_ux,{"x","y","z"}), + makeParser(str_momentum_function_uy,{"x","y","z"}), + makeParser(str_momentum_function_uz,{"x","y","z"}))); } else { //No need for momentum definition if external file is used std::string s_inj_style; @@ -511,7 +565,7 @@ void PlasmaInjector::parseMomentum (ParmParse& pp) XDim3 PlasmaInjector::getMomentum (Real x, Real y, Real z) const noexcept { - return inj_mom->getMomentum(x, y, z); // gamma*beta + return h_inj_mom->getMomentum(x, y, z, amrex::RandomEngine{}); // gamma*beta } bool PlasmaInjector::insideBounds (Real x, Real y, Real z) const noexcept @@ -532,17 +586,17 @@ bool PlasmaInjector::overlapsWith (const amrex::XDim3& lo, InjectorPosition* PlasmaInjector::getInjectorPosition () { - return inj_pos.get(); + return d_inj_pos; } InjectorDensity* PlasmaInjector::getInjectorDensity () { - return inj_rho.get(); + return d_inj_rho; } InjectorMomentum* PlasmaInjector::getInjectorMomentum () { - return inj_mom.get(); + return d_inj_mom; } diff --git a/Source/Initialization/WarpXInitData.cpp b/Source/Initialization/WarpXInitData.cpp index e0c8e2be4c7..b1e64335486 100644 --- a/Source/Initialization/WarpXInitData.cpp +++ b/Source/Initialization/WarpXInitData.cpp @@ -25,6 +25,43 @@ using namespace amrex; +void +WarpX::PostProcessBaseGrids (BoxArray& ba0) const +{ + if (numprocs != 0) { + const Box& dom = Geom(0).Domain(); + const IntVect& domlo = dom.smallEnd(); + const IntVect& domlen = dom.size(); + const IntVect sz = domlen / numprocs; + const IntVect extra = domlen - sz*numprocs; + BoxList bl; +#if (AMREX_SPACEDIM == 3) + for (int k = 0; k < numprocs[2]; ++k) { + // The first extra[2] blocks get one extra cell with a total of + // sz[2]+1. The rest get sz[2] cells. The docomposition in y + // and x directions are similar. + int klo = (k < extra[2]) ? k*(sz[2]+1) : (k*sz[2]+extra[2]); + int khi = (k < extra[2]) ? klo+(sz[2]+1)-1 : klo+sz[2]-1; + klo += domlo[2]; + khi += domlo[2]; +#endif + for (int j = 0; j < numprocs[1]; ++j) { + int jlo = (j < extra[1]) ? j*(sz[1]+1) : (j*sz[1]+extra[1]); + int jhi = (j < extra[1]) ? jlo+(sz[1]+1)-1 : jlo+sz[1]-1; + jlo += domlo[1]; + jhi += domlo[1]; + for (int i = 0; i < numprocs[0]; ++i) { + int ilo = (i < extra[0]) ? i*(sz[0]+1) : (i*sz[0]+extra[0]); + int ihi = (i < extra[0]) ? ilo+(sz[0]+1)-1 : ilo+sz[0]-1; + ilo += domlo[0]; + ihi += domlo[0]; + bl.push_back(Box(IntVect(AMREX_D_DECL(ilo,jlo,klo)), + IntVect(AMREX_D_DECL(ihi,jhi,khi)))); + AMREX_D_TERM(},},}) + ba0 = BoxArray(std::move(bl)); + } +} + void WarpX::InitData () { @@ -56,6 +93,10 @@ WarpX::InitData () BuildBufferMasks(); + if (WarpX::em_solver_medium==1) { + m_macroscopic_properties->InitData(); + } + InitDiagnostics(); if (ParallelDescriptor::IOProcessor()) { @@ -304,7 +345,6 @@ WarpX::InitLevelData (int lev, Real /*time*/) str_By_ext_grid_function); Store_parserString(pp, "Bz_external_grid_function(x,y,z)", str_Bz_ext_grid_function); - Bxfield_parser.reset(new ParserWrapper<3>( makeParser(str_Bx_ext_grid_function,{"x","y","z"}))); Byfield_parser.reset(new ParserWrapper<3>( @@ -316,25 +356,25 @@ WarpX::InitLevelData (int lev, Real /*time*/) InitializeExternalFieldsOnGridUsingParser(Bfield_fp[lev][0].get(), Bfield_fp[lev][1].get(), Bfield_fp[lev][2].get(), - Bxfield_parser.get(), - Byfield_parser.get(), - Bzfield_parser.get(), + getParser(Bxfield_parser), + getParser(Byfield_parser), + getParser(Bzfield_parser), lev); if (lev > 0) { InitializeExternalFieldsOnGridUsingParser(Bfield_aux[lev][0].get(), Bfield_aux[lev][1].get(), Bfield_aux[lev][2].get(), - Bxfield_parser.get(), - Byfield_parser.get(), - Bzfield_parser.get(), + getParser(Bxfield_parser), + getParser(Byfield_parser), + getParser(Bzfield_parser), lev); InitializeExternalFieldsOnGridUsingParser(Bfield_cp[lev][0].get(), Bfield_cp[lev][1].get(), Bfield_cp[lev][2].get(), - Bxfield_parser.get(), - Byfield_parser.get(), - Bzfield_parser.get(), + getParser(Bxfield_parser), + getParser(Byfield_parser), + getParser(Bzfield_parser), lev); } } @@ -365,25 +405,25 @@ WarpX::InitLevelData (int lev, Real /*time*/) InitializeExternalFieldsOnGridUsingParser(Efield_fp[lev][0].get(), Efield_fp[lev][1].get(), Efield_fp[lev][2].get(), - Exfield_parser.get(), - Eyfield_parser.get(), - Ezfield_parser.get(), + getParser(Exfield_parser), + getParser(Eyfield_parser), + getParser(Ezfield_parser), lev); if (lev > 0) { InitializeExternalFieldsOnGridUsingParser(Efield_aux[lev][0].get(), Efield_aux[lev][1].get(), Efield_aux[lev][2].get(), - Exfield_parser.get(), - Eyfield_parser.get(), - Ezfield_parser.get(), + getParser(Exfield_parser), + getParser(Eyfield_parser), + getParser(Ezfield_parser), lev); InitializeExternalFieldsOnGridUsingParser(Efield_cp[lev][0].get(), Efield_cp[lev][1].get(), Efield_cp[lev][2].get(), - Exfield_parser.get(), - Eyfield_parser.get(), - Ezfield_parser.get(), + getParser(Exfield_parser), + getParser(Eyfield_parser), + getParser(Ezfield_parser), lev); } } @@ -414,8 +454,8 @@ WarpX::InitLevelData (int lev, Real /*time*/) void WarpX::InitializeExternalFieldsOnGridUsingParser ( MultiFab *mfx, MultiFab *mfy, MultiFab *mfz, - ParserWrapper<3> *xfield_parser, ParserWrapper<3> *yfield_parser, - ParserWrapper<3> *zfield_parser, const int lev) + HostDeviceParser<3> const& xfield_parser, HostDeviceParser<3> const& yfield_parser, + HostDeviceParser<3> const& zfield_parser, const int lev) { const auto dx_lev = geom[lev].CellSizeArray(); @@ -450,7 +490,7 @@ WarpX::InitializeExternalFieldsOnGridUsingParser ( amrex::Real z = k*dx_lev[2] + real_box.lo(2) + fac_z; #endif // Initialize the x-component of the field. - mfxfab(i,j,k) = (*xfield_parser)(x,y,z); + mfxfab(i,j,k) = xfield_parser(x,y,z); }, [=] AMREX_GPU_DEVICE (int i, int j, int k) { amrex::Real fac_x = (1._rt - y_nodal_flag[0]) * dx_lev[0] * 0.5_rt; @@ -466,7 +506,7 @@ WarpX::InitializeExternalFieldsOnGridUsingParser ( amrex::Real z = k*dx_lev[2] + real_box.lo(2) + fac_z; #endif // Initialize the y-component of the field. - mfyfab(i,j,k) = (*yfield_parser)(x,y,z); + mfyfab(i,j,k) = yfield_parser(x,y,z); }, [=] AMREX_GPU_DEVICE (int i, int j, int k) { amrex::Real fac_x = (1._rt - z_nodal_flag[0]) * dx_lev[0] * 0.5_rt; @@ -482,7 +522,7 @@ WarpX::InitializeExternalFieldsOnGridUsingParser ( amrex::Real z = k*dx_lev[2] + real_box.lo(2) + fac_z; #endif // Initialize the z-component of the field. - mfzfab(i,j,k) = (*zfield_parser)(x,y,z); + mfzfab(i,j,k) = zfield_parser(x,y,z); } ); } diff --git a/Source/Laser/LaserParticleContainer.H b/Source/Laser/LaserParticleContainer.H index 95dc050941b..bc7806da361 100644 --- a/Source/Laser/LaserParticleContainer.H +++ b/Source/Laser/LaserParticleContainer.H @@ -73,35 +73,35 @@ public: protected: - std::string laser_name; + std::string m_laser_name; private: // runtime paramters - amrex::Vector position; //! Coordinates of one of the point of the antenna - amrex::Vector nvec; //! Normal of the plane of the antenna - amrex::Vector p_X;// ! Polarization + amrex::Vector m_position; //! Coordinates of one of the point of the antenna + amrex::Vector m_nvec; //! Normal of the plane of the antenna + amrex::Vector m_p_X;// ! Polarization - long pusher_algo = -1; - amrex::Real e_max = std::numeric_limits::quiet_NaN(); - amrex::Real wavelength = std::numeric_limits::quiet_NaN(); + long m_pusher_algo = -1; + amrex::Real m_e_max = std::numeric_limits::quiet_NaN(); + amrex::Real m_wavelength = std::numeric_limits::quiet_NaN(); - amrex::Real Z0_lab = 0; // Position of the antenna in the lab frame + amrex::Real m_Z0_lab = 0; // Position of the antenna in the lab frame - long min_particles_per_mode = 4; + long m_min_particles_per_mode = 4; // computed using runtime parameters - amrex::Vector p_Y; - amrex::Vector u_X; - amrex::Vector u_Y; - amrex::Real weight = std::numeric_limits::quiet_NaN(); - amrex::Real mobility = std::numeric_limits::quiet_NaN(); + amrex::Vector m_p_Y; + amrex::Vector m_u_X; + amrex::Vector m_u_Y; + amrex::Real m_weight = std::numeric_limits::quiet_NaN(); + amrex::Real m_mobility = std::numeric_limits::quiet_NaN(); // laser particle domain - amrex::RealBox laser_injection_box; + amrex::RealBox m_laser_injection_box; // Theoretical position of the antenna. Used if do_continuous_injection=1. // Track the position of the antenna until it enters the simulation domain. - amrex::Vector updated_position; + amrex::Vector m_updated_position; void ComputeSpacing (int lev, amrex::Real& Sx, amrex::Real& Sy) const; void ComputeWeightMobility (amrex::Real Sx, amrex::Real Sy); diff --git a/Source/Laser/LaserParticleContainer.cpp b/Source/Laser/LaserParticleContainer.cpp index c80f64d2ebf..05c6c6f2955 100644 --- a/Source/Laser/LaserParticleContainer.cpp +++ b/Source/Laser/LaserParticleContainer.cpp @@ -12,12 +12,13 @@ #include "Particles/MultiParticleContainer.H" #include "Particles/Pusher/GetAndSetPosition.H" +#include + #include #include #include #include - using namespace amrex; using namespace WarpXLaserProfiles; @@ -31,13 +32,13 @@ namespace LaserParticleContainer::LaserParticleContainer (AmrCore* amr_core, int ispecies, const std::string& name) : WarpXParticleContainer(amr_core, ispecies), - laser_name(name) + m_laser_name{name} { charge = 1.0; mass = std::numeric_limits::max(); do_back_transformed_diagnostics = 0; - ParmParse pp(laser_name); + ParmParse pp(m_laser_name); // Parse the type of laser profile and set the corresponding flag `profile` std::string laser_type_s; @@ -45,15 +46,20 @@ LaserParticleContainer::LaserParticleContainer (AmrCore* amr_core, int ispecies, std::transform(laser_type_s.begin(), laser_type_s.end(), laser_type_s.begin(), ::tolower); // Parse the properties of the antenna - pp.getarr("position", position); - pp.getarr("direction", nvec); - pp.getarr("polarization", p_X); + pp.getarr("position", m_position); + pp.getarr("direction", m_nvec); + pp.getarr("polarization", m_p_X); - pp.query("pusher_algo", pusher_algo); - pp.get("wavelength", wavelength); - pp.get("e_max", e_max); + pp.query("pusher_algo", m_pusher_algo); + pp.get("wavelength", m_wavelength); + pp.get("e_max", m_e_max); pp.query("do_continuous_injection", do_continuous_injection); - pp.query("min_particles_per_mode", min_particles_per_mode); + pp.query("min_particles_per_mode", m_min_particles_per_mode); + + if (m_e_max == amrex::Real(0.)){ + amrex::Print() << m_laser_name << " with zero amplitude disabled.\n"; + return; // Disable laser if amplitude is 0 + } //Select laser profile if(laser_profiles_dictionary.count(laser_type_s) == 0){ @@ -62,58 +68,63 @@ LaserParticleContainer::LaserParticleContainer (AmrCore* amr_core, int ispecies, m_up_laser_profile = laser_profiles_dictionary.at(laser_type_s)(); //__________ +#ifdef WARPX_DIM_XZ + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_nvec[1] == amrex::Real(0), + "Laser propagation direction must be 0 along y in 2D"); +#endif + // Plane normal - Real s = 1.0_rt / std::sqrt(nvec[0]*nvec[0] + nvec[1]*nvec[1] + nvec[2]*nvec[2]); - nvec = { nvec[0]*s, nvec[1]*s, nvec[2]*s }; + Real s = 1.0_rt / std::sqrt(m_nvec[0]*m_nvec[0] + m_nvec[1]*m_nvec[1] + m_nvec[2]*m_nvec[2]); + m_nvec = { m_nvec[0]*s, m_nvec[1]*s, m_nvec[2]*s }; if (WarpX::gamma_boost > 1.) { // Check that the laser direction is equal to the boost direction - AMREX_ALWAYS_ASSERT_WITH_MESSAGE( nvec[0]*WarpX::boost_direction[0] - + nvec[1]*WarpX::boost_direction[1] - + nvec[2]*WarpX::boost_direction[2] - 1. < 1.e-12, + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( m_nvec[0]*WarpX::boost_direction[0] + + m_nvec[1]*WarpX::boost_direction[1] + + m_nvec[2]*WarpX::boost_direction[2] - 1. < 1.e-12, "The Lorentz boost should be in the same direction as the laser propagation"); // Get the position of the plane, along the boost direction, in the lab frame // and convert the position of the antenna to the boosted frame - Z0_lab = nvec[0]*position[0] + nvec[1]*position[1] + nvec[2]*position[2]; - Real Z0_boost = Z0_lab/WarpX::gamma_boost; - position[0] += (Z0_boost-Z0_lab)*nvec[0]; - position[1] += (Z0_boost-Z0_lab)*nvec[1]; - position[2] += (Z0_boost-Z0_lab)*nvec[2]; + m_Z0_lab = m_nvec[0]*m_position[0] + m_nvec[1]*m_position[1] + m_nvec[2]*m_position[2]; + Real Z0_boost = m_Z0_lab/WarpX::gamma_boost; + m_position[0] += (Z0_boost-m_Z0_lab)*m_nvec[0]; + m_position[1] += (Z0_boost-m_Z0_lab)*m_nvec[1]; + m_position[2] += (Z0_boost-m_Z0_lab)*m_nvec[2]; } // The first polarization vector - s = 1.0_rt / std::sqrt(p_X[0]*p_X[0] + p_X[1]*p_X[1] + p_X[2]*p_X[2]); - p_X = { p_X[0]*s, p_X[1]*s, p_X[2]*s }; + s = 1.0_rt / std::sqrt(m_p_X[0]*m_p_X[0] + m_p_X[1]*m_p_X[1] + m_p_X[2]*m_p_X[2]); + m_p_X = { m_p_X[0]*s, m_p_X[1]*s, m_p_X[2]*s }; - Real const dp = std::inner_product(nvec.begin(), nvec.end(), p_X.begin(), 0.0); + Real const dp = std::inner_product(m_nvec.begin(), m_nvec.end(), m_p_X.begin(), 0.0); AMREX_ALWAYS_ASSERT_WITH_MESSAGE(std::abs(dp) < 1.0e-14, "Laser plane vector is not perpendicular to the main polarization vector"); - p_Y = CrossProduct(nvec, p_X); // The second polarization vector + m_p_Y = CrossProduct(m_nvec, m_p_X); // The second polarization vector #if (defined WARPX_DIM_3D) || (defined WARPX_DIM_RZ) - u_X = p_X; - u_Y = p_Y; + m_u_X = m_p_X; + m_u_Y = m_p_Y; #else - u_X = CrossProduct({0., 1., 0.}, nvec); - u_Y = {0., 1., 0.}; + m_u_X = CrossProduct({0., 1., 0.}, m_nvec); + m_u_Y = {0., 1., 0.}; #endif - laser_injection_box= Geom(0).ProbDomain(); + m_laser_injection_box= Geom(0).ProbDomain(); { Vector lo, hi; if (pp.queryarr("prob_lo", lo)) { - laser_injection_box.setLo(lo); + m_laser_injection_box.setLo(lo); } if (pp.queryarr("prob_hi", hi)) { - laser_injection_box.setHi(hi); + m_laser_injection_box.setHi(hi); } } if (do_continuous_injection){ // If laser antenna initially outside of the box, store its theoretical // position in z_antenna_th - updated_position = position; + m_updated_position = m_position; // Sanity checks int dir = WarpX::moving_window_dir; @@ -124,7 +135,7 @@ LaserParticleContainer::LaserParticleContainer (AmrCore* amr_core, int ispecies, windir[dir] = 1.0; #endif AMREX_ALWAYS_ASSERT_WITH_MESSAGE( - (nvec[0]-windir[0]) + (nvec[1]-windir[1]) + (nvec[2]-windir[2]) + (m_nvec[0]-windir[0]) + (m_nvec[1]-windir[1]) + (m_nvec[2]-windir[2]) < 1.e-12, "do_continous_injection for laser particle only works" + " if moving window direction and laser propagation direction are the same"); if ( WarpX::gamma_boost>1 ){ @@ -139,17 +150,17 @@ LaserParticleContainer::LaserParticleContainer (AmrCore* amr_core, int ispecies, //Init laser profile - AMREX_ALWAYS_ASSERT_WITH_MESSAGE(e_max > 0., + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_e_max > 0., "Laser amplitude (e_max) must be positive."); - AMREX_ALWAYS_ASSERT_WITH_MESSAGE(wavelength > 0., + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_wavelength > 0., "Laser wavelength must be positive."); CommonLaserParameters common_params; - common_params.wavelength = wavelength; - common_params.e_max = e_max; - common_params.p_X = p_X; - common_params.nvec = nvec; + common_params.wavelength = m_wavelength; + common_params.e_max = m_e_max; + common_params.p_X = m_p_X; + common_params.nvec = m_nvec; m_up_laser_profile->init(pp, ParmParse{"my_constants"}, common_params); } @@ -164,15 +175,17 @@ LaserParticleContainer::ContinuousInjection (const RealBox& injection_box) // So far, LaserParticleContainer::laser_injection_box contains the // outdated full problem domain at t=0. + if (m_e_max == amrex::Real(0.)) return; // Disable laser if amplitude is 0 + // Convert updated_position to Real* to use RealBox::contains(). #if (AMREX_SPACEDIM == 3) - const Real* p_pos = updated_position.dataPtr(); + const Real* p_pos = m_updated_position.dataPtr(); #else - const Real p_pos[2] = {updated_position[0], updated_position[2]}; + const Real p_pos[2] = {m_updated_position[0], m_updated_position[2]}; #endif if ( injection_box.contains(p_pos) ){ // Update laser_injection_box with current value - laser_injection_box = injection_box; + m_laser_injection_box = injection_box; // Inject laser particles. LaserParticleContainer::InitData // is called only once, when the antenna enters the simulation // domain. @@ -187,18 +200,20 @@ LaserParticleContainer::ContinuousInjection (const RealBox& injection_box) void LaserParticleContainer::UpdateContinuousInjectionPosition (Real dt) { + if (m_e_max == amrex::Real(0.)) return; // Disable laser if amplitude is 0 + int dir = WarpX::moving_window_dir; if (do_continuous_injection and (WarpX::gamma_boost > 1)){ // In boosted-frame simulations, the antenna has moved since the last // call to this function, and injection position needs to be updated #if ( AMREX_SPACEDIM == 3 ) - updated_position[dir] -= WarpX::beta_boost * + m_updated_position[dir] -= WarpX::beta_boost * WarpX::boost_direction[dir] * PhysConst::c * dt; #elif ( AMREX_SPACEDIM == 2 ) // In 2D, dir=0 corresponds to x and dir=1 corresponds to z // This needs to be converted in order to index `boost_direction` // which has 3 components, for both 2D and 3D simulations. - updated_position[2*dir] -= WarpX::beta_boost * + m_updated_position[2*dir] -= WarpX::beta_boost * WarpX::boost_direction[2*dir] * PhysConst::c * dt; #endif } @@ -215,6 +230,8 @@ LaserParticleContainer::InitData () void LaserParticleContainer::InitData (int lev) { + if (m_e_max == amrex::Real(0.)) return; // Disable laser if amplitude is 0 + // spacing of laser particles in the laser plane. // has to be done after geometry is set up. Real S_X, S_Y; @@ -225,23 +242,24 @@ LaserParticleContainer::InitData (int lev) // laser antenna. In the boosted frame, the antenna is moving. // Update its position with updated_position. if (do_continuous_injection){ - position = updated_position; + m_position = m_updated_position; } auto Transform = [&](int const i, int const j) -> Vector{ #if (AMREX_SPACEDIM == 3) - return { position[0] + (S_X*(Real(i)+0.5_rt))*u_X[0] + (S_Y*(Real(j)+0.5_rt))*u_Y[0], - position[1] + (S_X*(Real(i)+0.5_rt))*u_X[1] + (S_Y*(Real(j)+0.5_rt))*u_Y[1], - position[2] + (S_X*(Real(i)+0.5_rt))*u_X[2] + (S_Y*(Real(j)+0.5_rt))*u_Y[2] }; + return { m_position[0] + (S_X*(Real(i)+0.5_rt))*m_u_X[0] + (S_Y*(Real(j)+0.5_rt))*m_u_Y[0], + m_position[1] + (S_X*(Real(i)+0.5_rt))*m_u_X[1] + (S_Y*(Real(j)+0.5_rt))*m_u_Y[1], + m_position[2] + (S_X*(Real(i)+0.5_rt))*m_u_X[2] + (S_Y*(Real(j)+0.5_rt))*m_u_Y[2] }; #else + amrex::ignore_unused(j); # if (defined WARPX_DIM_RZ) - return { position[0] + (S_X*(Real(i)+0.5_rt)), + return { m_position[0] + (S_X*(Real(i)+0.5_rt)), 0.0_rt, - position[2]}; + m_position[2]}; # else - return { position[0] + (S_X*(Real(i)+0.5_rt))*u_X[0], + return { m_position[0] + (S_X*(Real(i)+0.5_rt))*m_u_X[0], 0.0_rt, - position[2] + (S_X*(Real(i)+0.5_rt))*u_X[2] }; + m_position[2] + (S_X*(Real(i)+0.5_rt))*m_u_X[2] }; # endif #endif }; @@ -249,13 +267,13 @@ LaserParticleContainer::InitData (int lev) // Given the "lab" frame coordinates, return the real coordinates in the laser plane coordinates auto InverseTransform = [&](const Vector& pos) -> Vector{ #if (AMREX_SPACEDIM == 3) - return {u_X[0]*(pos[0]-position[0])+u_X[1]*(pos[1]-position[1])+u_X[2]*(pos[2]-position[2]), - u_Y[0]*(pos[0]-position[0])+u_Y[1]*(pos[1]-position[1])+u_Y[2]*(pos[2]-position[2])}; + return {m_u_X[0]*(pos[0]-m_position[0])+m_u_X[1]*(pos[1]-m_position[1])+m_u_X[2]*(pos[2]-m_position[2]), + m_u_Y[0]*(pos[0]-m_position[0])+m_u_Y[1]*(pos[1]-m_position[1])+m_u_Y[2]*(pos[2]-m_position[2])}; #else # if (defined WARPX_DIM_RZ) - return {pos[0]-position[0], 0.0_rt}; + return {pos[0]-m_position[0], 0.0_rt}; # else - return {u_X[0]*(pos[0]-position[0])+u_X[2]*(pos[2]-position[2]), 0.0_rt}; + return {m_u_X[0]*(pos[0]-m_position[0])+m_u_X[2]*(pos[2]-m_position[2]), 0.0_rt}; # endif #endif }; @@ -273,8 +291,8 @@ LaserParticleContainer::InitData (int lev) plane_hi[1] = std::max(plane_hi[1], j); }; - const Real* prob_lo = laser_injection_box.lo(); - const Real* prob_hi = laser_injection_box.hi(); + const Real* prob_lo = m_laser_injection_box.lo(); + const Real* prob_hi = m_laser_injection_box.hi(); #if (AMREX_SPACEDIM == 3) compute_min_max(prob_lo[0], prob_lo[1], prob_lo[2]); compute_min_max(prob_hi[0], prob_lo[1], prob_lo[2]); @@ -335,7 +353,7 @@ LaserParticleContainer::InitData (int lev) #else const Real x[2] = {pos[0], pos[2]}; #endif - if (laser_injection_box.contains(x)) + if (m_laser_injection_box.contains(x)) { #ifndef WARPX_DIM_RZ for (int k = 0; k<2; ++k) { @@ -343,11 +361,11 @@ LaserParticleContainer::InitData (int lev) particle_y.push_back(pos[1]); particle_z.push_back(pos[2]); } - particle_w.push_back( weight); - particle_w.push_back(-weight); + particle_w.push_back( m_weight); + particle_w.push_back(-m_weight); #else // Particles are laid out in radial spokes - const int n_spokes = (WarpX::n_rz_azimuthal_modes - 1)*min_particles_per_mode; + const int n_spokes = (WarpX::n_rz_azimuthal_modes - 1)*m_min_particles_per_mode; for (int spoke = 0 ; spoke < n_spokes ; spoke++) { const Real phase = 2.*MathConst::pi*spoke/n_spokes; for (int k = 0; k<2; ++k) { @@ -355,7 +373,7 @@ LaserParticleContainer::InitData (int lev) particle_y.push_back(pos[0]*std::sin(phase)); particle_z.push_back(pos[2]); } - const Real r_weight = weight*2.*MathConst::pi*pos[0]/n_spokes; + const Real r_weight = m_weight*2.*MathConst::pi*pos[0]/n_spokes; particle_w.push_back( r_weight); particle_w.push_back(-r_weight); } @@ -390,18 +408,17 @@ LaserParticleContainer::Evolve (int lev, const MultiFab*, const MultiFab*, const MultiFab*, Real t, Real dt, DtType /*a_dt_type*/) { - WARPX_PROFILE("Laser::Evolve()"); - WARPX_PROFILE_VAR_NS("Laser::Evolve::Copy", blp_copy); - WARPX_PROFILE_VAR_NS("Laser::ParticlePush", blp_pp); - WARPX_PROFILE_VAR_NS("Laser::CurrentDepo", blp_cd); - WARPX_PROFILE_VAR_NS("Laser::Evolve::Accumulate", blp_accumulate); + WARPX_PROFILE("LaserParticleContainer::Evolve()"); + WARPX_PROFILE_VAR_NS("LaserParticleContainer::Evolve::ParticlePush", blp_pp); + + if (m_e_max == amrex::Real(0.)) return; // Disable laser if amplitude is 0 Real t_lab = t; if (WarpX::gamma_boost > 1) { // Convert time from the boosted to the lab-frame // (in order to later calculate the amplitude of the field, // at the position of the antenna, in the lab-frame) - t_lab = 1./WarpX::gamma_boost*t + WarpX::beta_boost*Z0_lab/PhysConst::c; + t_lab = 1./WarpX::gamma_boost*t + WarpX::beta_boost*m_Z0_lab/PhysConst::c; } // Update laser profile @@ -421,7 +438,7 @@ LaserParticleContainer::Evolve (int lev, int const thread_num = 0; #endif - Gpu::ManagedDeviceVector plane_Xp, plane_Yp, amplitude_E; + Gpu::DeviceVector plane_Xp, plane_Yp, amplitude_E; for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) { @@ -506,9 +523,11 @@ LaserParticleContainer::Evolve (int lev, } } + // This is necessary because of plane_Xp, plane_Yp and amplitude_E + amrex::Gpu::synchronize(); + if (cost && WarpX::load_balance_costs_update_algo == LoadBalanceCostsUpdateAlgo::Timers) { - amrex::Gpu::synchronize(); wt = amrex::second() - wt; amrex::HostDevice::Atomic::Add( &(*cost)[pti.index()], wt); } @@ -519,6 +538,7 @@ LaserParticleContainer::Evolve (int lev, void LaserParticleContainer::PostRestart () { + if (m_e_max == amrex::Real(0.)) return; // Disable laser if amplitude is 0 Real Sx, Sy; const int lev = finestLevel(); ComputeSpacing(lev, Sx, Sy); @@ -534,18 +554,18 @@ LaserParticleContainer::ComputeSpacing (int lev, Real& Sx, Real& Sy) const const Real eps = dx[0]*1.e-50; #endif #if (AMREX_SPACEDIM == 3) - Sx = std::min(std::min(dx[0]/(std::abs(u_X[0])+eps), - dx[1]/(std::abs(u_X[1])+eps)), - dx[2]/(std::abs(u_X[2])+eps)); - Sy = std::min(std::min(dx[0]/(std::abs(u_Y[0])+eps), - dx[1]/(std::abs(u_Y[1])+eps)), - dx[2]/(std::abs(u_Y[2])+eps)); + Sx = std::min(std::min(dx[0]/(std::abs(m_u_X[0])+eps), + dx[1]/(std::abs(m_u_X[1])+eps)), + dx[2]/(std::abs(m_u_X[2])+eps)); + Sy = std::min(std::min(dx[0]/(std::abs(m_u_Y[0])+eps), + dx[1]/(std::abs(m_u_Y[1])+eps)), + dx[2]/(std::abs(m_u_Y[2])+eps)); #else # if (defined WARPX_DIM_RZ) Sx = dx[0]; # else - Sx = std::min(dx[0]/(std::abs(u_X[0])+eps), - dx[2]/(std::abs(u_X[2])+eps)); + Sx = std::min(dx[0]/(std::abs(m_u_X[0])+eps), + dx[2]/(std::abs(m_u_X[2])+eps)); # endif Sy = 1.0; #endif @@ -556,19 +576,27 @@ LaserParticleContainer::ComputeWeightMobility (Real Sx, Real Sy) { constexpr Real eps = 0.01; constexpr Real fac = 1.0_rt / (2.0_rt * MathConst::pi * PhysConst::mu0 * PhysConst::c * PhysConst::c * eps); - weight = fac * wavelength * Sx * Sy / std::min(Sx,Sy) * e_max; + m_weight = fac * m_wavelength * Sx * Sy / std::min(Sx,Sy) * m_e_max; // The mobility is the constant of proportionality between the field to // be emitted, and the corresponding velocity that the particles need to have. - mobility = (Sx * Sy)/(weight * PhysConst::mu0 * PhysConst::c * PhysConst::c); + m_mobility = (Sx * Sy)/(m_weight * PhysConst::mu0 * PhysConst::c * PhysConst::c); // When running in the boosted-frame, the input parameters (and in particular // the amplitude of the field) are given in the lab-frame. // Therefore, the mobility needs to be modified by a factor WarpX::gamma_boost. - mobility = mobility/WarpX::gamma_boost; + m_mobility = m_mobility/WarpX::gamma_boost; + + // If mobility is too high (caused by a small wavelength compared to the grid size), + // calculated antenna particle velocities may exceed c, which can cause a segfault. + constexpr Real warning_tol = 0.1_rt; + if (m_wavelength < std::min(Sx,Sy)*warning_tol){ + amrex::Warning("WARNING: laser wavelength seems to be much smaller than the grid size." + " This may cause a segmentation fault"); + } } void -LaserParticleContainer::PushP (int lev, Real dt, +LaserParticleContainer::PushP (int /*lev*/, Real /*dt*/, const MultiFab&, const MultiFab&, const MultiFab&, const MultiFab&, const MultiFab&, const MultiFab&) { @@ -589,16 +617,16 @@ LaserParticleContainer::calculate_laser_plane_coordinates (const WarpXParIter& p { const auto GetPosition = GetParticlePosition(pti); - Real tmp_u_X_0 = u_X[0]; - Real tmp_u_X_2 = u_X[2]; - Real tmp_position_0 = position[0]; - Real tmp_position_2 = position[2]; + Real tmp_u_X_0 = m_u_X[0]; + Real tmp_u_X_2 = m_u_X[2]; + Real tmp_position_0 = m_position[0]; + Real tmp_position_2 = m_position[2]; #if (defined WARPX_DIM_3D) || (defined WARPX_DIM_RZ) - Real tmp_u_X_1 = u_X[1]; - Real tmp_u_Y_0 = u_Y[0]; - Real tmp_u_Y_1 = u_Y[1]; - Real tmp_u_Y_2 = u_Y[2]; - Real tmp_position_1 = position[1]; + Real tmp_u_X_1 = m_u_X[1]; + Real tmp_u_Y_0 = m_u_Y[0]; + Real tmp_u_Y_1 = m_u_Y[1]; + Real tmp_u_Y_2 = m_u_Y[2]; + Real tmp_position_1 = m_position[1]; #endif amrex::ParallelFor( @@ -647,15 +675,15 @@ LaserParticleContainer::update_laser_particle (WarpXParIter& pti, const auto GetPosition = GetParticlePosition(pti); auto SetPosition = SetParticlePosition(pti); - Real tmp_p_X_0 = p_X[0]; - Real tmp_p_X_1 = p_X[1]; - Real tmp_p_X_2 = p_X[2]; - Real tmp_nvec_0 = nvec[0]; - Real tmp_nvec_1 = nvec[1]; - Real tmp_nvec_2 = nvec[2]; + Real tmp_p_X_0 = m_p_X[0]; + Real tmp_p_X_1 = m_p_X[1]; + Real tmp_p_X_2 = m_p_X[2]; + Real tmp_nvec_0 = m_nvec[0]; + Real tmp_nvec_1 = m_nvec[1]; + Real tmp_nvec_2 = m_nvec[2]; // Copy member variables to tmp copies for GPU runs. - Real tmp_mobility = mobility; + Real tmp_mobility = m_mobility; Real gamma_boost = WarpX::gamma_boost; Real beta_boost = WarpX::beta_boost; amrex::ParallelFor( @@ -664,6 +692,9 @@ LaserParticleContainer::update_laser_particle (WarpXParIter& pti, // Calculate the velocity according to the amplitude of E const Real sign_charge = (pwp[i]>0) ? 1 : -1; const Real v_over_c = sign_charge * tmp_mobility * amplitude[i]; + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(amrex::Math::abs(v_over_c) < amrex::Real(1.), + "Error: calculated laser particle velocity greater than c." + "Make sure the laser wavelength and amplitude are accurately set."); // The velocity is along the laser polarization p_X Real vx = PhysConst::c * v_over_c * tmp_p_X_0; Real vy = PhysConst::c * v_over_c * tmp_p_X_1; diff --git a/Source/Laser/LaserProfiles.H b/Source/Laser/LaserProfiles.H index 585e65a8ec5..4ed2ac2e481 100644 --- a/Source/Laser/LaserProfiles.H +++ b/Source/Laser/LaserProfiles.H @@ -7,7 +7,7 @@ #ifndef WARPX_LaserProfiles_H_ #define WARPX_LaserProfiles_H_ -#include "Parser/WarpXParser.H" +#include "Parser/WarpXParserWrapper.H" #include #include @@ -134,6 +134,7 @@ private: amrex::Real zeta = 0; amrex::Real beta = 0; amrex::Real phi2 = 0; + amrex::Real phi0 = 0; amrex::Vector stc_direction; //! Direction of the spatio-temporal couplings amrex::Real theta_stc; //! Angle between polarization (p_X) and direction of spatiotemporal coupling (stc_direction) @@ -207,7 +208,8 @@ private: std::string field_function; } m_params; - WarpXParser m_parser; + WarpXParser m_parser; + std::unique_ptr > m_gpu_parser; }; /** @@ -342,15 +344,17 @@ private: /** Vector of temporal coordinates. For a non-uniform grid, it contains * all values of time. For a uniform grid, it contains only the start and stop * times and intermediate times are obtained with nt */ - amrex::Gpu::ManagedVector t_coords; + amrex::Vector t_coords; /** Vector or x coordinates. For a non-uniform grid, it contains all values * of space dimension x. For a uniform grid, it contains only the min and max * x coordinates, and intermediate positions are obtained with nx */ - amrex::Gpu::ManagedVector x_coords; + amrex::Vector h_x_coords; + amrex::Gpu::DeviceVector d_x_coords; /** Vector or y coordinates. For a non-uniform grid, it contains all values * of space dimension y. For a uniform grid, it contains only the min and max * y coordinates, and intermediate positions are obtained with ny */ - amrex::Gpu::ManagedVector y_coords; + amrex::Vector h_y_coords; + amrex::Gpu::DeviceVector d_y_coords; /** Size of the timestep range to load */ int time_chunk_size; /** Index of the first timestep in memory */ @@ -358,7 +362,7 @@ private: /** Index of the last timestep in memory */ int last_time_index; /** Field data */ - amrex::Gpu::ManagedVector E_data; + amrex::Gpu::DeviceVector E_data; } m_params; CommonLaserParameters m_common_params; diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp index 67c545096d0..3ec95b5934b 100644 --- a/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp +++ b/Source/Laser/LaserProfilesImpl/LaserProfileFieldFunction.cpp @@ -37,6 +37,8 @@ WarpXLaserProfiles::FieldFunctionLaserProfile::init ( for (auto const& s : symbols) { // make sure there no unknown symbols amrex::Abort("Laser Profile: Unknown symbol "+s); } + + m_gpu_parser = std::make_unique< ParserWrapper<3> >(m_parser); } void @@ -44,7 +46,9 @@ WarpXLaserProfiles::FieldFunctionLaserProfile::fill_amplitude ( const int np, Real const * AMREX_RESTRICT const Xp, Real const * AMREX_RESTRICT const Yp, Real t, Real * AMREX_RESTRICT const amplitude) const { - for (int i = 0; i < np; ++i) { - amplitude[i] = m_parser.eval(Xp[i], Yp[i], t); - } + auto parser = getParser(m_gpu_parser); + amrex::ParallelFor(np, [=] AMREX_GPU_DEVICE (int i) noexcept + { + amplitude[i] = parser(Xp[i], Yp[i], t); + }); } diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp index e4be9e90744..5c4092aa6a0 100644 --- a/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp +++ b/Source/Laser/LaserProfilesImpl/LaserProfileFromTXYEFile.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -55,7 +56,7 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::init ( //Allocate memory for E_data Vector const int data_size = m_params.time_chunk_size* m_params.nx*m_params.ny; - m_params.E_data = Gpu::ManagedVector(data_size); + m_params.E_data.resize(data_size); //Read first time chunck read_data_t_chuck(0, m_params.time_chunk_size); @@ -175,15 +176,15 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::parse_txye_file(std::string txye_f Abort("Failed to read coords from txye file"); if (!std::is_sorted(buf_y.begin(), buf_y.end())) Abort("Coordinates are not sorted in txye file"); - m_params.t_coords = Gpu::ManagedVector(buf_t.size()); - m_params.x_coords = Gpu::ManagedVector(buf_x.size()); - m_params.y_coords = Gpu::ManagedVector(buf_y.size()); + m_params.t_coords.resize(buf_t.size()); + m_params.h_x_coords.resize(buf_x.size()); + m_params.h_y_coords.resize(buf_y.size()); // Convert from double to amrex::Real std::transform(buf_t.begin(), buf_t.end(), m_params.t_coords.begin(), [](auto x) {return static_cast(x);} ); - std::transform(buf_x.begin(), buf_x.end(), m_params.x_coords.begin(), + std::transform(buf_x.begin(), buf_x.end(), m_params.h_x_coords.begin(), [](auto x) {return static_cast(x);} ); - std::transform(buf_y.begin(), buf_y.end(), m_params.y_coords.begin(), + std::transform(buf_y.begin(), buf_y.end(), m_params.h_y_coords.begin(), [](auto x) {return static_cast(x);} ); } @@ -191,7 +192,6 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::parse_txye_file(std::string txye_f char is_grid_uniform = m_params.is_grid_uniform; ParallelDescriptor::Bcast(&is_grid_uniform, 1, ParallelDescriptor::IOProcessorNumber()); - ParallelDescriptor::Barrier(); m_params.is_grid_uniform = is_grid_uniform; //Broadcast grid size and coordinate sizes @@ -203,26 +203,34 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::parse_txye_file(std::string txye_f //have size 2 and y_coords has size 1 in 2D and size 2 in 3D. int t_sizes[6] = {m_params.nt, m_params.nx, m_params.ny, static_cast(m_params.t_coords.size()), - static_cast(m_params.x_coords.size()), - static_cast(m_params.y_coords.size())}; + static_cast(m_params.h_x_coords.size()), + static_cast(m_params.h_y_coords.size())}; ParallelDescriptor::Bcast(t_sizes, 6, ParallelDescriptor::IOProcessorNumber()); - ParallelDescriptor::Barrier(); m_params.nt = t_sizes[0]; m_params.nx = t_sizes[1]; m_params.ny = t_sizes[2]; //Broadcast coordinates if(!ParallelDescriptor::IOProcessor()){ - m_params.t_coords = Gpu::ManagedVector(t_sizes[3]); - m_params.x_coords = Gpu::ManagedVector(t_sizes[4]); - m_params.y_coords = Gpu::ManagedVector(t_sizes[5]); + m_params.t_coords.resize(t_sizes[3]); + m_params.h_x_coords.resize(t_sizes[4]); + m_params.h_y_coords.resize(t_sizes[5]); } ParallelDescriptor::Bcast(m_params.t_coords.dataPtr(), m_params.t_coords.size(), ParallelDescriptor::IOProcessorNumber()); - ParallelDescriptor::Bcast(m_params.x_coords.dataPtr(), - m_params.x_coords.size(), ParallelDescriptor::IOProcessorNumber()); - ParallelDescriptor::Bcast(m_params.y_coords.dataPtr(), - m_params.y_coords.size(), ParallelDescriptor::IOProcessorNumber()); - ParallelDescriptor::Barrier(); + ParallelDescriptor::Bcast(m_params.h_x_coords.dataPtr(), + m_params.h_x_coords.size(), ParallelDescriptor::IOProcessorNumber()); + ParallelDescriptor::Bcast(m_params.h_y_coords.dataPtr(), + m_params.h_y_coords.size(), ParallelDescriptor::IOProcessorNumber()); + + m_params.d_x_coords.resize(m_params.h_x_coords.size()); + m_params.d_y_coords.resize(m_params.h_y_coords.size()); + Gpu::copyAsync(Gpu::hostToDevice, + m_params.h_x_coords.begin(), m_params.h_x_coords.end(), + m_params.d_x_coords.begin()); + Gpu::copyAsync(Gpu::hostToDevice, + m_params.h_y_coords.begin(), m_params.h_y_coords.end(), + m_params.d_y_coords.begin()); + Gpu::synchronize(); } std::pair @@ -254,9 +262,11 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::read_data_t_chuck(int t_begin, int //Indices of the first and last timestep to read auto i_first = max(0, t_begin); auto i_last = min(t_end-1, m_params.nt-1); - if(i_last-i_first+1 > m_params.E_data.size()) + if(i_last-i_first+1 > static_cast(m_params.E_data.size())) Abort("Data chunk to read from file is too large"); + Vector h_E_data(m_params.E_data.size()); + if(ParallelDescriptor::IOProcessor()){ //Read data chunk std::ifstream inp(m_params.txye_file_name, std::ios::binary); @@ -264,8 +274,8 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::read_data_t_chuck(int t_begin, int auto skip_amount = 1 + 3*sizeof(uint32_t) + m_params.t_coords.size()*sizeof(double) + - m_params.x_coords.size()*sizeof(double) + - m_params.y_coords.size()*sizeof(double) + + m_params.h_x_coords.size()*sizeof(double) + + m_params.h_y_coords.size()*sizeof(double) + sizeof(double)*t_begin*m_params.nx*m_params.ny; inp.ignore(skip_amount); if(!inp) Abort("Failed to read field data from txye file"); @@ -274,14 +284,16 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::read_data_t_chuck(int t_begin, int Vector buf_e(read_size); inp.read(reinterpret_cast(buf_e.dataPtr()), read_size*sizeof(double)); if(!inp) Abort("Failed to read field data from txye file"); - std::transform(buf_e.begin(), buf_e.end(), m_params.E_data.begin(), + std::transform(buf_e.begin(), buf_e.end(), h_E_data.begin(), [](auto x) {return static_cast(x);} ); } //Broadcast E_data - ParallelDescriptor::Bcast(m_params.E_data.dataPtr(), - m_params.E_data.size(), ParallelDescriptor::IOProcessorNumber()); - ParallelDescriptor::Barrier(); + ParallelDescriptor::Bcast(h_E_data.dataPtr(), + h_E_data.size(), ParallelDescriptor::IOProcessorNumber()); + + Gpu::copyAsync(Gpu::hostToDevice,h_E_data.begin(),h_E_data.end(),m_params.E_data.begin()); + Gpu::synchronize(); //Update first and last indices m_params.first_time_index = i_first; @@ -298,11 +310,11 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::internal_fill_amplitude_uniform( // Copy member variables to tmp copies // and get pointers to underlying data for GPU. const auto tmp_e_max = m_common_params.e_max; - const auto tmp_x_min = m_params.x_coords.front(); - const auto tmp_x_max = m_params.x_coords.back(); + const auto tmp_x_min = m_params.h_x_coords.front(); + const auto tmp_x_max = m_params.h_x_coords.back(); #if (AMREX_SPACEDIM == 3) - const auto tmp_y_min = m_params.y_coords.front(); - const auto tmp_y_max = m_params.y_coords.back(); + const auto tmp_y_min = m_params.h_y_coords.front(); + const auto tmp_y_max = m_params.h_y_coords.back(); #endif const auto tmp_nx = m_params.nx; #if (AMREX_SPACEDIM == 3) @@ -357,6 +369,7 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::internal_fill_amplitude_uniform( p_E_data[idx(idx_t_right, idx_x_left)], p_E_data[idx(idx_t_right, idx_x_right)], t, Xp[i])*tmp_e_max; + amrex::ignore_unused(Yp); #elif (AMREX_SPACEDIM == 3) //Find indices and coordinates along y @@ -404,17 +417,17 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::internal_fill_amplitude_nonuniform // Copy member variables to tmp copies // and get pointers to underlying data for GPU. const auto tmp_e_max = m_common_params.e_max; - const auto tmp_x_min = m_params.x_coords.front(); - const auto tmp_x_max = m_params.x_coords.back(); + const auto tmp_x_min = m_params.h_x_coords.front(); + const auto tmp_x_max = m_params.h_x_coords.back(); #if (AMREX_SPACEDIM == 3) - const auto tmp_y_min = m_params.y_coords.front(); - const auto tmp_y_max = m_params.y_coords.back(); + const auto tmp_y_min = m_params.h_y_coords.front(); + const auto tmp_y_max = m_params.h_y_coords.back(); #endif - const auto p_x_coords = m_params.x_coords.dataPtr(); - const int tmp_x_coords_size = static_cast(m_params.x_coords.size()); + const auto p_x_coords = m_params.d_x_coords.dataPtr(); + const int tmp_x_coords_size = static_cast(m_params.d_x_coords.size()); #if (AMREX_SPACEDIM == 3) - const auto p_y_coords = m_params.y_coords.dataPtr(); - const int tmp_y_coords_size = static_cast(m_params.y_coords.size()); + const auto p_y_coords = m_params.d_y_coords.dataPtr(); + const int tmp_y_coords_size = static_cast(m_params.d_y_coords.size()); #endif const auto p_E_data = m_params.E_data.dataPtr(); const auto tmp_idx_first_time = m_params.first_time_index; @@ -436,6 +449,8 @@ WarpXLaserProfiles::FromTXYEFileLaserProfile::internal_fill_amplitude_nonuniform amplitude[ip] = 0.0_rt; return; } +#else + amrex::ignore_unused(Yp); #endif //Find indices along x diff --git a/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp b/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp index c90d97f833d..e7dc795a57f 100644 --- a/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp +++ b/Source/Laser/LaserProfilesImpl/LaserProfileGaussian.cpp @@ -31,6 +31,7 @@ WarpXLaserProfiles::GaussianLaserProfile::init ( ppl.query("zeta", m_params.zeta); ppl.query("beta", m_params.beta); ppl.query("phi2", m_params.phi2); + ppl.query("phi0", m_params.phi0); m_params.stc_direction = m_common_params.p_X; ppl.queryarr("stc_direction", m_params.stc_direction); @@ -84,7 +85,7 @@ WarpXLaserProfiles::GaussianLaserProfile::fill_amplitude ( // Calculate a few factors which are independent of the macroparticle const Real k0 = 2.*MathConst::pi/m_common_params.wavelength; const Real inv_tau2 = 1._rt /(m_params.duration * m_params.duration); - const Real oscillation_phase = k0 * PhysConst::c * ( t - m_params.t_peak ); + const Real oscillation_phase = k0 * PhysConst::c * ( t - m_params.t_peak ) + m_params.phi0; // The coefficients below contain info about Gouy phase, // laser diffraction, and phase front curvature const Complex diffract_factor = diff --git a/Source/Make.WarpX b/Source/Make.WarpX index 60eb690e77b..4e73e4ca167 100644 --- a/Source/Make.WarpX +++ b/Source/Make.WarpX @@ -2,11 +2,18 @@ AMREX_HOME ?= ../../../amrex PICSAR_HOME ?= ../../../picsar OPENBC_HOME ?= ../../../openbc_poisson -CXXSTD = c++14 USE_MPI = TRUE USE_PARTICLES = TRUE USE_RPATH = TRUE BL_NO_FORT = TRUE +ifeq ($(USE_DPCPP),TRUE) + CXXSTD = c++17 +else + CXXSTD = c++14 +endif + +# required for AMReX async I/O +MPI_THREAD_MULTIPLE = TRUE ifeq ($(USE_GPU),TRUE) USE_OMP = FALSE @@ -127,6 +134,12 @@ DEFINES += -DPICSAR_GIT_VERSION=\"$(PICSAR_GIT_VERSION)\" DEFINES += -DPICSAR_NO_ASSUMED_ALIGNMENT DEFINES += -DWARPX +ifdef PARSER_DEPTH + DEFINES += -DWARPX_PARSER_DEPTH=$(PARSER_DEPTH) +else + DEFINES += -DWARPX_PARSER_DEPTH=24 +endif + ifeq ($(USE_OPENBC_POISSON),TRUE) include $(OPENBC_HOME)/Make.package DEFINES += -DFFT_FFTW -DMPIPARALLEL -DUSE_OPENBC_POISSON @@ -134,7 +147,7 @@ endif ifeq ($(USE_OPENPMD), TRUE) # try pkg-config query - ifeq (0, $(shell pkg-config "openPMD >= 0.11.0"; echo $$?)) + ifeq (0, $(shell pkg-config "openPMD >= 0.12.0"; echo $$?)) CXXFLAGS += $(shell pkg-config --cflags openPMD) LIBRARY_LOCATIONS += $(shell pkg-config --variable=libdir openPMD) libraries += $(shell pkg-config --libs-only-l openPMD) @@ -158,7 +171,15 @@ endif ifeq ($(USE_PSATD),TRUE) USERSuffix := $(USERSuffix).PSATD DEFINES += -DWARPX_USE_PSATD - ifeq ($(USE_CUDA),FALSE) # Running on CPU + ifeq ($(USE_CUDA),TRUE) + # Use cuFFT + libraries += -lcufft + else ifeq ($(USE_HIP),TRUE) + # Use rocFFT. ROC_PATH is defined in amrex + INCLUDE_LOCATIONS += $(ROC_PATH)/rocfft/include + LIBRARY_LOCATIONS += $(ROC_PATH)/rocfft/lib + libraries += -lrocfft + else # Running on CPU # Use FFTW ifeq ($(PRECISION),FLOAT) libraries += -lfftw3f_mpi -lfftw3f -lfftw3f_threads @@ -171,9 +192,6 @@ ifeq ($(USE_PSATD),TRUE) INCLUDE_LOCATIONS += $(FFTW_HOME)/include LIBRARY_LOCATIONS += $(FFTW_HOME)/lib endif - else - # Use cuFFT - libraries += -lcufft endif ifeq ($(USE_RZ),TRUE) # Use blas and lapack diff --git a/Source/Parallelization/GuardCellManager.H b/Source/Parallelization/GuardCellManager.H index 393fbe33d4d..5e9971544c6 100644 --- a/Source/Parallelization/GuardCellManager.H +++ b/Source/Parallelization/GuardCellManager.H @@ -76,6 +76,10 @@ public: // An extra guard cell is needed on the fine grid to do the interpolation // for E and B. amrex::IntVect ng_Extra = amrex::IntVect::TheZeroVector(); + + // Number of guard cells for local deposition of J and rho + amrex::IntVect ng_depos_J = amrex::IntVect::TheZeroVector(); + amrex::IntVect ng_depos_rho = amrex::IntVect::TheZeroVector(); }; #endif // GUARDCELLMANAGER_H_ diff --git a/Source/Parallelization/GuardCellManager.cpp b/Source/Parallelization/GuardCellManager.cpp index 5ce79fc866d..1b8d38f0c61 100644 --- a/Source/Parallelization/GuardCellManager.cpp +++ b/Source/Parallelization/GuardCellManager.cpp @@ -6,8 +6,11 @@ */ #include "GuardCellManager.H" #include "Filter/NCIGodfreyFilter.H" +#include "Utils/WarpXAlgorithmSelection.H" + #include #include +#include using namespace amrex; @@ -86,11 +89,20 @@ guardCellManager::Init( ng_alloc_J = IntVect(ngJx,ngJz); #endif - ng_alloc_Rho = ng_alloc_J+1; //One extra ghost cell, so that it's safe to deposit charge density + // Rho is needed both at the beginning and at the end of the PIC iteration. + // Hence we add one extra guard cell for the allocation of the MultiFab that + // contains rho, in order to account for the change in the particle positions + // within a time step + ng_alloc_Rho = ng_alloc_J+1; + + // Number of guard cells for local deposition of J and rho + ng_depos_J = ng_alloc_J; + ng_depos_rho = ng_alloc_Rho; + // after pushing particle. int ng_alloc_F_int = (do_moving_window) ? 2 : 0; // CKC solver requires one additional guard cell - if (maxwell_solver_id == 1) ng_alloc_F_int = std::max( ng_alloc_F_int, 1 ); + if (maxwell_solver_id == MaxwellSolverAlgo::CKC) ng_alloc_F_int = std::max( ng_alloc_F_int, 1 ); ng_alloc_F = IntVect(AMREX_D_DECL(ng_alloc_F_int, ng_alloc_F_int, ng_alloc_F_int)); #ifdef WARPX_USE_PSATD @@ -111,7 +123,11 @@ guardCellManager::Init( pp.query("ny_guard", ngFFt_y); pp.query("nz_guard", ngFFt_z); - IntVect ngFFT = IntVect(AMREX_D_DECL(ngFFt_x, ngFFt_y, ngFFt_z)); +#if (AMREX_SPACEDIM == 3) + IntVect ngFFT = IntVect(ngFFt_x, ngFFt_y, ngFFt_z); +#elif (AMREX_SPACEDIM == 2) + IntVect ngFFT = IntVect(ngFFt_x, ngFFt_z); +#endif for (int i_dim=0; i_dim(aux_is_nodal and !do_nodal)); diff --git a/Source/Parallelization/WarpXComm.cpp b/Source/Parallelization/WarpXComm.cpp index 79bcce2f462..ac619a9377a 100644 --- a/Source/Parallelization/WarpXComm.cpp +++ b/Source/Parallelization/WarpXComm.cpp @@ -57,7 +57,7 @@ WarpX::ExchangeWithPmlF (int lev) void WarpX::UpdateAuxilaryData () { - WARPX_PROFILE("UpdateAuxilaryData()"); + WARPX_PROFILE("WarpX::UpdateAuxilaryData()"); if (Bfield_aux[0][0]->ixType() == Bfield_fp[0][0]->ixType()) { UpdateAuxilaryDataSameType(); @@ -673,7 +673,7 @@ WarpX::FillBoundaryAux (int lev, IntVect ng) void WarpX::SyncCurrent () { - WARPX_PROFILE("SyncCurrent()"); + WARPX_PROFILE("WarpX::SyncCurrent()"); // Restrict fine patch current onto the coarse patch, before // summing the guard cells of the fine patch @@ -708,7 +708,7 @@ WarpX::SyncCurrent () void WarpX::SyncRho () { - WARPX_PROFILE("SyncRho()"); + WARPX_PROFILE("WarpX::SyncRho()"); if (!rho_fp[0]) return; const int ncomp = rho_fp[0]->nComp(); diff --git a/Source/Parallelization/WarpXComm_K.H b/Source/Parallelization/WarpXComm_K.H index 1b6eceb93cf..b490636eea0 100644 --- a/Source/Parallelization/WarpXComm_K.H +++ b/Source/Parallelization/WarpXComm_K.H @@ -8,6 +8,7 @@ #define WARPX_COMM_K_H_ #include +#include AMREX_GPU_DEVICE AMREX_FORCE_INLINE void warpx_interp_bfield_x (int j, int k, int l, @@ -384,6 +385,7 @@ void warpx_interp_nd_bfield_x (int j, int k, int l, { #if (AMREX_SPACEDIM == 2) Bxa(j,k,0) = 0.5*(Bxf(j,k-1,0) + Bxf(j,k,0)); + amrex::ignore_unused(l); #else Bxa(j,k,l) = 0.25*(Bxf(j,k-1,l-1) + Bxf(j,k,l-1) + Bxf(j,k-1,l) + Bxf(j,k,l)); #endif @@ -396,6 +398,7 @@ void warpx_interp_nd_bfield_y (int j, int k, int l, { #if (AMREX_SPACEDIM == 2) Bya(j,k,0) = 0.25*(Byf(j,k,0) + Byf(j-1,k,0) + Byf(j,k-1,0) + Byf(j-1,k-1,0)); + amrex::ignore_unused(l); #else Bya(j,k,l) = 0.25*(Byf(j-1,k,l-1) + Byf(j,k,l-1) + Byf(j-1,k,l) + Byf(j,k,l)); #endif @@ -408,6 +411,7 @@ void warpx_interp_nd_bfield_z (int j, int k, int l, { #if (AMREX_SPACEDIM == 2) Bza(j,k,0) = 0.5*(Bzf(j-1,k,0) + Bzf(j,k,0)); + amrex::ignore_unused(l); #else Bza(j,k,l) = 0.25*(Bzf(j-1,k-1,l) + Bzf(j,k-1,l) + Bzf(j-1,k,l) + Bzf(j,k,l)); #endif @@ -632,6 +636,7 @@ void warpx_interp_nd_efield_y (int j, int k, int l, { #if (AMREX_SPACEDIM == 2) Eya(j,k,0) = Eyf(j,k,0); + amrex::ignore_unused(l); #else Eya(j,k,l) = 0.5*(Eyf(j,k-1,l) + Eyf(j,k,l)); #endif @@ -644,6 +649,7 @@ void warpx_interp_nd_efield_z (int j, int k, int l, { #if (AMREX_SPACEDIM == 2) Eza(j,k,0) = 0.5*(Ezf(j,k-1,0) + Ezf(j,k,0)); + amrex::ignore_unused(l); #else Eza(j,k,l) = 0.5*(Ezf(j,k,l-1) + Ezf(j,k,l)); #endif diff --git a/Source/Parallelization/WarpXRegrid.cpp b/Source/Parallelization/WarpXRegrid.cpp index 6c6710121f5..374948aa8ef 100644 --- a/Source/Parallelization/WarpXRegrid.cpp +++ b/Source/Parallelization/WarpXRegrid.cpp @@ -58,8 +58,8 @@ WarpX::LoadBalance () // As specified in the above calls to makeSFC and makeKnapSack, the new // distribution mapping is NOT communicated to all ranks; the loadbalanced // dm is up-to-date only on root, and we can decide whether to broadcast - if (load_balance_efficiency_ratio_threshold > 0.0 - & ParallelDescriptor::MyProc() == ParallelDescriptor::IOProcessorNumber()) + if ((load_balance_efficiency_ratio_threshold > 0.0) + && (ParallelDescriptor::MyProc() == ParallelDescriptor::IOProcessorNumber())) { doLoadBalance = (proposedEfficiency > load_balance_efficiency_ratio_threshold*currentEfficiency); } @@ -299,8 +299,8 @@ WarpX::ComputeCostsHeuristic (amrex::Vector #include #include - +#include // When compiled for CPU, wrap WarpXParser and enable threading. // When compiled for GPU, store one copy of the parser in -// CUDA managed memory for __device__ code, and one copy of the parser -// in CUDA managed memory for __host__ code. This way, the parser can be +// device memory for __device__ code, and one copy of the parser +// in host memory for __host__ code. This way, the parser can be // efficiently called from both host and device. template class GpuParser { public: GpuParser (WarpXParser const& wp); + + GpuParser (GpuParser const&) = delete; + GpuParser (GpuParser &&) = delete; + void operator= (GpuParser const&) = delete; + void operator= (GpuParser &&) = delete; + void clear (); template @@ -37,10 +43,10 @@ public: amrex::GpuArray l_var{var...}; #if AMREX_DEVICE_COMPILE // WarpX compiled for GPU, function compiled for __device__ - return wp_ast_eval(m_gpu_parser.ast, l_var.data()); + return wp_ast_eval<0>(m_gpu_parser_ast, l_var.data()); #else // WarpX compiled for GPU, function compiled for __host__ - return wp_ast_eval(m_cpu_parser->ast, nullptr); + return wp_ast_eval<0>(m_cpu_parser->ast, nullptr); #endif #else @@ -51,16 +57,17 @@ public: int tid = 0; #endif m_var[tid] = amrex::GpuArray{var...}; - return wp_ast_eval(m_parser[tid]->ast, nullptr); + return wp_ast_eval<0>(m_parser[tid]->ast, nullptr); #endif } + void init_gpu_parser (WarpXParser const& wp); // public for CUDA -private: +protected: #ifdef AMREX_USE_GPU // Copy of the parser running on __device__ - struct wp_parser m_gpu_parser; + struct wp_node* m_gpu_parser_ast; // Copy of the parser running on __host__ struct wp_parser* m_cpu_parser; mutable amrex::GpuArray m_var; @@ -75,20 +82,11 @@ private: template GpuParser::GpuParser (WarpXParser const& wp) { + AMREX_ALWAYS_ASSERT(wp.depth() <= WARPX_PARSER_DEPTH); + #ifdef AMREX_USE_GPU struct wp_parser* a_wp = wp.m_parser; - // Initialize GPU parser: allocate memory in CUDA managed memory, - // copy all data needed on GPU to m_gpu_parser - m_gpu_parser.sz_mempool = wp_ast_size(a_wp->ast); - m_gpu_parser.p_root = (struct wp_node*) - amrex::The_Managed_Arena()->alloc(m_gpu_parser.sz_mempool); - m_gpu_parser.p_free = m_gpu_parser.p_root; - // 0: don't free the source - m_gpu_parser.ast = wp_parser_ast_dup(&m_gpu_parser, a_wp->ast, 0); - for (int i = 0; i < N; ++i) { - wp_parser_regvar_gpu(&m_gpu_parser, wp.m_varnames[i].c_str(), i); - } // Initialize CPU parser: m_cpu_parser = wp_parser_dup(a_wp); @@ -96,6 +94,9 @@ GpuParser::GpuParser (WarpXParser const& wp) wp_parser_regvar(m_cpu_parser, wp.m_varnames[i].c_str(), &m_var[i]); } + // Initialize GPU parser + init_gpu_parser(wp); + #else // not defined AMREX_USE_GPU #ifdef _OPENMP @@ -125,13 +126,44 @@ GpuParser::GpuParser (WarpXParser const& wp) #endif // AMREX_USE_GPU } +template +void GpuParser::init_gpu_parser (WarpXParser const& wp) +{ +#ifdef AMREX_USE_GPU + + // We create a temporary Parser on CPU for memcpy. We cannot use + // m_cpu_parser for this because the variables in m_cpu_parser are + // registered for CPU use. + struct wp_parser* cpu_tmp = wp_parser_dup(m_cpu_parser); + for (int i = 0; i < N; ++i) { + wp_parser_regvar_gpu(cpu_tmp, wp.m_varnames[i].c_str(), i); + } + + m_gpu_parser_ast = (struct wp_node*) + amrex::The_Arena()->alloc(cpu_tmp->sz_mempool); + amrex::Gpu::htod_memcpy_async(m_gpu_parser_ast, cpu_tmp->ast, cpu_tmp->sz_mempool); + + auto dp = m_gpu_parser_ast; + char* droot = (char*)dp; + char* croot = (char*)(cpu_tmp->ast); + amrex::single_task([=] AMREX_GPU_DEVICE () noexcept + { + wp_ast_update_device_ptr<0>(dp, droot, croot); + }); + + amrex::Gpu::synchronize(); + + wp_parser_delete(cpu_tmp); +#endif + amrex::ignore_unused(wp); +} template void GpuParser::clear () { #ifdef AMREX_USE_GPU - amrex::The_Managed_Arena()->free(m_gpu_parser.ast); + amrex::The_Arena()->free(m_gpu_parser_ast); wp_parser_delete(m_cpu_parser); #else for (int tid = 0; tid < nthreads; ++tid) diff --git a/Source/Parser/WarpXParser.H b/Source/Parser/WarpXParser.H index 703b1effc29..713402f77fe 100644 --- a/Source/Parser/WarpXParser.H +++ b/Source/Parser/WarpXParser.H @@ -52,6 +52,8 @@ public: void print () const; + int depth () const; + std::string const& expr () const; std::set symbols () const; @@ -84,9 +86,9 @@ amrex::Real WarpXParser::eval () const noexcept { #ifdef _OPENMP - return wp_ast_eval(m_parser[omp_get_thread_num()]->ast,nullptr); + return wp_ast_eval<0>(m_parser[omp_get_thread_num()]->ast,nullptr); #else - return wp_ast_eval(m_parser->ast,nullptr); + return wp_ast_eval<0>(m_parser->ast,nullptr); #endif } diff --git a/Source/Parser/WarpXParser.cpp b/Source/Parser/WarpXParser.cpp index dd000792b5a..38350ab3ccd 100644 --- a/Source/Parser/WarpXParser.cpp +++ b/Source/Parser/WarpXParser.cpp @@ -100,7 +100,7 @@ WarpXParser::registerVariables (std::vector const& names) const int tid = omp_get_thread_num(); struct wp_parser* p = m_parser[tid]; auto& v = m_variables[tid]; - for (int j = 0; j < names.size(); ++j) { + for (int j = 0; j < static_cast(names.size()); ++j) { wp_parser_regvar(p, names[j].c_str(), &(v[j])); m_varnames[tid].push_back(names[j]); } @@ -146,6 +146,18 @@ WarpXParser::print () const #endif } +int +WarpXParser::depth () const +{ + int n = 0; +#ifdef _OPENMP + wp_ast_depth(m_parser[omp_get_thread_num()]->ast, &n); +#else + wp_ast_depth(m_parser->ast, &n); +#endif + return n; +} + std::string const& WarpXParser::expr () const { diff --git a/Source/Parser/WarpXParserWrapper.H b/Source/Parser/WarpXParserWrapper.H index 608ba30d5db..bcd222910ec 100644 --- a/Source/Parser/WarpXParserWrapper.H +++ b/Source/Parser/WarpXParserWrapper.H @@ -1,4 +1,4 @@ -/* Copyright 2020 Revathi Jambunathan +/* Copyright 2020 Revathi Jambunathan, Weiqun Zhang * * This file is part of WarpX. * @@ -12,6 +12,45 @@ #include +#include + +/** + * \brief + * The HostDeviceParser struct is non-owning and is suitable for being + * value captured by device lamba. + */ + +template +struct HostDeviceParser +{ + template + AMREX_GPU_HOST_DEVICE + std::enable_if_t::value, + amrex::Real> + operator() (Ts... var) const noexcept + { +#ifdef AMREX_USE_GPU +#if AMREX_DEVICE_COMPILE +// WarpX compiled for GPU, function compiled for __device__ + amrex::GpuArray l_var{var...}; + return wp_ast_eval<0>(m_gpu_parser_ast, l_var.data()); +#else +// WarpX compiled for GPU, function compiled for __host__ + return (*m_gpu_parser)(var...); +#endif +#else +// WarpX compiled for CPU + return (*m_gpu_parser)(var...); +#endif + } + +#ifdef AMREX_USE_GPU + struct wp_node * m_gpu_parser_ast = nullptr; +#endif + GpuParser const* m_gpu_parser = nullptr; +}; + /** * \brief * The ParserWrapper struct is constructed to safely use the GpuParser class @@ -22,7 +61,7 @@ */ template struct ParserWrapper - : public amrex::Gpu::Managed, public GpuParser + : public GpuParser { using GpuParser::GpuParser; @@ -30,6 +69,28 @@ struct ParserWrapper void operator= (ParserWrapper const&) = delete; ~ParserWrapper() { GpuParser::clear(); } + + void operator() () const = delete; // Hide GpuParser::operator() + + HostDeviceParser getParser () const { +#ifdef AMREX_USE_GPU + return HostDeviceParser{this->m_gpu_parser_ast, static_cast const*>(this)}; +#else + return HostDeviceParser{static_cast const*>(this)}; +#endif + } }; +template +HostDeviceParser getParser (ParserWrapper const* parser_wrapper) +{ + return parser_wrapper ? parser_wrapper->getParser() : HostDeviceParser{}; +} + +template +HostDeviceParser getParser (std::unique_ptr > const& parser_wrapper) +{ + return parser_wrapper ? parser_wrapper->getParser() : HostDeviceParser{}; +} + #endif diff --git a/Source/Parser/wp_parser.l b/Source/Parser/wp_parser.l index 8e70203cbf4..f1d0fb59ed4 100644 --- a/Source/Parser/wp_parser.l +++ b/Source/Parser/wp_parser.l @@ -69,6 +69,6 @@ EXP ([Ee][-+]?[0-9]+) "\n" { return EOL; } /* everything else */ -. { yyerror("Unknow character %c\n", *yytext); } +. { yyerror("Unknown character %c\n", *yytext); } %% diff --git a/Source/Parser/wp_parser.lex.cpp b/Source/Parser/wp_parser.lex.cpp index 9a417e926f5..68a6dd75fc4 100644 --- a/Source/Parser/wp_parser.lex.cpp +++ b/Source/Parser/wp_parser.lex.cpp @@ -960,7 +960,7 @@ YY_RULE_SETUP /* everything else */ case 46: YY_RULE_SETUP -{ yyerror("Unknow character %c\n", *yytext); } +{ yyerror("Unknown character %c\n", *yytext); } YY_BREAK case 47: YY_RULE_SETUP diff --git a/Source/Parser/wp_parser_c.h b/Source/Parser/wp_parser_c.h index 182df712102..970d14e2f60 100644 --- a/Source/Parser/wp_parser_c.h +++ b/Source/Parser/wp_parser_c.h @@ -2,35 +2,117 @@ #define WP_PARSER_C_H_ #include "wp_parser_y.h" + #include +#include #include #include +#include +#include + #include +#include +#include +#include -#ifdef __cplusplus -extern "C" { -#endif +struct wp_parser* wp_c_parser_new (char const* function_body); - struct wp_parser* wp_c_parser_new (char const* function_body); +#ifdef AMREX_USE_GPU -#ifdef __cplusplus -} +template = 0> +AMREX_GPU_DEVICE AMREX_NO_INLINE +void wp_ast_update_device_ptr (struct wp_node* node, char* droot, char* hroot) +{ + switch (node->type) + { + case WP_NUMBER: + break; + case WP_SYMBOL: + { + auto p = (struct wp_symbol*)node; + p->name = droot + (p->name - hroot); + break; + } + case WP_ADD: + case WP_SUB: + case WP_MUL: + case WP_DIV: + { + node->l = (wp_node*)(droot + ((char*)node->l - hroot)); + node->r = (wp_node*)(droot + ((char*)node->r - hroot)); + wp_ast_update_device_ptr(node->l, droot, hroot); + wp_ast_update_device_ptr(node->r, droot, hroot); + break; + } + case WP_NEG: + { + node->l = (wp_node*)(droot + ((char*)node->l - hroot)); + wp_ast_update_device_ptr(node->l, droot, hroot); + break; + } + case WP_F1: + { + auto p = (struct wp_f1*)node; + p->l = (wp_node*)(droot + ((char*)p->l - hroot)); + wp_ast_update_device_ptr(p->l, droot, hroot); + break; + } + case WP_F2: + { + auto p = (struct wp_f2*)node; + p->l = (wp_node*)(droot + ((char*)p->l - hroot)); + p->r = (wp_node*)(droot + ((char*)p->r - hroot)); + wp_ast_update_device_ptr(p->l, droot, hroot); + wp_ast_update_device_ptr(p->r, droot, hroot); + break; + } + case WP_ADD_VP: + case WP_ADD_PP: + case WP_SUB_VP: + case WP_SUB_PP: + case WP_MUL_VP: + case WP_MUL_PP: + case WP_DIV_VP: + case WP_DIV_PP: + case WP_NEG_P: + { + // No need to update node->l and node->r that contain a string for + // variable name because we don't need them for device code. + break; + } + default: + { +#if AMREX_DEVICE_COMPILE + AMREX_DEVICE_PRINTF("wp_ast_update_device_ptr: unknown node type %d\n", + static_cast(node->type)); + amrex::Abort(); #endif + } + } +} -#ifdef __cplusplus +template = 0> +AMREX_GPU_DEVICE AMREX_NO_INLINE +void wp_ast_update_device_ptr (struct wp_node*, char*, char*) +{ +#if AMREX_DEVICE_COMPILE + AMREX_DEVICE_PRINTF("wp_ast_update_device_ptr: WARPX_PARSER_DEPTH %d not big enough\n", + WARPX_PARSER_DEPTH); + amrex::Abort(); +#endif +} -#include -#include +#endif +template = 0> AMREX_GPU_HOST_DEVICE -inline amrex_real -wp_ast_eval (struct wp_node* node, amrex_real const* x) +#ifdef AMREX_USE_GPU +AMREX_NO_INLINE +#endif +amrex::Real +wp_ast_eval (struct wp_node* node, amrex::Real const* x) { -#if defined(__SYCL_DEVICE_ONLY__) - assert(0); // Recursive funciton is not support in DPC++ - return 0.; -#else - amrex_real result; + amrex::Real result = 0.0; switch (node->type) { @@ -41,7 +123,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_SYMBOL: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i =((struct wp_symbol*)node)->ip.i; result = x[i]; #else @@ -51,45 +133,45 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_ADD: { - result = wp_ast_eval(node->l,x) + wp_ast_eval(node->r,x); + result = wp_ast_eval(node->l,x) + wp_ast_eval(node->r,x); break; } case WP_SUB: { - result = wp_ast_eval(node->l,x) - wp_ast_eval(node->r,x); + result = wp_ast_eval(node->l,x) - wp_ast_eval(node->r,x); break; } case WP_MUL: { - result = wp_ast_eval(node->l,x) * wp_ast_eval(node->r,x); + result = wp_ast_eval(node->l,x) * wp_ast_eval(node->r,x); break; } case WP_DIV: { - result = wp_ast_eval(node->l,x) / wp_ast_eval(node->r,x); + result = wp_ast_eval(node->l,x) / wp_ast_eval(node->r,x); break; } case WP_NEG: { - result = -wp_ast_eval(node->l,x); + result = -wp_ast_eval(node->l,x); break; } case WP_F1: { result = wp_call_f1(((struct wp_f1*)node)->ftype, - wp_ast_eval(((struct wp_f1*)node)->l,x)); + wp_ast_eval(((struct wp_f1*)node)->l,x)); break; } case WP_F2: { result = wp_call_f2(((struct wp_f2*)node)->ftype, - wp_ast_eval(((struct wp_f2*)node)->l,x), - wp_ast_eval(((struct wp_f2*)node)->r,x)); + wp_ast_eval(((struct wp_f2*)node)->l,x), + wp_ast_eval(((struct wp_f2*)node)->r,x)); break; } case WP_ADD_VP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->rip.i; result = node->lvp.v + x[i]; #else @@ -99,7 +181,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_ADD_PP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->lvp.ip.i; int j = node->rip.i; result = x[i] + x[j]; @@ -110,7 +192,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_SUB_VP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->rip.i; result = node->lvp.v - x[i]; #else @@ -120,7 +202,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_SUB_PP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->lvp.ip.i; int j = node->rip.i; result = x[i] - x[j]; @@ -131,7 +213,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_MUL_VP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->rip.i; result = node->lvp.v * x[i]; #else @@ -141,7 +223,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_MUL_PP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->lvp.ip.i; int j = node->rip.i; result = x[i] * x[j]; @@ -152,7 +234,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_DIV_VP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->rip.i; result = node->lvp.v / x[i]; #else @@ -162,7 +244,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_DIV_PP: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->lvp.ip.i; int j = node->rip.i; result = x[i] / x[j]; @@ -173,7 +255,7 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) } case WP_NEG_P: { -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) +#if AMREX_DEVICE_COMPILE int i = node->rip.i; result = -x[i]; #else @@ -182,11 +264,35 @@ wp_ast_eval (struct wp_node* node, amrex_real const* x) break; } default: - yyerror("wp_ast_eval: unknown node type %d\n", node->type); + { +#if AMREX_DEVICE_COMPILE + AMREX_DEVICE_PRINTF("wp_ast_eval: unknown node type %d\n", node->type); +#else + amrex::AllPrint() << "wp_ast_eval: unknown node type " << node->type << "\n"; +#endif + } } return result; +} + +template = 0> +AMREX_GPU_HOST_DEVICE +#ifdef AMREX_USE_GPU +AMREX_NO_INLINE #endif +amrex::Real +wp_ast_eval (struct wp_node* node, amrex::Real const* x) +{ +#if AMREX_DEVICE_COMPILE + AMREX_DEVICE_PRINTF("wp_ast_eval: WARPX_PARSER_DEPTH %d not big enough\n", + WARPX_PARSER_DEPTH); +#else + amrex::AllPrint() << "wp_ast_eval: WARPX_PARSER_DEPTH" << WARPX_PARSER_DEPTH + << "not big enough\n"; +#endif + amrex::ignore_unused(node, x); + return 0.; } inline @@ -229,10 +335,8 @@ wp_ast_get_symbols (struct wp_node* node, std::set& symbols) wp_ast_get_symbols(node->r, symbols); break; default: - yyerror("wp_ast_get_symbols: unknown node type %d\n", node->type); + amrex::AllPrint() << "wp_ast_get_symbols: unknown node type " << node->type << "\n"; } } #endif - -#endif diff --git a/Source/Parser/wp_parser_y.cpp b/Source/Parser/wp_parser_y.cpp index 57293ab87b9..0095d0170b1 100644 --- a/Source/Parser/wp_parser_y.cpp +++ b/Source/Parser/wp_parser_y.cpp @@ -1,10 +1,6 @@ -#include -#include -#include -#include -#include #include "wp_parser_y.h" #include "wp_parser.tab.h" +#include static struct wp_node* wp_root = NULL; @@ -21,7 +17,7 @@ wp_defexpr (struct wp_node* body) struct wp_node* wp_newnumber (amrex_real d) { - struct wp_number* r = (struct wp_number*) malloc(sizeof(struct wp_number)); + struct wp_number* r = (struct wp_number*) std::malloc(sizeof(struct wp_number)); r->type = WP_NUMBER; r->value = d; return (struct wp_node*) r; @@ -30,10 +26,10 @@ wp_newnumber (amrex_real d) struct wp_symbol* wp_makesymbol (char* name) { - struct wp_symbol* symbol = (struct wp_symbol*) malloc(sizeof(struct wp_symbol)); + struct wp_symbol* symbol = (struct wp_symbol*) std::malloc(sizeof(struct wp_symbol)); symbol->type = WP_SYMBOL; symbol->name = strdup(name); - symbol->ip.p = NULL; + symbol->ip.p = nullptr; return symbol; } @@ -46,7 +42,7 @@ wp_newsymbol (struct wp_symbol* symbol) struct wp_node* wp_newnode (enum wp_node_t type, struct wp_node* l, struct wp_node* r) { - struct wp_node* tmp = (struct wp_node*) malloc(sizeof(struct wp_node)); + struct wp_node* tmp = (struct wp_node*) std::malloc(sizeof(struct wp_node)); tmp->type = type; tmp->l = l; tmp->r = r; @@ -56,7 +52,7 @@ wp_newnode (enum wp_node_t type, struct wp_node* l, struct wp_node* r) struct wp_node* wp_newf1 (enum wp_f1_t ftype, struct wp_node* l) { - struct wp_f1* tmp = (struct wp_f1*) malloc(sizeof(struct wp_f1)); + struct wp_f1* tmp = (struct wp_f1*) std::malloc(sizeof(struct wp_f1)); tmp->type = WP_F1; tmp->l = l; tmp->ftype = ftype; @@ -66,7 +62,7 @@ wp_newf1 (enum wp_f1_t ftype, struct wp_node* l) struct wp_node* wp_newf2 (enum wp_f2_t ftype, struct wp_node* l, struct wp_node* r) { - struct wp_f2* tmp = (struct wp_f2*) malloc(sizeof(struct wp_f2)); + struct wp_f2* tmp = (struct wp_f2*) std::malloc(sizeof(struct wp_f2)); tmp->type = WP_F2; tmp->l = l; tmp->r = r; @@ -74,19 +70,13 @@ wp_newf2 (enum wp_f2_t ftype, struct wp_node* l, struct wp_node* r) return (struct wp_node*) tmp; } -AMREX_GPU_HOST_DEVICE void yyerror (char const *s, ...) { - va_list vl; + std::va_list vl; va_start(vl, s); -#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) - printf(s,"\n"); - assert(0); -#else - vfprintf(stderr, s, vl); - fprintf(stderr, "\n"); -#endif + std::vfprintf(stderr, s, vl); + std::fprintf(stderr, "\n"); va_end(vl); } @@ -95,17 +85,16 @@ yyerror (char const *s, ...) struct wp_parser* wp_parser_new (void) { - struct wp_parser* my_parser = (struct wp_parser*) malloc(sizeof(struct wp_parser)); + struct wp_parser* my_parser = (struct wp_parser*) std::malloc(sizeof(struct wp_parser)); my_parser->sz_mempool = wp_ast_size(wp_root); - my_parser->p_root = malloc(my_parser->sz_mempool); + my_parser->p_root = std::malloc(my_parser->sz_mempool); my_parser->p_free = my_parser->p_root; my_parser->ast = wp_parser_ast_dup(my_parser, wp_root,1); /* 1: free the source wp_root */ if ((char*)my_parser->p_root + my_parser->sz_mempool != (char*)my_parser->p_free) { - yyerror("wp_parser_new: error in memory size"); - exit(1); + amrex::Abort("wp_parser_new: error in memory size"); } wp_ast_optimize(my_parser->ast); @@ -116,8 +105,8 @@ wp_parser_new (void) void wp_parser_delete (struct wp_parser* parser) { - free(parser->p_root); - free(parser); + std::free(parser->p_root); + std::free(parser); } static size_t @@ -141,9 +130,9 @@ wp_parser_allocate (struct wp_parser* my_parser, size_t N) struct wp_parser* wp_parser_dup (struct wp_parser* source) { - struct wp_parser* dest = (struct wp_parser*) malloc(sizeof(struct wp_parser)); + struct wp_parser* dest = (struct wp_parser*) std::malloc(sizeof(struct wp_parser)); dest->sz_mempool = source->sz_mempool; - dest->p_root = malloc(dest->sz_mempool); + dest->p_root = std::malloc(dest->sz_mempool); dest->p_free = dest->p_root; dest->ast = wp_parser_ast_dup(dest, source->ast, 0); /* 0: don't free the source */ @@ -151,76 +140,10 @@ wp_parser_dup (struct wp_parser* source) return dest; } -AMREX_GPU_HOST_DEVICE -amrex_real -wp_call_f1 (enum wp_f1_t type, amrex_real a) -{ - switch (type) { - case WP_SQRT: return sqrt(a); - case WP_EXP: return exp(a); - case WP_LOG: return log(a); - case WP_LOG10: return log10(a); - case WP_SIN: return sin(a); - case WP_COS: return cos(a); - case WP_TAN: return tan(a); - case WP_ASIN: return asin(a); - case WP_ACOS: return acos(a); - case WP_ATAN: return atan(a); - case WP_SINH: return sinh(a); - case WP_COSH: return cosh(a); - case WP_TANH: return tanh(a); - case WP_ABS: return fabs(a); - case WP_POW_M3: return 1.0/(a*a*a); - case WP_POW_M2: return 1.0/(a*a); - case WP_POW_M1: return 1.0/a; - case WP_POW_P1: return a; - case WP_POW_P2: return a*a; - case WP_POW_P3: return a*a*a; - default: - yyerror("wp_call_f1: Unknow function %d", type); - return 0.0; - } -} - -AMREX_GPU_HOST_DEVICE -amrex_real -wp_call_f2 (enum wp_f2_t type, amrex_real a, amrex_real b) -{ - switch (type) { - case WP_POW: - return pow(a,b); - case WP_GT: - return (a > b) ? 1.0 : 0.0; - case WP_LT: - return (a < b) ? 1.0 : 0.0; - case WP_GEQ: - return (a >= b) ? 1.0 : 0.0; - case WP_LEQ: - return (a <= b) ? 1.0 : 0.0; - case WP_EQ: - return (a == b) ? 1.0 : 0.0; - case WP_NEQ: - return (a != b) ? 1.0 : 0.0; - case WP_AND: - return (a && b) ? 1.0 : 0.0; - case WP_OR: - return (a || b) ? 1.0 : 0.0; - case WP_HEAVISIDE: - return (a < 0.0) ? 0.0 : ((a > 0.0) ? 1.0 : b); - case WP_MIN: - return (a < b) ? a : b; - case WP_MAX: - return (a > b) ? a : b; - default: - yyerror("wp_call_f2: Unknow function %d", type); - return 0.0; - } -} - size_t wp_ast_size (struct wp_node* node) { - size_t result; + size_t result = 0; switch (node->type) { @@ -266,8 +189,8 @@ wp_ast_size (struct wp_node* node) + wp_ast_size(node->l); break; default: - yyerror("wp_ast_size: unknown node type %d\n", node->type); - exit(1); + amrex::AllPrint() << "wp_ast_size: unknown node type " <type << "\n"; + amrex::Abort(); } return result; @@ -276,7 +199,7 @@ wp_ast_size (struct wp_node* node) struct wp_node* wp_parser_ast_dup (struct wp_parser* my_parser, struct wp_node* node, int move) { - void* result; + void* result = nullptr; switch (node->type) { @@ -336,18 +259,18 @@ wp_parser_ast_dup (struct wp_parser* my_parser, struct wp_node* node, int move) ((struct wp_node*)result)->l = wp_parser_ast_dup(my_parser, node->l, move); break; default: - yyerror("wp_ast_dup: unknown node type %d\n", node->type); - exit(1); + amrex::AllPrint() << "wp_ast_dup: unknown node type " << node->type << "\n"; + amrex::Abort(); } if (move) { /* Note that we only do this on the original AST. We do not * need to call free for AST stored in wp_parser because the - * memory is not allocated with malloc directly. + * memory is not allocated with std::malloc directly. */ if (node->type == WP_SYMBOL) { - free(((struct wp_symbol*)node)->name); + std::free(((struct wp_symbol*)node)->name); } - free((void*)node); + std::free((void*)node); } return (struct wp_node*)result; } @@ -773,8 +696,8 @@ wp_ast_optimize (struct wp_node* node) } break; default: - yyerror("wp_ast_optimize: unknown node type %d\n", node->type); - exit(1); + amrex::AllPrint() << "wp_ast_optimize: unknown node type " << node->type << "\n"; + amrex::Abort(); } } @@ -784,28 +707,28 @@ wp_ast_print_f1 (struct wp_f1* f1) { wp_ast_print(f1->l); switch (f1->ftype) { - case WP_SQRT: printf("SQRT\n"); break; - case WP_EXP: printf("EXP\n"); break; - case WP_LOG: printf("LOG\n"); break; - case WP_LOG10: printf("LOG10\n"); break; - case WP_SIN: printf("SIN\n"); break; - case WP_COS: printf("COS\n"); break; - case WP_TAN: printf("TAN\n"); break; - case WP_ASIN: printf("ASIN\n"); break; - case WP_ACOS: printf("ACOS\n"); break; - case WP_ATAN: printf("ATAN\n"); break; - case WP_SINH: printf("SINH\n"); break; - case WP_COSH: printf("COSH\n"); break; - case WP_TANH: printf("TANH\n"); break; - case WP_ABS: printf("ABS\n"); break; - case WP_POW_M3: printf("POW(,-3)\n"); break; - case WP_POW_M2: printf("POW(,-2)\n"); break; - case WP_POW_M1: printf("POW(,-1)\n"); break; - case WP_POW_P1: printf("POW(,1)\n"); break; - case WP_POW_P2: printf("POW(,2)\n"); break; - case WP_POW_P3: printf("POW(,3)\n"); break; + case WP_SQRT: std::printf("SQRT\n"); break; + case WP_EXP: std::printf("EXP\n"); break; + case WP_LOG: std::printf("LOG\n"); break; + case WP_LOG10: std::printf("LOG10\n"); break; + case WP_SIN: std::printf("SIN\n"); break; + case WP_COS: std::printf("COS\n"); break; + case WP_TAN: std::printf("TAN\n"); break; + case WP_ASIN: std::printf("ASIN\n"); break; + case WP_ACOS: std::printf("ACOS\n"); break; + case WP_ATAN: std::printf("ATAN\n"); break; + case WP_SINH: std::printf("SINH\n"); break; + case WP_COSH: std::printf("COSH\n"); break; + case WP_TANH: std::printf("TANH\n"); break; + case WP_ABS: std::printf("ABS\n"); break; + case WP_POW_M3: std::printf("POW(,-3)\n"); break; + case WP_POW_M2: std::printf("POW(,-2)\n"); break; + case WP_POW_M1: std::printf("POW(,-1)\n"); break; + case WP_POW_P1: std::printf("POW(,1)\n"); break; + case WP_POW_P2: std::printf("POW(,2)\n"); break; + case WP_POW_P3: std::printf("POW(,3)\n"); break; default: - yyerror("wp_ast+print_f1: Unknow function %d", f1->ftype); + amrex::AllPrint() << "wp_ast+print_f1: Unknow function " << f1->ftype << "\n"; } } @@ -817,43 +740,43 @@ wp_ast_print_f2 (struct wp_f2* f2) wp_ast_print(f2->r); switch (f2->ftype) { case WP_POW: - printf("POW\n"); + std::printf("POW\n"); break; case WP_GT: - printf("GT\n"); + std::printf("GT\n"); break; case WP_LT: - printf("LT\n"); + std::printf("LT\n"); break; case WP_GEQ: - printf("GEQ\n"); + std::printf("GEQ\n"); break; case WP_LEQ: - printf("LEQ\n"); + std::printf("LEQ\n"); break; case WP_EQ: - printf("EQ\n"); + std::printf("EQ\n"); break; case WP_NEQ: - printf("NEQ\n"); + std::printf("NEQ\n"); break; case WP_AND: - printf("AND\n"); + std::printf("AND\n"); break; case WP_OR: - printf("OR\n"); + std::printf("OR\n"); break; case WP_HEAVISIDE: - printf("HEAVISIDE\n"); + std::printf("HEAVISIDE\n"); break; case WP_MIN: - printf("MIN\n"); + std::printf("MIN\n"); break; case WP_MAX: - printf("MAX\n"); + std::printf("MAX\n"); break; default: - yyerror("wp_ast_print_f2: Unknow function %d", f2->ftype); + amrex::AllPrint() << "wp_ast_print_f2: Unknown function " << f2->ftype << "\n"; } } @@ -863,34 +786,34 @@ wp_ast_print (struct wp_node* node) switch (node->type) { case WP_NUMBER: - printf("NUMBER: %.17g\n", ((struct wp_number*)node)->value); + std::printf("NUMBER: %.17g\n", ((struct wp_number*)node)->value); break; case WP_SYMBOL: - printf("VARIABLE: %s\n", ((struct wp_symbol*)node)->name); + std::printf("VARIABLE: %s\n", ((struct wp_symbol*)node)->name); break; case WP_ADD: wp_ast_print(node->l); wp_ast_print(node->r); - printf("ADD\n"); + std::printf("ADD\n"); break; case WP_SUB: wp_ast_print(node->l); wp_ast_print(node->r); - printf("SUB\n"); + std::printf("SUB\n"); break; case WP_MUL: wp_ast_print(node->l); wp_ast_print(node->r); - printf("MUL\n"); + std::printf("MUL\n"); break; case WP_DIV: wp_ast_print(node->l); wp_ast_print(node->r); - printf("DIV\n"); + std::printf("DIV\n"); break; case WP_NEG: wp_ast_print(node->l); - printf("NEG\n"); + std::printf("NEG\n"); break; case WP_F1: wp_ast_print_f1((struct wp_f1*)node); @@ -899,40 +822,104 @@ wp_ast_print (struct wp_node* node) wp_ast_print_f2((struct wp_f2*)node); break; case WP_ADD_VP: - printf("ADD: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); + std::printf("ADD: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); break; case WP_SUB_VP: - printf("SUM: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); + std::printf("SUM: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); break; case WP_MUL_VP: - printf("MUL: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); + std::printf("MUL: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); break; case WP_DIV_VP: - printf("DIV: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); + std::printf("DIV: %.17g %s\n", node->lvp.v, ((struct wp_symbol*)(node->r))->name); break; case WP_NEG_P: - printf("NEG: %s\n", ((struct wp_symbol*)(node->l))->name); + std::printf("NEG: %s\n", ((struct wp_symbol*)(node->l))->name); break; case WP_ADD_PP: - printf("ADD: %s %s\n", ((struct wp_symbol*)(node->l))->name, - ((struct wp_symbol*)(node->r))->name); + std::printf("ADD: %s %s\n", ((struct wp_symbol*)(node->l))->name, + ((struct wp_symbol*)(node->r))->name); break; case WP_SUB_PP: - printf("SUB: %s %s\n", ((struct wp_symbol*)(node->l))->name, - ((struct wp_symbol*)(node->r))->name); + std::printf("SUB: %s %s\n", ((struct wp_symbol*)(node->l))->name, + ((struct wp_symbol*)(node->r))->name); break; case WP_MUL_PP: - printf("MUL: %s %s\n", ((struct wp_symbol*)(node->l))->name, - ((struct wp_symbol*)(node->r))->name); + std::printf("MUL: %s %s\n", ((struct wp_symbol*)(node->l))->name, + ((struct wp_symbol*)(node->r))->name); break; case WP_DIV_PP: - printf("DIV: %s %s\n", ((struct wp_symbol*)(node->l))->name, - ((struct wp_symbol*)(node->r))->name); + std::printf("DIV: %s %s\n", ((struct wp_symbol*)(node->l))->name, + ((struct wp_symbol*)(node->r))->name); break; default: - yyerror("wp_ast_print: unknown node type %d\n", node->type); + amrex::AllPrint() << "wp_ast_print: unknown node type " << node->type << "\n"; + amrex::Abort(); + } +} + +void +wp_ast_depth (struct wp_node* node, int* n) +{ + int nl = 0; + int nr = 0; + switch (node->type) + { + case WP_NUMBER: + break; + case WP_SYMBOL: + break; + case WP_ADD: + wp_ast_depth(node->l, &nl); + wp_ast_depth(node->r, &nr); + break; + case WP_SUB: + wp_ast_depth(node->l, &nl); + wp_ast_depth(node->r, &nr); + break; + case WP_MUL: + wp_ast_depth(node->l, &nl); + wp_ast_depth(node->r, &nr); + break; + case WP_DIV: + wp_ast_depth(node->l, &nl); + wp_ast_depth(node->r, &nr); + break; + case WP_NEG: + wp_ast_depth(node->l, &nl); + break; + case WP_F1: + (*n)++; + wp_ast_depth(((struct wp_f1*)node)->l, &nl); + break; + case WP_F2: + (*n)++; + wp_ast_depth(((struct wp_f2*)node)->l, &nl); + wp_ast_depth(((struct wp_f2*)node)->r, &nr); + break; + case WP_ADD_VP: + break; + case WP_SUB_VP: + break; + case WP_MUL_VP: + break; + case WP_DIV_VP: + break; + case WP_NEG_P: + break; + case WP_ADD_PP: + break; + case WP_SUB_PP: + break; + case WP_MUL_PP: + break; + case WP_DIV_PP: + break; + default: + yyerror("wp_ast_depth: unknown node type %d\n", node->type); exit(1); } + *n += std::max(nl,nr) + 1; } void @@ -985,8 +972,8 @@ wp_ast_regvar (struct wp_node* node, char const* name, amrex_real* p) node->rip.p = ((struct wp_symbol*)(node->r))->ip.p; break; default: - yyerror("wp_ast_regvar: unknown node type %d\n", node->type); - exit(1); + amrex::AllPrint() << "wp_ast_regvar: unknown node type " << node->type << "\n"; + amrex::Abort(); } } @@ -1040,8 +1027,8 @@ wp_ast_regvar_gpu (struct wp_node* node, char const* name, int i) node->rip.i = ((struct wp_symbol*)(node->r))->ip.i; break; default: - yyerror("wp_ast_regvar_gpu: unknown node type %d\n", node->type); - exit(1); + amrex::AllPrint() << "wp_ast_regvar_gpu: unknown node type " << node->type << "\n"; + amrex::Abort(); } } @@ -1091,8 +1078,8 @@ void wp_ast_setconst (struct wp_node* node, char const* name, amrex_real c) wp_ast_setconst(node->r, name, c); break; default: - yyerror("wp_ast_setconst: unknown node type %d\n", node->type); - exit(1); + amrex::AllPrint() << "wp_ast_setconst: unknown node type " << node->type << "\n"; + amrex::Abort(); } } diff --git a/Source/Parser/wp_parser_y.h b/Source/Parser/wp_parser_y.h index d83815090fa..796794039a4 100644 --- a/Source/Parser/wp_parser_y.h +++ b/Source/Parser/wp_parser_y.h @@ -2,14 +2,16 @@ #define WP_PARSER_Y_H_ #include +#include #include +#include +#include -#ifdef __cplusplus #include -extern "C" { -#else -#include -#endif +#include +#include +#include +#include enum wp_f1_t { // Bulit-in functions with one argument WP_SQRT = 1, @@ -132,7 +134,6 @@ struct wp_node* wp_newf1 (enum wp_f1_t ftype, struct wp_node* l); struct wp_node* wp_newf2 (enum wp_f2_t ftype, struct wp_node* l, struct wp_node* r); -AMREX_GPU_HOST_DEVICE void yyerror (char const *s, ...); /*******************************************************************/ @@ -162,15 +163,93 @@ void wp_parser_setconst (struct wp_parser* parser, char const* name, amrex_real void wp_ast_optimize (struct wp_node* node); size_t wp_ast_size (struct wp_node* node); void wp_ast_print (struct wp_node* node); +void wp_ast_depth (struct wp_node* node, int* n); void wp_ast_regvar (struct wp_node* node, char const* name, amrex_real* p); void wp_ast_regvar_gpu (struct wp_node* node, char const* name, int i); void wp_ast_setconst (struct wp_node* node, char const* name, amrex_real c); -AMREX_GPU_HOST_DEVICE amrex_real wp_call_f1 (enum wp_f1_t type, amrex_real a); -AMREX_GPU_HOST_DEVICE amrex_real wp_call_f2 (enum wp_f2_t type, amrex_real a, amrex_real b); - -#ifdef __cplusplus +template ::value,int> = 0> +AMREX_GPU_HOST_DEVICE +#ifdef AMREX_USE_GPU +AMREX_NO_INLINE +#endif +T +wp_call_f1 (enum wp_f1_t type, T a) +{ + switch (type) { + case WP_SQRT: return std::sqrt(a); + case WP_EXP: return std::exp(a); + case WP_LOG: return std::log(a); + case WP_LOG10: return std::log10(a); + case WP_SIN: return std::sin(a); + case WP_COS: return std::cos(a); + case WP_TAN: return std::tan(a); + case WP_ASIN: return std::asin(a); + case WP_ACOS: return std::acos(a); + case WP_ATAN: return std::atan(a); + case WP_SINH: return std::sinh(a); + case WP_COSH: return std::cosh(a); + case WP_TANH: return std::tanh(a); + case WP_ABS: return amrex::Math::abs(a); + case WP_POW_M3: return 1.0/(a*a*a); + case WP_POW_M2: return 1.0/(a*a); + case WP_POW_M1: return 1.0/a; + case WP_POW_P1: return a; + case WP_POW_P2: return a*a; + case WP_POW_P3: return a*a*a; + default: +#if AMREX_DEVICE_COMPILE + AMREX_DEVICE_PRINTF("wp_call_f1: Unknown function %d\n", type); +#else + amrex::AllPrint() << "wp_call_f1: Unknown function " << type << "\n"; +#endif + return 0.0; + } } + + +template ::value,int> = 0> +AMREX_GPU_HOST_DEVICE +#ifdef AMREX_USE_GPU +AMREX_NO_INLINE #endif +T +wp_call_f2 (enum wp_f2_t type, T a, T b) +{ + switch (type) { + case WP_POW: + return std::pow(a,b); + case WP_GT: + return (a > b) ? 1.0 : 0.0; + case WP_LT: + return (a < b) ? 1.0 : 0.0; + case WP_GEQ: + return (a >= b) ? 1.0 : 0.0; + case WP_LEQ: + return (a <= b) ? 1.0 : 0.0; + case WP_EQ: + return (a == b) ? 1.0 : 0.0; + case WP_NEQ: + return (a != b) ? 1.0 : 0.0; + case WP_AND: + return ((a != T(0)) && (b != T(0))) ? 1.0 : 0.0; + case WP_OR: + return ((a != T(0)) || (b != T(0))) ? 1.0 : 0.0; + case WP_HEAVISIDE: + return (a < 0.0) ? 0.0 : ((a > 0.0) ? 1.0 : b); + case WP_MIN: + return (a < b) ? a : b; + case WP_MAX: + return (a > b) ? a : b; + default: +#if AMREX_DEVICE_COMPILE + AMREX_DEVICE_PRINTF("wp_call_f2: Unknown function %d\n", type); +#else + amrex::AllPrint() << "wp_call_f2: Unknown function " << type << "\n"; +#endif + amrex::Abort(); + return 0.0; + } +} #endif diff --git a/Source/Particles/CMakeLists.txt b/Source/Particles/CMakeLists.txt index 0a8abe4eb52..248de496025 100644 --- a/Source/Particles/CMakeLists.txt +++ b/Source/Particles/CMakeLists.txt @@ -13,4 +13,5 @@ add_subdirectory(ElementaryProcess) add_subdirectory(Gather) add_subdirectory(ParticleCreation) #add_subdirectory(Pusher) +add_subdirectory(Resampling) add_subdirectory(Sorting) diff --git a/Source/Particles/Collision/CollisionType.H b/Source/Particles/Collision/CollisionType.H index 96253097359..f1ffbe4397a 100644 --- a/Source/Particles/Collision/CollisionType.H +++ b/Source/Particles/Collision/CollisionType.H @@ -17,6 +17,7 @@ class CollisionType public: int m_species1_index; int m_species2_index; + int m_ndt; bool m_isSameSpecies; amrex::Real m_CoulombLog; @@ -38,7 +39,8 @@ public: int const lev, amrex::MFIter const& mfi, std::unique_ptr& species1, std::unique_ptr& species2, - bool const isSameSpecies, amrex::Real const CoulombLog ); + bool const isSameSpecies, amrex::Real const CoulombLog, + int const ndt ); }; diff --git a/Source/Particles/Collision/CollisionType.cpp b/Source/Particles/Collision/CollisionType.cpp index c1647709295..38825d00370 100644 --- a/Source/Particles/Collision/CollisionType.cpp +++ b/Source/Particles/Collision/CollisionType.cpp @@ -7,17 +7,14 @@ #include "CollisionType.H" #include "ShuffleFisherYates.H" #include "ElasticCollisionPerez.H" -#include +#include "Utils/ParticleUtils.H" +#include "WarpX.H" CollisionType::CollisionType( const std::vector& species_names, std::string const collision_name) { -#if defined WARPX_DIM_RZ - amrex::Abort("Collisions only work in Cartesian geometry for now."); -#endif - // read collision species std::vector collision_species; amrex::ParmParse pp(collision_name); @@ -29,7 +26,11 @@ CollisionType::CollisionType( m_CoulombLog = -1.0; pp.query("CoulombLog", m_CoulombLog); - for (int i=0; i(species_names.size()); i++) { if (species_names[i] == collision_species[0]) { m_species1_index = i; } @@ -51,42 +52,7 @@ using ParticleTileType = WarpXParticleContainer::ParticleTileType; using ParticleBins = DenseBins; using index_type = ParticleBins::index_type; -namespace { - - /* Find the particles and count the particles that are in each cell. - Note that this does *not* rearrange particle arrays */ - ParticleBins - findParticlesInEachCell( int const lev, MFIter const& mfi, - ParticleTileType const& ptile) { - - // Extract particle structures for this tile - int const np = ptile.numParticles(); - ParticleType const* particle_ptr = ptile.GetArrayOfStructs()().data(); - - // Extract box properties - Geometry const& geom = WarpX::GetInstance().Geom(lev); - Box const& cbx = mfi.tilebox(IntVect::TheZeroVector()); //Cell-centered box - const auto lo = lbound(cbx); - const auto dxi = geom.InvCellSizeArray(); - const auto plo = geom.ProbLoArray(); - - // Find particles that are in each cell; - // results are stored in the object `bins`. - ParticleBins bins; - bins.build(np, particle_ptr, cbx, - // Pass lambda function that returns the cell index - [=] AMREX_GPU_HOST_DEVICE (const ParticleType& p) noexcept -> IntVect - { - return IntVect(AMREX_D_DECL( - static_cast((p.pos(0)-plo[0])*dxi[0] - lo.x), - static_cast((p.pos(1)-plo[1])*dxi[1] - lo.y), - static_cast((p.pos(2)-plo[2])*dxi[2] - lo.z))); - }); - - return bins; - } - -} +using namespace ParticleUtils; /** Perform all binary collisions within a tile * @@ -95,13 +61,14 @@ namespace { * @param species1/2 pointer to species container * @param isSameSpecies true if collision is between same species * @param CoulombLog user input Coulomb logrithm + * @param ndt user input number of time stpes between collisions * */ void CollisionType::doCoulombCollisionsWithinTile ( int const lev, MFIter const& mfi, std::unique_ptr& species_1, std::unique_ptr& species_2, - bool const isSameSpecies, Real const CoulombLog ) + bool const isSameSpecies, Real const CoulombLog, int const ndt ) { if ( isSameSpecies ) // species_1 == species_2 @@ -133,15 +100,22 @@ void CollisionType::doCoulombCollisionsWithinTile const Real dt = WarpX::GetInstance().getdt(lev); Geometry const& geom = WarpX::GetInstance().Geom(lev); -#if (AMREX_SPACEDIM == 2) + Box const& cbx = mfi.tilebox(IntVect::TheZeroVector()); //Cell-centered box + const auto lo = lbound(cbx); + const auto hi = ubound(cbx); + int nz = hi.y-lo.y+1; +#if defined WARPX_DIM_XZ auto dV = geom.CellSize(0) * geom.CellSize(1); +#elif defined WARPX_DIM_RZ + auto dr = geom.CellSize(0); + auto dz = geom.CellSize(1); #elif (AMREX_SPACEDIM == 3) auto dV = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2); #endif // Loop over cells - amrex::ParallelFor( n_cells, - [=] AMREX_GPU_DEVICE (int i_cell) noexcept + amrex::ParallelForRNG( n_cells, + [=] AMREX_GPU_DEVICE (int i_cell, amrex::RandomEngine const& engine) noexcept { // The particles from species1 that are in the cell `i_cell` are // given by the `indices_1[cell_start_1:cell_stop_1]` @@ -154,7 +128,14 @@ void CollisionType::doCoulombCollisionsWithinTile { // shuffle ShuffleFisherYates( - indices_1, cell_start_1, cell_half_1 ); + indices_1, cell_start_1, cell_half_1, engine ); + +#if defined WARPX_DIM_RZ + int ri = (i_cell - i_cell%nz) / nz; + auto dV = MathConst::pi*(2.0_rt*ri+1.0_rt)*dr*dr*dz; +#else + amrex::ignore_unused(nz); +#endif // Call the function in order to perform collisions ElasticCollisionPerez( @@ -163,7 +144,7 @@ void CollisionType::doCoulombCollisionsWithinTile indices_1, indices_1, ux_1, uy_1, uz_1, ux_1, uy_1, uz_1, w_1, w_1, q1, q1, m1, m1, Real(-1.0), Real(-1.0), - dt, CoulombLog, dV ); + dt*ndt, CoulombLog, dV, engine ); } } ); @@ -209,15 +190,22 @@ void CollisionType::doCoulombCollisionsWithinTile const Real dt = WarpX::GetInstance().getdt(lev); Geometry const& geom = WarpX::GetInstance().Geom(lev); -#if (AMREX_SPACEDIM == 2) + Box const& cbx = mfi.tilebox(IntVect::TheZeroVector()); //Cell-centered box + const auto lo = lbound(cbx); + const auto hi = ubound(cbx); + int nz = hi.y-lo.y+1; +#if defined WARPX_DIM_XZ auto dV = geom.CellSize(0) * geom.CellSize(1); +#elif defined WARPX_DIM_RZ + auto dr = geom.CellSize(0); + auto dz = geom.CellSize(1); #elif (AMREX_SPACEDIM == 3) auto dV = geom.CellSize(0) * geom.CellSize(1) * geom.CellSize(2); #endif // Loop over cells - amrex::ParallelFor( n_cells, - [=] AMREX_GPU_DEVICE (int i_cell) noexcept + amrex::ParallelForRNG( n_cells, + [=] AMREX_GPU_DEVICE (int i_cell, amrex::RandomEngine const& engine) noexcept { // The particles from species1 that are in the cell `i_cell` are // given by the `indices_1[cell_start_1:cell_stop_1]` @@ -236,8 +224,15 @@ void CollisionType::doCoulombCollisionsWithinTile cell_stop_2 - cell_start_2 >= 1 ) { // shuffle - ShuffleFisherYates(indices_1, cell_start_1, cell_stop_1); - ShuffleFisherYates(indices_2, cell_start_2, cell_stop_2); + ShuffleFisherYates(indices_1, cell_start_1, cell_stop_1, engine); + ShuffleFisherYates(indices_2, cell_start_2, cell_stop_2, engine); + +#if defined WARPX_DIM_RZ + int ri = (i_cell - i_cell%nz) / nz; + auto dV = MathConst::pi*(2.0_rt*ri+1.0_rt)*dr*dr*dz; +#else + amrex::ignore_unused(nz); +#endif // Call the function in order to perform collisions ElasticCollisionPerez( @@ -245,7 +240,7 @@ void CollisionType::doCoulombCollisionsWithinTile indices_1, indices_2, ux_1, uy_1, uz_1, ux_2, uy_2, uz_2, w_1, w_2, q1, q2, m1, m2, Real(-1.0), Real(-1.0), - dt, CoulombLog, dV ); + dt*ndt, CoulombLog, dV, engine ); } } ); diff --git a/Source/Particles/Collision/ComputeTemperature.H b/Source/Particles/Collision/ComputeTemperature.H index 8128e40ec89..50e1f4a4b67 100644 --- a/Source/Particles/Collision/ComputeTemperature.H +++ b/Source/Particles/Collision/ComputeTemperature.H @@ -26,7 +26,7 @@ T_R ComputeTemperature ( T_R vz = T_R(0.0); T_R vs = T_R(0.0); T_R gm = T_R(0.0); T_R us = T_R(0.0); - for (int i = Is; i < Ie; ++i) + for (int i = Is; i < static_cast(Ie); ++i) { us = ( ux[ I[i] ] * ux[ I[i] ] + uy[ I[i] ] * uy[ I[i] ] + diff --git a/Source/Particles/Collision/ElasticCollisionPerez.H b/Source/Particles/Collision/ElasticCollisionPerez.H index e98a76a920a..35f784f57e3 100644 --- a/Source/Particles/Collision/ElasticCollisionPerez.H +++ b/Source/Particles/Collision/ElasticCollisionPerez.H @@ -46,7 +46,8 @@ void ElasticCollisionPerez ( T_R const q1, T_R const q2, T_R const m1, T_R const m2, T_R const T1, T_R const T2, - T_R const dt, T_R const L, T_R const dV) + T_R const dt, T_R const L, T_R const dV, + amrex::RandomEngine const& engine) { int NI1 = I1e - I1s; @@ -69,16 +70,16 @@ void ElasticCollisionPerez ( T_R n1 = T_R(0.0); T_R n2 = T_R(0.0); T_R n12 = T_R(0.0); - for (int i1=I1s; i1(I1e); ++i1) { n1 += w1[ I1[i1] ]; } + for (int i2=I2s; i2(I2e); ++i2) { n2 += w2[ I2[i2] ]; } n1 = n1 / dV; n2 = n2 / dV; { int i1 = I1s; int i2 = I2s; for (int k = 0; k < amrex::max(NI1,NI2); ++k) { n12 += amrex::min( w1[ I1[i1] ], w2[ I2[i2] ] ); - ++i1; if ( i1 == I1e ) { i1 = I1s; } - ++i2; if ( i2 == I2e ) { i2 = I2s; } + ++i1; if ( i1 == static_cast(I1e) ) { i1 = I1s; } + ++i2; if ( i2 == static_cast(I2e) ) { i2 = I2s; } } n12 = n12 / dV; } @@ -101,9 +102,10 @@ void ElasticCollisionPerez ( u2x[ I2[i2] ], u2y[ I2[i2] ], u2z[ I2[i2] ], n1, n2, n12, q1, m1, w1[ I1[i1] ], q2, m2, w2[ I2[i2] ], - dt, L, lmdD); - ++i1; if ( i1 == I1e ) { i1 = I1s; } - ++i2; if ( i2 == I2e ) { i2 = I2s; } + dt, L, lmdD, + engine); + ++i1; if ( i1 == static_cast(I1e) ) { i1 = I1s; } + ++i2; if ( i2 == static_cast(I2e) ) { i2 = I2s; } } } diff --git a/Source/Particles/Collision/ShuffleFisherYates.H b/Source/Particles/Collision/ShuffleFisherYates.H index 614b44d37db..d7a302e3a41 100644 --- a/Source/Particles/Collision/ShuffleFisherYates.H +++ b/Source/Particles/Collision/ShuffleFisherYates.H @@ -17,14 +17,15 @@ template AMREX_GPU_HOST_DEVICE AMREX_INLINE -void ShuffleFisherYates (T_index *array, T_index const is, T_index const ie) +void ShuffleFisherYates (T_index *array, T_index const is, T_index const ie, + amrex::RandomEngine const& engine) { int j; T_index buf; - for (int i = ie-1; i >= is+1; --i) + for (int i = ie-1; i >= static_cast(is+1); --i) { // get random number j: is <= j <= i - j = amrex::Random_int(i-is+1) + is; + j = amrex::Random_int(i-is+1, engine) + is; // swop the ith array element with the jth buf = array[i]; array[i] = array[j]; diff --git a/Source/Particles/Collision/UpdateMomentumPerezElastic.H b/Source/Particles/Collision/UpdateMomentumPerezElastic.H index 4b0585f3b05..a2b02a62b18 100644 --- a/Source/Particles/Collision/UpdateMomentumPerezElastic.H +++ b/Source/Particles/Collision/UpdateMomentumPerezElastic.H @@ -32,7 +32,8 @@ void UpdateMomentumPerezElastic ( T_R const n1, T_R const n2, T_R const n12, T_R const q1, T_R const m1, T_R const w1, T_R const q2, T_R const m2, T_R const w2, - T_R const dt, T_R const L, T_R const lmdD) + T_R const dt, T_R const L, T_R const lmdD, + amrex::RandomEngine const& engine) { // If g = u1 - u2 = 0, do not collide. @@ -126,7 +127,7 @@ void UpdateMomentumPerezElastic ( s = amrex::min(s,sp); // Get random numbers - T_R r = amrex::Random(); + T_R r = amrex::Random(engine); // Compute scattering angle T_R cosXs; @@ -138,7 +139,7 @@ void UpdateMomentumPerezElastic ( cosXs = T_R(1.0) + s * std::log(r); // Avoid the bug when r is too small such that cosXs < -1 if ( cosXs >= T_R(-1.0) ) { break; } - r = amrex::Random(); + r = amrex::Random(engine); } } else if ( s > T_R(0.1) && s <= T_R(3.0) ) @@ -161,7 +162,7 @@ void UpdateMomentumPerezElastic ( sinXs = std::sqrt(T_R(1.0) - cosXs*cosXs); // Get random azimuthal angle - T_R const phis = amrex::Random() * T_R(2.0) * MathConst::pi; + T_R const phis = amrex::Random(engine) * T_R(2.0) * MathConst::pi; T_R const cosphis = std::cos(phis); T_R const sinphis = std::sin(phis); @@ -237,7 +238,7 @@ void UpdateMomentumPerezElastic ( } // Rejection method - r = amrex::Random(); + r = amrex::Random(engine); if ( w2 > r*amrex::max(w1, w2) ) { u1x = p1fx / m1; @@ -248,7 +249,7 @@ void UpdateMomentumPerezElastic ( AMREX_ASSERT(!std::isinf(u1x+u1y+u1z+u2x+u2y+u2z)); #endif } - r = amrex::Random(); + r = amrex::Random(engine); if ( w1 > r*amrex::max(w1, w2) ) { u2x = p2fx / m2; diff --git a/Source/Particles/Deposition/ChargeDeposition.H b/Source/Particles/Deposition/ChargeDeposition.H index db5ede67372..18013318834 100644 --- a/Source/Particles/Deposition/ChargeDeposition.H +++ b/Source/Particles/Deposition/ChargeDeposition.H @@ -11,6 +11,8 @@ #include "Particles/Pusher/GetAndSetPosition.H" #include "Particles/ShapeFactors.H" +#include + /* \brief Charge Deposition for thread thread_num * /param GetPosition : A functor for returning the particle position. * \param wp : Pointer to array of particle weights. @@ -99,8 +101,8 @@ void doChargeDepositionShapeN(const GetParticlePosition& GetPosition, // Compute shape factor along x // i: leftmost grid point that the particle touches - amrex::Real sx[depos_order + 1]; - int i; + amrex::Real sx[depos_order + 1] = {0._rt}; + int i = 0; Compute_shape_factor< depos_order > const compute_shape_factor; if (rho_type[0] == NODE) { i = compute_shape_factor(sx, x); @@ -111,8 +113,8 @@ void doChargeDepositionShapeN(const GetParticlePosition& GetPosition, #if (defined WARPX_DIM_3D) // y direction const amrex::Real y = (yp - ymin)*dyi; - amrex::Real sy[depos_order + 1]; - int j; + amrex::Real sy[depos_order + 1] = {0._rt}; + int j = 0; if (rho_type[1] == NODE) { j = compute_shape_factor(sy, y); } else if (rho_type[1] == CELL) { @@ -121,8 +123,8 @@ void doChargeDepositionShapeN(const GetParticlePosition& GetPosition, #endif // z direction const amrex::Real z = (zp - zmin)*dzi; - amrex::Real sz[depos_order + 1]; - int k; + amrex::Real sz[depos_order + 1] = {0._rt}; + int k = 0; if (rho_type[zdir] == NODE) { k = compute_shape_factor(sz, z); } else if (rho_type[zdir] == CELL) { @@ -139,8 +141,9 @@ void doChargeDepositionShapeN(const GetParticlePosition& GetPosition, #if (defined WARPX_DIM_RZ) Complex xy = xy0; // Throughout the following loop, xy takes the value e^{i m theta} for (int imode=1 ; imode < n_rz_azimuthal_modes ; imode++) { - amrex::Gpu::Atomic::Add( &rho_arr(lo.x+i+ix, lo.y+k+iz, 0, 2*imode-1), sx[ix]*sz[iz]*wq*xy.real()); - amrex::Gpu::Atomic::Add( &rho_arr(lo.x+i+ix, lo.y+k+iz, 0, 2*imode ), sx[ix]*sz[iz]*wq*xy.imag()); + // The factor 2 on the weighting comes from the normalization of the modes + amrex::Gpu::Atomic::Add( &rho_arr(lo.x+i+ix, lo.y+k+iz, 0, 2*imode-1), 2._rt*sx[ix]*sz[iz]*wq*xy.real()); + amrex::Gpu::Atomic::Add( &rho_arr(lo.x+i+ix, lo.y+k+iz, 0, 2*imode ), 2._rt*sx[ix]*sz[iz]*wq*xy.imag()); xy = xy*xy0; } #endif @@ -159,6 +162,10 @@ void doChargeDepositionShapeN(const GetParticlePosition& GetPosition, #endif } ); + +#ifndef WARPX_DIM_RZ + amrex::ignore_unused(n_rz_azimuthal_modes); +#endif } #endif // CHARGEDEPOSITION_H_ diff --git a/Source/Particles/Deposition/CurrentDeposition.H b/Source/Particles/Deposition/CurrentDeposition.H index fa895a76556..b116bbb64b9 100644 --- a/Source/Particles/Deposition/CurrentDeposition.H +++ b/Source/Particles/Deposition/CurrentDeposition.H @@ -154,8 +154,8 @@ void doDepositionShapeN(const GetParticlePosition& GetPosition, // Keep these double to avoid bug in single precision double sx_node[depos_order + 1]; double sx_cell[depos_order + 1]; - int j_node; - int j_cell; + int j_node = 0; + int j_cell = 0; Compute_shape_factor< depos_order > const compute_shape_factor; if (jx_type[0] == NODE || jy_type[0] == NODE || jz_type[0] == NODE) { j_node = compute_shape_factor(sx_node, xmid); @@ -184,8 +184,8 @@ void doDepositionShapeN(const GetParticlePosition& GetPosition, const double ymid = (yp - ymin)*dyi - dts2dy*vy; double sy_node[depos_order + 1]; double sy_cell[depos_order + 1]; - int k_node; - int k_cell; + int k_node = 0; + int k_cell = 0; if (jx_type[1] == NODE || jy_type[1] == NODE || jz_type[1] == NODE) { k_node = compute_shape_factor(sy_node, ymid); } @@ -211,8 +211,8 @@ void doDepositionShapeN(const GetParticlePosition& GetPosition, const double zmid = (zp - zmin)*dzi - dts2dz*vz; double sz_node[depos_order + 1]; double sz_cell[depos_order + 1]; - int l_node; - int l_cell; + int l_node = 0; + int l_cell = 0; if (jx_type[zdir] == NODE || jy_type[zdir] == NODE || jz_type[zdir] == NODE) { l_node = compute_shape_factor(sz_node, zmid); } @@ -249,12 +249,12 @@ void doDepositionShapeN(const GetParticlePosition& GetPosition, Complex xy = xy0; // Note that xy is equal to e^{i m theta} for (int imode=1 ; imode < n_rz_azimuthal_modes ; imode++) { // The factor 2 on the weighting comes from the normalization of the modes - amrex::Gpu::Atomic::Add( &jx_arr(lo.x+j_jx+ix, lo.y+l_jx+iz, 0, 2*imode-1), 2.*sx_jx[ix]*sz_jx[iz]*wqx*xy.real()); - amrex::Gpu::Atomic::Add( &jx_arr(lo.x+j_jx+ix, lo.y+l_jx+iz, 0, 2*imode ), 2.*sx_jx[ix]*sz_jx[iz]*wqx*xy.imag()); - amrex::Gpu::Atomic::Add( &jy_arr(lo.x+j_jy+ix, lo.y+l_jy+iz, 0, 2*imode-1), 2.*sx_jy[ix]*sz_jy[iz]*wqy*xy.real()); - amrex::Gpu::Atomic::Add( &jy_arr(lo.x+j_jy+ix, lo.y+l_jy+iz, 0, 2*imode ), 2.*sx_jy[ix]*sz_jy[iz]*wqy*xy.imag()); - amrex::Gpu::Atomic::Add( &jz_arr(lo.x+j_jz+ix, lo.y+l_jz+iz, 0, 2*imode-1), 2.*sx_jz[ix]*sz_jz[iz]*wqz*xy.real()); - amrex::Gpu::Atomic::Add( &jz_arr(lo.x+j_jz+ix, lo.y+l_jz+iz, 0, 2*imode ), 2.*sx_jz[ix]*sz_jz[iz]*wqz*xy.imag()); + amrex::Gpu::Atomic::Add( &jx_arr(lo.x+j_jx+ix, lo.y+l_jx+iz, 0, 2*imode-1), 2._rt*sx_jx[ix]*sz_jx[iz]*wqx*xy.real()); + amrex::Gpu::Atomic::Add( &jx_arr(lo.x+j_jx+ix, lo.y+l_jx+iz, 0, 2*imode ), 2._rt*sx_jx[ix]*sz_jx[iz]*wqx*xy.imag()); + amrex::Gpu::Atomic::Add( &jy_arr(lo.x+j_jy+ix, lo.y+l_jy+iz, 0, 2*imode-1), 2._rt*sx_jy[ix]*sz_jy[iz]*wqy*xy.real()); + amrex::Gpu::Atomic::Add( &jy_arr(lo.x+j_jy+ix, lo.y+l_jy+iz, 0, 2*imode ), 2._rt*sx_jy[ix]*sz_jy[iz]*wqy*xy.imag()); + amrex::Gpu::Atomic::Add( &jz_arr(lo.x+j_jz+ix, lo.y+l_jz+iz, 0, 2*imode-1), 2._rt*sx_jz[ix]*sz_jz[iz]*wqz*xy.real()); + amrex::Gpu::Atomic::Add( &jz_arr(lo.x+j_jz+ix, lo.y+l_jz+iz, 0, 2*imode ), 2._rt*sx_jz[ix]*sz_jz[iz]*wqz*xy.imag()); xy = xy*xy0; } #endif @@ -552,8 +552,9 @@ void doEsirkepovDepositionShapeN (const GetParticlePosition& GetPosition, for (int imode=1 ; imode < n_rz_azimuthal_modes ; imode++) { // The factor 2 comes from the normalization of the modes // The minus sign comes from the different convention with respect to Davidson et al. - const Complex djt_cmplx = -2._rt * I*(i_new-1 + i + xmin*dxi)*wq*invdtdx/(amrex::Real)imode* - (sx_new[i]*sz_new[k]*(xy_new - xy_mid) + sx_old[i]*sz_old[k]*(xy_mid - xy_old)); + const Complex djt_cmplx = -2._rt * I*(i_new-1 + i + xmin*dxi)*wq*invdtdx/(amrex::Real)imode + *(Complex(sx_new[i]*sz_new[k], 0.)*(xy_new - xy_mid) + + Complex(sx_old[i]*sz_old[k], 0.)*(xy_mid - xy_old)); amrex::Gpu::Atomic::Add( &Jy_arr(lo.x+i_new-1+i, lo.y+k_new-1+k, 0, 2*imode-1), djt_cmplx.real()); amrex::Gpu::Atomic::Add( &Jy_arr(lo.x+i_new-1+i, lo.y+k_new-1+k, 0, 2*imode), djt_cmplx.imag()); xy_new = xy_new*xy_new0; @@ -630,6 +631,9 @@ void doVayDepositionShapeN (const GetParticlePosition& GetPosition, const long n_rz_azimuthal_modes) { #if (defined WARPX_DIM_RZ) + amrex::ignore_unused(GetPosition, + wp, uxp, uyp, uzp, ion_lev, jx_fab, jy_fab, jz_fab, + np_to_depose, dt, dx, xyzmin, lo, q, n_rz_azimuthal_modes); amrex::Abort("Vay deposition not implemented in RZ geometry"); #endif diff --git a/Source/Particles/ElementaryProcess/CMakeLists.txt b/Source/Particles/ElementaryProcess/CMakeLists.txt index bc3bff03117..b85caafbc95 100644 --- a/Source/Particles/ElementaryProcess/CMakeLists.txt +++ b/Source/Particles/ElementaryProcess/CMakeLists.txt @@ -3,7 +3,7 @@ target_sources(WarpX Ionization.cpp ) -if(WarpX_HAVE_QED) +if(WarpX_QED) target_sources(WarpX PRIVATE QEDPairGeneration.cpp diff --git a/Source/Particles/ElementaryProcess/Ionization.H b/Source/Particles/ElementaryProcess/Ionization.H index f2f8dc6b055..f02a3c5c0a1 100644 --- a/Source/Particles/ElementaryProcess/Ionization.H +++ b/Source/Particles/ElementaryProcess/Ionization.H @@ -69,7 +69,7 @@ struct IonizationFilterFunc template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - bool operator() (const PData& ptd, int i) const noexcept + bool operator() (const PData& ptd, int i, amrex::RandomEngine const& engine) const noexcept { using namespace amrex::literals; @@ -115,7 +115,7 @@ struct IonizationFilterFunc std::exp( m_adk_exp_prefactor[ion_lev]/E ); amrex::Real p = 1. - std::exp( - w_dtau ); - amrex::Real random_draw = amrex::Random(); + amrex::Real random_draw = amrex::Random(engine); if (random_draw < p) { return true; @@ -129,7 +129,7 @@ struct IonizationTransformFunc { template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - void operator() (DstData& dst, SrcData& src, int i_src, int i_dst) const noexcept + void operator() (DstData& /*dst*/, SrcData& src, int i_src, int /*i_dst*/) const noexcept { src.m_runtime_idata[0][i_src] += 1; } diff --git a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerDummyTable.H b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerDummyTable.H deleted file mode 100644 index 95f65923eaf..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerDummyTable.H +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#ifndef WARPX_breit_wheeler_dummy_tables_h_ -#define WARPX_breit_wheeler_dummy_tables_h_ - -#include "BreitWheelerEngineInnards.H" - -#include - -#include -#include - -namespace QedUtils{ - -//A default mini-table used for test purposes -const struct //BreitWheelerEngineInnardsDummy -{ - picsar::multi_physics::breit_wheeler_engine_ctrl ctrl{ - 0.001, /*chi_phot_min*/ - 0.1, /*chi_phot_tdndt_min*/ - 200, /*chi_phot_tdndt_max*/ - 64, /*chi_phot_tdndt_how_many*/ - 0.01, /*chi_phot_tpair_min*/ - 200, /*chi_phot_tpair_max*/ - 2, /*chi_phot_tpair_how_many*/ - 2 /*chi_frac_tpair_how_many*/ - }; - std::vector TTfunc_coords{ - -2.302585093, -2.181935848, -2.061286602, -1.940637357, - -1.819988111, -1.699338866, -1.578689621, -1.458040375, - -1.33739113, -1.216741884, -1.096092639, -0.9754433937, - -0.8547941483, -0.7341449029, -0.6134956575, -0.4928464122, - -0.3721971668, -0.2515479214, -0.130898676, -0.01024943059, - 0.1103998148, 0.2310490602, 0.3516983056, 0.472347551, - 0.5929967964, 0.7136460417, 0.8342952871, 0.9549445325, - 1.075593778, 1.196243023, 1.316892269, 1.437541514, - 1.558190759, 1.678840005, 1.79948925, 1.920138496, - 2.040787741, 2.161436986, 2.282086232, 2.402735477, - 2.523384723, 2.644033968, 2.764683213, 2.885332459, - 3.005981704, 3.12663095, 3.247280195, 3.36792944, - 3.488578686, 3.609227931, 3.729877176, 3.850526422, - 3.971175667, 4.091824913, 4.212474158, 4.333123403, - 4.453772649, 4.574421894, 4.69507114, 4.815720385, - 4.93636963, 5.057018876, 5.177668121, 5.298317367 - }; - std::vector TTfunc_data{ - -32.75941722, -29.48929687, -26.5638705, -23.94401054, - -21.59504068, -19.48623016, -17.59034545, -15.88325289, - -14.34356639, -12.95233518, -11.69276696, -10.54998244, - -9.510797773, -8.563531596, -7.697833953, -6.90453462, - -6.175508601, -5.503556869, -4.882300625, -4.306087546, - -3.76990867, -3.269324729, -2.800400851, -2.359648711, - -1.943975268, -1.550637379, -1.177201596, -0.8215085915, - -0.4816416657, -0.1558988919, 0.1572315255, 0.4590930403, - 0.7508800304, 1.033654828, 1.308362775, 1.575845526, - 1.836852816, 2.092052851, 2.342041503, 2.587350441, - 2.828454313, 3.065777115, 3.299697807, 3.530555306, - 3.758652908, 3.984262221, 4.207626639, 4.428964367, - 4.648471027, 4.866321957, 5.082674363, 5.297669484, - 5.511434783, 5.724086013, 5.935728862, 6.146459973, - 6.356367276, 6.565529804, 6.774017335, 6.981890203, - 7.189199547, 7.395988084, 7.602291337, 7.808139168 - }; - std::vector cum_distrib_coords_1{ - -4.605170186, 5.298317367 - };//_____________________________ - std::vector cum_distrib_coords_2{ - 0, 0.5 - };//_____________________________ - std::vector cum_distrib_data{ - -std::numeric_limits::infinity(), - -0.6931471806, - -std::numeric_limits::infinity(), - -0.6931471806 - };//_____________________________ -} BreitWheelerEngineInnardsDummy; - -}; - -#endif // WARPX_breit_wheeler_dummy_tables_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineInnards.H b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineInnards.H deleted file mode 100644 index d6c644aa377..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineInnards.H +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#ifndef WARPX_breit_wheeler_engine_innards_h_ -#define WARPX_breit_wheeler_engine_innards_h_ - -#include "QedWrapperCommons.H" - -#include - -//This includes only the definition of a simple datastructure -//used to control the Breit Wheeler engine. -#include - -/** - * This structure holds all the parameters required to use the - * Breit Wheeler engine: a POD control structure and lookup - * tables data. - */ -struct BreitWheelerEngineInnards -{ - // Control parameters (a POD struct) - // ctrl contains several parameters: - // - chi_phot_min : the minium chi parameter to be - // considered by the engine - // - chi_phot_tdndt_min : minimun chi for sub-table 1 (1D) - // - chi_phot_tdndt_max : maximum chi for sub-table 1 (1D) - // - chi_phot_tdndt_how_many : how many points to use for sub-table 1 (1D) - // - chi_phot_tpair_min : minimun chi for sub-table 2 (1D) - // - chi_phot_tpair_max : maximum chi for sub-table 2 (1D) - // - chi_phot_tpair_how_many : how many points to use for chi for sub-table 2 (2D) - // - chi_frac_tpair_how_many : how many points to use for the second axis of sub-table 2 (2D) - picsar::multi_physics::breit_wheeler_engine_ctrl ctrl; - - //Lookup table data - //---sub-table 1 (1D) - amrex::Gpu::ManagedVector TTfunc_coords; - amrex::Gpu::ManagedVector TTfunc_data; - //--- - - //---sub-table 2 (2D) - amrex::Gpu::ManagedVector cum_distrib_coords_1; - amrex::Gpu::ManagedVector cum_distrib_coords_2; - amrex::Gpu::ManagedVector cum_distrib_data; - //______ -}; -//========================================================== - -#endif //WARPX_breit_wheeler_engine_innards_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineTableBuilder.H b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineTableBuilder.H deleted file mode 100644 index 98b0b17a461..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineTableBuilder.H +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#ifndef WARPX_breit_wheeler_engine_table_builder_h_ -#define WARPX_breit_wheeler_engine_table_builder_h_ - -#include "QedWrapperCommons.H" -#include "BreitWheelerEngineInnards.H" - -//This includes only the definition of a simple datastructure -//used to control the Breit Wheeler engine. -#include - -/** - * A class which computes the lookup tables for the Breit Wheeler engine. - */ -class BreitWheelerEngineTableBuilder{ - public: - /** - * Computes the tables. - * @param[in] ctrl control parameters to generate the tables - * @param[out] innards structure holding both a copy of ctrl and lookup tables data - */ - void compute_table - (picsar::multi_physics::breit_wheeler_engine_ctrl ctrl, - BreitWheelerEngineInnards& innards) const; -}; - -#endif //WARPX_breit_wheeler_engine_table_builder_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineTableBuilder.cpp b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineTableBuilder.cpp deleted file mode 100644 index 5f3538079ba..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineTableBuilder.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#include "BreitWheelerEngineTableBuilder.H" - -//Include the full Breit Wheeler engine with table generation support -//(after some consistency tests). This requires to have a recent version -// of the Boost library. -#ifdef PXRMP_CORE_ONLY -# error The Table Builder is incompatible with PXRMP_CORE_ONLY -#endif - -#ifdef __PICSAR_MULTIPHYSICS_BREIT_WHEELER_ENGINE__ -# warning breit_wheeler_engine.hpp should not have been included before reaching this point. -#endif -#include -//_______________________________________________ - -//Some handy aliases -using PicsarBreitWheelerEngine = picsar::multi_physics:: - breit_wheeler_engine; - -using PicsarBreitWheelerCtrl = - picsar::multi_physics::breit_wheeler_engine_ctrl; -//_______________________________________________ - -void -BreitWheelerEngineTableBuilder::compute_table - (PicsarBreitWheelerCtrl ctrl, - BreitWheelerEngineInnards& innards) const -{ - PicsarBreitWheelerEngine bw_engine( - std::move(QedUtils::DummyStruct()), 1.0, ctrl); - - bw_engine.compute_dN_dt_lookup_table(); - bw_engine.compute_cumulative_pair_table(); - - auto bw_innards_picsar = bw_engine.export_innards(); - - //Copy data in a GPU-friendly data-structure - innards.ctrl = bw_innards_picsar.bw_ctrl; - innards.TTfunc_coords.assign(bw_innards_picsar.TTfunc_table_coords_ptr, - bw_innards_picsar.TTfunc_table_coords_ptr + - bw_innards_picsar.TTfunc_table_coords_how_many); - innards.TTfunc_data.assign(bw_innards_picsar.TTfunc_table_data_ptr, - bw_innards_picsar.TTfunc_table_data_ptr + - bw_innards_picsar.TTfunc_table_data_how_many); - innards.cum_distrib_coords_1.assign( - bw_innards_picsar.cum_distrib_table_coords_1_ptr, - bw_innards_picsar.cum_distrib_table_coords_1_ptr + - bw_innards_picsar.cum_distrib_table_coords_1_how_many); - innards.cum_distrib_coords_2.assign( - bw_innards_picsar.cum_distrib_table_coords_2_ptr, - bw_innards_picsar.cum_distrib_table_coords_2_ptr + - bw_innards_picsar.cum_distrib_table_coords_2_how_many); - innards.cum_distrib_data.assign( - bw_innards_picsar.cum_distrib_table_data_ptr, - bw_innards_picsar.cum_distrib_table_data_ptr + - bw_innards_picsar.cum_distrib_table_data_how_many); - //____ -} diff --git a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.H b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.H index 7056ac884d9..a73e2e17f45 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.H +++ b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.H @@ -8,39 +8,49 @@ #define WARPX_breit_wheeler_engine_wrapper_h_ #include "QedWrapperCommons.H" -#include "BreitWheelerEngineInnards.H" +#include "QedChiFunctions.H" +#include "Utils/WarpXConst.H" #include #include #include -//#define PXRMP_CORE_ONLY allows importing only the 'core functions' of the -//Breit Wheeler engine of the QED PICSAR library. -#define PXRMP_CORE_ONLY -#include - -//Lookup table building function is in a dedicated (optional) class to -//avoid including heavy dependencies if they are not needed. -#ifdef WARPX_QED_TABLE_GEN -# include "BreitWheelerEngineTableBuilder.H" -#endif +#include +#include #include +#include + +// Aliases ============================= +using BW_dndt_table_params = + picsar::multi_physics::phys::breit_wheeler:: + dndt_lookup_table_params; + +using BW_dndt_table = + picsar::multi_physics::phys::breit_wheeler:: + dndt_lookup_table< + amrex::Real, + amrex::Gpu::DeviceVector>; + +using BW_dndt_table_view = BW_dndt_table::view_type; + +using BW_pair_prod_table_params = + picsar::multi_physics::phys::breit_wheeler:: + pair_prod_lookup_table_params; -//Some handy aliases +using BW_pair_prod_table = + picsar::multi_physics::phys::breit_wheeler:: + pair_prod_lookup_table< + amrex::Real, + amrex::Gpu::DeviceVector>; -// The engine has two templated arguments: the numerical type -// and a random number generator. However, random numbers are not -// used to generate the lookup tables and the static member -// functions which are called in the functors do not use -// random numbers as well. Therefore, an empty "DummyStruct" -// can be passed as a template parameter. -using PicsarBreitWheelerEngine = picsar::multi_physics:: - breit_wheeler_engine; +using BW_pair_prod_table_view = BW_pair_prod_table::view_type; -using PicsarBreitWheelerCtrl = - picsar::multi_physics::breit_wheeler_engine_ctrl; -//__________ +struct PicsarBreitWheelerCtrl +{ + BW_dndt_table_params dndt_params; + BW_pair_prod_table_params pair_prod_params; +}; // Functors ================================== @@ -68,11 +78,12 @@ public: */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - amrex::Real operator() () const noexcept + amrex::Real operator() (amrex::RandomEngine const& engine) const noexcept { + namespace pxr_bw = picsar::multi_physics::phys::breit_wheeler; + //A random number in [0,1) should be provided as an argument. - return PicsarBreitWheelerEngine:: - internal_get_optical_depth(amrex::Random()); + return pxr_bw::get_optical_depth(amrex::Random(engine)); } }; //____________________________________________ @@ -85,71 +96,76 @@ class BreitWheelerEvolveOpticalDepth { public: - BreitWheelerEvolveOpticalDepth () = default; + /** + * Default constructor: it leaves the functor in a non-initialized state. + */ + BreitWheelerEvolveOpticalDepth (){} + /** - * Constructor acquires a reference to control parameters and - * lookup tables data. - * lookup_table uses non-owning vectors under the hood. So no new data - * allocations should be triggered on GPU + * Constructor to be used to initialize the functor. + * + * @param[in] table_view a view of a BW_dndt_table lookup table + * @param[in] bw_minimum_chi_phot the minimum quantum parameter to evolve the optical depth */ - BreitWheelerEvolveOpticalDepth(BreitWheelerEngineInnards& r_innards): - m_ctrl{r_innards.ctrl}, - m_TTfunc_size{r_innards.TTfunc_coords.size()}, - m_p_TTfunc_coords{r_innards.TTfunc_coords.dataPtr()}, - m_p_TTfunc_data{r_innards.TTfunc_data.dataPtr()} - {}; + BreitWheelerEvolveOpticalDepth ( + const BW_dndt_table_view table_view, + const amrex::ParticleReal bw_minimum_chi_phot): + m_table_view{table_view}, m_bw_minimum_chi_phot{bw_minimum_chi_phot}{}; /** * Evolves the optical depth. It can be used on GPU. - * @param[in] px,py,pz momentum components of the photon (SI units) + * If the quantum parameter parameter of the photon is + * < bw_minimum_chi_phot, the method returns immediately. + * The method returns also if the energy of the photon is insufficient + * to generate a pair. + * + * @param[in] ux,uy,uz gamma*v components of the photon. * @param[in] ex,ey,ez electric field components (SI units) * @param[in] bx,by,bz magnetic field components (SI units) * @param[in] dt timestep (SI units) - * @param[in,out] opt_depth optical depth of the photon. It is modified by the method. - * @return a flag which is 1 if optical depth becomes negative (i.e. a pair has to be generated). + * @param[in,out] opt_depth optical depth of the photon. + * @return a flag which is 1 if chi_phot was out of table */ - AMREX_GPU_HOST_DEVICE + AMREX_GPU_DEVICE AMREX_FORCE_INLINE int operator()( - amrex::Real px, amrex::Real py, amrex::Real pz, - amrex::Real ex, amrex::Real ey, amrex::Real ez, - amrex::Real bx, amrex::Real by, amrex::Real bz, - amrex::Real dt, amrex::Real& opt_depth) const noexcept + const amrex::Real ux, const amrex::Real uy, const amrex::Real uz, + const amrex::Real ex, const amrex::Real ey, const amrex::Real ez, + const amrex::Real bx, const amrex::Real by, const amrex::Real bz, + const amrex::Real dt, amrex::Real& opt_depth) const noexcept { - bool has_event_happened{false}; - - //the library provides the time (< dt) at which the event occurs, but this - //feature won't be used in WarpX for now. - amrex::Real unused_event_time{0.0}; - - PicsarBreitWheelerEngine:: - internal_evolve_opt_depth_and_determine_event( - px, py, pz, - ex, ey, ez, - bx, by, bz, - dt, opt_depth, - has_event_happened, unused_event_time, - m_dummy_lambda, - picsar::multi_physics::lookup_1d{ - m_TTfunc_size, - m_p_TTfunc_coords, - m_p_TTfunc_data}, - m_ctrl); - - return has_event_happened; + namespace pxr_m = picsar::multi_physics::math; + namespace pxr_p = picsar::multi_physics::phys; + namespace pxr_bw = picsar::multi_physics::phys::breit_wheeler; + + constexpr amrex::Real m_e = PhysConst::m_e; + const auto u_norm = std::sqrt(ux*ux + uy*uy + uz*uz); + const auto energy = u_norm*m_e*PhysConst::c; + + const auto chi_phot = QedUtils::chi_photon( + m_e*ux, m_e*uy, m_e*uz, ex, ey, ez, bx, by, bz); + + //Optical depth is not evolved for photons having less energy than what is + //required to generate a pair or a quantum parameter smaller than + //m_bw_minimum_chi_phot + const auto gamma_photon = u_norm/PhysConst::c; + if (gamma_photon < pxr_m::two || + chi_phot < m_bw_minimum_chi_phot) + return 0; + + const auto is_out = pxr_bw::evolve_optical_depth< + amrex::Real, + BW_dndt_table_view, + pxr_p::unit_system::SI>( + energy, chi_phot, dt, opt_depth, m_table_view); + + return is_out; } private: - //laser wavelength is not used with SI units - amrex::Real m_dummy_lambda{1.0}; - - PicsarBreitWheelerCtrl m_ctrl; - - //lookup table data - size_t m_TTfunc_size; - amrex::Real* m_p_TTfunc_coords; - amrex::Real* m_p_TTfunc_data; + BW_dndt_table_view m_table_view; + amrex::ParticleReal m_bw_minimum_chi_phot; }; /** @@ -159,85 +175,89 @@ private: class BreitWheelerGeneratePairs { public: + + /** + * Default constructor: it leaves the functor in a non-initialized state. + */ + BreitWheelerGeneratePairs (){} + /** * Constructor acquires pointers to control parameters and * lookup tables data. * lookup_table uses non-owning vectors under the hood. So no new data * allocations should be triggered on GPU + * + * @param[in] table_view a BW_pair_prod_table_view */ - BreitWheelerGeneratePairs( - BreitWheelerEngineInnards& r_innards): - m_ctrl{r_innards.ctrl}, - m_cum_distrib_coords_1_size{r_innards.cum_distrib_coords_1.size()}, - m_cum_distrib_coords_2_size{r_innards.cum_distrib_coords_2.size()}, - m_p_distrib_coords_1{r_innards.cum_distrib_coords_1.data()}, - m_p_distrib_coords_2{r_innards.cum_distrib_coords_2.data()}, - m_p_cum_distrib_data{r_innards.cum_distrib_data.data() - }{}; + BreitWheelerGeneratePairs (const BW_pair_prod_table_view table_view): + m_table_view{table_view}{}; /** - * Generates sampling (template parameter) pairs according to Breit Wheeler process. + * Generates pairs according to Breit Wheeler process. * It can be used on GPU. - * @param[in] px,py,pz momentum components of the photon (SI units) + * Warning: the energy of the photon must be > 2mec^2, but it is not checked + * in this method. + * + * @param[in] ux,uy,uz gamma*v components of the photon (SI units) * @param[in] ex,ey,ez electric field components (SI units) * @param[in] bx,by,bz magnetic field components (SI units) - * @param[in] weight of the photon (code units) - * @param[out] e_px,e_py,e_pz momenta of generated electrons. Each array should have size=sampling (SI units) - * @param[out] p_px,p_py,p_pz momenta of generated positrons. Each array should have size=sampling (SI units) - * @param[out] e_weight,p_weight weight of the generated particles Each array should have size=sampling (code units). + * @param[out] e_ux,e_uy,e_uz gamma*v components of generated electron (SI units) + * @param[out] p_ux,p_uy,p_uz gamma*v components of generated positron (SI units) + * @return a flag which is 1 if chi_photon was out of table */ - template - AMREX_GPU_HOST_DEVICE + AMREX_GPU_DEVICE AMREX_FORCE_INLINE - void operator()( - amrex::Real px, amrex::Real py, amrex::Real pz, - amrex::Real ex, amrex::Real ey, amrex::Real ez, - amrex::Real bx, amrex::Real by, amrex::Real bz, - amrex::Real weight, - amrex::Real* e_px, amrex::Real* e_py, amrex::Real* e_pz, - amrex::Real* p_px, amrex::Real* p_py, amrex::Real* p_pz, - amrex::Real* e_weight, amrex::Real* p_weight) const noexcept + int operator()( + const amrex::Real ux, const amrex::Real uy, const amrex::Real uz, + const amrex::Real ex, const amrex::Real ey, const amrex::Real ez, + const amrex::Real bx, const amrex::Real by, const amrex::Real bz, + amrex::Real& e_ux, amrex::Real& e_uy, amrex::Real& e_uz, + amrex::Real& p_ux, amrex::Real& p_uy, amrex::Real& p_uz) const noexcept { - //[sampling] random numbers are needed - picsar::multi_physics::picsar_array - rand_zero_one_minus_epsi; - for(auto& el : rand_zero_one_minus_epsi) el = amrex::Random(); - - const auto p_rand = rand_zero_one_minus_epsi.data(); - - PicsarBreitWheelerEngine:: - internal_generate_breit_wheeler_pairs( - px, py, pz, - ex, ey, ez, - bx, by, bz, - weight, sampling, - e_px, e_py, e_pz, - p_px, p_py, p_pz, - e_weight, p_weight, - m_dummy_lambda, - picsar::multi_physics::lookup_2d{ - m_cum_distrib_coords_1_size, - m_p_distrib_coords_1, - m_cum_distrib_coords_2_size, - m_p_distrib_coords_2, - m_p_cum_distrib_data - }, - m_ctrl, - p_rand); + using namespace amrex; + namespace pxr_m = picsar::multi_physics::math; + namespace pxr_p = picsar::multi_physics::phys; + namespace pxr_bw = picsar::multi_physics::phys::breit_wheeler; + + const auto rand_zero_one_minus_epsi = amrex::Random(); + + constexpr ParticleReal me = PhysConst::m_e; + constexpr ParticleReal one_over_me = 1._prt/me; + + // Particle momentum is stored as gamma * velocity. + // Convert to m * gamma * velocity + auto px = ux*me; + auto py = uy*me; + auto pz = uz*me; + + const auto chi_photon = QedUtils::chi_photon( + px, py, pz, ex, ey, ez, bx, by, bz); + + const auto momentum_photon = pxr_m::vec3{px, py, pz}; + auto momentum_ele = pxr_m::vec3(); + auto momentum_pos = pxr_m::vec3(); + + const auto is_out = pxr_bw::generate_breit_wheeler_pairs< + amrex::Real, + BW_pair_prod_table_view, + pxr_p::unit_system::SI>( + chi_photon, momentum_photon, + rand_zero_one_minus_epsi, + m_table_view, + momentum_ele, momentum_pos); + + e_ux = momentum_ele[0]*one_over_me; + e_uy = momentum_ele[1]*one_over_me; + e_uz = momentum_ele[2]*one_over_me; + p_ux = momentum_pos[0]*one_over_me; + p_uy = momentum_pos[1]*one_over_me; + p_uz = momentum_pos[2]*one_over_me; + + return is_out; } private: - //laser wavelenght is not used with SI units - const amrex::Real m_dummy_lambda{1.0}; - - const PicsarBreitWheelerCtrl m_ctrl; - - //lookup table data - size_t m_cum_distrib_coords_1_size; - size_t m_cum_distrib_coords_2_size; - amrex::Real* m_p_distrib_coords_1; - amrex::Real* m_p_distrib_coords_2; - amrex::Real* m_p_cum_distrib_data; + BW_pair_prod_table_view m_table_view; }; // Factory class ============================= @@ -256,17 +276,17 @@ public: /** * Builds the functor to initialize the optical depth */ - BreitWheelerGetOpticalDepth build_optical_depth_functor (); + BreitWheelerGetOpticalDepth build_optical_depth_functor () const; /** * Builds the functor to evolve the optical depth */ - BreitWheelerEvolveOpticalDepth build_evolve_functor (); + BreitWheelerEvolveOpticalDepth build_evolve_functor () const; /** * Builds the functor to generate the pairs */ - BreitWheelerGeneratePairs build_pair_functor (); + BreitWheelerGeneratePairs build_pair_functor () const; /** * Checks if the optical tables are properly initialized @@ -274,52 +294,62 @@ public: bool are_lookup_tables_initialized () const; /** - * Init lookup tables from raw binary data. - * @param[in] raw_data a Vector of char - * @return true if it succeeds, false if it cannot parse raw_data + * Export lookup tables data into a raw binary Vector + * + * @return the data in binary format. The Vector is empty if tables were + * not previously initialized. */ - bool init_lookup_tables_from_raw_data (const amrex::Vector& raw_data); + std::vector export_lookup_tables_data () const; /** - * Init lookup tables using built-in dummy tables - * for test purposes. + * Init lookup tables from raw binary data. + * + * @param[in] raw_data a vector of char + * @param[in] bw_minimum_chi_phot minimum chi parameter to evolve the optical depth of a photon + * @return true if it succeeds, false if it cannot parse raw_data */ - void init_dummy_tables(); + bool init_lookup_tables_from_raw_data ( + const std::vector& raw_data, + const amrex::Real bw_minimum_chi_phot); /** - * Export lookup tables data into a raw binary Vector - * @return the data in binary format. The Vector is empty if tables were - * not previously initialized. + * Init lookup tables using built-in (low resolution) tables + * + * @param[in] bw_minimum_chi_phot minimum chi parameter to evolve the optical depth of a photon */ - amrex::Vector export_lookup_tables_data () const; + void init_builtin_tables(const amrex::Real bw_minimum_chi_phot); /** * Computes the lookup tables. It does nothing unless WarpX is compiled with QED_TABLE_GEN=TRUE + * * @param[in] ctrl control params to generate the tables + * @param[in] bw_minimum_chi_phot minimum chi parameter to evolve the optical depth of a photon */ - void compute_lookup_tables (PicsarBreitWheelerCtrl ctrl); + void compute_lookup_tables (const PicsarBreitWheelerCtrl ctrl, + const amrex::Real bw_minimum_chi_phot); /** - * gets default (reasonable) values for the control parameters + * gets default values for the control parameters + * * @return default control params to generate the tables */ PicsarBreitWheelerCtrl get_default_ctrl() const; - /** - * returns a constant reference to the control parameters - * @return const reference to control parameters - */ - const PicsarBreitWheelerCtrl& get_ref_ctrl() const; + amrex::Real get_minimum_chi_phot() const; private: bool m_lookup_tables_initialized = false; - BreitWheelerEngineInnards m_innards; + //Variables to store the minimum chi parameters to enable + //Quantum Synchrotron process + amrex::Real m_bw_minimum_chi_phot; + + BW_dndt_table m_dndt_table; + BW_pair_prod_table m_pair_prod_table; + + void init_builtin_dndt_table(); + void init_builtin_pair_prod_table(); -//Table builing is available only if WarpX is compiled with QED_TABLE_GEN=TRUE -#ifdef WARPX_QED_TABLE_GEN - BreitWheelerEngineTableBuilder m_table_builder; -#endif }; diff --git a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.cpp b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.cpp index e1eb55714f2..4d12e276343 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.cpp +++ b/Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.cpp @@ -6,14 +6,22 @@ */ #include "BreitWheelerEngineWrapper.H" -#include "QedTableParserHelperFunctions.H" -#include "BreitWheelerDummyTable.H" +#include + +//Functions needed to generate a new table +#ifdef WARPX_QED_TABLE_GEN +# include +#endif + +#include #include +#include +#include using namespace std; -using namespace QedUtils; using namespace amrex; +namespace pxr_sr = picsar::multi_physics::utils::serialization; //This file provides a wrapper aroud the breit_wheeler engine //provided by the PICSAR library @@ -23,25 +31,26 @@ using namespace amrex; BreitWheelerEngine::BreitWheelerEngine (){} BreitWheelerGetOpticalDepth -BreitWheelerEngine::build_optical_depth_functor () +BreitWheelerEngine::build_optical_depth_functor () const { return BreitWheelerGetOpticalDepth(); } BreitWheelerEvolveOpticalDepth -BreitWheelerEngine::build_evolve_functor () +BreitWheelerEngine::build_evolve_functor () const { AMREX_ALWAYS_ASSERT(m_lookup_tables_initialized); - return BreitWheelerEvolveOpticalDepth(m_innards); + return BreitWheelerEvolveOpticalDepth(m_dndt_table.get_view(), + m_bw_minimum_chi_phot); } BreitWheelerGeneratePairs -BreitWheelerEngine::build_pair_functor () +BreitWheelerEngine::build_pair_functor () const { AMREX_ALWAYS_ASSERT(m_lookup_tables_initialized); - return BreitWheelerGeneratePairs(m_innards); + return BreitWheelerGeneratePairs(m_pair_prod_table.get_view()); } bool BreitWheelerEngine::are_lookup_tables_initialized () const @@ -51,150 +60,60 @@ bool BreitWheelerEngine::are_lookup_tables_initialized () const bool BreitWheelerEngine::init_lookup_tables_from_raw_data ( - const Vector& raw_data) + const vector& raw_data, + const amrex::Real bw_minimum_chi_phot) { - const char* p_data = raw_data.data(); - const char* const p_last = &raw_data.back(); - bool is_ok; - - //Header (control parameters) - tie(is_ok, m_innards.ctrl.chi_phot_min, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_phot_tdndt_min, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_phot_tdndt_max, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_phot_tdndt_how_many, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_phot_tpair_min, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_phot_tpair_max, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_phot_tpair_how_many, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_frac_tpair_how_many, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - //___________________________ - - //Data - Vector tndt_coords(m_innards.ctrl.chi_phot_tdndt_how_many); - Vector tndt_data(m_innards.ctrl.chi_phot_tdndt_how_many); - Vector cum_tab_coords1(m_innards.ctrl.chi_phot_tpair_how_many); - Vector cum_tab_coords2(m_innards.ctrl.chi_frac_tpair_how_many); - Vector cum_tab_data(m_innards.ctrl.chi_phot_tpair_how_many* - m_innards.ctrl.chi_frac_tpair_how_many); - - tie(is_ok, tndt_coords, p_data) = - parse_raw_data_vec( - p_data, tndt_coords.size(), p_last); - if(!is_ok) return false; - m_innards.TTfunc_coords.assign(tndt_coords.begin(), tndt_coords.end()); - - tie(is_ok, tndt_data, p_data) = - parse_raw_data_vec( - p_data, tndt_data.size(), p_last); - if(!is_ok) return false; - m_innards.TTfunc_data.assign(tndt_data.begin(), tndt_data.end()); - - tie(is_ok, cum_tab_coords1, p_data) = - parse_raw_data_vec( - p_data, cum_tab_coords1.size(), p_last); - if(!is_ok) return false; - m_innards.cum_distrib_coords_1.assign( - cum_tab_coords1.begin(), cum_tab_coords1.end()); - - tie(is_ok, cum_tab_coords2, p_data) = - parse_raw_data_vec( - p_data, cum_tab_coords2.size(), p_last); - if(!is_ok) return false; - m_innards.cum_distrib_coords_2.assign( - cum_tab_coords2.begin(), cum_tab_coords2.end()); - - tie(is_ok, cum_tab_data, p_data) = - parse_raw_data_vec( - p_data, cum_tab_data.size(), p_last); - if(!is_ok) return false; - m_innards.cum_distrib_data.assign( - cum_tab_data.begin(), cum_tab_data.end()); - - //___________________________ + auto raw_iter = raw_data.begin(); + const auto size_first = pxr_sr::get_out(raw_iter); + if(size_first <= 0 || size_first >= raw_data.size() ) return false; + + const auto raw_dndt_table = vector{ + raw_iter, raw_iter+size_first}; + + const auto raw_pair_prod_table = vector{ + raw_iter+size_first, raw_data.end()}; + + m_dndt_table = BW_dndt_table{raw_dndt_table}; + m_pair_prod_table = BW_pair_prod_table{raw_pair_prod_table}; + + if (!m_dndt_table.is_init() || !m_pair_prod_table.is_init()) + return false; + + m_bw_minimum_chi_phot = bw_minimum_chi_phot; + + amrex::Gpu::synchronize(); + m_lookup_tables_initialized = true; return true; } -void BreitWheelerEngine::init_dummy_tables() +void BreitWheelerEngine::init_builtin_tables( + const amrex::Real bw_minimum_chi_phot) { - m_innards.ctrl = QedUtils::BreitWheelerEngineInnardsDummy.ctrl; - m_innards.TTfunc_coords.assign( - QedUtils::BreitWheelerEngineInnardsDummy.TTfunc_coords.begin(), - QedUtils::BreitWheelerEngineInnardsDummy.TTfunc_coords.end()); - m_innards.TTfunc_data.assign( - QedUtils::BreitWheelerEngineInnardsDummy.TTfunc_data.begin(), - QedUtils::BreitWheelerEngineInnardsDummy.TTfunc_data.end()); - m_innards.cum_distrib_coords_1.assign( - QedUtils::BreitWheelerEngineInnardsDummy.cum_distrib_coords_1.begin(), - QedUtils::BreitWheelerEngineInnardsDummy.cum_distrib_coords_1.end()); - m_innards.cum_distrib_coords_2.assign( - QedUtils::BreitWheelerEngineInnardsDummy.cum_distrib_coords_2.begin(), - QedUtils::BreitWheelerEngineInnardsDummy.cum_distrib_coords_2.end()); - m_innards.cum_distrib_data.assign( - QedUtils::BreitWheelerEngineInnardsDummy.cum_distrib_data.begin(), - QedUtils::BreitWheelerEngineInnardsDummy.cum_distrib_data.end()); + init_builtin_dndt_table(); + init_builtin_pair_prod_table(); + m_bw_minimum_chi_phot = bw_minimum_chi_phot; m_lookup_tables_initialized = true; } -Vector BreitWheelerEngine::export_lookup_tables_data () const +vector BreitWheelerEngine::export_lookup_tables_data () const { - Vector res{}; - - if(!m_lookup_tables_initialized) - return res; - - add_data_to_vector_char(&m_innards.ctrl.chi_phot_min, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_phot_tdndt_min, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_phot_tdndt_max, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_phot_tdndt_how_many, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_phot_tpair_min, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_phot_tpair_max, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_phot_tpair_how_many, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_frac_tpair_how_many, 1, res); - - add_data_to_vector_char(m_innards.TTfunc_coords.data(), - m_innards.TTfunc_coords.size(), res); - add_data_to_vector_char(m_innards.TTfunc_data.data(), - m_innards.TTfunc_data.size(), res); - add_data_to_vector_char(m_innards.cum_distrib_coords_1.data(), - m_innards.cum_distrib_coords_1.size(), res); - add_data_to_vector_char(m_innards.cum_distrib_coords_2.data(), - m_innards.cum_distrib_coords_2.size(), res); - add_data_to_vector_char(m_innards.cum_distrib_data.data(), - m_innards.cum_distrib_data.size(), res); + if(!m_lookup_tables_initialized) + return vector{}; + + const auto data_dndt = m_dndt_table.serialize(); + const auto data_pair_prod = m_pair_prod_table.serialize(); + + const uint64_t size_first = data_dndt.size(); + + vector res{}; + pxr_sr::put_in(size_first, res); + for (const auto& tmp : data_dndt) + pxr_sr::put_in(tmp, res); + for (const auto& tmp : data_pair_prod) + pxr_sr::put_in(tmp, res); return res; } @@ -202,22 +121,1105 @@ Vector BreitWheelerEngine::export_lookup_tables_data () const PicsarBreitWheelerCtrl BreitWheelerEngine::get_default_ctrl() const { - return PicsarBreitWheelerCtrl(); + namespace pxr_bw = picsar::multi_physics::phys::breit_wheeler; + return PicsarBreitWheelerCtrl{ + pxr_bw::default_dndt_lookup_table_params, + pxr_bw::default_pair_prod_lookup_table_params + }; } -const PicsarBreitWheelerCtrl& -BreitWheelerEngine::get_ref_ctrl() const +amrex::Real +BreitWheelerEngine::get_minimum_chi_phot() const { - return m_innards.ctrl; + return m_bw_minimum_chi_phot; } void BreitWheelerEngine::compute_lookup_tables ( - PicsarBreitWheelerCtrl ctrl) + PicsarBreitWheelerCtrl ctrl, + const amrex::Real bw_minimum_chi_phot) { #ifdef WARPX_QED_TABLE_GEN - m_table_builder.compute_table(ctrl, m_innards); + m_dndt_table = BW_dndt_table{ctrl.dndt_params}; + m_dndt_table.generate(true); //Progress bar is displayed + m_pair_prod_table = BW_pair_prod_table{ctrl.pair_prod_params}; + m_pair_prod_table.generate(true); //Progress bar is displayed + m_bw_minimum_chi_phot = bw_minimum_chi_phot; + + amrex::Gpu::synchronize(); + m_lookup_tables_initialized = true; +#else + amrex::ignore_unused(ctrl, bw_minimum_chi_phot); + amrex::Abort("WarpX was not compiled with table generation support!"); #endif } +void BreitWheelerEngine::init_builtin_dndt_table() +{ + BW_dndt_table_params dndt_params; + dndt_params.chi_phot_min = 0.02_rt; + dndt_params.chi_phot_max = 200.0_rt; + dndt_params.chi_phot_how_many = 64; + + const auto vals = amrex::Gpu::DeviceVector{ + -1.34808e+02_rt, -1.16674e+02_rt, -1.01006e+02_rt, -8.74694e+01_rt, + -7.57742e+01_rt, -6.56699e+01_rt, -5.69401e+01_rt, -4.93981e+01_rt, + -4.28821e+01_rt, -3.72529e+01_rt, -3.23897e+01_rt, -2.81885e+01_rt, + -2.45593e+01_rt, -2.14243e+01_rt, -1.87166e+01_rt, -1.63779e+01_rt, + -1.43584e+01_rt, -1.26145e+01_rt, -1.11091e+01_rt, -9.80975e+00_rt, + -8.68862e+00_rt, -7.72163e+00_rt, -6.88795e+00_rt, -6.16965e+00_rt, + -5.55120e+00_rt, -5.01921e+00_rt, -4.56210e+00_rt, -4.16989e+00_rt, + -3.83395e+00_rt, -3.54680e+00_rt, -3.30202e+00_rt, -3.09403e+00_rt, + -2.91801e+00_rt, -2.76980e+00_rt, -2.64579e+00_rt, -2.54287e+00_rt, + -2.45833e+00_rt, -2.38982e+00_rt, -2.33532e+00_rt, -2.29307e+00_rt, + -2.26152e+00_rt, -2.23935e+00_rt, -2.22540e+00_rt, -2.21867e+00_rt, + -2.21827e+00_rt, -2.22343e+00_rt, -2.23350e+00_rt, -2.24788e+00_rt, + -2.26607e+00_rt, -2.28761e+00_rt, -2.31212e+00_rt, -2.33926e+00_rt, + -2.36871e+00_rt, -2.40023e+00_rt, -2.43357e+00_rt, -2.46853e+00_rt, + -2.50493e+00_rt, -2.54261e+00_rt, -2.58143e+00_rt, -2.62127e+00_rt, + -2.66201e+00_rt, -2.70357e+00_rt, -2.74585e+00_rt, -2.78877e+00_rt}; + + amrex::Gpu::synchronize(); + + m_dndt_table = BW_dndt_table{dndt_params, vals}; +} + +void BreitWheelerEngine::init_builtin_pair_prod_table() +{ + BW_pair_prod_table_params pair_prod_params; + pair_prod_params.chi_phot_min = 0.02_rt; + pair_prod_params.chi_phot_max = 200.0_rt; + pair_prod_params.chi_phot_how_many = 64; + pair_prod_params.frac_how_many = 64; + + const auto vals = amrex::Gpu::DeviceVector{ + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 3.35120e-221_rt, + 1.13067e-188_rt, 2.14228e-163_rt, 3.39948e-143_rt, 1.09215e-126_rt, + 5.94019e-113_rt, 2.37985e-101_rt, 2.00771e-91_rt, 7.70540e-83_rt, + 2.39749e-75_rt, 9.40741e-69_rt, 6.56422e-63_rt, 1.06836e-57_rt, + 5.03881e-53_rt, 8.20929e-49_rt, 5.33414e-45_rt, 1.55651e-41_rt, + 2.25171e-38_rt, 1.75472e-35_rt, 7.90210e-33_rt, 2.18314e-30_rt, + 3.89470e-28_rt, 4.68899e-26_rt, 3.95815e-24_rt, 2.42185e-22_rt, + 1.10577e-20_rt, 3.86465e-19_rt, 1.05740e-17_rt, 2.31035e-16_rt, + 4.10266e-15_rt, 6.01463e-14_rt, 7.38207e-13_rt, 7.68074e-12_rt, + 6.85101e-11_rt, 5.29188e-10_rt, 3.57202e-09_rt, 2.12435e-08_rt, + 1.12141e-07_rt, 5.28995e-07_rt, 2.24354e-06_rt, 8.60245e-06_rt, + 2.99719e-05_rt, 9.53273e-05_rt, 2.77953e-04_rt, 7.45878e-04_rt, + 1.84871e-03_rt, 4.24642e-03_rt, 9.06751e-03_rt, 1.80527e-02_rt, + 3.36054e-02_rt, 5.86505e-02_rt, 9.62278e-02_rt, 1.48823e-01_rt, + 2.17569e-01_rt, 3.01546e-01_rt, 3.97478e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 4.96543e-229_rt, 1.61201e-191_rt, + 2.10361e-163_rt, 1.48853e-141_rt, 4.31513e-124_rt, 8.03657e-110_rt, + 6.02583e-98_rt, 6.48154e-88_rt, 2.47897e-79_rt, 6.56015e-72_rt, + 1.97905e-65_rt, 9.97097e-60_rt, 1.12913e-54_rt, 3.63334e-50_rt, + 4.00764e-46_rt, 1.76373e-42_rt, 3.50658e-39_rt, 3.48978e-36_rt, + 1.89363e-33_rt, 6.01935e-31_rt, 1.19104e-28_rt, 1.54482e-26_rt, + 1.37287e-24_rt, 8.68465e-23_rt, 4.04195e-21_rt, 1.42438e-19_rt, + 3.89736e-18_rt, 8.46436e-17_rt, 1.48776e-15_rt, 2.15299e-14_rt, + 2.60458e-13_rt, 2.66996e-12_rt, 2.34743e-11_rt, 1.78939e-10_rt, + 1.19414e-09_rt, 7.03769e-09_rt, 3.69190e-08_rt, 1.73618e-07_rt, + 7.36647e-07_rt, 2.83643e-06_rt, 9.96391e-06_rt, 3.20868e-05_rt, + 9.51418e-05_rt, 2.60801e-04_rt, 6.63347e-04_rt, 1.57087e-03_rt, + 3.47431e-03_rt, 7.19766e-03_rt, 1.40053e-02_rt, 2.56621e-02_rt, + 4.43869e-02_rt, 7.26462e-02_rt, 1.12767e-01_rt, 1.66410e-01_rt, + 2.34012e-01_rt, 3.14373e-01_rt, 4.04524e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, + 0.00000e+00_rt, 1.05570e-243_rt, 2.71557e-198_rt, 7.19045e-166_rt, + 1.44533e-141_rt, 1.11742e-122_rt, 1.39503e-107_rt, 3.03452e-95_rt, + 5.61714e-85_rt, 2.65318e-76_rt, 7.00707e-69_rt, 1.83945e-62_rt, + 7.38996e-57_rt, 6.32013e-52_rt, 1.48739e-47_rt, 1.17966e-43_rt, + 3.70790e-40_rt, 5.26675e-37_rt, 3.76381e-34_rt, 1.47876e-31_rt, + 3.43911e-29_rt, 5.03750e-27_rt, 4.89780e-25_rt, 3.30531e-23_rt, + 1.60870e-21_rt, 5.83599e-20_rt, 1.62380e-18_rt, 3.55242e-17_rt, + 6.24487e-16_rt, 8.99102e-15_rt, 1.07814e-13_rt, 1.09288e-12_rt, + 9.48909e-12_rt, 7.14037e-11_rt, 4.70552e-10_rt, 2.74127e-09_rt, + 1.42365e-08_rt, 6.64113e-08_rt, 2.80174e-07_rt, 1.07555e-06_rt, + 3.77812e-06_rt, 1.22054e-05_rt, 3.64294e-05_rt, 1.00878e-04_rt, + 2.60162e-04_rt, 6.27062e-04_rt, 1.41706e-03_rt, 3.01137e-03_rt, + 6.03424e-03_rt, 1.14306e-02_rt, 2.05179e-02_rt, 3.49780e-02_rt, + 5.67520e-02_rt, 8.78195e-02_rt, 1.29868e-01_rt, 1.83904e-01_rt, + 2.49888e-01_rt, 3.26495e-01_rt, 4.11098e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, + 7.26024e-270_rt, 5.56502e-211_rt, 9.89467e-172_rt, 1.05165e-143_rt, + 1.07452e-122_rt, 2.29464e-106_rt, 2.58816e-93_rt, 1.20441e-82_rt, + 9.10565e-74_rt, 2.88914e-66_rt, 7.57859e-60_rt, 2.70227e-54_rt, + 1.90184e-49_rt, 3.51404e-45_rt, 2.12810e-41_rt, 5.03275e-38_rt, + 5.34704e-35_rt, 2.85882e-32_rt, 8.43982e-30_rt, 1.48542e-27_rt, + 1.66142e-25_rt, 1.24599e-23_rt, 6.55645e-22_rt, 2.51605e-20_rt, + 7.27852e-19_rt, 1.63315e-17_rt, 2.91337e-16_rt, 4.22167e-15_rt, + 5.06352e-14_rt, 5.11048e-13_rt, 4.40374e-12_rt, 3.28180e-11_rt, + 2.13937e-10_rt, 1.23238e-09_rt, 6.33036e-09_rt, 2.92316e-08_rt, + 1.22231e-07_rt, 4.65857e-07_rt, 1.62791e-06_rt, 5.24366e-06_rt, + 1.56444e-05_rt, 4.34216e-05_rt, 1.12566e-04_rt, 2.73554e-04_rt, + 6.25248e-04_rt, 1.34820e-03_rt, 2.75018e-03_rt, 5.32098e-03_rt, + 9.78768e-03_rt, 1.71549e-02_rt, 2.87089e-02_rt, 4.59637e-02_rt, + 7.05331e-02_rt, 1.03927e-01_rt, 1.47294e-01_rt, 2.01146e-01_rt, + 2.65135e-01_rt, 3.37925e-01_rt, 4.17229e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, + 1.33696e-233_rt, 1.05001e-182_rt, 8.88418e-149_rt, 1.50181e-124_rt, + 2.18820e-106_rt, 2.87640e-92_rt, 5.55963e-81_rt, 9.33671e-72_rt, + 4.45389e-64_rt, 1.37058e-57_rt, 4.88856e-52_rt, 3.10614e-47_rt, + 4.85334e-43_rt, 2.38628e-39_rt, 4.47261e-36_rt, 3.71812e-33_rt, + 1.54748e-30_rt, 3.55680e-28_rt, 4.89187e-26_rt, 4.30188e-24_rt, + 2.55620e-22_rt, 1.07505e-20_rt, 3.32812e-19_rt, 7.84181e-18_rt, + 1.44715e-16_rt, 2.14389e-15_rt, 2.60471e-14_rt, 2.64398e-13_rt, + 2.27907e-12_rt, 1.69219e-11_rt, 1.09596e-10_rt, 6.26073e-10_rt, + 3.18581e-09_rt, 1.45679e-08_rt, 6.03332e-08_rt, 2.27904e-07_rt, + 7.90163e-07_rt, 2.52880e-06_rt, 7.50871e-06_rt, 2.07815e-05_rt, + 5.38356e-05_rt, 1.31037e-04_rt, 3.00711e-04_rt, 6.52692e-04_rt, + 1.34377e-03_rt, 2.63113e-03_rt, 4.91155e-03_rt, 8.76045e-03_rt, + 1.49612e-02_rt, 2.45118e-02_rt, 3.85956e-02_rt, 5.85053e-02_rt, + 8.55165e-02_rt, 1.20719e-01_rt, 1.64829e-01_rt, 2.18003e-01_rt, + 2.79711e-01_rt, 3.48679e-01_rt, 4.22943e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 1.43228e-275_rt, + 2.90526e-202_rt, 2.85407e-158_rt, 6.08538e-129_rt, 5.37574e-108_rt, + 2.72332e-92_rt, 4.40352e-80_rt, 2.53579e-70_rt, 2.41452e-62_rt, + 1.05824e-55_rt, 4.33779e-50_rt, 2.75960e-45_rt, 3.95059e-41_rt, + 1.68168e-37_rt, 2.63427e-34_rt, 1.79233e-31_rt, 6.03771e-29_rt, + 1.11818e-26_rt, 1.23925e-24_rt, 8.80945e-23_rt, 4.25371e-21_rt, + 1.46338e-19_rt, 3.73368e-18_rt, 7.30873e-17_rt, 1.12985e-15_rt, + 1.41392e-14_rt, 1.46326e-13_rt, 1.27566e-12_rt, 9.52018e-12_rt, + 6.16825e-11_rt, 3.51273e-10_rt, 1.77753e-09_rt, 8.06971e-09_rt, + 3.31493e-08_rt, 1.24157e-07_rt, 4.26866e-07_rt, 1.35544e-06_rt, + 3.99674e-06_rt, 1.09977e-05_rt, 2.83656e-05_rt, 6.88522e-05_rt, + 1.57855e-04_rt, 3.42959e-04_rt, 7.08237e-04_rt, 1.39397e-03_rt, + 2.62155e-03_rt, 4.72160e-03_rt, 8.16139e-03_rt, 1.35653e-02_rt, + 2.17204e-02_rt, 3.35592e-02_rt, 5.01121e-02_rt, 7.24274e-02_rt, + 1.01462e-01_rt, 1.37952e-01_rt, 1.82280e-01_rt, 2.34366e-01_rt, + 2.93590e-01_rt, 3.58779e-01_rt, 4.28265e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 1.51593e-238_rt, + 3.48684e-175_rt, 3.72191e-137_rt, 8.44188e-112_rt, 1.08918e-93_rt, + 4.14348e-80_rt, 1.49893e-69_rt, 4.14701e-61_rt, 3.30780e-54_rt, + 1.84292e-48_rt, 1.32364e-43_rt, 1.89875e-39_rt, 7.50022e-36_rt, + 1.03805e-32_rt, 6.05229e-30_rt, 1.71569e-27_rt, 2.64805e-25_rt, + 2.43622e-23_rt, 1.43764e-21_rt, 5.77804e-20_rt, 1.66200e-18_rt, + 3.56565e-17_rt, 5.90700e-16_rt, 7.78145e-15_rt, 8.35746e-14_rt, + 7.47669e-13_rt, 5.67517e-12_rt, 3.71387e-11_rt, 2.12468e-10_rt, + 1.07564e-09_rt, 4.87056e-09_rt, 1.99124e-08_rt, 7.41165e-08_rt, + 2.53026e-07_rt, 7.97501e-07_rt, 2.33436e-06_rt, 6.37912e-06_rt, + 1.63518e-05_rt, 3.94847e-05_rt, 9.01614e-05_rt, 1.95367e-04_rt, + 4.02983e-04_rt, 7.93547e-04_rt, 1.49569e-03_rt, 2.70473e-03_rt, + 4.70293e-03_rt, 7.87848e-03_rt, 1.27392e-02_rt, 1.99162e-02_rt, + 3.01520e-02_rt, 4.42696e-02_rt, 6.31203e-02_rt, 8.75113e-02_rt, + 1.18119e-01_rt, 1.55396e-01_rt, 1.99486e-01_rt, 2.50156e-01_rt, + 3.06761e-01_rt, 3.68251e-01_rt, 4.33220e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 0.00000e+00_rt, 1.49266e-206_rt, + 8.76968e-152_rt, 6.58450e-119_rt, 5.51350e-97_rt, 2.52491e-81_rt, + 1.40295e-69_rt, 1.90378e-60_rt, 3.82392e-53_rt, 3.57979e-47_rt, + 3.35629e-42_rt, 5.35264e-38_rt, 2.12010e-34_rt, 2.75294e-31_rt, + 1.44327e-28_rt, 3.58262e-26_rt, 4.76628e-24_rt, 3.74791e-22_rt, + 1.88382e-20_rt, 6.44864e-19_rt, 1.58345e-17_rt, 2.91116e-16_rt, + 4.15302e-15_rt, 4.73727e-14_rt, 4.43185e-13_rt, 3.47475e-12_rt, + 2.32589e-11_rt, 1.35057e-10_rt, 6.89789e-10_rt, 3.13627e-09_rt, + 1.28287e-08_rt, 4.76470e-08_rt, 1.62000e-07_rt, 5.07870e-07_rt, + 1.47750e-06_rt, 4.01156e-06_rt, 1.02170e-05_rt, 2.45213e-05_rt, + 5.56861e-05_rt, 1.20099e-04_rt, 2.46812e-04_rt, 4.84774e-04_rt, + 9.12525e-04_rt, 1.65029e-03_rt, 2.87391e-03_rt, 4.82923e-03_rt, + 7.84506e-03_rt, 1.23420e-02_rt, 1.88336e-02_rt, 2.79182e-02_rt, + 4.02565e-02_rt, 5.65371e-02_rt, 7.74272e-02_rt, 1.03514e-01_rt, + 1.35243e-01_rt, 1.72849e-01_rt, 2.16312e-01_rt, 2.65312e-01_rt, + 3.19222e-01_rt, 3.77120e-01_rt, 4.37832e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 1.58793e-273_rt, 6.61011e-179_rt, + 1.46284e-131_rt, 3.87939e-103_rt, 3.51382e-84_rt, 1.23011e-70_rt, + 1.77378e-60_rt, 1.41107e-52_rt, 2.93962e-46_rt, 4.32235e-41_rt, + 8.68976e-37_rt, 3.77603e-33_rt, 4.91941e-30_rt, 2.44280e-27_rt, + 5.53569e-25_rt, 6.57043e-23_rt, 4.54673e-21_rt, 1.99639e-19_rt, + 5.95176e-18_rt, 1.27267e-16_rt, 2.04147e-15_rt, 2.54936e-14_rt, + 2.55622e-13_rt, 2.11212e-12_rt, 1.47004e-11_rt, 8.78122e-11_rt, + 4.57466e-10_rt, 2.10737e-09_rt, 8.68763e-09_rt, 3.23866e-08_rt, + 1.10177e-07_rt, 3.44792e-07_rt, 9.99597e-07_rt, 2.70151e-06_rt, + 6.84404e-06_rt, 1.63337e-05_rt, 3.68843e-05_rt, 7.91224e-05_rt, + 1.61808e-04_rt, 3.16473e-04_rt, 5.93691e-04_rt, 1.07105e-03_rt, + 1.86257e-03_rt, 3.12902e-03_rt, 5.08803e-03_rt, 8.02264e-03_rt, + 1.22864e-02_rt, 1.83032e-02_rt, 2.65600e-02_rt, 3.75911e-02_rt, + 5.19532e-02_rt, 7.01924e-02_rt, 9.28038e-02_rt, 1.20187e-01_rt, + 1.52603e-01_rt, 1.90133e-01_rt, 2.32648e-01_rt, 2.79792e-01_rt, + 3.30980e-01_rt, 3.85415e-01_rt, 4.42123e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 8.41483e-237_rt, 5.13669e-155_rt, + 4.37898e-114_rt, 1.65375e-89_rt, 4.10179e-73_rt, 2.12988e-61_rt, + 1.30962e-52_rt, 8.98269e-46_rt, 2.64139e-40_rt, 7.84631e-36_rt, + 4.17035e-32_rt, 5.89256e-29_rt, 2.93719e-26_rt, 6.35624e-24_rt, + 6.97817e-22_rt, 4.37819e-20_rt, 1.72232e-18_rt, 4.57083e-17_rt, + 8.67713e-16_rt, 1.23555e-14_rt, 1.37184e-13_rt, 1.22642e-12_rt, + 9.06732e-12_rt, 5.66989e-11_rt, 3.05620e-10_rt, 1.44321e-09_rt, + 6.05407e-09_rt, 2.28311e-08_rt, 7.82116e-08_rt, 2.45582e-07_rt, + 7.12411e-07_rt, 1.92258e-06_rt, 4.85639e-06_rt, 1.15442e-05_rt, + 2.59492e-05_rt, 5.53927e-05_rt, 1.12722e-04_rt, 2.19423e-04_rt, + 4.09838e-04_rt, 7.36549e-04_rt, 1.27685e-03_rt, 2.14000e-03_rt, + 3.47470e-03_rt, 5.47600e-03_rt, 8.39063e-03_rt, 1.25195e-02_rt, + 1.82164e-02_rt, 2.58816e-02_rt, 3.59497e-02_rt, 4.88719e-02_rt, + 6.50927e-02_rt, 8.50218e-02_rt, 1.09003e-01_rt, 1.37286e-01_rt, + 1.69993e-01_rt, 2.07098e-01_rt, 2.48412e-01_rt, 2.93572e-01_rt, + 3.42050e-01_rt, 3.93165e-01_rt, 4.46113e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 4.55844e-205_rt, 2.27106e-134_rt, + 5.55927e-99_rt, 9.97211e-78_rt, 1.51108e-63_rt, 2.06349e-53_rt, + 8.30931e-46_rt, 6.85779e-40_rt, 3.71472e-35_rt, 2.77086e-31_rt, + 4.66556e-28_rt, 2.49733e-25_rt, 5.42730e-23_rt, 5.73037e-21_rt, + 3.36330e-19_rt, 1.21642e-17_rt, 2.93735e-16_rt, 5.04529e-15_rt, + 6.48460e-14_rt, 6.49787e-13_rt, 5.24973e-12_rt, 3.51590e-11_rt, + 1.99764e-10_rt, 9.81792e-10_rt, 4.24316e-09_rt, 1.63536e-08_rt, + 5.68862e-08_rt, 1.80455e-07_rt, 5.26740e-07_rt, 1.42588e-06_rt, + 3.60409e-06_rt, 8.55743e-06_rt, 1.91878e-05_rt, 4.08205e-05_rt, + 8.27388e-05_rt, 1.60372e-04_rt, 2.98248e-04_rt, 5.33760e-04_rt, + 9.21711e-04_rt, 1.53947e-03_rt, 2.49239e-03_rt, 3.91911e-03_rt, + 5.99602e-03_rt, 8.94020e-03_rt, 1.30102e-02_rt, 1.85040e-02_rt, + 2.57530e-02_rt, 3.51130e-02_rt, 4.69503e-02_rt, 6.16258e-02_rt, + 7.94746e-02_rt, 1.00785e-01_rt, 1.25778e-01_rt, 1.54584e-01_rt, + 1.87231e-01_rt, 2.23623e-01_rt, 2.63542e-01_rt, 3.06640e-01_rt, + 3.52450e-01_rt, 4.00398e-01_rt, 4.49822e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 1.20482e-177_rt, 1.57838e-116_rt, + 6.29271e-86_rt, 1.51798e-67_rt, 2.81107e-55_rt, 1.65577e-46_rt, + 6.32592e-40_rt, 8.37836e-35_rt, 1.05225e-30_rt, 2.37828e-27_rt, + 1.48323e-24_rt, 3.43190e-22_rt, 3.64024e-20_rt, 2.06740e-18_rt, + 7.06337e-17_rt, 1.58711e-15_rt, 2.51381e-14_rt, 2.96472e-13_rt, + 2.72023e-12_rt, 2.01198e-11_rt, 1.23499e-10_rt, 6.44398e-10_rt, + 2.91607e-09_rt, 1.16386e-08_rt, 4.15573e-08_rt, 1.34372e-07_rt, + 3.97561e-07_rt, 1.08599e-06_rt, 2.76028e-06_rt, 6.57236e-06_rt, + 1.47471e-05_rt, 3.13448e-05_rt, 6.34004e-05_rt, 1.22532e-04_rt, + 2.27096e-04_rt, 4.04916e-04_rt, 6.96571e-04_rt, 1.15913e-03_rt, + 1.87012e-03_rt, 2.93148e-03_rt, 4.47305e-03_rt, 6.65524e-03_rt, + 9.67030e-03_rt, 1.37419e-02_rt, 1.91225e-02_rt, 2.60882e-02_rt, + 3.49313e-02_rt, 4.59500e-02_rt, 5.94364e-02_rt, 7.56627e-02_rt, + 9.48661e-02_rt, 1.17234e-01_rt, 1.42889e-01_rt, 1.71879e-01_rt, + 2.04164e-01_rt, 2.39612e-01_rt, 2.77996e-01_rt, 3.18995e-01_rt, + 3.62205e-01_rt, 4.07143e-01_rt, 4.53269e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 0.00000e+00_rt, 5.99544e-154_rt, 4.14924e-101_rt, + 1.20684e-74_rt, 9.62151e-59_rt, 3.96369e-48_rt, 1.54418e-40_rt, + 7.71031e-35_rt, 2.10168e-30_rt, 7.46541e-27_rt, 6.01388e-24_rt, + 1.59000e-21_rt, 1.78233e-19_rt, 1.01722e-17_rt, 3.38066e-16_rt, + 7.23646e-15_rt, 1.07766e-13_rt, 1.18561e-12_rt, 1.01039e-11_rt, + 6.92812e-11_rt, 3.94155e-10_rt, 1.90798e-09_rt, 8.02357e-09_rt, + 2.98251e-08_rt, 9.94357e-08_rt, 3.01028e-07_rt, 8.36260e-07_rt, + 2.15109e-06_rt, 5.16335e-06_rt, 1.16436e-05_rt, 2.48126e-05_rt, + 5.02246e-05_rt, 9.70014e-05_rt, 1.79467e-04_rt, 3.19203e-04_rt, + 5.47500e-04_rt, 9.08129e-04_rt, 1.46030e-03_rt, 2.28159e-03_rt, + 3.47064e-03_rt, 5.14926e-03_rt, 7.46373e-03_rt, 1.05849e-02_rt, + 1.47071e-02_rt, 2.00450e-02_rt, 2.68295e-02_rt, 3.53013e-02_rt, + 4.57031e-02_rt, 5.82712e-02_rt, 7.32249e-02_rt, 9.07564e-02_rt, + 1.11020e-01_rt, 1.34124e-01_rt, 1.60119e-01_rt, 1.88994e-01_rt, + 2.20669e-01_rt, 2.54993e-01_rt, 2.91748e-01_rt, 3.30646e-01_rt, + 3.71339e-01_rt, 4.13428e-01_rt, 4.56472e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.09018e-270_rt, 1.80390e-133_rt, 8.80988e-88_rt, + 6.82879e-65_rt, 3.91287e-51_rt, 6.01679e-42_rt, 2.24300e-35_rt, + 1.93029e-30_rt, 1.34253e-26_rt, 1.60141e-23_rt, 5.29015e-21_rt, + 6.66294e-19_rt, 3.99298e-17_rt, 1.33392e-15_rt, 2.79016e-14_rt, + 3.98728e-13_rt, 4.16160e-12_rt, 3.34150e-11_rt, 2.15053e-10_rt, + 1.14642e-09_rt, 5.19866e-09_rt, 2.04954e-08_rt, 7.15253e-08_rt, + 2.24297e-07_rt, 6.40075e-07_rt, 1.68005e-06_rt, 4.09311e-06_rt, + 9.32854e-06_rt, 2.00230e-05_rt, 4.07133e-05_rt, 7.88211e-05_rt, + 1.45942e-04_rt, 2.59447e-04_rt, 4.44372e-04_rt, 7.35533e-04_rt, + 1.17976e-03_rt, 1.83811e-03_rt, 2.78792e-03_rt, 4.12440e-03_rt, + 5.96172e-03_rt, 8.43331e-03_rt, 1.16912e-02_rt, 1.59043e-02_rt, + 2.12556e-02_rt, 2.79383e-02_rt, 3.61506e-02_rt, 4.60903e-02_rt, + 5.79473e-02_rt, 7.18973e-02_rt, 8.80942e-02_rt, 1.06663e-01_rt, + 1.27692e-01_rt, 1.51229e-01_rt, 1.77275e-01_rt, 2.05781e-01_rt, + 2.36645e-01_rt, 2.69715e-01_rt, 3.04786e-01_rt, 3.41606e-01_rt, + 3.79881e-01_rt, 4.19280e-01_rt, 4.59446e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 5.35199e-234_rt, 8.99239e-116_rt, 2.90990e-76_rt, + 1.84101e-56_rt, 1.48341e-44_rt, 1.33146e-36_rt, 6.53485e-31_rt, + 1.23103e-26_rt, 2.63040e-23_rt, 1.22212e-20_rt, 1.86879e-18_rt, + 1.24017e-16_rt, 4.32619e-15_rt, 9.09794e-14_rt, 1.27546e-12_rt, + 1.28549e-11_rt, 9.86818e-11_rt, 6.03541e-10_rt, 3.04728e-09_rt, + 1.30681e-08_rt, 4.87097e-08_rt, 1.60813e-07_rt, 4.77647e-07_rt, + 1.29308e-06_rt, 3.22569e-06_rt, 7.48383e-06_rt, 1.62764e-05_rt, + 3.34090e-05_rt, 6.50994e-05_rt, 1.21031e-04_rt, 2.15646e-04_rt, + 3.69643e-04_rt, 6.11643e-04_rt, 9.79912e-04_rt, 1.52406e-03_rt, + 2.30657e-03_rt, 3.40403e-03_rt, 4.90794e-03_rt, 6.92498e-03_rt, + 9.57657e-03_rt, 1.29977e-02_rt, 1.73352e-02_rt, 2.27448e-02_rt, + 2.93879e-02_rt, 3.74277e-02_rt, 4.70243e-02_rt, 5.83301e-02_rt, + 7.14846e-02_rt, 8.66092e-02_rt, 1.03802e-01_rt, 1.23135e-01_rt, + 1.44647e-01_rt, 1.68343e-01_rt, 1.94190e-01_rt, 2.22119e-01_rt, + 2.52019e-01_rt, 2.83744e-01_rt, 3.17108e-01_rt, 3.51895e-01_rt, + 3.87858e-01_rt, 4.24726e-01_rt, 4.62208e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.10366e-202_rt, 1.77441e-100_rt, 2.63415e-66_rt, + 3.57775e-49_rt, 7.23954e-39_rt, 5.58144e-32_rt, 4.75140e-27_rt, + 2.40872e-23_rt, 1.85636e-20_rt, 3.81877e-18_rt, 3.00131e-16_rt, + 1.14488e-14_rt, 2.50208e-13_rt, 3.52728e-12_rt, 3.49933e-11_rt, + 2.60808e-10_rt, 1.53525e-09_rt, 7.42117e-09_rt, 3.03789e-08_rt, + 1.07940e-07_rt, 3.39608e-07_rt, 9.61749e-07_rt, 2.48489e-06_rt, + 5.92398e-06_rt, 1.31553e-05_rt, 2.74316e-05_rt, 5.40808e-05_rt, + 1.01397e-04_rt, 1.81718e-04_rt, 3.12656e-04_rt, 5.18437e-04_rt, + 8.31268e-04_rt, 1.29265e-03_rt, 1.95454e-03_rt, 2.88028e-03_rt, + 4.14512e-03_rt, 5.83644e-03_rt, 8.05333e-03_rt, 1.09058e-02_rt, + 1.45133e-02_rt, 1.90030e-02_rt, 2.45068e-02_rt, 3.11594e-02_rt, + 3.90942e-02_rt, 4.84403e-02_rt, 5.93190e-02_rt, 7.18397e-02_rt, + 8.60969e-02_rt, 1.02167e-01_rt, 1.20103e-01_rt, 1.39937e-01_rt, + 1.61673e-01_rt, 1.85287e-01_rt, 2.10728e-01_rt, 2.37914e-01_rt, + 2.66737e-01_rt, 2.97060e-01_rt, 3.28721e-01_rt, 3.61536e-01_rt, + 3.95300e-01_rt, 4.29789e-01_rt, 4.64771e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.26685e-175_rt, 2.94133e-87_rt, 1.06599e-57_rt, + 7.16756e-43_rt, 6.01109e-34_rt, 5.55779e-28_rt, 1.04131e-23_rt, + 1.69811e-20_rt, 5.42194e-18_rt, 5.51650e-16_rt, 2.43927e-14_rt, + 5.76631e-13_rt, 8.41263e-12_rt, 8.39366e-11_rt, 6.17584e-10_rt, + 3.54609e-09_rt, 1.65933e-08_rt, 6.54488e-08_rt, 2.23480e-07_rt, + 6.74877e-07_rt, 1.83390e-06_rt, 4.54829e-06_rt, 1.04169e-05_rt, + 2.22481e-05_rt, 4.46771e-05_rt, 8.49453e-05_rt, 1.53828e-04_rt, + 2.66678e-04_rt, 4.44534e-04_rt, 7.15225e-04_rt, 1.11441e-03_rt, + 1.68646e-03_rt, 2.48513e-03_rt, 3.57389e-03_rt, 5.02603e-03_rt, + 6.92425e-03_rt, 9.35997e-03_rt, 1.24322e-02_rt, 1.62459e-02_rt, + 2.09105e-02_rt, 2.65373e-02_rt, 3.32377e-02_rt, 4.11201e-02_rt, + 5.02877e-02_rt, 6.08358e-02_rt, 7.28492e-02_rt, 8.64000e-02_rt, + 1.01545e-01_rt, 1.18325e-01_rt, 1.36760e-01_rt, 1.56853e-01_rt, + 1.78584e-01_rt, 2.01912e-01_rt, 2.26777e-01_rt, 2.53095e-01_rt, + 2.80762e-01_rt, 3.09658e-01_rt, 3.39641e-01_rt, 3.70556e-01_rt, + 4.02235e-01_rt, 4.34495e-01_rt, 4.67149e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.06823e-152_rt, 7.84694e-76_rt, 2.94312e-50_rt, + 2.01640e-37_rt, 1.08049e-29_rt, 1.59848e-24_rt, 8.09748e-21_rt, + 4.95566e-18_rt, 7.38924e-16_rt, 4.09157e-14_rt, 1.10057e-12_rt, + 1.72052e-11_rt, 1.77014e-10_rt, 1.31001e-09_rt, 7.44426e-09_rt, + 3.41152e-08_rt, 1.30907e-07_rt, 4.33080e-07_rt, 1.26417e-06_rt, + 3.31680e-06_rt, 7.94014e-06_rt, 1.75580e-05_rt, 3.62307e-05_rt, + 7.03591e-05_rt, 1.29511e-04_rt, 2.27332e-04_rt, 3.82492e-04_rt, + 6.19605e-04_rt, 9.70054e-04_rt, 1.47267e-03_rt, 2.17418e-03_rt, + 3.12943e-03_rt, 4.40132e-03_rt, 6.06038e-03_rt, 8.18413e-03_rt, + 1.08561e-02_rt, 1.41646e-02_rt, 1.82011e-02_rt, 2.30590e-02_rt, + 2.88314e-02_rt, 3.56093e-02_rt, 4.34800e-02_rt, 5.25249e-02_rt, + 6.28178e-02_rt, 7.44230e-02_rt, 8.73941e-02_rt, 1.01772e-01_rt, + 1.17585e-01_rt, 1.34846e-01_rt, 1.53553e-01_rt, 1.73689e-01_rt, + 1.95219e-01_rt, 2.18095e-01_rt, 2.42252e-01_rt, 2.67610e-01_rt, + 2.94075e-01_rt, 3.21540e-01_rt, 3.49886e-01_rt, 3.78983e-01_rt, + 4.08691e-01_rt, 4.38866e-01_rt, 4.69354e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 4.95800e-132_rt, 5.90844e-66_rt, 7.98796e-44_rt, + 1.04041e-32_rt, 5.17752e-26_rt, 1.57228e-21_rt, 2.57237e-18_rt, + 6.74966e-16_rt, 5.21207e-14_rt, 1.70592e-12_rt, 2.98623e-11_rt, + 3.26542e-10_rt, 2.48412e-09_rt, 1.41997e-08_rt, 6.45408e-08_rt, + 2.43387e-07_rt, 7.86698e-07_rt, 2.23553e-06_rt, 5.69801e-06_rt, + 1.32378e-05_rt, 2.83996e-05_rt, 5.68658e-05_rt, 1.07218e-04_rt, + 1.91761e-04_rt, 3.27358e-04_rt, 5.36214e-04_rt, 8.46544e-04_rt, + 1.29309e-03_rt, 1.91741e-03_rt, 2.76798e-03_rt, 3.89998e-03_rt, + 5.37485e-03_rt, 7.25969e-03_rt, 9.62628e-03_rt, 1.25501e-02_rt, + 1.61089e-02_rt, 2.03818e-02_rt, 2.54473e-02_rt, 3.13823e-02_rt, + 3.82604e-02_rt, 4.61506e-02_rt, 5.51159e-02_rt, 6.52122e-02_rt, + 7.64871e-02_rt, 8.89788e-02_rt, 1.02716e-01_rt, 1.17715e-01_rt, + 1.33982e-01_rt, 1.51513e-01_rt, 1.70289e-01_rt, 1.90281e-01_rt, + 2.11449e-01_rt, 2.33740e-01_rt, 2.57091e-01_rt, 2.81428e-01_rt, + 3.06668e-01_rt, 3.32719e-01_rt, 3.59480e-01_rt, 3.86844e-01_rt, + 4.14698e-01_rt, 4.42924e-01_rt, 4.71399e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.44533e-114_rt, 2.04002e-57_rt, 2.92203e-38_rt, + 1.24009e-28_rt, 7.91692e-23_rt, 6.11998e-19_rt, 3.77057e-16_rt, + 4.75895e-14_rt, 2.08099e-12_rt, 4.32387e-11_rt, 5.22135e-10_rt, + 4.19201e-09_rt, 2.45648e-08_rt, 1.12319e-07_rt, 4.20867e-07_rt, + 1.34091e-06_rt, 3.73661e-06_rt, 9.30974e-06_rt, 2.11026e-05_rt, + 4.41295e-05_rt, 8.61053e-05_rt, 1.58221e-04_rt, 2.75907e-04_rt, + 4.59522e-04_rt, 7.34912e-04_rt, 1.13379e-03_rt, 1.69391e-03_rt, + 2.45902e-03_rt, 3.47858e-03_rt, 4.80725e-03_rt, 6.50424e-03_rt, + 8.63244e-03_rt, 1.12574e-02_rt, 1.44465e-02_rt, 1.82673e-02_rt, + 2.27868e-02_rt, 2.80705e-02_rt, 3.41806e-02_rt, 4.11754e-02_rt, + 4.91083e-02_rt, 5.80271e-02_rt, 6.79724e-02_rt, 7.89781e-02_rt, + 9.10699e-02_rt, 1.04265e-01_rt, 1.18573e-01_rt, 1.33994e-01_rt, + 1.50518e-01_rt, 1.68128e-01_rt, 1.86798e-01_rt, 2.06493e-01_rt, + 2.27168e-01_rt, 2.48773e-01_rt, 2.71249e-01_rt, 2.94530e-01_rt, + 3.18543e-01_rt, 3.43210e-01_rt, 3.68448e-01_rt, 3.94169e-01_rt, + 4.20281e-01_rt, 4.46688e-01_rt, 4.73294e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.79517e-99_rt, 4.91237e-50_rt, 1.89220e-33_rt, + 4.16777e-25_rt, 4.51239e-20_rt, 1.06937e-16_rt, 2.83302e-14_rt, + 1.89962e-12_rt, 5.08130e-11_rt, 7.12850e-10_rt, 6.24477e-09_rt, + 3.83849e-08_rt, 1.79496e-07_rt, 6.76626e-07_rt, 2.14545e-06_rt, + 5.90819e-06_rt, 1.44810e-05_rt, 3.21995e-05_rt, 6.59426e-05_rt, + 1.25896e-04_rt, 2.26284e-04_rt, 3.86005e-04_rt, 6.29103e-04_rt, + 9.85056e-04_rt, 1.48883e-03_rt, 2.18072e-03_rt, 3.10596e-03_rt, + 4.31421e-03_rt, 5.85876e-03_rt, 7.79575e-03_rt, 1.01832e-02_rt, + 1.30801e-02_rt, 1.65454e-02_rt, 2.06369e-02_rt, 2.54106e-02_rt, + 3.09194e-02_rt, 3.72129e-02_rt, 4.43359e-02_rt, 5.23287e-02_rt, + 6.12256e-02_rt, 7.10552e-02_rt, 8.18396e-02_rt, 9.35946e-02_rt, + 1.06329e-01_rt, 1.20045e-01_rt, 1.34737e-01_rt, 1.50394e-01_rt, + 1.66999e-01_rt, 1.84525e-01_rt, 2.02942e-01_rt, 2.22214e-01_rt, + 2.42297e-01_rt, 2.63144e-01_rt, 2.84700e-01_rt, 3.06910e-01_rt, + 3.29711e-01_rt, 3.53037e-01_rt, 3.76820e-01_rt, 4.00988e-01_rt, + 4.25466e-01_rt, 4.50180e-01_rt, 4.75050e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.99614e-86_rt, 1.18517e-43_rt, 2.74527e-29_rt, + 4.69191e-22_rt, 1.09641e-17_rt, 9.35339e-15_rt, 1.19488e-12_rt, + 4.63800e-11_rt, 8.11222e-10_rt, 8.10414e-09_rt, 5.37917e-08_rt, + 2.62460e-07_rt, 1.00976e-06_rt, 3.22103e-06_rt, 8.83961e-06_rt, + 2.14581e-05_rt, 4.70665e-05_rt, 9.48423e-05_rt, 1.77895e-04_rt, + 3.13887e-04_rt, 5.25459e-04_rt, 8.40437e-04_rt, 1.29179e-03_rt, + 1.91736e-03_rt, 2.75939e-03_rt, 3.86386e-03_rt, 5.27975e-03_rt, + 7.05813e-03_rt, 9.25132e-03_rt, 1.19119e-02_rt, 1.50919e-02_rt, + 1.88418e-02_rt, 2.32102e-02_rt, 2.82423e-02_rt, 3.39802e-02_rt, + 4.04618e-02_rt, 4.77204e-02_rt, 5.57847e-02_rt, 6.46781e-02_rt, + 7.44188e-02_rt, 8.50196e-02_rt, 9.64880e-02_rt, 1.08826e-01_rt, + 1.22030e-01_rt, 1.36091e-01_rt, 1.50996e-01_rt, 1.66726e-01_rt, + 1.83257e-01_rt, 2.00562e-01_rt, 2.18609e-01_rt, 2.37361e-01_rt, + 2.56778e-01_rt, 2.76817e-01_rt, 2.97432e-01_rt, 3.18573e-01_rt, + 3.40189e-01_rt, 3.62224e-01_rt, 3.84623e-01_rt, 4.07328e-01_rt, + 4.30279e-01_rt, 4.53416e-01_rt, 4.76677e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.77431e-75_rt, 3.91782e-38_rt, 1.09378e-25_rt, + 2.05310e-19_rt, 1.27532e-15_rt, 4.49930e-13_rt, 3.06002e-11_rt, + 7.40487e-10_rt, 8.97080e-09_rt, 6.68206e-08_rt, 3.48911e-07_rt, + 1.39420e-06_rt, 4.53089e-06_rt, 1.25092e-05_rt, 3.02964e-05_rt, + 6.59394e-05_rt, 1.31379e-04_rt, 2.43107e-04_rt, 4.22591e-04_rt, + 6.96427e-04_rt, 1.09621e-03_rt, 1.65813e-03_rt, 2.42244e-03_rt, + 3.43262e-03_rt, 4.73461e-03_rt, 6.37581e-03_rt, 8.40429e-03_rt, + 1.08678e-02_rt, 1.38130e-02_rt, 1.72847e-02_rt, 2.13252e-02_rt, + 2.59738e-02_rt, 3.12662e-02_rt, 3.72342e-02_rt, 4.39056e-02_rt, + 5.13035e-02_rt, 5.94468e-02_rt, 6.83497e-02_rt, 7.80219e-02_rt, + 8.84686e-02_rt, 9.96904e-02_rt, 1.11684e-01_rt, 1.24442e-01_rt, + 1.37952e-01_rt, 1.52199e-01_rt, 1.67164e-01_rt, 1.82824e-01_rt, + 1.99155e-01_rt, 2.16128e-01_rt, 2.33711e-01_rt, 2.51871e-01_rt, + 2.70572e-01_rt, 2.89776e-01_rt, 3.09443e-01_rt, 3.29532e-01_rt, + 3.49998e-01_rt, 3.70799e-01_rt, 3.91888e-01_rt, 4.13218e-01_rt, + 4.34743e-01_rt, 4.56414e-01_rt, 4.78182e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 2.11236e-65_rt, 2.32556e-33_rt, 1.42680e-22_rt, + 3.97114e-17_rt, 7.84976e-14_rt, 1.29110e-11_rt, 5.09207e-10_rt, + 8.19002e-09_rt, 7.22268e-08_rt, 4.17383e-07_rt, 1.77099e-06_rt, + 5.95385e-06_rt, 1.67202e-05_rt, 4.07382e-05_rt, 8.85495e-05_rt, + 1.75349e-04_rt, 3.21469e-04_rt, 5.52521e-04_rt, 8.99188e-04_rt, + 1.39672e-03_rt, 2.08420e-03_rt, 3.00366e-03_rt, 4.19911e-03_rt, + 5.71555e-03_rt, 7.59803e-03_rt, 9.89077e-03_rt, 1.26364e-02_rt, + 1.58752e-02_rt, 1.96447e-02_rt, 2.39791e-02_rt, 2.89089e-02_rt, + 3.44608e-02_rt, 4.06575e-02_rt, 4.75177e-02_rt, 5.50559e-02_rt, + 6.32824e-02_rt, 7.22038e-02_rt, 8.18227e-02_rt, 9.21382e-02_rt, + 1.03146e-01_rt, 1.14838e-01_rt, 1.27203e-01_rt, 1.40227e-01_rt, + 1.53895e-01_rt, 1.68186e-01_rt, 1.83080e-01_rt, 1.98553e-01_rt, + 2.14580e-01_rt, 2.31133e-01_rt, 2.48184e-01_rt, 2.65702e-01_rt, + 2.83656e-01_rt, 3.02014e-01_rt, 3.20741e-01_rt, 3.39803e-01_rt, + 3.59165e-01_rt, 3.78790e-01_rt, 3.98643e-01_rt, 4.18685e-01_rt, + 4.38880e-01_rt, 4.59190e-01_rt, 4.79576e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 5.64885e-57_rt, 3.13115e-29_rt, 7.09352e-20_rt, + 3.79397e-15_rt, 2.78788e-12_rt, 2.37089e-10_rt, 5.83808e-09_rt, + 6.59592e-08_rt, 4.42016e-07_rt, 2.05080e-06_rt, 7.27176e-06_rt, + 2.10524e-05_rt, 5.21029e-05_rt, 1.13928e-04_rt, 2.25496e-04_rt, + 4.11446e-04_rt, 7.01839e-04_rt, 1.13153e-03_rt, 1.73923e-03_rt, + 2.56646e-03_rt, 3.65640e-03_rt, 5.05278e-03_rt, 6.79885e-03_rt, + 8.93646e-03_rt, 1.15053e-02_rt, 1.45424e-02_rt, 1.80813e-02_rt, + 2.21521e-02_rt, 2.67812e-02_rt, 3.19909e-02_rt, 3.77995e-02_rt, + 4.42216e-02_rt, 5.12676e-02_rt, 5.89446e-02_rt, 6.72559e-02_rt, + 7.62016e-02_rt, 8.57786e-02_rt, 9.59811e-02_rt, 1.06800e-01_rt, + 1.18225e-01_rt, 1.30242e-01_rt, 1.42837e-01_rt, 1.55990e-01_rt, + 1.69685e-01_rt, 1.83900e-01_rt, 1.98614e-01_rt, 2.13804e-01_rt, + 2.29445e-01_rt, 2.45513e-01_rt, 2.61983e-01_rt, 2.78828e-01_rt, + 2.96021e-01_rt, 3.13534e-01_rt, 3.31340e-01_rt, 3.49410e-01_rt, + 3.67716e-01_rt, 3.86228e-01_rt, 4.04918e-01_rt, 4.23756e-01_rt, + 4.42713e-01_rt, 4.61759e-01_rt, 4.80865e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.09171e-49_rt, 1.17015e-25_rt, 1.53257e-17_rt, + 1.97065e-13_rt, 6.15660e-11_rt, 2.96033e-09_rt, 4.85087e-08_rt, + 4.03814e-07_rt, 2.13401e-06_rt, 8.18820e-06_rt, 2.48564e-05_rt, + 6.32274e-05_rt, 1.40265e-04_rt, 2.79269e-04_rt, 5.09674e-04_rt, + 8.66300e-04_rt, 1.38820e-03_rt, 2.11734e-03_rt, 3.09715e-03_rt, + 4.37128e-03_rt, 5.98236e-03_rt, 7.97107e-03_rt, 1.03753e-02_rt, + 1.32296e-02_rt, 1.65649e-02_rt, 2.04079e-02_rt, 2.47814e-02_rt, + 2.97039e-02_rt, 3.51902e-02_rt, 4.12509e-02_rt, 4.78932e-02_rt, + 5.51205e-02_rt, 6.29333e-02_rt, 7.13291e-02_rt, 8.03025e-02_rt, + 8.98461e-02_rt, 9.99498e-02_rt, 1.10602e-01_rt, 1.21789e-01_rt, + 1.33495e-01_rt, 1.45705e-01_rt, 1.58401e-01_rt, 1.71564e-01_rt, + 1.85175e-01_rt, 1.99214e-01_rt, 2.13660e-01_rt, 2.28491e-01_rt, + 2.43687e-01_rt, 2.59225e-01_rt, 2.75082e-01_rt, 2.91238e-01_rt, + 3.07668e-01_rt, 3.24350e-01_rt, 3.41261e-01_rt, 3.58379e-01_rt, + 3.75680e-01_rt, 3.93142e-01_rt, 4.10742e-01_rt, 4.28456e-01_rt, + 4.46262e-01_rt, 4.64136e-01_rt, 4.82057e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 2.17999e-43_rt, 1.44503e-22_rt, 1.61168e-15_rt, + 6.04581e-12_rt, 9.01779e-10_rt, 2.64847e-08_rt, 3.05151e-07_rt, + 1.95048e-06_rt, 8.39295e-06_rt, 2.73221e-05_rt, 7.25035e-05_rt, + 1.64880e-04_rt, 3.32701e-04_rt, 6.10751e-04_rt, 1.03895e-03_rt, + 1.66060e-03_rt, 2.52064e-03_rt, 3.66392e-03_rt, 5.13381e-03_rt, + 6.97107e-03_rt, 9.21300e-03_rt, 1.18928e-02_rt, 1.50395e-02_rt, + 1.86772e-02_rt, 2.28260e-02_rt, 2.75012e-02_rt, 3.27141e-02_rt, + 3.84722e-02_rt, 4.47790e-02_rt, 5.16352e-02_rt, 5.90381e-02_rt, + 6.69827e-02_rt, 7.54613e-02_rt, 8.44644e-02_rt, 9.39806e-02_rt, + 1.03997e-01_rt, 1.14500e-01_rt, 1.25473e-01_rt, 1.36901e-01_rt, + 1.48766e-01_rt, 1.61051e-01_rt, 1.73738e-01_rt, 1.86809e-01_rt, + 2.00245e-01_rt, 2.14027e-01_rt, 2.28137e-01_rt, 2.42554e-01_rt, + 2.57261e-01_rt, 2.72239e-01_rt, 2.87468e-01_rt, 3.02930e-01_rt, + 3.18607e-01_rt, 3.34479e-01_rt, 3.50529e-01_rt, 3.66738e-01_rt, + 3.83088e-01_rt, 3.99562e-01_rt, 4.16141e-01_rt, 4.32808e-01_rt, + 4.49545e-01_rt, 4.66334e-01_rt, 4.83158e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 6.12543e-38_rt, 6.85566e-20_rt, 9.09852e-14_rt, + 1.17683e-10_rt, 9.26345e-09_rt, 1.77633e-07_rt, 1.50917e-06_rt, + 7.67527e-06_rt, 2.76457e-05_rt, 7.80584e-05_rt, 1.84367e-04_rt, + 3.80484e-04_rt, 7.07218e-04_rt, 1.21002e-03_rt, 1.93657e-03_rt, + 2.93453e-03_rt, 4.24963e-03_rt, 5.92428e-03_rt, 7.99658e-03_rt, + 1.04997e-02_rt, 1.34615e-02_rt, 1.69048e-02_rt, 2.08471e-02_rt, + 2.53013e-02_rt, 3.02755e-02_rt, 3.57742e-02_rt, 4.17979e-02_rt, + 4.83441e-02_rt, 5.54072e-02_rt, 6.29794e-02_rt, 7.10509e-02_rt, + 7.96100e-02_rt, 8.86436e-02_rt, 9.81373e-02_rt, 1.08076e-01_rt, + 1.18444e-01_rt, 1.29224e-01_rt, 1.40401e-01_rt, 1.51955e-01_rt, + 1.63871e-01_rt, 1.76131e-01_rt, 1.88717e-01_rt, 2.01613e-01_rt, + 2.14801e-01_rt, 2.28264e-01_rt, 2.41986e-01_rt, 2.55951e-01_rt, + 2.70141e-01_rt, 2.84543e-01_rt, 2.99139e-01_rt, 3.13914e-01_rt, + 3.28855e-01_rt, 3.43945e-01_rt, 3.59170e-01_rt, 3.74517e-01_rt, + 3.89970e-01_rt, 4.05517e-01_rt, 4.21143e-01_rt, 4.36836e-01_rt, + 4.52581e-01_rt, 4.68365e-01_rt, 4.84176e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.16256e-33_rt, 1.42325e-17_rt, 3.00080e-12_rt, + 1.54610e-09_rt, 7.00286e-08_rt, 9.28729e-07_rt, 6.06220e-06_rt, + 2.52969e-05_rt, 7.81137e-05_rt, 1.94977e-04_rt, 4.16312e-04_rt, + 7.89827e-04_rt, 1.36715e-03_rt, 2.20057e-03_rt, 3.34031e-03_rt, + 4.83252e-03_rt, 6.71797e-03_rt, 9.03129e-03_rt, 1.18008e-02_rt, + 1.50485e-02_rt, 1.87904e-02_rt, 2.30372e-02_rt, 2.77946e-02_rt, + 3.30639e-02_rt, 3.88427e-02_rt, 4.51252e-02_rt, 5.19032e-02_rt, + 5.91659e-02_rt, 6.69010e-02_rt, 7.50945e-02_rt, 8.37314e-02_rt, + 9.27957e-02_rt, 1.02271e-01_rt, 1.12140e-01_rt, 1.22387e-01_rt, + 1.32993e-01_rt, 1.43941e-01_rt, 1.55215e-01_rt, 1.66797e-01_rt, + 1.78671e-01_rt, 1.90822e-01_rt, 2.03232e-01_rt, 2.15886e-01_rt, + 2.28770e-01_rt, 2.41869e-01_rt, 2.55168e-01_rt, 2.68655e-01_rt, + 2.82314e-01_rt, 2.96134e-01_rt, 3.10102e-01_rt, 3.24206e-01_rt, + 3.38434e-01_rt, 3.52773e-01_rt, 3.67214e-01_rt, 3.81745e-01_rt, + 3.96356e-01_rt, 4.11036e-01_rt, 4.25774e-01_rt, 4.40561e-01_rt, + 4.55387e-01_rt, 4.70242e-01_rt, 4.85116e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.77813e-29_rt, 1.44680e-15_rt, 6.22031e-11_rt, + 1.44615e-08_rt, 4.06135e-07_rt, 3.91507e-06_rt, 2.03425e-05_rt, + 7.15233e-05_rt, 1.93276e-04_rt, 4.33552e-04_rt, 8.48163e-04_rt, + 1.49588e-03_rt, 2.43410e-03_rt, 3.71562e-03_rt, 5.38660e-03_rt, + 7.48549e-03_rt, 1.00427e-02_rt, 1.30811e-02_rt, 1.66160e-02_rt, + 2.06565e-02_rt, 2.52060e-02_rt, 3.02631e-02_rt, 3.58223e-02_rt, + 4.18747e-02_rt, 4.84089e-02_rt, 5.54111e-02_rt, 6.28663e-02_rt, + 7.07580e-02_rt, 7.90688e-02_rt, 8.77809e-02_rt, 9.68763e-02_rt, + 1.06337e-01_rt, 1.16144e-01_rt, 1.26280e-01_rt, 1.36728e-01_rt, + 1.47470e-01_rt, 1.58490e-01_rt, 1.69771e-01_rt, 1.81297e-01_rt, + 1.93054e-01_rt, 2.05027e-01_rt, 2.17202e-01_rt, 2.29565e-01_rt, + 2.42104e-01_rt, 2.54806e-01_rt, 2.67660e-01_rt, 2.80655e-01_rt, + 2.93779e-01_rt, 3.07022e-01_rt, 3.20374e-01_rt, 3.33826e-01_rt, + 3.47369e-01_rt, 3.60993e-01_rt, 3.74691e-01_rt, 3.88454e-01_rt, + 4.02275e-01_rt, 4.16145e-01_rt, 4.30057e-01_rt, 4.44004e-01_rt, + 4.57979e-01_rt, 4.71975e-01_rt, 4.85984e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.27452e-25_rt, 7.93605e-14_rt, 8.63184e-10_rt, + 1.00847e-07_rt, 1.87306e-06_rt, 1.36999e-05_rt, 5.84249e-05_rt, + 1.77091e-04_rt, 4.26306e-04_rt, 8.71675e-04_rt, 1.58062e-03_rt, + 2.61657e-03_rt, 4.03518e-03_rt, 5.88232e-03_rt, 8.19354e-03_rt, + 1.09944e-02_rt, 1.43012e-02_rt, 1.81224e-02_rt, 2.24595e-02_rt, + 2.73084e-02_rt, 3.26606e-02_rt, 3.85039e-02_rt, 4.48233e-02_rt, + 5.16019e-02_rt, 5.88213e-02_rt, 6.64621e-02_rt, 7.45046e-02_rt, + 8.29285e-02_rt, 9.17139e-02_rt, 1.00841e-01_rt, 1.10290e-01_rt, + 1.20043e-01_rt, 1.30080e-01_rt, 1.40385e-01_rt, 1.50940e-01_rt, + 1.61729e-01_rt, 1.72737e-01_rt, 1.83949e-01_rt, 1.95351e-01_rt, + 2.06930e-01_rt, 2.18673e-01_rt, 2.30567e-01_rt, 2.42603e-01_rt, + 2.54769e-01_rt, 2.67055e-01_rt, 2.79452e-01_rt, 2.91951e-01_rt, + 3.04543e-01_rt, 3.17220e-01_rt, 3.29974e-01_rt, 3.42800e-01_rt, + 3.55688e-01_rt, 3.68635e-01_rt, 3.81632e-01_rt, 3.94674e-01_rt, + 4.07756e-01_rt, 4.20872e-01_rt, 4.34016e-01_rt, 4.47185e-01_rt, + 4.60372e-01_rt, 4.73574e-01_rt, 4.86785e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.44202e-22_rt, 2.55451e-12_rt, 8.46798e-09_rt, + 5.45600e-07_rt, 7.08563e-06_rt, 4.08072e-05_rt, 1.46658e-04_rt, + 3.90884e-04_rt, 8.51202e-04_rt, 1.60605e-03_rt, 2.72653e-03_rt, + 4.27184e-03_rt, 6.28755e-03_rt, 8.80575e-03_rt, 1.18463e-02_rt, + 1.54184e-02_rt, 1.95227e-02_rt, 2.41527e-02_rt, 2.92967e-02_rt, + 3.49387e-02_rt, 4.10601e-02_rt, 4.76399e-02_rt, 5.46561e-02_rt, + 6.20859e-02_rt, 6.99065e-02_rt, 7.80948e-02_rt, 8.66286e-02_rt, + 9.54859e-02_rt, 1.04646e-01_rt, 1.14088e-01_rt, 1.23792e-01_rt, + 1.33741e-01_rt, 1.43918e-01_rt, 1.54304e-01_rt, 1.64885e-01_rt, + 1.75647e-01_rt, 1.86575e-01_rt, 1.97657e-01_rt, 2.08880e-01_rt, + 2.20233e-01_rt, 2.31706e-01_rt, 2.43289e-01_rt, 2.54973e-01_rt, + 2.66748e-01_rt, 2.78608e-01_rt, 2.90545e-01_rt, 3.02551e-01_rt, + 3.14621e-01_rt, 3.26749e-01_rt, 3.38928e-01_rt, 3.51154e-01_rt, + 3.63422e-01_rt, 3.75728e-01_rt, 3.88066e-01_rt, 4.00434e-01_rt, + 4.12826e-01_rt, 4.25241e-01_rt, 4.37673e-01_rt, 4.50121e-01_rt, + 4.62580e-01_rt, 4.75049e-01_rt, 4.87523e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 6.34899e-20_rt, 5.18780e-11_rt, 6.15571e-08_rt, + 2.37013e-06_rt, 2.25825e-05_rt, 1.05734e-04_rt, 3.27625e-04_rt, + 7.81043e-04_rt, 1.55910e-03_rt, 2.74338e-03_rt, 4.39812e-03_rt, + 6.56886e-03_rt, 9.28382e-03_rt, 1.25563e-02_rt, 1.63876e-02_rt, + 2.07697e-02_rt, 2.56876e-02_rt, 3.11213e-02_rt, 3.70476e-02_rt, + 4.34410e-02_rt, 5.02751e-02_rt, 5.75228e-02_rt, 6.51575e-02_rt, + 7.31527e-02_rt, 8.14833e-02_rt, 9.01246e-02_rt, 9.90536e-02_rt, + 1.08248e-01_rt, 1.17687e-01_rt, 1.27352e-01_rt, 1.37223e-01_rt, + 1.47284e-01_rt, 1.57520e-01_rt, 1.67914e-01_rt, 1.78453e-01_rt, + 1.89125e-01_rt, 1.99918e-01_rt, 2.10821e-01_rt, 2.21824e-01_rt, + 2.32917e-01_rt, 2.44093e-01_rt, 2.55342e-01_rt, 2.66659e-01_rt, + 2.78036e-01_rt, 2.89468e-01_rt, 3.00948e-01_rt, 3.12472e-01_rt, + 3.24035e-01_rt, 3.35634e-01_rt, 3.47263e-01_rt, 3.58920e-01_rt, + 3.70601e-01_rt, 3.82304e-01_rt, 3.94025e-01_rt, 4.05762e-01_rt, + 4.17513e-01_rt, 4.29276e-01_rt, 4.41049e-01_rt, 4.52830e-01_rt, + 4.64617e-01_rt, 4.76409e-01_rt, 4.88204e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.23682e-17_rt, 7.07629e-10_rt, 3.45337e-07_rt, + 8.51604e-06_rt, 6.20521e-05_rt, 2.42815e-04_rt, 6.61580e-04_rt, + 1.43165e-03_rt, 2.64983e-03_rt, 4.38816e-03_rt, 6.69315e-03_rt, + 9.58883e-03_rt, 1.30811e-02_rt, 1.71623e-02_rt, 2.18148e-02_rt, + 2.70141e-02_rt, 3.27317e-02_rt, 3.89366e-02_rt, 4.55966e-02_rt, + 5.26795e-02_rt, 6.01537e-02_rt, 6.79886e-02_rt, 7.61550e-02_rt, + 8.46252e-02_rt, 9.33733e-02_rt, 1.02375e-01_rt, 1.11607e-01_rt, + 1.21049e-01_rt, 1.30681e-01_rt, 1.40485e-01_rt, 1.50445e-01_rt, + 1.60546e-01_rt, 1.70773e-01_rt, 1.81113e-01_rt, 1.91555e-01_rt, + 2.02089e-01_rt, 2.12704e-01_rt, 2.23391e-01_rt, 2.34143e-01_rt, + 2.44952e-01_rt, 2.55812e-01_rt, 2.66716e-01_rt, 2.77660e-01_rt, + 2.88637e-01_rt, 2.99645e-01_rt, 3.10678e-01_rt, 3.21734e-01_rt, + 3.32810e-01_rt, 3.43902e-01_rt, 3.55008e-01_rt, 3.66127e-01_rt, + 3.77255e-01_rt, 3.88392e-01_rt, 3.99537e-01_rt, 4.10687e-01_rt, + 4.21842e-01_rt, 4.33001e-01_rt, 4.44163e-01_rt, 4.55328e-01_rt, + 4.66494e-01_rt, 4.77662e-01_rt, 4.88831e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.19109e-15_rt, 6.84291e-09_rt, 1.54848e-06_rt, + 2.59650e-05_rt, 1.49964e-04_rt, 5.02268e-04_rt, 1.22395e-03_rt, + 2.43500e-03_rt, 4.22038e-03_rt, 6.62970e-03_rt, 9.68294e-03_rt, + 1.33777e-02_rt, 1.76958e-02_rt, 2.26088e-02_rt, 2.80820e-02_rt, + 3.40772e-02_rt, 4.05551e-02_rt, 4.74768e-02_rt, 5.48042e-02_rt, + 6.25010e-02_rt, 7.05331e-02_rt, 7.88685e-02_rt, 8.74775e-02_rt, + 9.63328e-02_rt, 1.05409e-01_rt, 1.14683e-01_rt, 1.24134e-01_rt, + 1.33743e-01_rt, 1.43491e-01_rt, 1.53363e-01_rt, 1.63345e-01_rt, + 1.73424e-01_rt, 1.83587e-01_rt, 1.93824e-01_rt, 2.04125e-01_rt, + 2.14482e-01_rt, 2.24887e-01_rt, 2.35333e-01_rt, 2.45813e-01_rt, + 2.56323e-01_rt, 2.66857e-01_rt, 2.77411e-01_rt, 2.87981e-01_rt, + 2.98564e-01_rt, 3.09157e-01_rt, 3.19757e-01_rt, 3.30363e-01_rt, + 3.40972e-01_rt, 3.51582e-01_rt, 3.62194e-01_rt, 3.72805e-01_rt, + 3.83415e-01_rt, 3.94024e-01_rt, 4.04631e-01_rt, 4.15235e-01_rt, + 4.25837e-01_rt, 4.36437e-01_rt, 4.47034e-01_rt, 4.57630e-01_rt, + 4.68224e-01_rt, 4.78817e-01_rt, 4.89409e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 6.24054e-14_rt, 4.91529e-08_rt, 5.72022e-06_rt, + 6.86778e-05_rt, 3.24283e-04_rt, 9.48942e-04_rt, 2.09875e-03_rt, + 3.88104e-03_rt, 6.35297e-03_rt, 9.53137e-03_rt, 1.34045e-02_rt, + 1.79422e-02_rt, 2.31033e-02_rt, 2.88412e-02_rt, 3.51073e-02_rt, + 4.18532e-02_rt, 4.90324e-02_rt, 5.66011e-02_rt, 6.45182e-02_rt, + 7.27460e-02_rt, 8.12500e-02_rt, 8.99985e-02_rt, 9.89631e-02_rt, + 1.08118e-01_rt, 1.17439e-01_rt, 1.26907e-01_rt, 1.36501e-01_rt, + 1.46205e-01_rt, 1.56004e-01_rt, 1.65883e-01_rt, 1.75832e-01_rt, + 1.85839e-01_rt, 1.95894e-01_rt, 2.05989e-01_rt, 2.16116e-01_rt, + 2.26268e-01_rt, 2.36440e-01_rt, 2.46627e-01_rt, 2.56824e-01_rt, + 2.67026e-01_rt, 2.77231e-01_rt, 2.87436e-01_rt, 2.97638e-01_rt, + 3.07836e-01_rt, 3.18027e-01_rt, 3.28210e-01_rt, 3.38385e-01_rt, + 3.48550e-01_rt, 3.58706e-01_rt, 3.68851e-01_rt, 3.78987e-01_rt, + 3.89112e-01_rt, 3.99228e-01_rt, 4.09334e-01_rt, 4.19431e-01_rt, + 4.29521e-01_rt, 4.39603e-01_rt, 4.49680e-01_rt, 4.59750e-01_rt, + 4.69817e-01_rt, 4.79880e-01_rt, 4.89940e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.93231e-12_rt, 2.73037e-07_rt, 1.78694e-05_rt, + 1.60620e-04_rt, 6.36814e-04_rt, 1.65732e-03_rt, 3.36918e-03_rt, + 5.84631e-03_rt, 9.10512e-03_rt, 1.31238e-02_rt, 1.78577e-02_rt, + 2.32510e-02_rt, 2.92429e-02_rt, 3.57726e-02_rt, 4.27817e-02_rt, + 5.02155e-02_rt, 5.80236e-02_rt, 6.61603e-02_rt, 7.45842e-02_rt, + 8.32580e-02_rt, 9.21486e-02_rt, 1.01226e-01_rt, 1.10465e-01_rt, + 1.19840e-01_rt, 1.29333e-01_rt, 1.38923e-01_rt, 1.48595e-01_rt, + 1.58334e-01_rt, 1.68127e-01_rt, 1.77965e-01_rt, 1.87835e-01_rt, + 1.97731e-01_rt, 2.07644e-01_rt, 2.17567e-01_rt, 2.27496e-01_rt, + 2.37425e-01_rt, 2.47350e-01_rt, 2.57267e-01_rt, 2.67173e-01_rt, + 2.77067e-01_rt, 2.86945e-01_rt, 2.96806e-01_rt, 3.06650e-01_rt, + 3.16474e-01_rt, 3.26280e-01_rt, 3.36065e-01_rt, 3.45830e-01_rt, + 3.55576e-01_rt, 3.65303e-01_rt, 3.75011e-01_rt, 3.84701e-01_rt, + 3.94374e-01_rt, 4.04031e-01_rt, 4.13672e-01_rt, 4.23300e-01_rt, + 4.32916e-01_rt, 4.42520e-01_rt, 4.52115e-01_rt, 4.61702e-01_rt, + 4.71282e-01_rt, 4.80858e-01_rt, 4.90430e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.79786e-11_rt, 1.21439e-06_rt, 4.82824e-05_rt, + 3.37662e-04_rt, 1.15028e-03_rt, 2.70359e-03_rt, 5.10744e-03_rt, + 8.38478e-03_rt, 1.25037e-02_rt, 1.74026e-02_rt, 2.30071e-02_rt, + 2.92397e-02_rt, 3.60251e-02_rt, 4.32929e-02_rt, 5.09793e-02_rt, + 5.90267e-02_rt, 6.73840e-02_rt, 7.60060e-02_rt, 8.48530e-02_rt, + 9.38900e-02_rt, 1.03086e-01_rt, 1.12415e-01_rt, 1.21853e-01_rt, + 1.31380e-01_rt, 1.40978e-01_rt, 1.50630e-01_rt, 1.60324e-01_rt, + 1.70049e-01_rt, 1.79792e-01_rt, 1.89547e-01_rt, 1.99304e-01_rt, + 2.09058e-01_rt, 2.18804e-01_rt, 2.28535e-01_rt, 2.38249e-01_rt, + 2.47942e-01_rt, 2.57611e-01_rt, 2.67255e-01_rt, 2.76871e-01_rt, + 2.86458e-01_rt, 2.96015e-01_rt, 3.05543e-01_rt, 3.15040e-01_rt, + 3.24507e-01_rt, 3.33944e-01_rt, 3.43351e-01_rt, 3.52730e-01_rt, + 3.62080e-01_rt, 3.71405e-01_rt, 3.80703e-01_rt, 3.89978e-01_rt, + 3.99229e-01_rt, 4.08460e-01_rt, 4.17671e-01_rt, 4.26864e-01_rt, + 4.36042e-01_rt, 4.45205e-01_rt, 4.54356e-01_rt, 4.63498e-01_rt, + 4.72630e-01_rt, 4.81757e-01_rt, 4.90880e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 5.03976e-10_rt, 4.45642e-06_rt, 1.15052e-04_rt, + 6.47172e-04_rt, 1.93239e-03_rt, 4.15645e-03_rt, 7.36609e-03_rt, + 1.15221e-02_rt, 1.65428e-02_rt, 2.23308e-02_rt, 2.87872e-02_rt, + 3.58187e-02_rt, 4.33409e-02_rt, 5.12788e-02_rt, 5.95667e-02_rt, + 6.81476e-02_rt, 7.69720e-02_rt, 8.59975e-02_rt, 9.51872e-02_rt, + 1.04510e-01_rt, 1.13938e-01_rt, 1.23448e-01_rt, 1.33020e-01_rt, + 1.42637e-01_rt, 1.52284e-01_rt, 1.61949e-01_rt, 1.71620e-01_rt, + 1.81288e-01_rt, 1.90946e-01_rt, 2.00586e-01_rt, 2.10205e-01_rt, + 2.19795e-01_rt, 2.29355e-01_rt, 2.38881e-01_rt, 2.48370e-01_rt, + 2.57821e-01_rt, 2.67232e-01_rt, 2.76602e-01_rt, 2.85931e-01_rt, + 2.95219e-01_rt, 3.04465e-01_rt, 3.13670e-01_rt, 3.22835e-01_rt, + 3.31961e-01_rt, 3.41048e-01_rt, 3.50099e-01_rt, 3.59113e-01_rt, + 3.68093e-01_rt, 3.77041e-01_rt, 3.85957e-01_rt, 3.94845e-01_rt, + 4.03706e-01_rt, 4.12541e-01_rt, 4.21354e-01_rt, 4.30145e-01_rt, + 4.38918e-01_rt, 4.47675e-01_rt, 4.56418e-01_rt, 4.65148e-01_rt, + 4.73870e-01_rt, 4.82584e-01_rt, 4.91293e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 4.76242e-09_rt, 1.38470e-05_rt, 2.45877e-04_rt, + 1.14480e-03_rt, 3.04803e-03_rt, 6.06887e-03_rt, 1.01723e-02_rt, + 1.52539e-02_rt, 2.11867e-02_rt, 2.78440e-02_rt, 3.51097e-02_rt, + 4.28814e-02_rt, 5.10706e-02_rt, 5.96018e-02_rt, 6.84111e-02_rt, + 7.74442e-02_rt, 8.66556e-02_rt, 9.60064e-02_rt, 1.05464e-01_rt, + 1.15002e-01_rt, 1.24595e-01_rt, 1.34225e-01_rt, 1.43875e-01_rt, + 1.53531e-01_rt, 1.63181e-01_rt, 1.72816e-01_rt, 1.82427e-01_rt, + 1.92008e-01_rt, 2.01552e-01_rt, 2.11056e-01_rt, 2.20516e-01_rt, + 2.29928e-01_rt, 2.39291e-01_rt, 2.48603e-01_rt, 2.57862e-01_rt, + 2.67069e-01_rt, 2.76224e-01_rt, 2.85325e-01_rt, 2.94374e-01_rt, + 3.03372e-01_rt, 3.12318e-01_rt, 3.21215e-01_rt, 3.30064e-01_rt, + 3.38867e-01_rt, 3.47624e-01_rt, 3.56338e-01_rt, 3.65011e-01_rt, + 3.73644e-01_rt, 3.82241e-01_rt, 3.90802e-01_rt, 3.99330e-01_rt, + 4.07828e-01_rt, 4.16298e-01_rt, 4.24742e-01_rt, 4.33163e-01_rt, + 4.41564e-01_rt, 4.49946e-01_rt, 4.58312e-01_rt, 4.66665e-01_rt, + 4.75008e-01_rt, 4.83343e-01_rt, 4.91673e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.35560e-08_rt, 3.72534e-05_rt, 4.78125e-04_rt, + 1.88884e-03_rt, 4.55138e-03_rt, 8.47206e-03_rt, 1.35259e-02_rt, + 1.95478e-02_rt, 2.63739e-02_rt, 3.38573e-02_rt, 4.18721e-02_rt, + 5.03127e-02_rt, 5.90915e-02_rt, 6.81360e-02_rt, 7.73863e-02_rt, + 8.67932e-02_rt, 9.63157e-02_rt, 1.05920e-01_rt, 1.15579e-01_rt, + 1.25268e-01_rt, 1.34970e-01_rt, 1.44667e-01_rt, 1.54348e-01_rt, + 1.64000e-01_rt, 1.73616e-01_rt, 1.83188e-01_rt, 1.92711e-01_rt, + 2.02180e-01_rt, 2.11590e-01_rt, 2.20941e-01_rt, 2.30229e-01_rt, + 2.39453e-01_rt, 2.48613e-01_rt, 2.57708e-01_rt, 2.66738e-01_rt, + 2.75704e-01_rt, 2.84606e-01_rt, 2.93446e-01_rt, 3.02224e-01_rt, + 3.10943e-01_rt, 3.19603e-01_rt, 3.28207e-01_rt, 3.36757e-01_rt, + 3.45254e-01_rt, 3.53701e-01_rt, 3.62100e-01_rt, 3.70454e-01_rt, + 3.78764e-01_rt, 3.87033e-01_rt, 3.95264e-01_rt, 4.03460e-01_rt, + 4.11622e-01_rt, 4.19754e-01_rt, 4.27858e-01_rt, 4.35938e-01_rt, + 4.43994e-01_rt, 4.52032e-01_rt, 4.60052e-01_rt, 4.68059e-01_rt, + 4.76054e-01_rt, 4.84041e-01_rt, 4.92022e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.83437e-07_rt, 8.84690e-05_rt, 8.56627e-04_rt, + 2.93343e-03_rt, 6.47963e-03_rt, 1.13727e-02_rt, 1.74006e-02_rt, + 2.43477e-02_rt, 3.20245e-02_rt, 4.02730e-02_rt, 4.89649e-02_rt, + 5.79966e-02_rt, 6.72849e-02_rt, 7.67632e-02_rt, 8.63775e-02_rt, + 9.60845e-02_rt, 1.05849e-01_rt, 1.15643e-01_rt, 1.25444e-01_rt, + 1.35231e-01_rt, 1.44992e-01_rt, 1.54713e-01_rt, 1.64385e-01_rt, + 1.74000e-01_rt, 1.83552e-01_rt, 1.93036e-01_rt, 2.02448e-01_rt, + 2.11787e-01_rt, 2.21050e-01_rt, 2.30236e-01_rt, 2.39345e-01_rt, + 2.48377e-01_rt, 2.57331e-01_rt, 2.66210e-01_rt, 2.75013e-01_rt, + 2.83743e-01_rt, 2.92401e-01_rt, 3.00988e-01_rt, 3.09507e-01_rt, + 3.17960e-01_rt, 3.26348e-01_rt, 3.34675e-01_rt, 3.42943e-01_rt, + 3.51154e-01_rt, 3.59310e-01_rt, 3.67415e-01_rt, 3.75470e-01_rt, + 3.83480e-01_rt, 3.91446e-01_rt, 3.99371e-01_rt, 4.07258e-01_rt, + 4.15111e-01_rt, 4.22931e-01_rt, 4.30721e-01_rt, 4.38486e-01_rt, + 4.46227e-01_rt, 4.53947e-01_rt, 4.61649e-01_rt, 4.69337e-01_rt, + 4.77013e-01_rt, 4.84680e-01_rt, 4.92342e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 8.05120e-07_rt, 1.88562e-04_rt, 1.42938e-03_rt, + 4.32199e-03_rt, 8.84910e-03_rt, 1.47535e-02_rt, 2.17477e-02_rt, + 2.95801e-02_rt, 3.80469e-02_rt, 4.69880e-02_rt, 5.62785e-02_rt, + 6.58213e-02_rt, 7.55404e-02_rt, 8.53767e-02_rt, 9.52835e-02_rt, + 1.05224e-01_rt, 1.15169e-01_rt, 1.25097e-01_rt, 1.34988e-01_rt, + 1.44829e-01_rt, 1.54608e-01_rt, 1.64317e-01_rt, 1.73949e-01_rt, + 1.83499e-01_rt, 1.92964e-01_rt, 2.02341e-01_rt, 2.11628e-01_rt, + 2.20825e-01_rt, 2.29931e-01_rt, 2.38947e-01_rt, 2.47873e-01_rt, + 2.56711e-01_rt, 2.65462e-01_rt, 2.74128e-01_rt, 2.82710e-01_rt, + 2.91212e-01_rt, 2.99634e-01_rt, 3.07980e-01_rt, 3.16252e-01_rt, + 3.24452e-01_rt, 3.32584e-01_rt, 3.40650e-01_rt, 3.48652e-01_rt, + 3.56595e-01_rt, 3.64480e-01_rt, 3.72310e-01_rt, 3.80089e-01_rt, + 3.87820e-01_rt, 3.95504e-01_rt, 4.03147e-01_rt, 4.10749e-01_rt, + 4.18315e-01_rt, 4.25848e-01_rt, 4.33350e-01_rt, 4.40825e-01_rt, + 4.48275e-01_rt, 4.55704e-01_rt, 4.63115e-01_rt, 4.70510e-01_rt, + 4.77893e-01_rt, 4.85267e-01_rt, 4.92635e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 2.92242e-06_rt, 3.65922e-04_rt, 2.24206e-03_rt, + 6.08232e-03_rt, 1.16544e-02_rt, 1.85759e-02_rt, 2.65021e-02_rt, + 3.51606e-02_rt, 4.43448e-02_rt, 5.38996e-02_rt, 6.37086e-02_rt, + 7.36840e-02_rt, 8.37593e-02_rt, 9.38837e-02_rt, 1.04018e-01_rt, + 1.14133e-01_rt, 1.24206e-01_rt, 1.34219e-01_rt, 1.44158e-01_rt, + 1.54014e-01_rt, 1.63779e-01_rt, 1.73447e-01_rt, 1.83015e-01_rt, + 1.92480e-01_rt, 2.01841e-01_rt, 2.11097e-01_rt, 2.20249e-01_rt, + 2.29296e-01_rt, 2.38241e-01_rt, 2.47084e-01_rt, 2.55827e-01_rt, + 2.64474e-01_rt, 2.73025e-01_rt, 2.81484e-01_rt, 2.89853e-01_rt, + 2.98135e-01_rt, 3.06333e-01_rt, 3.14449e-01_rt, 3.22487e-01_rt, + 3.30449e-01_rt, 3.38339e-01_rt, 3.46160e-01_rt, 3.53915e-01_rt, + 3.61607e-01_rt, 3.69240e-01_rt, 3.76815e-01_rt, 3.84338e-01_rt, + 3.91810e-01_rt, 3.99234e-01_rt, 4.06615e-01_rt, 4.13955e-01_rt, + 4.21258e-01_rt, 4.28526e-01_rt, 4.35762e-01_rt, 4.42971e-01_rt, + 4.50154e-01_rt, 4.57316e-01_rt, 4.64458e-01_rt, 4.71585e-01_rt, + 4.78700e-01_rt, 4.85805e-01_rt, 4.92904e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 8.99958e-06_rt, 6.54588e-04_rt, 3.33256e-03_rt, + 8.22390e-03_rt, 1.48698e-02_rt, 2.27846e-02_rt, 3.15873e-02_rt, + 4.09999e-02_rt, 5.08222e-02_rt, 6.09102e-02_rt, 7.11594e-02_rt, + 8.14935e-02_rt, 9.18562e-02_rt, 1.02206e-01_rt, 1.12511e-01_rt, + 1.22750e-01_rt, 1.32904e-01_rt, 1.42962e-01_rt, 1.52914e-01_rt, + 1.62754e-01_rt, 1.72479e-01_rt, 1.82084e-01_rt, 1.91570e-01_rt, + 2.00935e-01_rt, 2.10180e-01_rt, 2.19307e-01_rt, 2.28316e-01_rt, + 2.37211e-01_rt, 2.45992e-01_rt, 2.54663e-01_rt, 2.63227e-01_rt, + 2.71686e-01_rt, 2.80044e-01_rt, 2.88304e-01_rt, 2.96468e-01_rt, + 3.04541e-01_rt, 3.12525e-01_rt, 3.20424e-01_rt, 3.28241e-01_rt, + 3.35980e-01_rt, 3.43644e-01_rt, 3.51237e-01_rt, 3.58761e-01_rt, + 3.66220e-01_rt, 3.73617e-01_rt, 3.80957e-01_rt, 3.88241e-01_rt, + 3.95474e-01_rt, 4.02659e-01_rt, 4.09799e-01_rt, 4.16897e-01_rt, + 4.23957e-01_rt, 4.30981e-01_rt, 4.37974e-01_rt, 4.44938e-01_rt, + 4.51876e-01_rt, 4.58792e-01_rt, 4.65689e-01_rt, 4.72571e-01_rt, + 4.79439e-01_rt, 4.86298e-01_rt, 4.93151e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 2.40362e-05_rt, 1.09102e-03_rt, 4.72659e-03_rt, + 1.07377e-02_rt, 1.84532e-02_rt, 2.73132e-02_rt, 3.69221e-02_rt, + 4.70091e-02_rt, 5.73883e-02_rt, 6.79309e-02_rt, 7.85465e-02_rt, + 8.91714e-02_rt, 9.97598e-02_rt, 1.10279e-01_rt, 1.20706e-01_rt, + 1.31025e-01_rt, 1.41223e-01_rt, 1.51293e-01_rt, 1.61230e-01_rt, + 1.71030e-01_rt, 1.80694e-01_rt, 1.90220e-01_rt, 1.99610e-01_rt, + 2.08865e-01_rt, 2.17987e-01_rt, 2.26979e-01_rt, 2.35844e-01_rt, + 2.44584e-01_rt, 2.53204e-01_rt, 2.61706e-01_rt, 2.70095e-01_rt, + 2.78373e-01_rt, 2.86545e-01_rt, 2.94614e-01_rt, 3.02583e-01_rt, + 3.10458e-01_rt, 3.18240e-01_rt, 3.25935e-01_rt, 3.33545e-01_rt, + 3.41075e-01_rt, 3.48528e-01_rt, 3.55907e-01_rt, 3.63216e-01_rt, + 3.70459e-01_rt, 3.77639e-01_rt, 3.84760e-01_rt, 3.91825e-01_rt, + 3.98837e-01_rt, 4.05801e-01_rt, 4.12719e-01_rt, 4.19594e-01_rt, + 4.26431e-01_rt, 4.33232e-01_rt, 4.40000e-01_rt, 4.46740e-01_rt, + 4.53454e-01_rt, 4.60145e-01_rt, 4.66817e-01_rt, 4.73473e-01_rt, + 4.80116e-01_rt, 4.86749e-01_rt, 4.93376e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 5.67455e-05_rt, 1.70998e-03_rt, 6.43514e-03_rt, + 1.35980e-02_rt, 2.23497e-02_rt, 3.20889e-02_rt, 4.24247e-02_rt, + 5.31040e-02_rt, 6.39605e-02_rt, 7.48834e-02_rt, 8.57977e-02_rt, + 9.66524e-02_rt, 1.07412e-01_rt, 1.18054e-01_rt, 1.28561e-01_rt, + 1.38923e-01_rt, 1.49134e-01_rt, 1.59189e-01_rt, 1.69089e-01_rt, + 1.78831e-01_rt, 1.88419e-01_rt, 1.97854e-01_rt, 2.07139e-01_rt, + 2.16278e-01_rt, 2.25272e-01_rt, 2.34128e-01_rt, 2.42848e-01_rt, + 2.51436e-01_rt, 2.59897e-01_rt, 2.68236e-01_rt, 2.76455e-01_rt, + 2.84560e-01_rt, 2.92554e-01_rt, 3.00442e-01_rt, 3.08227e-01_rt, + 3.15914e-01_rt, 3.23508e-01_rt, 3.31011e-01_rt, 3.38428e-01_rt, + 3.45762e-01_rt, 3.53018e-01_rt, 3.60199e-01_rt, 3.67309e-01_rt, + 3.74352e-01_rt, 3.81331e-01_rt, 3.88250e-01_rt, 3.95112e-01_rt, + 4.01921e-01_rt, 4.08681e-01_rt, 4.15395e-01_rt, 4.22065e-01_rt, + 4.28697e-01_rt, 4.35293e-01_rt, 4.41856e-01_rt, 4.48390e-01_rt, + 4.54898e-01_rt, 4.61383e-01_rt, 4.67849e-01_rt, 4.74298e-01_rt, + 4.80735e-01_rt, 4.87162e-01_rt, 4.93583e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.20376e-04_rt, 2.54037e-03_rt, 8.45388e-03_rt, + 1.67654e-02_rt, 2.64972e-02_rt, 3.70377e-02_rt, 4.80170e-02_rt, + 5.92074e-02_rt, 7.04660e-02_rt, 8.17011e-02_rt, 9.28535e-02_rt, + 1.03885e-01_rt, 1.14770e-01_rt, 1.25493e-01_rt, 1.36045e-01_rt, + 1.46421e-01_rt, 1.56620e-01_rt, 1.66640e-01_rt, 1.76485e-01_rt, + 1.86156e-01_rt, 1.95657e-01_rt, 2.04993e-01_rt, 2.14168e-01_rt, + 2.23186e-01_rt, 2.32052e-01_rt, 2.40771e-01_rt, 2.49349e-01_rt, + 2.57789e-01_rt, 2.66096e-01_rt, 2.74276e-01_rt, 2.82334e-01_rt, + 2.90273e-01_rt, 2.98099e-01_rt, 3.05816e-01_rt, 3.13428e-01_rt, + 3.20940e-01_rt, 3.28355e-01_rt, 3.35680e-01_rt, 3.42916e-01_rt, + 3.50069e-01_rt, 3.57142e-01_rt, 3.64140e-01_rt, 3.71065e-01_rt, + 3.77923e-01_rt, 3.84716e-01_rt, 3.91449e-01_rt, 3.98125e-01_rt, + 4.04747e-01_rt, 4.11319e-01_rt, 4.17845e-01_rt, 4.24328e-01_rt, + 4.30772e-01_rt, 4.37179e-01_rt, 4.43554e-01_rt, 4.49899e-01_rt, + 4.56219e-01_rt, 4.62515e-01_rt, 4.68792e-01_rt, 4.75053e-01_rt, + 4.81301e-01_rt, 4.87540e-01_rt, 4.93771e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 2.32725e-04_rt, 3.60193e-03_rt, 1.07643e-02_rt, + 2.01911e-02_rt, 3.08302e-02_rt, 4.20879e-02_rt, 5.36273e-02_rt, + 6.52517e-02_rt, 7.68429e-02_rt, 8.83291e-02_rt, 9.96663e-02_rt, + 1.10828e-01_rt, 1.21798e-01_rt, 1.32570e-01_rt, 1.43139e-01_rt, + 1.53506e-01_rt, 1.63672e-01_rt, 1.73641e-01_rt, 1.83418e-01_rt, + 1.93008e-01_rt, 2.02416e-01_rt, 2.11648e-01_rt, 2.20710e-01_rt, + 2.29607e-01_rt, 2.38345e-01_rt, 2.46931e-01_rt, 2.55369e-01_rt, + 2.63666e-01_rt, 2.71826e-01_rt, 2.79855e-01_rt, 2.87759e-01_rt, + 2.95541e-01_rt, 3.03208e-01_rt, 3.10764e-01_rt, 3.18213e-01_rt, + 3.25561e-01_rt, 3.32811e-01_rt, 3.39969e-01_rt, 3.47038e-01_rt, + 3.54022e-01_rt, 3.60926e-01_rt, 3.67754e-01_rt, 3.74509e-01_rt, + 3.81196e-01_rt, 3.87818e-01_rt, 3.94380e-01_rt, 4.00884e-01_rt, + 4.07334e-01_rt, 4.13734e-01_rt, 4.20088e-01_rt, 4.26399e-01_rt, + 4.32670e-01_rt, 4.38905e-01_rt, 4.45107e-01_rt, 4.51280e-01_rt, + 4.57427e-01_rt, 4.63551e-01_rt, 4.69655e-01_rt, 4.75744e-01_rt, + 4.81819e-01_rt, 4.87885e-01_rt, 4.93944e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 4.15090e-04_rt, 4.90330e-03_rt, 1.33363e-02_rt, + 2.38205e-02_rt, 3.52837e-02_rt, 4.71726e-02_rt, 5.91921e-02_rt, + 7.11790e-02_rt, 8.30406e-02_rt, 9.47240e-02_rt, 1.06200e-01_rt, + 1.17453e-01_rt, 1.28476e-01_rt, 1.39268e-01_rt, 1.49832e-01_rt, + 1.60170e-01_rt, 1.70290e-01_rt, 1.80196e-01_rt, 1.89897e-01_rt, + 1.99399e-01_rt, 2.08709e-01_rt, 2.17835e-01_rt, 2.26783e-01_rt, + 2.35561e-01_rt, 2.44174e-01_rt, 2.52630e-01_rt, 2.60934e-01_rt, + 2.69093e-01_rt, 2.77113e-01_rt, 2.84999e-01_rt, 2.92756e-01_rt, + 3.00392e-01_rt, 3.07909e-01_rt, 3.15314e-01_rt, 3.22612e-01_rt, + 3.29806e-01_rt, 3.36903e-01_rt, 3.43906e-01_rt, 3.50819e-01_rt, + 3.57647e-01_rt, 3.64395e-01_rt, 3.71066e-01_rt, 3.77664e-01_rt, + 3.84194e-01_rt, 3.90659e-01_rt, 3.97062e-01_rt, 4.03409e-01_rt, + 4.09701e-01_rt, 4.15943e-01_rt, 4.22139e-01_rt, 4.28292e-01_rt, + 4.34405e-01_rt, 4.40483e-01_rt, 4.46527e-01_rt, 4.52542e-01_rt, + 4.58531e-01_rt, 4.64497e-01_rt, 4.70444e-01_rt, 4.76375e-01_rt, + 4.82292e-01_rt, 4.88200e-01_rt, 4.94102e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 6.90267e-04_rt, 6.44151e-03_rt, 1.61308e-02_rt, + 2.75971e-02_rt, 3.97962e-02_rt, 5.22323e-02_rt, 6.46571e-02_rt, + 7.69417e-02_rt, 8.90185e-02_rt, 1.00853e-01_rt, 1.12429e-01_rt, + 1.23741e-01_rt, 1.34790e-01_rt, 1.45581e-01_rt, 1.56120e-01_rt, + 1.66416e-01_rt, 1.76478e-01_rt, 1.86314e-01_rt, 1.95932e-01_rt, + 2.05343e-01_rt, 2.14554e-01_rt, 2.23573e-01_rt, 2.32410e-01_rt, + 2.41070e-01_rt, 2.49562e-01_rt, 2.57892e-01_rt, 2.66068e-01_rt, + 2.74096e-01_rt, 2.81983e-01_rt, 2.89733e-01_rt, 2.97354e-01_rt, + 3.04851e-01_rt, 3.12229e-01_rt, 3.19493e-01_rt, 3.26649e-01_rt, + 3.33702e-01_rt, 3.40655e-01_rt, 3.47515e-01_rt, 3.54284e-01_rt, + 3.60969e-01_rt, 3.67572e-01_rt, 3.74099e-01_rt, 3.80552e-01_rt, + 3.86937e-01_rt, 3.93257e-01_rt, 3.99516e-01_rt, 4.05718e-01_rt, + 4.11865e-01_rt, 4.17963e-01_rt, 4.24014e-01_rt, 4.30023e-01_rt, + 4.35991e-01_rt, 4.41924e-01_rt, 4.47824e-01_rt, 4.53695e-01_rt, + 4.59539e-01_rt, 4.65361e-01_rt, 4.71164e-01_rt, 4.76951e-01_rt, + 4.82724e-01_rt, 4.88488e-01_rt, 4.94246e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.07999e-03_rt, 8.20281e-03_rt, 1.91038e-02_rt, + 3.14654e-02_rt, 4.43117e-02_rt, 5.72152e-02_rt, 6.99770e-02_rt, + 8.25021e-02_rt, 9.47464e-02_rt, 1.06692e-01_rt, 1.18335e-01_rt, + 1.29679e-01_rt, 1.40733e-01_rt, 1.51505e-01_rt, 1.62007e-01_rt, + 1.72251e-01_rt, 1.82247e-01_rt, 1.92006e-01_rt, 2.01540e-01_rt, + 2.10857e-01_rt, 2.19969e-01_rt, 2.28884e-01_rt, 2.37610e-01_rt, + 2.46157e-01_rt, 2.54532e-01_rt, 2.62744e-01_rt, 2.70798e-01_rt, + 2.78702e-01_rt, 2.86463e-01_rt, 2.94086e-01_rt, 3.01579e-01_rt, + 3.08946e-01_rt, 3.16194e-01_rt, 3.23327e-01_rt, 3.30352e-01_rt, + 3.37272e-01_rt, 3.44094e-01_rt, 3.50821e-01_rt, 3.57458e-01_rt, + 3.64009e-01_rt, 3.70480e-01_rt, 3.76873e-01_rt, 3.83194e-01_rt, + 3.89446e-01_rt, 3.95633e-01_rt, 4.01759e-01_rt, 4.07828e-01_rt, + 4.13843e-01_rt, 4.19808e-01_rt, 4.25727e-01_rt, 4.31603e-01_rt, + 4.37440e-01_rt, 4.43240e-01_rt, 4.49008e-01_rt, 4.54747e-01_rt, + 4.60460e-01_rt, 4.66150e-01_rt, 4.71821e-01_rt, 4.77476e-01_rt, + 4.83118e-01_rt, 4.88751e-01_rt, 4.94377e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.60237e-03_rt, 1.01645e-02_rt, 2.22089e-02_rt, + 3.53733e-02_rt, 4.87807e-02_rt, 6.20782e-02_rt, 7.51161e-02_rt, + 8.78315e-02_rt, 1.00202e-01_rt, 1.12226e-01_rt, 1.23909e-01_rt, + 1.35265e-01_rt, 1.46305e-01_rt, 1.57046e-01_rt, 1.67501e-01_rt, + 1.77684e-01_rt, 1.87610e-01_rt, 1.97290e-01_rt, 2.06737e-01_rt, + 2.15962e-01_rt, 2.24975e-01_rt, 2.33788e-01_rt, 2.42409e-01_rt, + 2.50847e-01_rt, 2.59111e-01_rt, 2.67209e-01_rt, 2.75148e-01_rt, + 2.82935e-01_rt, 2.90578e-01_rt, 2.98083e-01_rt, 3.05456e-01_rt, + 3.12702e-01_rt, 3.19829e-01_rt, 3.26841e-01_rt, 3.33744e-01_rt, + 3.40543e-01_rt, 3.47242e-01_rt, 3.53846e-01_rt, 3.60361e-01_rt, + 3.66790e-01_rt, 3.73139e-01_rt, 3.79410e-01_rt, 3.85609e-01_rt, + 3.91739e-01_rt, 3.97804e-01_rt, 4.03808e-01_rt, 4.09755e-01_rt, + 4.15649e-01_rt, 4.21493e-01_rt, 4.27291e-01_rt, 4.33046e-01_rt, + 4.38762e-01_rt, 4.44442e-01_rt, 4.50089e-01_rt, 4.55707e-01_rt, + 4.61300e-01_rt, 4.66870e-01_rt, 4.72421e-01_rt, 4.77956e-01_rt, + 4.83478e-01_rt, 4.88991e-01_rt, 4.94497e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 2.26985e-03_rt, 1.22974e-02_rt, 2.53998e-02_rt, + 3.92737e-02_rt, 5.31612e-02_rt, 6.67863e-02_rt, 8.00466e-02_rt, + 9.29094e-02_rt, 1.05372e-01_rt, 1.17447e-01_rt, 1.29149e-01_rt, + 1.40498e-01_rt, 1.51513e-01_rt, 1.62211e-01_rt, 1.72612e-01_rt, + 1.82731e-01_rt, 1.92583e-01_rt, 2.02182e-01_rt, 2.11543e-01_rt, + 2.20677e-01_rt, 2.29596e-01_rt, 2.38310e-01_rt, 2.46830e-01_rt, + 2.55164e-01_rt, 2.63323e-01_rt, 2.71313e-01_rt, 2.79144e-01_rt, + 2.86822e-01_rt, 2.94354e-01_rt, 3.01748e-01_rt, 3.09009e-01_rt, + 3.16144e-01_rt, 3.23159e-01_rt, 3.30059e-01_rt, 3.36849e-01_rt, + 3.43535e-01_rt, 3.50121e-01_rt, 3.56613e-01_rt, 3.63016e-01_rt, + 3.69332e-01_rt, 3.75568e-01_rt, 3.81728e-01_rt, 3.87814e-01_rt, + 3.93833e-01_rt, 3.99786e-01_rt, 4.05679e-01_rt, 4.11515e-01_rt, + 4.17298e-01_rt, 4.23031e-01_rt, 4.28718e-01_rt, 4.34362e-01_rt, + 4.39968e-01_rt, 4.45537e-01_rt, 4.51075e-01_rt, 4.56583e-01_rt, + 4.62066e-01_rt, 4.67527e-01_rt, 4.72968e-01_rt, 4.78394e-01_rt, + 4.83806e-01_rt, 4.89209e-01_rt, 4.94606e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 3.08797e-03_rt, 1.45678e-02_rt, 2.86330e-02_rt, + 4.31255e-02_rt, 5.74186e-02_rt, 7.13124e-02_rt, 8.47486e-02_rt, + 9.77223e-02_rt, 1.10249e-01_rt, 1.22352e-01_rt, 1.34055e-01_rt, + 1.45385e-01_rt, 1.56364e-01_rt, 1.67014e-01_rt, 1.77355e-01_rt, + 1.87406e-01_rt, 1.97184e-01_rt, 2.06704e-01_rt, 2.15980e-01_rt, + 2.25025e-01_rt, 2.33852e-01_rt, 2.42472e-01_rt, 2.50895e-01_rt, + 2.59132e-01_rt, 2.67191e-01_rt, 2.75081e-01_rt, 2.82810e-01_rt, + 2.90386e-01_rt, 2.97816e-01_rt, 3.05106e-01_rt, 3.12264e-01_rt, + 3.19296e-01_rt, 3.26206e-01_rt, 3.33002e-01_rt, 3.39689e-01_rt, + 3.46271e-01_rt, 3.52754e-01_rt, 3.59142e-01_rt, 3.65441e-01_rt, + 3.71655e-01_rt, 3.77787e-01_rt, 3.83844e-01_rt, 3.89828e-01_rt, + 3.95744e-01_rt, 4.01595e-01_rt, 4.07386e-01_rt, 4.13120e-01_rt, + 4.18802e-01_rt, 4.24433e-01_rt, 4.30019e-01_rt, 4.35563e-01_rt, + 4.41068e-01_rt, 4.46537e-01_rt, 4.51974e-01_rt, 4.57382e-01_rt, + 4.62765e-01_rt, 4.68125e-01_rt, 4.73467e-01_rt, 4.78792e-01_rt, + 4.84105e-01_rt, 4.89409e-01_rt, 4.94706e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 4.05505e-03_rt, 1.69406e-02_rt, 3.18685e-02_rt, + 4.68937e-02_rt, 6.15255e-02_rt, 7.56363e-02_rt, 8.92090e-02_rt, + 1.02263e-01_rt, 1.14830e-01_rt, 1.26943e-01_rt, 1.38635e-01_rt, + 1.49935e-01_rt, 1.60871e-01_rt, 1.71468e-01_rt, 1.81747e-01_rt, + 1.91730e-01_rt, 2.01433e-01_rt, 2.10874e-01_rt, 2.20068e-01_rt, + 2.29028e-01_rt, 2.37767e-01_rt, 2.46298e-01_rt, 2.54630e-01_rt, + 2.62775e-01_rt, 2.70740e-01_rt, 2.78536e-01_rt, 2.86171e-01_rt, + 2.93651e-01_rt, 3.00986e-01_rt, 3.08180e-01_rt, 3.15242e-01_rt, + 3.22178e-01_rt, 3.28993e-01_rt, 3.35693e-01_rt, 3.42284e-01_rt, + 3.48770e-01_rt, 3.55158e-01_rt, 3.61451e-01_rt, 3.67655e-01_rt, + 3.73774e-01_rt, 3.79813e-01_rt, 3.85775e-01_rt, 3.91665e-01_rt, + 3.97487e-01_rt, 4.03245e-01_rt, 4.08943e-01_rt, 4.14584e-01_rt, + 4.20173e-01_rt, 4.25712e-01_rt, 4.31206e-01_rt, 4.36657e-01_rt, + 4.42070e-01_rt, 4.47448e-01_rt, 4.52793e-01_rt, 4.58110e-01_rt, + 4.63402e-01_rt, 4.68671e-01_rt, 4.73921e-01_rt, 4.79156e-01_rt, + 4.84378e-01_rt, 4.89590e-01_rt, 4.94797e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 5.16268e-03_rt, 1.93807e-02_rt, 3.50712e-02_rt, + 5.05499e-02_rt, 6.54611e-02_rt, 7.97445e-02_rt, 9.34202e-02_rt, + 1.06530e-01_rt, 1.19118e-01_rt, 1.31227e-01_rt, 1.42897e-01_rt, + 1.54160e-01_rt, 1.65049e-01_rt, 1.75589e-01_rt, 1.85806e-01_rt, + 1.95719e-01_rt, 2.05350e-01_rt, 2.14714e-01_rt, 2.23829e-01_rt, + 2.32708e-01_rt, 2.41364e-01_rt, 2.49810e-01_rt, 2.58057e-01_rt, + 2.66114e-01_rt, 2.73993e-01_rt, 2.81701e-01_rt, 2.89247e-01_rt, + 2.96640e-01_rt, 3.03885e-01_rt, 3.10992e-01_rt, 3.17965e-01_rt, + 3.24812e-01_rt, 3.31539e-01_rt, 3.38151e-01_rt, 3.44654e-01_rt, + 3.51053e-01_rt, 3.57353e-01_rt, 3.63559e-01_rt, 3.69676e-01_rt, + 3.75708e-01_rt, 3.81660e-01_rt, 3.87536e-01_rt, 3.93341e-01_rt, + 3.99077e-01_rt, 4.04750e-01_rt, 4.10362e-01_rt, 4.15919e-01_rt, + 4.21423e-01_rt, 4.26878e-01_rt, 4.32287e-01_rt, 4.37655e-01_rt, + 4.42984e-01_rt, 4.48278e-01_rt, 4.53540e-01_rt, 4.58773e-01_rt, + 4.63982e-01_rt, 4.69168e-01_rt, 4.74335e-01_rt, 4.79487e-01_rt, + 4.84626e-01_rt, 4.89756e-01_rt, 4.94880e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 6.39685e-03_rt, 2.18546e-02_rt, 3.82111e-02_rt, + 5.40718e-02_rt, 6.92104e-02_rt, 8.36286e-02_rt, 9.73795e-02_rt, + 1.10524e-01_rt, 1.23118e-01_rt, 1.35213e-01_rt, 1.46852e-01_rt, + 1.58074e-01_rt, 1.68912e-01_rt, 1.79395e-01_rt, 1.89548e-01_rt, + 1.99395e-01_rt, 2.08955e-01_rt, 2.18246e-01_rt, 2.27285e-01_rt, + 2.36086e-01_rt, 2.44664e-01_rt, 2.53030e-01_rt, 2.61197e-01_rt, + 2.69173e-01_rt, 2.76971e-01_rt, 2.84597e-01_rt, 2.92062e-01_rt, + 2.99372e-01_rt, 3.06536e-01_rt, 3.13561e-01_rt, 3.20453e-01_rt, + 3.27218e-01_rt, 3.33864e-01_rt, 3.40395e-01_rt, 3.46817e-01_rt, + 3.53135e-01_rt, 3.59355e-01_rt, 3.65481e-01_rt, 3.71519e-01_rt, + 3.77472e-01_rt, 3.83345e-01_rt, 3.89142e-01_rt, 3.94868e-01_rt, + 4.00526e-01_rt, 4.06121e-01_rt, 4.11656e-01_rt, 4.17135e-01_rt, + 4.22561e-01_rt, 4.27939e-01_rt, 4.33272e-01_rt, 4.38563e-01_rt, + 4.43816e-01_rt, 4.49033e-01_rt, 4.54219e-01_rt, 4.59377e-01_rt, + 4.64510e-01_rt, 4.69620e-01_rt, 4.74712e-01_rt, 4.79788e-01_rt, + 4.84852e-01_rt, 4.89907e-01_rt, 4.94955e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 7.73935e-03_rt, 2.43318e-02_rt, 4.12635e-02_rt, + 5.74425e-02_rt, 7.27638e-02_rt, 8.72850e-02_rt, 1.01088e-01_rt, + 1.14250e-01_rt, 1.26839e-01_rt, 1.38911e-01_rt, 1.50515e-01_rt, + 1.61692e-01_rt, 1.72478e-01_rt, 1.82903e-01_rt, 1.92995e-01_rt, + 2.02776e-01_rt, 2.12267e-01_rt, 2.21488e-01_rt, 2.30456e-01_rt, + 2.39184e-01_rt, 2.47688e-01_rt, 2.55980e-01_rt, 2.64071e-01_rt, + 2.71973e-01_rt, 2.79694e-01_rt, 2.87245e-01_rt, 2.94634e-01_rt, + 3.01869e-01_rt, 3.08958e-01_rt, 3.15907e-01_rt, 3.22724e-01_rt, + 3.29415e-01_rt, 3.35985e-01_rt, 3.42442e-01_rt, 3.48790e-01_rt, + 3.55035e-01_rt, 3.61181e-01_rt, 3.67234e-01_rt, 3.73199e-01_rt, + 3.79079e-01_rt, 3.84880e-01_rt, 3.90605e-01_rt, 3.96259e-01_rt, + 4.01846e-01_rt, 4.07370e-01_rt, 4.12834e-01_rt, 4.18242e-01_rt, + 4.23598e-01_rt, 4.28906e-01_rt, 4.34169e-01_rt, 4.39390e-01_rt, + 4.44573e-01_rt, 4.49722e-01_rt, 4.54838e-01_rt, 4.59927e-01_rt, + 4.64990e-01_rt, 4.70032e-01_rt, 4.75055e-01_rt, 4.80063e-01_rt, + 4.85058e-01_rt, 4.90044e-01_rt, 4.95023e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 9.16934e-03_rt, 2.67855e-02_rt, 4.42088e-02_rt, + 6.06502e-02_rt, 7.61162e-02_rt, 9.07138e-02_rt, 1.04550e-01_rt, + 1.17718e-01_rt, 1.30292e-01_rt, 1.42335e-01_rt, 1.53900e-01_rt, + 1.65031e-01_rt, 1.75764e-01_rt, 1.86132e-01_rt, 1.96163e-01_rt, + 2.05881e-01_rt, 2.15308e-01_rt, 2.24462e-01_rt, 2.33362e-01_rt, + 2.42022e-01_rt, 2.50457e-01_rt, 2.58679e-01_rt, 2.66701e-01_rt, + 2.74532e-01_rt, 2.82184e-01_rt, 2.89665e-01_rt, 2.96984e-01_rt, + 3.04149e-01_rt, 3.11168e-01_rt, 3.18048e-01_rt, 3.24796e-01_rt, + 3.31418e-01_rt, 3.37920e-01_rt, 3.44309e-01_rt, 3.50589e-01_rt, + 3.56766e-01_rt, 3.62845e-01_rt, 3.68832e-01_rt, 3.74729e-01_rt, + 3.80544e-01_rt, 3.86278e-01_rt, 3.91938e-01_rt, 3.97527e-01_rt, + 4.03048e-01_rt, 4.08507e-01_rt, 4.13906e-01_rt, 4.19250e-01_rt, + 4.24543e-01_rt, 4.29786e-01_rt, 4.34985e-01_rt, 4.40143e-01_rt, + 4.45263e-01_rt, 4.50348e-01_rt, 4.55402e-01_rt, 4.60427e-01_rt, + 4.65428e-01_rt, 4.70407e-01_rt, 4.75367e-01_rt, 4.80312e-01_rt, + 4.85245e-01_rt, 4.90168e-01_rt, 4.95086e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.06648e-02_rt, 2.91925e-02_rt, 4.70320e-02_rt, + 6.36874e-02_rt, 7.92659e-02_rt, 9.39181e-02_rt, 1.07773e-01_rt, + 1.20936e-01_rt, 1.33488e-01_rt, 1.45499e-01_rt, 1.57023e-01_rt, + 1.68106e-01_rt, 1.78787e-01_rt, 1.89099e-01_rt, 1.99072e-01_rt, + 2.08730e-01_rt, 2.18095e-01_rt, 2.27187e-01_rt, 2.36023e-01_rt, + 2.44619e-01_rt, 2.52990e-01_rt, 2.61147e-01_rt, 2.69104e-01_rt, + 2.76871e-01_rt, 2.84458e-01_rt, 2.91874e-01_rt, 2.99129e-01_rt, + 3.06230e-01_rt, 3.13185e-01_rt, 3.20001e-01_rt, 3.26686e-01_rt, + 3.33245e-01_rt, 3.39684e-01_rt, 3.46010e-01_rt, 3.52228e-01_rt, + 3.58344e-01_rt, 3.64361e-01_rt, 3.70286e-01_rt, 3.76124e-01_rt, + 3.81877e-01_rt, 3.87552e-01_rt, 3.93151e-01_rt, 3.98680e-01_rt, + 4.04143e-01_rt, 4.09542e-01_rt, 4.14883e-01_rt, 4.20168e-01_rt, + 4.25402e-01_rt, 4.30587e-01_rt, 4.35728e-01_rt, 4.40828e-01_rt, + 4.45890e-01_rt, 4.50918e-01_rt, 4.55914e-01_rt, 4.60882e-01_rt, + 4.65826e-01_rt, 4.70748e-01_rt, 4.75651e-01_rt, 4.80540e-01_rt, + 4.85415e-01_rt, 4.90282e-01_rt, 4.95143e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.22036e-02_rt, 3.15337e-02_rt, 4.97224e-02_rt, + 6.65502e-02_rt, 8.22144e-02_rt, 9.69035e-02_rt, 1.10765e-01_rt, + 1.23915e-01_rt, 1.36441e-01_rt, 1.48416e-01_rt, 1.59898e-01_rt, + 1.70933e-01_rt, 1.81564e-01_rt, 1.91823e-01_rt, 2.01740e-01_rt, + 2.11341e-01_rt, 2.20648e-01_rt, 2.29681e-01_rt, 2.38457e-01_rt, + 2.46994e-01_rt, 2.55304e-01_rt, 2.63402e-01_rt, 2.71299e-01_rt, + 2.79006e-01_rt, 2.86533e-01_rt, 2.93890e-01_rt, 3.01085e-01_rt, + 3.08127e-01_rt, 3.15024e-01_rt, 3.21782e-01_rt, 3.28408e-01_rt, + 3.34909e-01_rt, 3.41291e-01_rt, 3.47560e-01_rt, 3.53722e-01_rt, + 3.59780e-01_rt, 3.65742e-01_rt, 3.71611e-01_rt, 3.77393e-01_rt, + 3.83091e-01_rt, 3.88711e-01_rt, 3.94256e-01_rt, 3.99730e-01_rt, + 4.05139e-01_rt, 4.10484e-01_rt, 4.15771e-01_rt, 4.21003e-01_rt, + 4.26183e-01_rt, 4.31316e-01_rt, 4.36404e-01_rt, 4.41451e-01_rt, + 4.46461e-01_rt, 4.51436e-01_rt, 4.56380e-01_rt, 4.61296e-01_rt, + 4.66188e-01_rt, 4.71058e-01_rt, 4.75910e-01_rt, 4.80746e-01_rt, + 4.85570e-01_rt, 4.90385e-01_rt, 4.95194e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.37649e-02_rt, 3.37939e-02_rt, 5.22729e-02_rt, + 6.92381e-02_rt, 8.49658e-02_rt, 9.96773e-02_rt, 1.13537e-01_rt, + 1.26668e-01_rt, 1.39164e-01_rt, 1.51102e-01_rt, 1.62541e-01_rt, + 1.73530e-01_rt, 1.84111e-01_rt, 1.94319e-01_rt, 2.04184e-01_rt, + 2.13731e-01_rt, 2.22983e-01_rt, 2.31961e-01_rt, 2.40683e-01_rt, + 2.49163e-01_rt, 2.57418e-01_rt, 2.65461e-01_rt, 2.73302e-01_rt, + 2.80954e-01_rt, 2.88426e-01_rt, 2.95728e-01_rt, 3.02869e-01_rt, + 3.09857e-01_rt, 3.16700e-01_rt, 3.23404e-01_rt, 3.29977e-01_rt, + 3.36426e-01_rt, 3.42755e-01_rt, 3.48972e-01_rt, 3.55081e-01_rt, + 3.61089e-01_rt, 3.66999e-01_rt, 3.72817e-01_rt, 3.78548e-01_rt, + 3.84196e-01_rt, 3.89765e-01_rt, 3.95261e-01_rt, 4.00686e-01_rt, + 4.06045e-01_rt, 4.11341e-01_rt, 4.16579e-01_rt, 4.21762e-01_rt, + 4.26894e-01_rt, 4.31979e-01_rt, 4.37019e-01_rt, 4.42018e-01_rt, + 4.46980e-01_rt, 4.51907e-01_rt, 4.56804e-01_rt, 4.61673e-01_rt, + 4.66517e-01_rt, 4.71340e-01_rt, 4.76145e-01_rt, 4.80934e-01_rt, + 4.85711e-01_rt, 4.90479e-01_rt, 4.95241e-01_rt, 5.00000e-01_rt, + 0.00000e+00_rt, 1.53294e-02_rt, 3.59616e-02_rt, 5.46799e-02_rt, + 7.17528e-02_rt, 8.75258e-02_rt, 1.02248e-01_rt, 1.16098e-01_rt, + 1.29206e-01_rt, 1.41671e-01_rt, 1.53571e-01_rt, 1.64968e-01_rt, + 1.75913e-01_rt, 1.86446e-01_rt, 1.96606e-01_rt, 2.06420e-01_rt, + 2.15917e-01_rt, 2.25118e-01_rt, 2.34045e-01_rt, 2.42715e-01_rt, + 2.51144e-01_rt, 2.59348e-01_rt, 2.67339e-01_rt, 2.75129e-01_rt, + 2.82730e-01_rt, 2.90152e-01_rt, 2.97404e-01_rt, 3.04495e-01_rt, + 3.11433e-01_rt, 3.18226e-01_rt, 3.24882e-01_rt, 3.31406e-01_rt, + 3.37806e-01_rt, 3.44088e-01_rt, 3.50257e-01_rt, 3.56319e-01_rt, + 3.62279e-01_rt, 3.68143e-01_rt, 3.73914e-01_rt, 3.79599e-01_rt, + 3.85201e-01_rt, 3.90725e-01_rt, 3.96175e-01_rt, 4.01555e-01_rt, + 4.06869e-01_rt, 4.12121e-01_rt, 4.17314e-01_rt, 4.22453e-01_rt, + 4.27541e-01_rt, 4.32581e-01_rt, 4.37578e-01_rt, 4.42533e-01_rt, + 4.47452e-01_rt, 4.52336e-01_rt, 4.57189e-01_rt, 4.62015e-01_rt, + 4.66816e-01_rt, 4.71596e-01_rt, 4.76358e-01_rt, 4.81105e-01_rt, + 4.85839e-01_rt, 4.90564e-01_rt, 4.95284e-01_rt, 5.00000e-01_rt}; + + amrex::Gpu::synchronize(); + + m_pair_prod_table = BW_pair_prod_table{pair_prod_params, vals}; +} //============================================ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/CMakeLists.txt b/Source/Particles/ElementaryProcess/QEDInternals/CMakeLists.txt index 1bd803cdea7..e3465936822 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/CMakeLists.txt +++ b/Source/Particles/ElementaryProcess/QEDInternals/CMakeLists.txt @@ -3,10 +3,3 @@ target_sources(WarpX BreitWheelerEngineWrapper.cpp QuantumSyncEngineWrapper.cpp ) - -#if(WARPX_QED_TABLE_GEN) -# target_sources(WarpX -# PRIVATE -# BreitWheelerEngineTableBuilder.cpp -# QuantumSyncEngineTableBuilder.cpp -#endif() diff --git a/Source/Particles/ElementaryProcess/QEDInternals/Make.package b/Source/Particles/ElementaryProcess/QEDInternals/Make.package index 432057e9e05..1bd619d1c73 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/Make.package +++ b/Source/Particles/ElementaryProcess/QEDInternals/Make.package @@ -1,9 +1,2 @@ CEXE_sources += BreitWheelerEngineWrapper.cpp CEXE_sources += QuantumSyncEngineWrapper.cpp - -#Table generation is enabled only if QED_TABLE_GEN is -#set to true -ifeq ($(QED_TABLE_GEN),TRUE) - CEXE_sources += BreitWheelerEngineTableBuilder.cpp - CEXE_sources += QuantumSyncEngineTableBuilder.cpp -endif diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QedChiFunctions.H b/Source/Particles/ElementaryProcess/QEDInternals/QedChiFunctions.H index bdeb6df5734..b6ac5c5e46e 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/QedChiFunctions.H +++ b/Source/Particles/ElementaryProcess/QEDInternals/QedChiFunctions.H @@ -15,10 +15,7 @@ #include "QedWrapperCommons.H" -//#define PXRMP_CORE_ONLY allows importing only the 'core functions' of the -//QED library. -#define PXRMP_CORE_ONLY -#include +#include namespace QedUtils{ /** @@ -32,14 +29,13 @@ namespace QedUtils{ AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real chi_photon( - amrex::Real px, amrex::Real py, amrex::Real pz, - amrex::Real ex, amrex::Real ey, amrex::Real ez, - amrex::Real bx, amrex::Real by, amrex::Real bz) + const amrex::Real px, const amrex::Real py, const amrex::Real pz, + const amrex::Real ex, const amrex::Real ey, const amrex::Real ez, + const amrex::Real bx, const amrex::Real by, const amrex::Real bz) { - //laser wavelength is unused if SI units are set - const amrex::Real dummy_lambda = 1.0; - return picsar::multi_physics::chi_photon( - px, py, pz, ex, ey, ez, bx, by, bz, dummy_lambda); + namespace pxr_p = picsar::multi_physics::phys; + return pxr_p::chi_photon( + px, py, pz, ex, ey, ez, bx, by, bz); } /** @@ -52,17 +48,16 @@ namespace QedUtils{ */ AMREX_GPU_DEVICE AMREX_FORCE_INLINE - amrex::Real chi_lepton( - amrex::Real px, amrex::Real py, amrex::Real pz, - amrex::Real ex, amrex::Real ey, amrex::Real ez, - amrex::Real bx, amrex::Real by, amrex::Real bz) + amrex::Real chi_ele_pos( + const amrex::Real px, const amrex::Real py, const amrex::Real pz, + const amrex::Real ex, const amrex::Real ey, const amrex::Real ez, + const amrex::Real bx, const amrex::Real by, const amrex::Real bz) { - //laser wavelength is unused if SI units are set - const amrex::Real dummy_lambda = 1.0; - return picsar::multi_physics::chi_lepton( - px, py, pz, ex, ey, ez, bx, by, bz, dummy_lambda); + namespace pxr_p = picsar::multi_physics::phys; + return pxr_p::chi_ele_pos( + px, py, pz, ex, ey, ez, bx, by, bz); } //_________ -}; +} #endif //WARPX_amrex_qed_chi_functions_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QedTableParserHelperFunctions.H b/Source/Particles/ElementaryProcess/QEDInternals/QedTableParserHelperFunctions.H deleted file mode 100644 index dd66f626f5a..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/QedTableParserHelperFunctions.H +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright 2019 Luca Fedeli, Maxence Thevenet - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#ifndef WARPX_amrex_qed_table_parser_helper_functions_h_ -#define WARPX_amrex_qed_table_parser_helper_functions_h_ - -/** - * This header contains helper functions to safely extract data - * (e.g. integers, floating point numbers) from raw binary data - * (i.e. a char*) and to convert arrays into raw binary data. - */ - -#include -#include - -namespace QedUtils{ - /** - * This function safely extracts an amrex::Vector from raw binary data. - * T must be a simple datatype (e.g. an int, a float, a double...). - * - * @param[in] p_data a pointer to the binary stream - * @param[in] how_many how many T should be read from stream - * @param[in] p_last a pointer to the last element of the char* array - * @return {a tuple containing - * 1) flag (which is false if p_last is exceeded) - * 2) a Vector of T - * 3) a pointer to a new location of the binary data (after having read how_many T)} - */ - template - std::tuple, const char*>parse_raw_data_vec( - const char* p_data, size_t how_many, const char* const p_last) - { - amrex::Vector res; - if(p_data + sizeof(T)*how_many > p_last) - return std::make_tuple(false, res, nullptr); - - auto r_data = reinterpret_cast(p_data); - - res.assign(r_data, r_data + how_many); - - p_data += sizeof(T)*how_many; - return std::make_tuple(true, res, p_data); - } - - /** - * This function safely extracts a T from raw binary data. - * T must be a simple datatype (e.g. an int, a float, a double...). - * - * @param[in] p_data a pointer to the binary stream - * @param[in] p_last a pointer to the last element of the char* array - * @return {a tuple containing - * 1) flag (which is false if p_last is exceeded) - * 2) a T - * 3) a pointer to a new location of the binary data (after having read 1 T)} - */ - template - std::tuple parse_raw_data( - const char* p_data, const char* const p_last) - { - T res; - if(p_data + sizeof(T) > p_last) - return std::make_tuple(false, res, nullptr); - - auto r_data = reinterpret_cast(p_data); - - res = *r_data; - - p_data += sizeof(T); - return std::make_tuple(true, res, p_data); - } - - /** - * This function converts a C-style array of T into - * a Vector (i.e. raw binary data) and adds it - * to an existing Vector passed by reference - * @param[in] p_data a pointer to the beginning of the array - * @param[in] how_many number of elements of type T in the array - * @param[in,out] raw_data data will be appended to this vector - */ - template - void add_data_to_vector_char ( - const T* p_data, size_t how_many, amrex::Vector& raw_data) - { - raw_data.insert( - raw_data.end(), - reinterpret_cast(p_data), - reinterpret_cast(p_data) + - sizeof(T)*how_many - ); - } -}; - -#endif //WARPX_amrex_qed_table_parser_helper_functions_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QedWrapperCommons.H b/Source/Particles/ElementaryProcess/QEDInternals/QedWrapperCommons.H index 1d4500a8105..75fc2fd7999 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/QedWrapperCommons.H +++ b/Source/Particles/ElementaryProcess/QEDInternals/QedWrapperCommons.H @@ -8,35 +8,31 @@ #define WARPX_amrex_qed_wrapper_commons_h_ /** - * This header contains some common #define directives and a - * 'dummy' class used by the QED library wrappers and related + * This header contains some common #define directives + * used by the QED library wrappers and related * components. */ #include #include +#include /** * PICSAR uses PXRMP_GPU to decorate methods which should be * compiled for GPU. The user has to set it to the right value * (AMREX_GPU_DEVICE in this case). - * PXRMP_WITH_SI_UNITS sets the library to use International - * System units. */ #define PXRMP_GPU AMREX_GPU_HOST_DEVICE -#define PXRMP_WITH_SI_UNITS //_________________________ /** - * A namespace called 'QedUtils' is used to encapsulate - * free functions (defined elsewhere) and an - * empty datastructure (DummyStruct), which is re-used by several - * components. + * PICSAR uses internally some specifiers analogous to + * AMREX_RESTRICT and AMREX_FORCE_INLINE. These definitions + * set the aformentioned specifiers to AMREX_RESTRICT and + * AMREX_FORCE_INLINE. */ -namespace QedUtils{ - struct DummyStruct{}; -}; +#define PXRMP_RESTRICT AMREX_RESTRICT +#define PXRMP_FORCE_INLINE AMREX_FORCE_INLINE //_________________________ - #endif //WARPX_amrex_qed_wrapper_commons_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncDummyTable.H b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncDummyTable.H deleted file mode 100644 index f34c521a349..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncDummyTable.H +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#ifndef WARPX_quantum_sync_dummy_tables_h_ -#define WARPX_quantum_sync_dummy_tables_h_ - -#include "QuantumSyncEngineInnards.H" - -#include -#include - -#include - -namespace QedUtils{ - -//A default mini-table used for test purposes -const struct //QuantumSyncEngineInnardsDummy -{ - picsar::multi_physics::quantum_synchrotron_engine_ctrl ctrl{ - 0.001, /*chi_part_min*/ - 0.001, /*chi_part_tdndt_min*/ - 200, /*chi_part_tdndt_max*/ - 64, /*chi_part_tdndt_how_many*/ - 0.001, /*chi_part_tem_min*/ - 200, /*chi_part_tem_max*/ - 2, /*chi_part_tem_how_many*/ - 2 /*prob_tem_how_many*/ - }; - std::vector KKfunc_coords{ - -6.907755279, -6.714008094, -6.520260909, -6.326513724, - -6.13276654, -5.939019355, -5.74527217, -5.551524985, - -5.3577778, -5.164030615, -4.97028343, -4.776536246, - -4.582789061, -4.389041876, -4.195294691, -4.001547506, - -3.807800321, -3.614053137, -3.420305952, -3.226558767, - -3.032811582, -2.839064397, -2.645317212, -2.451570027, - -2.257822843, -2.064075658, -1.870328473, -1.676581288, - -1.482834103, -1.289086918, -1.095339733, -0.9015925486, - -0.7078453638, -0.5140981789, -0.3203509941, -0.1266038092, - 0.06714337561, 0.2608905605, 0.4546377453, 0.6483849302, - 0.842132115, 1.0358793, 1.229626485, 1.42337367, - 1.617120854, 1.810868039, 2.004615224, 2.198362409, - 2.392109594, 2.585856779, 2.779603964, 2.973351148, - 3.167098333, 3.360845518, 3.554592703, 3.748339888, - 3.942087073, 4.135834257, 4.329581442, 4.523328627, - 4.717075812, 4.910822997, 5.104570182, 5.298317367 - }; - std::vector KKfunc_data{ - -7.968431811, -7.639082211, -7.326295546, -7.02752527, - -6.740710773, -6.464172009, -6.196529608, -5.93664402 - -5.683568899, -5.436515162, -5.194823127, -4.957940775, - -4.725406674, -4.49683649, -4.2719122, -4.050373372, - -3.832009948, -3.616656119, -3.404184903, -3.194503151, - -2.987546751, -2.783275883, -2.581670257, -2.382724345, - -2.1864427, -1.992835514, -1.801914573, -1.613689793, - -1.428166439, -1.2453431, -1.065210351, -0.8877500928, - -0.7129353081, -0.5407301909, -0.3710904422, -0.2039636495, - -0.03928968527, 0.1229988926, 0.2829764221, 0.4407236701, - 0.5963272798, 0.7498791107, 0.9014754584, 1.051216164, - 1.199203636, 1.345541816, 1.490335133, 1.633687478, - 1.775701241, 1.916476434, 2.056109933, 2.194694829, - 2.332319922, 2.469069336, 2.605022252, 2.740252763, - 2.874829832, 3.008817314, 3.142273988, 3.27525366, - 3.407805563, 3.539975021, 3.671803889, 3.803330346 - };//_____________________________ - std::vector cum_distrib_coords_1{ - -6.907755279, 5.298317367 - };//_____________________________ - std::vector cum_distrib_coords_2{ - 0, 0.5 - };//_____________________________ - std::vector cum_distrib_data{ - -std::numeric_limits::infinity(), - -0.6931471806, - -std::numeric_limits::infinity(), - -0.6931471806 - };//_____________________________ -} QuantumSyncEngineInnardsDummy; - -}; - -#endif //WARPX_quantum_sync_dummy_tables_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineInnards.H b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineInnards.H deleted file mode 100644 index 64e67690a45..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineInnards.H +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#ifndef WARPX_quantum_sync_engine_innards_h_ -#define WARPX_quantum_sync_engine_innards_h_ - -#include "QedWrapperCommons.H" - -#include - -//This includes only the definition of a simple datastructure -//used to control the Quantum Synchrotron engine. -#include - -/** - * This structure holds all the parameters required to use the - * Quantum Synchrotron engine: a POD control structure and lookup - * tables data. - */ -struct QuantumSynchrotronEngineInnards -{ - // Control parameters (a POD struct) - // ctrl contains several parameters: - // - chi_part_min : the minium chi parameter to be - // considered by the engine - // - chi_part_tdndt_min : minimun chi for sub-table 1 (1D) - // - chi_part_tdndt_max : maximum chi for sub-table 1 (1D) - // - chi_part_tdndt_how_many : how many points to use for sub-table 1 (1D) - // - chi_part_tem_min : minimun chi for sub-table 2 (1D) - // - chi_part_tem_max : maximum chi for sub-table 2 (1D) - // - chi_part_tem_how_many : how many points to use for chi for sub-table 2 (2D) - // - prob_tem_how_many : how many points to use for the second axis of sub-table 2 (2D) - picsar::multi_physics::quantum_synchrotron_engine_ctrl ctrl; - - //Lookup table data - //---sub-table 1 (1D) - amrex::Gpu::ManagedDeviceVector KKfunc_coords; - amrex::Gpu::ManagedDeviceVector KKfunc_data; - //--- - - //---sub-table 2 (2D) - amrex::Gpu::ManagedVector cum_distrib_coords_1; - amrex::Gpu::ManagedVector cum_distrib_coords_2; - amrex::Gpu::ManagedVector cum_distrib_data; - //______ -}; -//========================================================== - -#endif //WARPX_quantum_sync_engine_innards_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineTableBuilder.H b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineTableBuilder.H deleted file mode 100644 index 16be2d5eb20..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineTableBuilder.H +++ /dev/null @@ -1,32 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#ifndef WARPX_quantum_sync_engine_table_builder_h_ -#define WARPX_quantum_sync_engine_table_builder_h_ - -#include "QedWrapperCommons.H" -#include "QuantumSyncEngineInnards.H" - -//This includes only the definition of a simple datastructure -//used to control the Quantum Synchrotron engine. -#include - -/** - * A class which computes the lookup tables for the Quantum Synchrotron engine. - */ -class QuantumSynchrotronEngineTableBuilder{ -public: - /** - * Computes the tables. - * @param[in] ctrl control parameters to generate the tables - * @param[out] innards structure holding both a copy of ctrl and lookup tables data - */ - void compute_table - (picsar::multi_physics::quantum_synchrotron_engine_ctrl ctrl, - QuantumSynchrotronEngineInnards& innards) const; -}; - -#endif //WARPX_quantum_sync_engine_table_builder_h_ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineTableBuilder.cpp b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineTableBuilder.cpp deleted file mode 100644 index e9670aa7efb..00000000000 --- a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineTableBuilder.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2019 Luca Fedeli - * - * This file is part of WarpX. - * - * License: BSD-3-Clause-LBNL - */ -#include "QuantumSyncEngineTableBuilder.H" - -//Include the full Quantum Synchrotron engine with table generation support -//(after some consistency tests). This requires to have a recent version -// of the Boost library. -#ifdef PXRMP_CORE_ONLY -# error The Table Builder is incompatible with PXRMP_CORE_ONLY -#endif - -#ifdef __PICSAR_MULTIPHYSICS_BREIT_WHEELER_ENGINE__ -# warning quantum_sync_engine.hpp should not have been included before reaching this point. -#endif -#include -//_______________________________________________ - -//Some handy aliases -using PicsarQuantumSynchrotronEngine = picsar::multi_physics:: - quantum_synchrotron_engine; - -using PicsarQuantumSynchrotronCtrl = - picsar::multi_physics::quantum_synchrotron_engine_ctrl; -//_______________________________________________ - -void -QuantumSynchrotronEngineTableBuilder::compute_table - (PicsarQuantumSynchrotronCtrl ctrl, - QuantumSynchrotronEngineInnards& innards) const -{ - PicsarQuantumSynchrotronEngine qs_engine( - std::move(QedUtils::DummyStruct()), 1.0, ctrl); - - qs_engine.compute_dN_dt_lookup_table(); - qs_engine.compute_cumulative_phot_em_table(); - - auto qs_innards_picsar = qs_engine.export_innards(); - - //Copy data in a GPU-friendly data-structure - innards.ctrl = qs_innards_picsar.qs_ctrl; - innards.KKfunc_coords.assign(qs_innards_picsar.KKfunc_table_coords_ptr, - qs_innards_picsar.KKfunc_table_coords_ptr + - qs_innards_picsar.KKfunc_table_coords_how_many); - innards.KKfunc_data.assign(qs_innards_picsar.KKfunc_table_data_ptr, - qs_innards_picsar.KKfunc_table_data_ptr + - qs_innards_picsar.KKfunc_table_data_how_many); - innards.cum_distrib_coords_1.assign( - qs_innards_picsar.cum_distrib_table_coords_1_ptr, - qs_innards_picsar.cum_distrib_table_coords_1_ptr + - qs_innards_picsar.cum_distrib_table_coords_1_how_many); - innards.cum_distrib_coords_2.assign( - qs_innards_picsar.cum_distrib_table_coords_2_ptr, - qs_innards_picsar.cum_distrib_table_coords_2_ptr + - qs_innards_picsar.cum_distrib_table_coords_2_how_many); - innards.cum_distrib_data.assign( - qs_innards_picsar.cum_distrib_table_data_ptr, - qs_innards_picsar.cum_distrib_table_data_ptr + - qs_innards_picsar.cum_distrib_table_data_how_many); - //____ -} diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.H b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.H index fe7d560cd02..ece636aad97 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.H +++ b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.H @@ -8,39 +8,49 @@ #define WARPX_quantum_sync_engine_wrapper_h_ #include "QedWrapperCommons.H" -#include "QuantumSyncEngineInnards.H" +#include "QedChiFunctions.H" +#include "Utils/WarpXConst.H" #include #include #include -//#define PXRMP_CORE_ONLY allows importing only the 'core functions' of the -//Quantum Synchrotron engine of the QED PICSAR library. -#define PXRMP_CORE_ONLY -#include - -//Lookup table building function is in a dedicated (optional) class to -//avoid including heavy dependencies if they are not needed. -#ifdef WARPX_QED_TABLE_GEN -# include "QuantumSyncEngineTableBuilder.H" -#endif +#include +#include #include +#include + +// Aliases ============================= +using QS_dndt_table_params = + picsar::multi_physics::phys::quantum_sync:: + dndt_lookup_table_params; + +using QS_dndt_table = + picsar::multi_physics::phys::quantum_sync:: + dndt_lookup_table< + amrex::Real, + amrex::Gpu::DeviceVector>; + +using QS_dndt_table_view = QS_dndt_table::view_type; + +using QS_phot_em_table_params = + picsar::multi_physics::phys::quantum_sync:: + photon_emission_lookup_table_params; -//Some handy aliases +using QS_phot_em_table = + picsar::multi_physics::phys::quantum_sync:: + photon_emission_lookup_table< + amrex::Real, + amrex::Gpu::DeviceVector>; -// The engine has two templated arguments: the numerical type -// and a random number generator. However, random numbers are not -// used to generate the lookup tables and the static member -// functions which are called in the functors do not use -// random numbers as well. Therefore, an empty "DummyStruct" -// can be passed as a template parameter. -using PicsarQuantumSynchrotronEngine = picsar::multi_physics:: - quantum_synchrotron_engine; +using QS_phot_em_table_view = QS_phot_em_table::view_type; -using PicsarQuantumSynchrotronCtrl = - picsar::multi_physics::quantum_synchrotron_engine_ctrl; -//__________ +struct PicsarQuantumSyncCtrl +{ + QS_dndt_table_params dndt_params; + QS_phot_em_table_params phot_em_params; +}; // Functors ================================== @@ -70,9 +80,10 @@ public: AMREX_FORCE_INLINE amrex::Real operator() () const noexcept { + namespace pxr_qs = picsar::multi_physics::phys::quantum_sync; + //A random number in [0,1) should be provided as an argument. - return PicsarQuantumSynchrotronEngine:: - internal_get_optical_depth(amrex::Random()); + return pxr_qs::get_optical_depth(amrex::Random()); } }; //____________________________________________ @@ -85,154 +96,158 @@ class QuantumSynchrotronEvolveOpticalDepth { public: - QuantumSynchrotronEvolveOpticalDepth () = default; + /** + * Default constructor: it leaves the functor in a non-initialized state. + */ + QuantumSynchrotronEvolveOpticalDepth (){} /** - * Constructor acquires pointers to control parameters and - * lookup tables data. - * lookup_table uses non-owning vectors under the hood. So no new data - * allocations should be triggered on GPU + * Constructor to be used to initialize the functor. + * + * @param[in] table_view a view of a QS_dndt_table lookup table + * @param[in] qs_minimum_chi_part the minimum quantum parameter to evolve the optical depth */ - QuantumSynchrotronEvolveOpticalDepth( - QuantumSynchrotronEngineInnards& r_innards): - m_ctrl{r_innards.ctrl}, - m_KKfunc_size{r_innards.KKfunc_coords.size()}, - m_p_KKfunc_coords{r_innards.KKfunc_coords.dataPtr()}, - m_p_KKfunc_data{r_innards.KKfunc_data.dataPtr()} - {}; + QuantumSynchrotronEvolveOpticalDepth ( + const QS_dndt_table_view table_view, + const amrex::ParticleReal qs_minimum_chi_part): + m_table_view{table_view}, m_qs_minimum_chi_part{qs_minimum_chi_part}{}; /** * Evolves the optical depth. It can be used on GPU. - * @param[in] px,py,pz momentum components of the lepton (SI units) + * If the quantum parameter parameter of the particle is + * < qs_minimum_chi_part, the method returns immediately. + * + * @param[in] ux,uy,uz gamma*v components of the lepton. * @param[in] ex,ey,ez electric field components (SI units) * @param[in] bx,by,bz magnetic field components (SI units) * @param[in] dt timestep (SI units) - * @param[in,out] opt_depth optical depth of the lepton. It is modified by the method. - * @return a flag which is 1 if optical depth becomes negative (i.e. a photon has to be generated). + * @param[in,out] opt_depth optical depth of the particle. + * @return a flag which is 1 if chi_part was out of table. */ - AMREX_GPU_HOST_DEVICE + AMREX_GPU_DEVICE AMREX_FORCE_INLINE int operator()( - amrex::Real px, amrex::Real py, amrex::Real pz, - amrex::Real ex, amrex::Real ey, amrex::Real ez, - amrex::Real bx, amrex::Real by, amrex::Real bz, - amrex::Real dt, amrex::Real& opt_depth) const noexcept + const amrex::Real ux, const amrex::Real uy, const amrex::Real uz, + const amrex::Real ex, const amrex::Real ey, const amrex::Real ez, + const amrex::Real bx, const amrex::Real by, const amrex::Real bz, + const amrex::Real dt, amrex::Real& opt_depth) const noexcept { - bool has_event_happened{false}; - - //the library provides the time (< dt) at which the event occurs, but this - //feature won't be used in WarpX for now. - amrex::Real unused_event_time{0.0}; - - PicsarQuantumSynchrotronEngine:: - internal_evolve_opt_depth_and_determine_event( - px, py, pz, - ex, ey, ez, - bx, by, bz, - dt, opt_depth, - has_event_happened, unused_event_time, - m_dummy_lambda, - picsar::multi_physics::lookup_1d{ - m_KKfunc_size, - m_p_KKfunc_coords, - m_p_KKfunc_data}, - m_ctrl); - - return has_event_happened; - } + using namespace amrex::literals; + namespace pxr_p = picsar::multi_physics::phys; + namespace pxr_qs = picsar::multi_physics::phys::quantum_sync; -private: - //laser wavelength is not used with SI units - amrex::Real m_dummy_lambda{1.0}; + constexpr amrex::Real m_e = PhysConst::m_e; + constexpr amrex::Real inv_c2 = 1._rt/(PhysConst::c*PhysConst::c); + const amrex::Real gamma = std::sqrt(1._rt + (ux*ux + uy*uy + uz*uz)*inv_c2); + const auto energy = gamma*m_e*PhysConst::c*PhysConst::c; + + const auto chi_part = QedUtils::chi_ele_pos( + m_e*ux, m_e*uy, m_e*uz, ex, ey, ez, bx, by, bz); + + if (chi_part < m_qs_minimum_chi_part) + return 0; + + const auto is_out = pxr_qs::evolve_optical_depth< + amrex::Real, + QS_dndt_table_view, + pxr_p::unit_system::SI>( + energy, chi_part, dt, opt_depth, m_table_view); - PicsarQuantumSynchrotronCtrl m_ctrl; + return is_out; + } - //lookup table data - size_t m_KKfunc_size; - amrex::Real* m_p_KKfunc_coords; - amrex::Real* m_p_KKfunc_data; +private: + QS_dndt_table_view m_table_view; + amrex::ParticleReal m_qs_minimum_chi_part; }; /** * Functor to generate a photon via the Quantum Synchrotron process * and to update momentum accordingly */ -class QuantumSynchrotronGeneratePhotonAndUpdateMomentum +class QuantumSynchrotronPhotonEmission { public: + + /** + * Default constructor: it leaves the functor in a non-initialized state. + */ + QuantumSynchrotronPhotonEmission (){} + /** * Constructor acquires pointers to control parameters and * lookup tables data. * lookup_table uses non-owning vectors under the hood. So no new data * allocations should be triggered on GPU + * + * @param[in] table_view a view of a QS_phot_em_table lookup table */ - QuantumSynchrotronGeneratePhotonAndUpdateMomentum( - QuantumSynchrotronEngineInnards& r_innards): - m_ctrl{r_innards.ctrl}, - m_cum_distrib_coords_1_size{r_innards.cum_distrib_coords_1.size()}, - m_cum_distrib_coords_2_size{r_innards.cum_distrib_coords_2.size()}, - m_p_distrib_coords_1{r_innards.cum_distrib_coords_1.data()}, - m_p_distrib_coords_2{r_innards.cum_distrib_coords_2.data()}, - m_p_cum_distrib_data{r_innards.cum_distrib_data.data()} - {}; + QuantumSynchrotronPhotonEmission ( + const QS_phot_em_table_view table_view): + m_table_view{table_view}{}; /** - * Generates sampling (template parameter) photons according to Quantum Synchrotron process. + * Generates photons according to Quantum Synchrotron process. * It can be used on GPU. - * @param[in,out] px,py,pz momentum components of the lepton. They are modified (SI units) + * + * @param[in,out] ux,uy,uz gamma*v components of the lepton. They are modified (SI units) * @param[in] ex,ey,ez electric field components (SI units) * @param[in] bx,by,bz magnetic field components (SI units) - * @param[in] weight of the lepton (code units) - * @param[out] g_px,g_py,g_pz momenta of generated photons. Each array should have size=sampling (SI units) - * @param[out] g_weight weight of the generated photons. Array should have size=sampling (code units) + * @param[out] g_ux,g_uy,g_uz gamma*v components of the generated photon (SI units) + * @return a flag which is 1 if chi_photon was out of table */ - template - AMREX_GPU_HOST_DEVICE + AMREX_GPU_DEVICE AMREX_FORCE_INLINE - void operator()( - amrex::Real* px, amrex::Real* py, amrex::Real* pz, - amrex::Real ex, amrex::Real ey, amrex::Real ez, - amrex::Real bx, amrex::Real by, amrex::Real bz, - amrex::Real weight, - amrex::Real* g_px, amrex::Real* g_py, amrex::Real* g_pz, - amrex::Real* g_weight) const noexcept + bool operator()( + amrex::Real& ux, amrex::Real& uy, amrex::Real& uz, + const amrex::Real ex, const amrex::Real ey, const amrex::Real ez, + const amrex::Real bx, const amrex::Real by, const amrex::Real bz, + amrex::Real& g_ux, amrex::Real& g_uy, amrex::Real& g_uz) const noexcept { - //[sampling] random numbers are needed - amrex::GpuArray - rand_zero_one_minus_epsi; - for(auto& el : rand_zero_one_minus_epsi) el = amrex::Random(); - - PicsarQuantumSynchrotronEngine:: - internal_generate_photons_and_update_momentum( - *px, *py, *pz, - ex, ey, ez, - bx, by, bz, - weight, sampling, - g_px, g_py, g_pz, - g_weight, - m_dummy_lambda, - picsar::multi_physics::lookup_2d{ - m_cum_distrib_coords_1_size, - m_p_distrib_coords_1, - m_cum_distrib_coords_2_size, - m_p_distrib_coords_2, - m_p_cum_distrib_data}, - m_ctrl, - rand_zero_one_minus_epsi.data()); + using namespace amrex; + namespace pxr_m = picsar::multi_physics::math; + namespace pxr_p = picsar::multi_physics::phys; + namespace pxr_qs = picsar::multi_physics::phys::quantum_sync; + + const auto rand_zero_one_minus_epsi = amrex::Random(); + + constexpr ParticleReal me = PhysConst::m_e; + constexpr ParticleReal one_over_me = 1._prt/me; + + // Particle momentum is stored as gamma * velocity. + // Convert to m * gamma * velocity + auto px = ux*me; + auto py = uy*me; + auto pz = uz*me; + + const auto chi_particle = QedUtils::chi_ele_pos( + px, py, pz, ex, ey, ez, bx, by, bz); + + auto momentum_particle = pxr_m::vec3{px, py, pz}; + auto momentum_photon = pxr_m::vec3(); + + const auto is_out = pxr_qs::generate_photon_update_momentum< + amrex::Real, + QS_phot_em_table_view, + pxr_p::unit_system::SI>( + chi_particle, momentum_particle, + rand_zero_one_minus_epsi, + m_table_view, + momentum_photon); + + ux = momentum_particle[0]*one_over_me; + uy = momentum_particle[1]*one_over_me; + uz = momentum_particle[2]*one_over_me; + g_ux = momentum_photon[0]*one_over_me; + g_uy = momentum_photon[1]*one_over_me; + g_uz = momentum_photon[2]*one_over_me; + + return is_out; } private: - //laser wavelenght is not used with SI units - const amrex::Real m_dummy_lambda{1.0}; - - const PicsarQuantumSynchrotronCtrl m_ctrl; + QS_phot_em_table_view m_table_view; - //lookup table data - size_t m_cum_distrib_coords_1_size; - size_t m_cum_distrib_coords_2_size; - amrex::Real* m_p_distrib_coords_1; - amrex::Real* m_p_distrib_coords_2; - amrex::Real* m_p_cum_distrib_data; }; // Factory class ============================= @@ -261,7 +276,7 @@ public: /** * Builds the functor to generate photons */ - QuantumSynchrotronGeneratePhotonAndUpdateMomentum build_phot_em_functor (); + QuantumSynchrotronPhotonEmission build_phot_em_functor (); /** * Checks if the optical tables are properly initialized @@ -269,53 +284,60 @@ public: bool are_lookup_tables_initialized () const; /** - * Init lookup tables from raw binary data. - * @param[in] raw_data a Vector of char - * @return true if it succeeds, false if it cannot parse raw_data + * Export lookup tables data into a raw binary Vector + * + * @return the data in binary format. The Vector is empty if tables were + * not previously initialized. */ - bool init_lookup_tables_from_raw_data (const amrex::Vector& raw_data); + std::vector export_lookup_tables_data () const; /** - * Init lookup tables using built-in dummy tables - * for test purposes. + * Init lookup tables from raw binary data. + * + * @param[in] raw_data a vector of char + * @param[in] qs_minimum_chi_part minimum chi parameter to evolve the optical depth of a particle. + * @return true if it succeeds, false if it cannot parse raw_data */ - void init_dummy_tables(); + bool init_lookup_tables_from_raw_data (const std::vector& raw_data, + const amrex::Real qs_minimum_chi_part); /** - * Export lookup tables data into a raw binary Vector - * @return the data in binary format. The Vector is empty if tables were - * not previously initialized. + * Init lookup tables using built-in (low resolution) tables + * + * @param[in] qs_minimum_chi_part minimum chi parameter to evolve the optical depth of a particle. */ - amrex::Vector export_lookup_tables_data () const; + void init_builtin_tables(const amrex::Real qs_minimum_chi_part); /** * Computes the lookup tables. It does nothing unless WarpX is compiled with QED_TABLE_GEN=TRUE + * * @param[in] ctrl control params to generate the tables + * @param[in] qs_minimum_chi_part minimum chi parameter to evolve the optical depth of a particle. */ - void compute_lookup_tables (PicsarQuantumSynchrotronCtrl ctrl); + void compute_lookup_tables (PicsarQuantumSyncCtrl ctrl, + const amrex::Real qs_minimum_chi_part); /** - * gets default (reasonable) values for the control parameters + * gets default values for the control parameters + * * @return default control params to generate the tables */ - PicsarQuantumSynchrotronCtrl get_default_ctrl() const; + PicsarQuantumSyncCtrl get_default_ctrl() const; - /** - * returns a constant reference to the control parameters - * @return const reference to control parameters - */ - const PicsarQuantumSynchrotronCtrl& get_ref_ctrl() const; + amrex::Real get_minimum_chi_part() const; private: bool m_lookup_tables_initialized = false; - QuantumSynchrotronEngineInnards m_innards; + //Variables to store the minimum chi parameters to enable + //Quantum Synchrotron process + amrex::Real m_qs_minimum_chi_part; -//Table builing is available only if the libray is compiled with QED_TABLE_GEN=TRUE -#ifdef WARPX_QED_TABLE_GEN - QuantumSynchrotronEngineTableBuilder m_table_builder; -#endif + QS_dndt_table m_dndt_table; + QS_phot_em_table m_phot_em_table; + void init_builtin_dndt_table(); + void init_builtin_phot_em_table(); }; //============================================ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.cpp b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.cpp index b185251d878..3f839ce91b6 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.cpp +++ b/Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.cpp @@ -6,14 +6,20 @@ */ #include "QuantumSyncEngineWrapper.H" -#include "QedTableParserHelperFunctions.H" -#include "QuantumSyncDummyTable.H" +//Functions needed to generate a new table +#ifdef WARPX_QED_TABLE_GEN +# include +#endif + +#include #include +#include +#include using namespace std; -using namespace QedUtils; using namespace amrex; +namespace pxr_sr = picsar::multi_physics::utils::serialization; //This file provides a wrapper aroud the quantum_sync engine //provided by the PICSAR library @@ -32,14 +38,15 @@ QuantumSynchrotronEvolveOpticalDepth QuantumSynchrotronEngine::build_evolve_func { AMREX_ALWAYS_ASSERT(m_lookup_tables_initialized); - return QuantumSynchrotronEvolveOpticalDepth(m_innards); + return QuantumSynchrotronEvolveOpticalDepth(m_dndt_table.get_view(), + m_qs_minimum_chi_part); } -QuantumSynchrotronGeneratePhotonAndUpdateMomentum QuantumSynchrotronEngine::build_phot_em_functor () +QuantumSynchrotronPhotonEmission QuantumSynchrotronEngine::build_phot_em_functor () { AMREX_ALWAYS_ASSERT(m_lookup_tables_initialized); - return QuantumSynchrotronGeneratePhotonAndUpdateMomentum(m_innards); + return QuantumSynchrotronPhotonEmission(m_phot_em_table.get_view()); } @@ -50,173 +57,1171 @@ bool QuantumSynchrotronEngine::are_lookup_tables_initialized () const bool QuantumSynchrotronEngine::init_lookup_tables_from_raw_data ( - const Vector& raw_data) + const vector& raw_data, + const amrex::Real qs_minimum_chi_part) { - const char* p_data = raw_data.data(); - const char* const p_last = &raw_data.back(); - bool is_ok; - - //Header (control parameters) - tie(is_ok, m_innards.ctrl.chi_part_min, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_part_tdndt_min, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_part_tdndt_max, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_part_tdndt_how_many, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_part_tem_min, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_part_tem_max, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.chi_part_tem_how_many, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - tie(is_ok, m_innards.ctrl.prob_tem_how_many, p_data) = - parse_raw_data( - p_data, p_last); - if(!is_ok) return false; - - //___________________________ - - //Data - Vector tndt_coords(m_innards.ctrl.chi_part_tdndt_how_many); - Vector tndt_data(m_innards.ctrl.chi_part_tdndt_how_many); - Vector cum_tab_coords1(m_innards.ctrl.chi_part_tem_how_many); - Vector cum_tab_coords2(m_innards.ctrl.prob_tem_how_many); - Vector cum_tab_data(m_innards.ctrl.chi_part_tem_how_many* - m_innards.ctrl.prob_tem_how_many); - - tie(is_ok, tndt_coords, p_data) = - parse_raw_data_vec( - p_data, tndt_coords.size(), p_last); - if(!is_ok) return false; - m_innards.KKfunc_coords.assign(tndt_coords.begin(), tndt_coords.end()); - - tie(is_ok, tndt_data, p_data) = - parse_raw_data_vec( - p_data, tndt_data.size(), p_last); - if(!is_ok) return false; - m_innards.KKfunc_data.assign(tndt_data.begin(), tndt_data.end()); - - tie(is_ok, cum_tab_coords1, p_data) = - parse_raw_data_vec( - p_data, cum_tab_coords1.size(), p_last); - if(!is_ok) return false; - m_innards.cum_distrib_coords_1.assign( - cum_tab_coords1.begin(), cum_tab_coords1.end()); - - tie(is_ok, cum_tab_coords2, p_data) = - parse_raw_data_vec( - p_data, cum_tab_coords2.size(), p_last); - if(!is_ok) return false; - m_innards.cum_distrib_coords_2.assign( - cum_tab_coords2.begin(), cum_tab_coords2.end()); - - tie(is_ok, cum_tab_data, p_data) = - parse_raw_data_vec( - p_data, cum_tab_data.size(), p_last); - if(!is_ok) return false; - m_innards.cum_distrib_data.assign( - cum_tab_data.begin(), cum_tab_data.end()); - - //___________________________ + auto raw_iter = raw_data.begin(); + const auto size_first = pxr_sr::get_out(raw_iter); + if(size_first <= 0 || size_first >= raw_data.size() ) return false; + + const auto raw_dndt_table = vector{ + raw_iter, raw_iter+size_first}; + + const auto raw_phot_em_table = vector{ + raw_iter+size_first, raw_data.end()}; + + m_dndt_table = QS_dndt_table{raw_dndt_table}; + m_phot_em_table = QS_phot_em_table{raw_phot_em_table}; + + if (!m_dndt_table.is_init() || !m_phot_em_table.is_init()) + return false; + + m_qs_minimum_chi_part = qs_minimum_chi_part; + + amrex::Gpu::synchronize(); + m_lookup_tables_initialized = true; return true; } -void QuantumSynchrotronEngine::init_dummy_tables() +void QuantumSynchrotronEngine::init_builtin_tables( + const amrex::Real qs_minimum_chi_part) { - m_innards.ctrl = QedUtils::QuantumSyncEngineInnardsDummy.ctrl; - m_innards.KKfunc_coords.assign( - QedUtils::QuantumSyncEngineInnardsDummy.KKfunc_coords.begin(), - QedUtils::QuantumSyncEngineInnardsDummy.KKfunc_coords.end()); - m_innards.KKfunc_data.assign( - QedUtils::QuantumSyncEngineInnardsDummy.KKfunc_data.begin(), - QedUtils::QuantumSyncEngineInnardsDummy.KKfunc_data.end()); - m_innards.cum_distrib_coords_1.assign( - QedUtils::QuantumSyncEngineInnardsDummy.cum_distrib_coords_1.begin(), - QedUtils::QuantumSyncEngineInnardsDummy.cum_distrib_coords_1.end()); - m_innards.cum_distrib_coords_2.assign( - QedUtils::QuantumSyncEngineInnardsDummy.cum_distrib_coords_2.begin(), - QedUtils::QuantumSyncEngineInnardsDummy.cum_distrib_coords_2.end()); - m_innards.cum_distrib_data.assign( - QedUtils::QuantumSyncEngineInnardsDummy.cum_distrib_data.begin(), - QedUtils::QuantumSyncEngineInnardsDummy.cum_distrib_data.end()); + init_builtin_dndt_table(); + init_builtin_phot_em_table(); + m_qs_minimum_chi_part = qs_minimum_chi_part; m_lookup_tables_initialized = true; } -Vector QuantumSynchrotronEngine::export_lookup_tables_data () const +vector QuantumSynchrotronEngine::export_lookup_tables_data () const { - Vector res{}; - - if(!m_lookup_tables_initialized) - return res; - - add_data_to_vector_char(&m_innards.ctrl.chi_part_min, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_part_tdndt_min, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_part_tdndt_max, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_part_tdndt_how_many, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_part_tem_min, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_part_tem_max, 1, res); - add_data_to_vector_char(&m_innards.ctrl.chi_part_tem_how_many, 1, res); - add_data_to_vector_char(&m_innards.ctrl.prob_tem_how_many, 1, res); - - add_data_to_vector_char(m_innards.KKfunc_coords.data(), - m_innards.KKfunc_coords.size(), res); - add_data_to_vector_char(m_innards.KKfunc_data.data(), - m_innards.KKfunc_data.size(), res); - add_data_to_vector_char(m_innards.cum_distrib_coords_1.data(), - m_innards.cum_distrib_coords_1.size(), res); - add_data_to_vector_char(m_innards.cum_distrib_coords_2.data(), - m_innards.cum_distrib_coords_2.size(), res); - add_data_to_vector_char(m_innards.cum_distrib_data.data(), - m_innards.cum_distrib_data.size(), res); + if(!m_lookup_tables_initialized) + return vector{}; + + const auto data_dndt = m_dndt_table.serialize(); + const auto data_phot_em = m_phot_em_table.serialize(); + + const uint64_t size_first = data_dndt.size(); + + vector res{}; + pxr_sr::put_in(size_first, res); + for (const auto& tmp : data_dndt) + pxr_sr::put_in(tmp, res); + for (const auto& tmp : data_phot_em) + pxr_sr::put_in(tmp, res); return res; } -PicsarQuantumSynchrotronCtrl +PicsarQuantumSyncCtrl QuantumSynchrotronEngine::get_default_ctrl() const { - return PicsarQuantumSynchrotronCtrl(); + namespace pxr_qs = picsar::multi_physics::phys::quantum_sync; + return PicsarQuantumSyncCtrl{ + pxr_qs::default_dndt_lookup_table_params, + pxr_qs::default_photon_emission_lookup_table_params + }; } -const PicsarQuantumSynchrotronCtrl& -QuantumSynchrotronEngine::get_ref_ctrl() const +amrex::Real +QuantumSynchrotronEngine::get_minimum_chi_part() const { - return m_innards.ctrl; + return m_qs_minimum_chi_part; } void QuantumSynchrotronEngine::compute_lookup_tables ( - PicsarQuantumSynchrotronCtrl ctrl) + PicsarQuantumSyncCtrl ctrl, + const amrex::Real qs_minimum_chi_part) { #ifdef WARPX_QED_TABLE_GEN - m_table_builder.compute_table(ctrl, m_innards); + m_dndt_table = QS_dndt_table{ctrl.dndt_params}; + m_dndt_table.generate(true); //Progress bar is displayed + m_phot_em_table = QS_phot_em_table{ctrl.phot_em_params}; + m_phot_em_table.generate(true); //Progress bar is displayed + m_qs_minimum_chi_part = qs_minimum_chi_part; + + amrex::Gpu::synchronize(); + m_lookup_tables_initialized = true; +#else + amrex::ignore_unused(ctrl, qs_minimum_chi_part); + amrex::Abort("WarpX was not compiled with table generation support!"); #endif } +void QuantumSynchrotronEngine::init_builtin_dndt_table() +{ + QS_dndt_table_params dndt_params; + dndt_params.chi_part_min = 1.0e-3_rt; + dndt_params.chi_part_max = 200.0_rt; + dndt_params.chi_part_how_many = 64; + + + const auto vals = amrex::Gpu::DeviceVector{ + -6.13623e+00_rt, -5.94268e+00_rt, -5.74917e+00_rt, -5.55571e+00_rt, + -5.36231e+00_rt, -5.16898e+00_rt, -4.97575e+00_rt, -4.78262e+00_rt, + -4.58961e+00_rt, -4.39677e+00_rt, -4.20410e+00_rt, -4.01166e+00_rt, + -3.81948e+00_rt, -3.62762e+00_rt, -3.43612e+00_rt, -3.24506e+00_rt, + -3.05451e+00_rt, -2.86456e+00_rt, -2.67529e+00_rt, -2.48681e+00_rt, + -2.29924e+00_rt, -2.11267e+00_rt, -1.92724e+00_rt, -1.74307e+00_rt, + -1.56028e+00_rt, -1.37897e+00_rt, -1.19927e+00_rt, -1.02127e+00_rt, + -8.45055e-01_rt, -6.70705e-01_rt, -4.98281e-01_rt, -3.27829e-01_rt, + -1.59381e-01_rt, 7.04180e-03_rt, 1.71434e-01_rt, 3.33803e-01_rt, + 4.94165e-01_rt, 6.52549e-01_rt, 8.08994e-01_rt, 9.63549e-01_rt, + 1.11627e+00_rt, 1.26722e+00_rt, 1.41648e+00_rt, 1.56411e+00_rt, + 1.71020e+00_rt, 1.85484e+00_rt, 1.99811e+00_rt, 2.14009e+00_rt, + 2.28088e+00_rt, 2.42056e+00_rt, 2.55922e+00_rt, 2.69694e+00_rt, + 2.83379e+00_rt, 2.96984e+00_rt, 3.10518e+00_rt, 3.23987e+00_rt, + 3.37396e+00_rt, 3.50752e+00_rt, 3.64060e+00_rt, 3.77324e+00_rt, + 3.90549e+00_rt, 4.03740e+00_rt, 4.16899e+00_rt, 4.30031e+00_rt}; + + amrex::Gpu::synchronize(); + + m_dndt_table = QS_dndt_table{dndt_params, vals}; +} + + +void QuantumSynchrotronEngine::init_builtin_phot_em_table() +{ + QS_phot_em_table_params phot_em_params; + phot_em_params.chi_part_min = 1.0e-3_rt; + phot_em_params.chi_part_max = 200.0_rt; + phot_em_params.frac_min = 1.0e-12_rt; + phot_em_params.chi_part_how_many = 64; + phot_em_params.frac_how_many = 64; + + +const auto vals = amrex::Gpu::DeviceVector{ +-6.83368e+00_rt, -6.68749e+00_rt, -6.54129e+00_rt, -6.39510e+00_rt, +-6.24890e+00_rt, -6.10271e+00_rt, -5.95651e+00_rt, -5.81031e+00_rt, +-5.66412e+00_rt, -5.51792e+00_rt, -5.37173e+00_rt, -5.22554e+00_rt, +-5.07934e+00_rt, -4.93315e+00_rt, -4.78696e+00_rt, -4.64076e+00_rt, +-4.49457e+00_rt, -4.34839e+00_rt, -4.20220e+00_rt, -4.05602e+00_rt, +-3.90984e+00_rt, -3.76367e+00_rt, -3.61751e+00_rt, -3.47136e+00_rt, +-3.32522e+00_rt, -3.17911e+00_rt, -3.03302e+00_rt, -2.88697e+00_rt, +-2.74097e+00_rt, -2.59504e+00_rt, -2.44919e+00_rt, -2.30347e+00_rt, +-2.15790e+00_rt, -2.01255e+00_rt, -1.86749e+00_rt, -1.72282e+00_rt, +-1.57867e+00_rt, -1.43521e+00_rt, -1.29270e+00_rt, -1.15146e+00_rt, +-1.01194e+00_rt, -8.74744e-01_rt, -7.40699e-01_rt, -6.10940e-01_rt, +-4.87011e-01_rt, -3.70993e-01_rt, -2.65630e-01_rt, -1.74333e-01_rt, +-1.00881e-01_rt, -4.84058e-02_rt, -1.74572e-02_rt, -4.04316e-03_rt, +-4.68433e-04_rt, -1.82946e-05_rt, -1.28390e-07_rt, -7.14633e-11_rt, +-1.32240e-11_rt, -1.32236e-11_rt, -1.32236e-11_rt, -1.32236e-11_rt, +-1.32236e-11_rt, -1.32236e-11_rt, -1.32236e-11_rt, 0.00000e+00_rt, +-6.89807e+00_rt, -6.75188e+00_rt, -6.60568e+00_rt, -6.45948e+00_rt, +-6.31329e+00_rt, -6.16709e+00_rt, -6.02090e+00_rt, -5.87470e+00_rt, +-5.72851e+00_rt, -5.58231e+00_rt, -5.43612e+00_rt, -5.28992e+00_rt, +-5.14373e+00_rt, -4.99753e+00_rt, -4.85134e+00_rt, -4.70515e+00_rt, +-4.55896e+00_rt, -4.41277e+00_rt, -4.26658e+00_rt, -4.12040e+00_rt, +-3.97422e+00_rt, -3.82804e+00_rt, -3.68188e+00_rt, -3.53572e+00_rt, +-3.38958e+00_rt, -3.24346e+00_rt, -3.09736e+00_rt, -2.95129e+00_rt, +-2.80526e+00_rt, -2.65930e+00_rt, -2.51341e+00_rt, -2.36763e+00_rt, +-2.22199e+00_rt, -2.07654e+00_rt, -1.93134e+00_rt, -1.78648e+00_rt, +-1.64208e+00_rt, -1.49829e+00_rt, -1.35533e+00_rt, -1.21348e+00_rt, +-1.07314e+00_rt, -9.34824e-01_rt, -7.99268e-01_rt, -6.67454e-01_rt, +-5.40730e-01_rt, -4.20925e-01_rt, -3.10479e-01_rt, -2.12523e-01_rt, +-1.30747e-01_rt, -6.87573e-02_rt, -2.85300e-02_rt, -8.21443e-03_rt, +-1.33651e-03_rt, -8.87127e-05_rt, -1.42937e-06_rt, -2.40958e-09_rt, +-2.41052e-12_rt, -2.30649e-12_rt, -2.30649e-12_rt, -2.30649e-12_rt, +-2.30649e-12_rt, -2.30649e-12_rt, -2.30649e-12_rt, 0.00000e+00_rt, +-6.96242e+00_rt, -6.81622e+00_rt, -6.67002e+00_rt, -6.52383e+00_rt, +-6.37763e+00_rt, -6.23144e+00_rt, -6.08524e+00_rt, -5.93905e+00_rt, +-5.79285e+00_rt, -5.64665e+00_rt, -5.50046e+00_rt, -5.35427e+00_rt, +-5.20807e+00_rt, -5.06188e+00_rt, -4.91568e+00_rt, -4.76949e+00_rt, +-4.62330e+00_rt, -4.47711e+00_rt, -4.33092e+00_rt, -4.18474e+00_rt, +-4.03856e+00_rt, -3.89238e+00_rt, -3.74621e+00_rt, -3.60005e+00_rt, +-3.45390e+00_rt, -3.30777e+00_rt, -3.16165e+00_rt, -3.01557e+00_rt, +-2.86953e+00_rt, -2.72353e+00_rt, -2.57761e+00_rt, -2.43178e+00_rt, +-2.28607e+00_rt, -2.14053e+00_rt, -1.99521e+00_rt, -1.85018e+00_rt, +-1.70556e+00_rt, -1.56148e+00_rt, -1.41813e+00_rt, -1.27574e+00_rt, +-1.13468e+00_rt, -9.95396e-01_rt, -8.58520e-01_rt, -7.24912e-01_rt, +-5.95749e-01_rt, -4.72630e-01_rt, -3.57712e-01_rt, -2.53820e-01_rt, +-1.64441e-01_rt, -9.33509e-02_rt, -4.35013e-02_rt, -1.49849e-02_rt, +-3.22456e-03_rt, -3.31351e-04_rt, -1.05911e-05_rt, -5.25639e-08_rt, +-1.48073e-11_rt, -2.08211e-12_rt, -2.08211e-12_rt, -2.08211e-12_rt, +-2.08211e-12_rt, -2.08211e-12_rt, -2.08211e-12_rt, 0.00000e+00_rt, +-7.02671e+00_rt, -6.88051e+00_rt, -6.73432e+00_rt, -6.58812e+00_rt, +-6.44193e+00_rt, -6.29573e+00_rt, -6.14954e+00_rt, -6.00334e+00_rt, +-5.85714e+00_rt, -5.71095e+00_rt, -5.56475e+00_rt, -5.41856e+00_rt, +-5.27236e+00_rt, -5.12617e+00_rt, -4.97998e+00_rt, -4.83378e+00_rt, +-4.68759e+00_rt, -4.54140e+00_rt, -4.39521e+00_rt, -4.24903e+00_rt, +-4.10284e+00_rt, -3.95666e+00_rt, -3.81049e+00_rt, -3.66433e+00_rt, +-3.51817e+00_rt, -3.37203e+00_rt, -3.22591e+00_rt, -3.07981e+00_rt, +-2.93375e+00_rt, -2.78773e+00_rt, -2.64177e+00_rt, -2.49590e+00_rt, +-2.35013e+00_rt, -2.20451e+00_rt, -2.05908e+00_rt, -1.91392e+00_rt, +-1.76911e+00_rt, -1.62477e+00_rt, -1.48106e+00_rt, -1.33822e+00_rt, +-1.19652e+00_rt, -1.05638e+00_rt, -9.18354e-01_rt, -7.83181e-01_rt, +-6.51887e-01_rt, -5.25873e-01_rt, -4.07032e-01_rt, -2.97887e-01_rt, +-2.01651e-01_rt, -1.22060e-01_rt, -6.26329e-02_rt, -2.50149e-02_rt, +-6.77615e-03_rt, -9.98061e-04_rt, -5.61030e-05_rt, -6.78360e-07_rt, +-6.88729e-10_rt, -9.53426e-12_rt, -9.52349e-12_rt, -9.52349e-12_rt, +-9.52349e-12_rt, -9.52349e-12_rt, -9.52349e-12_rt, 0.00000e+00_rt, +-7.09094e+00_rt, -6.94475e+00_rt, -6.79855e+00_rt, -6.65236e+00_rt, +-6.50616e+00_rt, -6.35997e+00_rt, -6.21377e+00_rt, -6.06757e+00_rt, +-5.92138e+00_rt, -5.77518e+00_rt, -5.62899e+00_rt, -5.48279e+00_rt, +-5.33660e+00_rt, -5.19040e+00_rt, -5.04421e+00_rt, -4.89802e+00_rt, +-4.75182e+00_rt, -4.60563e+00_rt, -4.45944e+00_rt, -4.31326e+00_rt, +-4.16707e+00_rt, -4.02089e+00_rt, -3.87471e+00_rt, -3.72855e+00_rt, +-3.58239e+00_rt, -3.43624e+00_rt, -3.29011e+00_rt, -3.14400e+00_rt, +-2.99792e+00_rt, -2.85188e+00_rt, -2.70589e+00_rt, -2.55998e+00_rt, +-2.41416e+00_rt, -2.26847e+00_rt, -2.12295e+00_rt, -1.97766e+00_rt, +-1.83268e+00_rt, -1.68811e+00_rt, -1.54410e+00_rt, -1.40085e+00_rt, +-1.25860e+00_rt, -1.11772e+00_rt, -9.78681e-01_rt, -8.42140e-01_rt, +-7.08987e-01_rt, -5.80444e-01_rt, -4.58170e-01_rt, -3.44398e-01_rt, +-2.42039e-01_rt, -1.54649e-01_rt, -8.59929e-02_rt, -3.88131e-02_rt, +-1.27086e-02_rt, -2.51783e-03_rt, -2.25371e-04_rt, -5.67634e-06_rt, +-1.83116e-08_rt, -7.44138e-12_rt, -5.50848e-12_rt, -5.50848e-12_rt, +-5.50848e-12_rt, -5.50848e-12_rt, -5.50848e-12_rt, 0.00000e+00_rt, +-7.15510e+00_rt, -7.00891e+00_rt, -6.86271e+00_rt, -6.71652e+00_rt, +-6.57032e+00_rt, -6.42413e+00_rt, -6.27793e+00_rt, -6.13173e+00_rt, +-5.98554e+00_rt, -5.83934e+00_rt, -5.69315e+00_rt, -5.54695e+00_rt, +-5.40076e+00_rt, -5.25456e+00_rt, -5.10837e+00_rt, -4.96218e+00_rt, +-4.81598e+00_rt, -4.66979e+00_rt, -4.52360e+00_rt, -4.37741e+00_rt, +-4.23123e+00_rt, -4.08504e+00_rt, -3.93887e+00_rt, -3.79269e+00_rt, +-3.64653e+00_rt, -3.50038e+00_rt, -3.35424e+00_rt, -3.20812e+00_rt, +-3.06202e+00_rt, -2.91597e+00_rt, -2.76995e+00_rt, -2.62400e+00_rt, +-2.47814e+00_rt, -2.33239e+00_rt, -2.18679e+00_rt, -2.04139e+00_rt, +-1.89626e+00_rt, -1.75149e+00_rt, -1.60722e+00_rt, -1.46360e+00_rt, +-1.32087e+00_rt, -1.17934e+00_rt, -1.03942e+00_rt, -9.01681e-01_rt, +-7.66906e-01_rt, -6.36155e-01_rt, -5.10880e-01_rt, -3.93046e-01_rt, +-2.85258e-01_rt, -1.90814e-01_rt, -1.13487e-01_rt, -5.66916e-02_rt, +-2.17036e-02_rt, -5.48859e-03_rt, -7.20753e-04_rt, -3.32360e-05_rt, +-2.81000e-07_rt, -1.48662e-10_rt, -7.22000e-12_rt, -7.21945e-12_rt, +-7.21945e-12_rt, -7.21945e-12_rt, -7.21945e-12_rt, 0.00000e+00_rt, +-7.21918e+00_rt, -7.07298e+00_rt, -6.92678e+00_rt, -6.78059e+00_rt, +-6.63439e+00_rt, -6.48820e+00_rt, -6.34200e+00_rt, -6.19581e+00_rt, +-6.04961e+00_rt, -5.90341e+00_rt, -5.75722e+00_rt, -5.61102e+00_rt, +-5.46483e+00_rt, -5.31863e+00_rt, -5.17244e+00_rt, -5.02625e+00_rt, +-4.88005e+00_rt, -4.73386e+00_rt, -4.58767e+00_rt, -4.44148e+00_rt, +-4.29529e+00_rt, -4.14911e+00_rt, -4.00293e+00_rt, -3.85675e+00_rt, +-3.71059e+00_rt, -3.56443e+00_rt, -3.41828e+00_rt, -3.27215e+00_rt, +-3.12605e+00_rt, -2.97997e+00_rt, -2.83394e+00_rt, -2.68796e+00_rt, +-2.54205e+00_rt, -2.39625e+00_rt, -2.25057e+00_rt, -2.10508e+00_rt, +-1.95982e+00_rt, -1.81488e+00_rt, -1.67037e+00_rt, -1.52644e+00_rt, +-1.38329e+00_rt, -1.24118e+00_rt, -1.10049e+00_rt, -9.61708e-01_rt, +-8.25515e-01_rt, -6.92837e-01_rt, -5.64941e-01_rt, -4.43549e-01_rt, +-3.30974e-01_rt, -2.30216e-01_rt, -1.44899e-01_rt, -7.87630e-02_rt, +-3.43106e-02_rt, -1.06083e-02_rt, -1.91011e-03_rt, -1.44988e-04_rt, +-2.71228e-06_rt, -4.95880e-09_rt, -1.54320e-11_rt, -1.52724e-11_rt, +-1.52724e-11_rt, -1.52724e-11_rt, -1.52724e-11_rt, 0.00000e+00_rt, +-7.28314e+00_rt, -7.13695e+00_rt, -6.99075e+00_rt, -6.84455e+00_rt, +-6.69836e+00_rt, -6.55216e+00_rt, -6.40597e+00_rt, -6.25977e+00_rt, +-6.11358e+00_rt, -5.96738e+00_rt, -5.82118e+00_rt, -5.67499e+00_rt, +-5.52879e+00_rt, -5.38260e+00_rt, -5.23640e+00_rt, -5.09021e+00_rt, +-4.94402e+00_rt, -4.79782e+00_rt, -4.65163e+00_rt, -4.50544e+00_rt, +-4.35925e+00_rt, -4.21307e+00_rt, -4.06689e+00_rt, -3.92071e+00_rt, +-3.77454e+00_rt, -3.62837e+00_rt, -3.48222e+00_rt, -3.33609e+00_rt, +-3.18997e+00_rt, -3.04388e+00_rt, -2.89783e+00_rt, -2.75182e+00_rt, +-2.60588e+00_rt, -2.46003e+00_rt, -2.31429e+00_rt, -2.16871e+00_rt, +-2.02334e+00_rt, -1.87825e+00_rt, -1.73353e+00_rt, -1.58933e+00_rt, +-1.44580e+00_rt, -1.30319e+00_rt, -1.16182e+00_rt, -1.02213e+00_rt, +-8.84697e-01_rt, -7.50336e-01_rt, -6.20152e-01_rt, -4.95651e-01_rt, +-3.78870e-01_rt, -2.72504e-01_rt, -1.79934e-01_rt, -1.04967e-01_rt, +-5.08903e-02_rt, -1.85694e-02_rt, -4.33586e-03_rt, -4.96245e-04_rt, +-1.78626e-05_rt, -9.39993e-08_rt, -2.60318e-11_rt, -8.32656e-12_rt, +-8.32656e-12_rt, -8.32656e-12_rt, -8.32656e-12_rt, 0.00000e+00_rt, +-7.34698e+00_rt, -7.20078e+00_rt, -7.05459e+00_rt, -6.90839e+00_rt, +-6.76220e+00_rt, -6.61600e+00_rt, -6.46980e+00_rt, -6.32361e+00_rt, +-6.17741e+00_rt, -6.03122e+00_rt, -5.88502e+00_rt, -5.73883e+00_rt, +-5.59263e+00_rt, -5.44644e+00_rt, -5.30024e+00_rt, -5.15405e+00_rt, +-5.00785e+00_rt, -4.86166e+00_rt, -4.71547e+00_rt, -4.56928e+00_rt, +-4.42309e+00_rt, -4.27690e+00_rt, -4.13072e+00_rt, -3.98454e+00_rt, +-3.83836e+00_rt, -3.69220e+00_rt, -3.54604e+00_rt, -3.39989e+00_rt, +-3.25377e+00_rt, -3.10767e+00_rt, -2.96159e+00_rt, -2.81557e+00_rt, +-2.66959e+00_rt, -2.52370e+00_rt, -2.37791e+00_rt, -2.23225e+00_rt, +-2.08678e+00_rt, -1.94155e+00_rt, -1.79666e+00_rt, -1.65221e+00_rt, +-1.50836e+00_rt, -1.36532e+00_rt, -1.22336e+00_rt, -1.08286e+00_rt, +-9.44344e-01_rt, -8.08515e-01_rt, -6.76333e-01_rt, -5.49115e-01_rt, +-4.28649e-01_rt, -3.17331e-01_rt, -2.18253e-01_rt, -1.35109e-01_rt, +-7.16006e-02_rt, -2.99558e-02_rt, -8.66338e-03_rt, -1.39061e-03_rt, +-8.58450e-05_rt, -1.08211e-06_rt, -8.81213e-10_rt, -9.14124e-12_rt, +-9.13691e-12_rt, -9.13691e-12_rt, -9.13691e-12_rt, 0.00000e+00_rt, +-7.41066e+00_rt, -7.26447e+00_rt, -7.11827e+00_rt, -6.97207e+00_rt, +-6.82588e+00_rt, -6.67968e+00_rt, -6.53349e+00_rt, -6.38729e+00_rt, +-6.24109e+00_rt, -6.09490e+00_rt, -5.94870e+00_rt, -5.80251e+00_rt, +-5.65631e+00_rt, -5.51012e+00_rt, -5.36392e+00_rt, -5.21773e+00_rt, +-5.07153e+00_rt, -4.92534e+00_rt, -4.77915e+00_rt, -4.63296e+00_rt, +-4.48677e+00_rt, -4.34058e+00_rt, -4.19439e+00_rt, -4.04821e+00_rt, +-3.90204e+00_rt, -3.75586e+00_rt, -3.60970e+00_rt, -3.46355e+00_rt, +-3.31742e+00_rt, -3.17130e+00_rt, -3.02522e+00_rt, -2.87917e+00_rt, +-2.73317e+00_rt, -2.58724e+00_rt, -2.44140e+00_rt, -2.29568e+00_rt, +-2.15012e+00_rt, -2.00477e+00_rt, -1.85972e+00_rt, -1.71506e+00_rt, +-1.57092e+00_rt, -1.42749e+00_rt, -1.28501e+00_rt, -1.14382e+00_rt, +-1.00436e+00_rt, -8.67244e-01_rt, -7.33318e-01_rt, -6.03728e-01_rt, +-4.80039e-01_rt, -3.64367e-01_rt, -2.59499e-01_rt, -1.68904e-01_rt, +-9.64162e-02_rt, -4.51735e-02_rt, -1.55832e-02_rt, -3.30468e-03_rt, +-3.18182e-04_rt, -8.23951e-06_rt, -2.20029e-08_rt, -9.32820e-12_rt, +-8.44891e-12_rt, -8.44891e-12_rt, -8.44891e-12_rt, 0.00000e+00_rt, +-7.47416e+00_rt, -7.32796e+00_rt, -7.18177e+00_rt, -7.03557e+00_rt, +-6.88938e+00_rt, -6.74318e+00_rt, -6.59698e+00_rt, -6.45079e+00_rt, +-6.30459e+00_rt, -6.15840e+00_rt, -6.01220e+00_rt, -5.86601e+00_rt, +-5.71981e+00_rt, -5.57362e+00_rt, -5.42742e+00_rt, -5.28123e+00_rt, +-5.13503e+00_rt, -4.98884e+00_rt, -4.84264e+00_rt, -4.69645e+00_rt, +-4.55026e+00_rt, -4.40407e+00_rt, -4.25789e+00_rt, -4.11170e+00_rt, +-3.96552e+00_rt, -3.81935e+00_rt, -3.67318e+00_rt, -3.52703e+00_rt, +-3.38089e+00_rt, -3.23476e+00_rt, -3.08866e+00_rt, -2.94260e+00_rt, +-2.79657e+00_rt, -2.65061e+00_rt, -2.50473e+00_rt, -2.35895e+00_rt, +-2.21331e+00_rt, -2.06786e+00_rt, -1.92267e+00_rt, -1.77782e+00_rt, +-1.63344e+00_rt, -1.48967e+00_rt, -1.34674e+00_rt, -1.20493e+00_rt, +-1.06464e+00_rt, -9.26404e-01_rt, -7.90955e-01_rt, -6.59295e-01_rt, +-5.32790e-01_rt, -4.13301e-01_rt, -3.03311e-01_rt, -2.06012e-01_rt, +-1.25166e-01_rt, -6.44255e-02_rt, -2.57044e-02_rt, -6.85590e-03_rt, +-9.51994e-04_rt, -4.46223e-05_rt, -3.19559e-07_rt, -7.43913e-11_rt, +-3.27816e-12_rt, -3.27816e-12_rt, -3.27816e-12_rt, 0.00000e+00_rt, +-7.53744e+00_rt, -7.39124e+00_rt, -7.24504e+00_rt, -7.09885e+00_rt, +-6.95265e+00_rt, -6.80646e+00_rt, -6.66026e+00_rt, -6.51407e+00_rt, +-6.36787e+00_rt, -6.22167e+00_rt, -6.07548e+00_rt, -5.92928e+00_rt, +-5.78309e+00_rt, -5.63689e+00_rt, -5.49070e+00_rt, -5.34450e+00_rt, +-5.19831e+00_rt, -5.05211e+00_rt, -4.90592e+00_rt, -4.75973e+00_rt, +-4.61354e+00_rt, -4.46735e+00_rt, -4.32116e+00_rt, -4.17497e+00_rt, +-4.02879e+00_rt, -3.88262e+00_rt, -3.73645e+00_rt, -3.59029e+00_rt, +-3.44414e+00_rt, -3.29801e+00_rt, -3.15189e+00_rt, -3.00581e+00_rt, +-2.85977e+00_rt, -2.71378e+00_rt, -2.56785e+00_rt, -2.42203e+00_rt, +-2.27632e+00_rt, -2.13078e+00_rt, -1.98547e+00_rt, -1.84046e+00_rt, +-1.69585e+00_rt, -1.55179e+00_rt, -1.40846e+00_rt, -1.26611e+00_rt, +-1.12510e+00_rt, -9.85880e-01_rt, -8.49102e-01_rt, -7.15634e-01_rt, +-5.86670e-01_rt, -4.63841e-01_rt, -3.49344e-01_rt, -2.46067e-01_rt, +-1.57574e-01_rt, -8.77228e-02_rt, -3.94737e-02_rt, -1.27172e-02_rt, +-2.38737e-03_rt, -1.82476e-04_rt, -2.94710e-06_rt, -2.70147e-09_rt, +-3.03868e-12_rt, -3.03213e-12_rt, -3.03213e-12_rt, 0.00000e+00_rt, +-7.60045e+00_rt, -7.45425e+00_rt, -7.30806e+00_rt, -7.16186e+00_rt, +-7.01567e+00_rt, -6.86947e+00_rt, -6.72328e+00_rt, -6.57708e+00_rt, +-6.43088e+00_rt, -6.28469e+00_rt, -6.13849e+00_rt, -5.99230e+00_rt, +-5.84610e+00_rt, -5.69991e+00_rt, -5.55371e+00_rt, -5.40752e+00_rt, +-5.26132e+00_rt, -5.11513e+00_rt, -4.96893e+00_rt, -4.82274e+00_rt, +-4.67655e+00_rt, -4.53036e+00_rt, -4.38417e+00_rt, -4.23798e+00_rt, +-4.09180e+00_rt, -3.94562e+00_rt, -3.79945e+00_rt, -3.65328e+00_rt, +-3.50713e+00_rt, -3.36099e+00_rt, -3.21487e+00_rt, -3.06877e+00_rt, +-2.92271e+00_rt, -2.77669e+00_rt, -2.63074e+00_rt, -2.48486e+00_rt, +-2.33910e+00_rt, -2.19348e+00_rt, -2.04806e+00_rt, -1.90291e+00_rt, +-1.75811e+00_rt, -1.61378e+00_rt, -1.47010e+00_rt, -1.32729e+00_rt, +-1.18564e+00_rt, -1.04556e+00_rt, -9.07623e-01_rt, -7.72576e-01_rt, +-6.41466e-01_rt, -5.15718e-01_rt, -3.97268e-01_rt, -2.88696e-01_rt, +-1.93301e-01_rt, -1.14918e-01_rt, -5.71378e-02_rt, -2.15106e-02_rt, +-5.17615e-03_rt, -5.91934e-04_rt, -1.86877e-05_rt, -5.48948e-08_rt, +-9.48919e-12_rt, -8.28793e-12_rt, -8.28793e-12_rt, 0.00000e+00_rt, +-7.66315e+00_rt, -7.51696e+00_rt, -7.37076e+00_rt, -7.22456e+00_rt, +-7.07837e+00_rt, -6.93217e+00_rt, -6.78598e+00_rt, -6.63978e+00_rt, +-6.49359e+00_rt, -6.34739e+00_rt, -6.20119e+00_rt, -6.05500e+00_rt, +-5.90880e+00_rt, -5.76261e+00_rt, -5.61641e+00_rt, -5.47022e+00_rt, +-5.32402e+00_rt, -5.17783e+00_rt, -5.03163e+00_rt, -4.88544e+00_rt, +-4.73925e+00_rt, -4.59306e+00_rt, -4.44687e+00_rt, -4.30068e+00_rt, +-4.15450e+00_rt, -4.00831e+00_rt, -3.86214e+00_rt, -3.71597e+00_rt, +-3.56981e+00_rt, -3.42366e+00_rt, -3.27753e+00_rt, -3.13143e+00_rt, +-2.98535e+00_rt, -2.83931e+00_rt, -2.69332e+00_rt, -2.54741e+00_rt, +-2.40159e+00_rt, -2.25591e+00_rt, -2.11039e+00_rt, -1.96511e+00_rt, +-1.82014e+00_rt, -1.67559e+00_rt, -1.53160e+00_rt, -1.38838e+00_rt, +-1.24617e+00_rt, -1.10535e+00_rt, -9.66389e-01_rt, -8.29963e-01_rt, +-6.96980e-01_rt, -5.68682e-01_rt, -4.46770e-01_rt, -3.33534e-01_rt, +-2.31969e-01_rt, -1.45746e-01_rt, -7.87448e-02_rt, -3.37167e-02_rt, +-9.95408e-03_rt, -1.58635e-03_rt, -8.70168e-05_rt, -6.67831e-07_rt, +-9.66782e-11_rt, -7.57749e-12_rt, -7.57749e-12_rt, 0.00000e+00_rt, +-7.72548e+00_rt, -7.57929e+00_rt, -7.43309e+00_rt, -7.28690e+00_rt, +-7.14070e+00_rt, -6.99450e+00_rt, -6.84831e+00_rt, -6.70211e+00_rt, +-6.55592e+00_rt, -6.40972e+00_rt, -6.26352e+00_rt, -6.11733e+00_rt, +-5.97113e+00_rt, -5.82494e+00_rt, -5.67874e+00_rt, -5.53255e+00_rt, +-5.38635e+00_rt, -5.24016e+00_rt, -5.09396e+00_rt, -4.94777e+00_rt, +-4.80158e+00_rt, -4.65539e+00_rt, -4.50920e+00_rt, -4.36301e+00_rt, +-4.21682e+00_rt, -4.07064e+00_rt, -3.92446e+00_rt, -3.77829e+00_rt, +-3.63212e+00_rt, -3.48597e+00_rt, -3.33983e+00_rt, -3.19371e+00_rt, +-3.04762e+00_rt, -2.90156e+00_rt, -2.75555e+00_rt, -2.60961e+00_rt, +-2.46374e+00_rt, -2.31800e+00_rt, -2.17240e+00_rt, -2.02701e+00_rt, +-1.88189e+00_rt, -1.73714e+00_rt, -1.59288e+00_rt, -1.44929e+00_rt, +-1.30660e+00_rt, -1.16512e+00_rt, -1.02527e+00_rt, -8.87642e-01_rt, +-7.53022e-01_rt, -6.22499e-01_rt, -4.97561e-01_rt, -3.80228e-01_rt, +-2.73191e-01_rt, -1.79866e-01_rt, -1.04173e-01_rt, -4.96236e-02_rt, +-1.73398e-02_rt, -3.63192e-03_rt, -3.14100e-04_rt, -5.32039e-06_rt, +-3.17026e-09_rt, -2.22048e-11_rt, -2.22045e-11_rt, 0.00000e+00_rt, +-7.78738e+00_rt, -7.64118e+00_rt, -7.49499e+00_rt, -7.34879e+00_rt, +-7.20259e+00_rt, -7.05640e+00_rt, -6.91020e+00_rt, -6.76401e+00_rt, +-6.61781e+00_rt, -6.47162e+00_rt, -6.32542e+00_rt, -6.17922e+00_rt, +-6.03303e+00_rt, -5.88683e+00_rt, -5.74064e+00_rt, -5.59444e+00_rt, +-5.44825e+00_rt, -5.30205e+00_rt, -5.15586e+00_rt, -5.00966e+00_rt, +-4.86347e+00_rt, -4.71728e+00_rt, -4.57109e+00_rt, -4.42490e+00_rt, +-4.27871e+00_rt, -4.13253e+00_rt, -3.98635e+00_rt, -3.84017e+00_rt, +-3.69400e+00_rt, -3.54785e+00_rt, -3.40170e+00_rt, -3.25557e+00_rt, +-3.10947e+00_rt, -2.96339e+00_rt, -2.81736e+00_rt, -2.67138e+00_rt, +-2.52548e+00_rt, -2.37968e+00_rt, -2.23401e+00_rt, -2.08852e+00_rt, +-1.94327e+00_rt, -1.79834e+00_rt, -1.65385e+00_rt, -1.50995e+00_rt, +-1.36683e+00_rt, -1.22477e+00_rt, -1.08414e+00_rt, -9.45461e-01_rt, +-8.09413e-01_rt, -6.76947e-01_rt, -5.49366e-01_rt, -4.28446e-01_rt, +-3.16580e-01_rt, -2.16891e-01_rt, -1.33169e-01_rt, -6.93177e-02_rt, +-2.78380e-02_rt, -7.30271e-03_rt, -9.19046e-04_rt, -2.98964e-05_rt, +-6.03839e-08_rt, -1.53989e-11_rt, -1.52887e-11_rt, 0.00000e+00_rt, +-7.84876e+00_rt, -7.70257e+00_rt, -7.55637e+00_rt, -7.41017e+00_rt, +-7.26398e+00_rt, -7.11778e+00_rt, -6.97159e+00_rt, -6.82539e+00_rt, +-6.67920e+00_rt, -6.53300e+00_rt, -6.38680e+00_rt, -6.24061e+00_rt, +-6.09441e+00_rt, -5.94822e+00_rt, -5.80202e+00_rt, -5.65583e+00_rt, +-5.50963e+00_rt, -5.36344e+00_rt, -5.21724e+00_rt, -5.07105e+00_rt, +-4.92485e+00_rt, -4.77866e+00_rt, -4.63247e+00_rt, -4.48628e+00_rt, +-4.34009e+00_rt, -4.19391e+00_rt, -4.04772e+00_rt, -3.90155e+00_rt, +-3.75538e+00_rt, -3.60921e+00_rt, -3.46306e+00_rt, -3.31693e+00_rt, +-3.17081e+00_rt, -3.02472e+00_rt, -2.87867e+00_rt, -2.73266e+00_rt, +-2.58673e+00_rt, -2.44087e+00_rt, -2.29514e+00_rt, -2.14957e+00_rt, +-2.00420e+00_rt, -1.85912e+00_rt, -1.71442e+00_rt, -1.57024e+00_rt, +-1.42675e+00_rt, -1.28418e+00_rt, -1.14288e+00_rt, -1.00328e+00_rt, +-8.65978e-01_rt, -7.31817e-01_rt, -6.01928e-01_rt, -4.77871e-01_rt, +-3.61762e-01_rt, -2.56416e-01_rt, -1.65391e-01_rt, -9.27046e-02_rt, +-4.17753e-02_rt, -1.31925e-02_rt, -2.26148e-03_rt, -1.25980e-04_rt, +-6.99647e-07_rt, -1.03404e-11_rt, 0.00000e+00_rt, 0.00000e+00_rt, +-7.90955e+00_rt, -7.76336e+00_rt, -7.61716e+00_rt, -7.47096e+00_rt, +-7.32477e+00_rt, -7.17857e+00_rt, -7.03238e+00_rt, -6.88618e+00_rt, +-6.73999e+00_rt, -6.59379e+00_rt, -6.44759e+00_rt, -6.30140e+00_rt, +-6.15520e+00_rt, -6.00901e+00_rt, -5.86281e+00_rt, -5.71662e+00_rt, +-5.57042e+00_rt, -5.42423e+00_rt, -5.27803e+00_rt, -5.13184e+00_rt, +-4.98564e+00_rt, -4.83945e+00_rt, -4.69326e+00_rt, -4.54707e+00_rt, +-4.40088e+00_rt, -4.25469e+00_rt, -4.10851e+00_rt, -3.96233e+00_rt, +-3.81615e+00_rt, -3.66999e+00_rt, -3.52383e+00_rt, -3.37769e+00_rt, +-3.23156e+00_rt, -3.08546e+00_rt, -2.93939e+00_rt, -2.79336e+00_rt, +-2.64739e+00_rt, -2.50150e+00_rt, -2.35571e+00_rt, -2.21006e+00_rt, +-2.06460e+00_rt, -1.91938e+00_rt, -1.77450e+00_rt, -1.63007e+00_rt, +-1.48625e+00_rt, -1.34325e+00_rt, -1.20135e+00_rt, -1.06094e+00_rt, +-9.22547e-01_rt, -7.86905e-01_rt, -6.55002e-01_rt, -5.28204e-01_rt, +-4.08376e-01_rt, -2.98032e-01_rt, -2.00444e-01_rt, -1.19547e-01_rt, +-5.92784e-02_rt, -2.18163e-02_rt, -4.82406e-03_rt, -4.18699e-04_rt, +-5.35426e-06_rt, -5.59711e-10_rt, 0.00000e+00_rt, 0.00000e+00_rt, +-7.96965e+00_rt, -7.82346e+00_rt, -7.67726e+00_rt, -7.53107e+00_rt, +-7.38487e+00_rt, -7.23867e+00_rt, -7.09248e+00_rt, -6.94628e+00_rt, +-6.80009e+00_rt, -6.65389e+00_rt, -6.50770e+00_rt, -6.36150e+00_rt, +-6.21530e+00_rt, -6.06911e+00_rt, -5.92291e+00_rt, -5.77672e+00_rt, +-5.63052e+00_rt, -5.48433e+00_rt, -5.33813e+00_rt, -5.19194e+00_rt, +-5.04574e+00_rt, -4.89955e+00_rt, -4.75336e+00_rt, -4.60717e+00_rt, +-4.46098e+00_rt, -4.31479e+00_rt, -4.16860e+00_rt, -4.02242e+00_rt, +-3.87624e+00_rt, -3.73007e+00_rt, -3.58391e+00_rt, -3.43776e+00_rt, +-3.29163e+00_rt, -3.14552e+00_rt, -2.99943e+00_rt, -2.85338e+00_rt, +-2.70739e+00_rt, -2.56146e+00_rt, -2.41562e+00_rt, -2.26990e+00_rt, +-2.12435e+00_rt, -1.97902e+00_rt, -1.83398e+00_rt, -1.68933e+00_rt, +-1.54522e+00_rt, -1.40183e+00_rt, -1.25941e+00_rt, -1.11829e+00_rt, +-9.78952e-01_rt, -8.42015e-01_rt, -7.08354e-01_rt, -5.79163e-01_rt, +-4.56081e-01_rt, -3.41341e-01_rt, -2.37906e-01_rt, -1.49508e-01_rt, +-8.02862e-02_rt, -3.35354e-02_rt, -9.14653e-03_rt, -1.14413e-03_rt, +-2.91028e-05_rt, -1.37503e-08_rt, 0.00000e+00_rt, 0.00000e+00_rt, +-8.02897e+00_rt, -7.88277e+00_rt, -7.73657e+00_rt, -7.59038e+00_rt, +-7.44418e+00_rt, -7.29799e+00_rt, -7.15179e+00_rt, -7.00560e+00_rt, +-6.85940e+00_rt, -6.71320e+00_rt, -6.56701e+00_rt, -6.42081e+00_rt, +-6.27462e+00_rt, -6.12842e+00_rt, -5.98223e+00_rt, -5.83603e+00_rt, +-5.68983e+00_rt, -5.54364e+00_rt, -5.39744e+00_rt, -5.25125e+00_rt, +-5.10506e+00_rt, -4.95886e+00_rt, -4.81267e+00_rt, -4.66648e+00_rt, +-4.52029e+00_rt, -4.37410e+00_rt, -4.22791e+00_rt, -4.08173e+00_rt, +-3.93555e+00_rt, -3.78937e+00_rt, -3.64321e+00_rt, -3.49705e+00_rt, +-3.35091e+00_rt, -3.20479e+00_rt, -3.05869e+00_rt, -2.91262e+00_rt, +-2.76660e+00_rt, -2.62064e+00_rt, -2.47476e+00_rt, -2.32899e+00_rt, +-2.18336e+00_rt, -2.03792e+00_rt, -1.89274e+00_rt, -1.74791e+00_rt, +-1.60355e+00_rt, -1.45981e+00_rt, -1.31693e+00_rt, -1.17520e+00_rt, +-1.03502e+00_rt, -8.96956e-01_rt, -7.61760e-01_rt, -6.30479e-01_rt, +-5.04555e-01_rt, -3.85958e-01_rt, -2.77349e-01_rt, -1.82185e-01_rt, +-1.04585e-01_rt, -4.85205e-02_rt, -1.57346e-02_rt, -2.66015e-03_rt, +-1.19304e-04_rt, -1.95626e-07_rt, 0.00000e+00_rt, 0.00000e+00_rt, +-8.08738e+00_rt, -7.94118e+00_rt, -7.79499e+00_rt, -7.64879e+00_rt, +-7.50260e+00_rt, -7.35640e+00_rt, -7.21020e+00_rt, -7.06401e+00_rt, +-6.91781e+00_rt, -6.77162e+00_rt, -6.62542e+00_rt, -6.47922e+00_rt, +-6.33303e+00_rt, -6.18683e+00_rt, -6.04064e+00_rt, -5.89444e+00_rt, +-5.74825e+00_rt, -5.60205e+00_rt, -5.45586e+00_rt, -5.30966e+00_rt, +-5.16347e+00_rt, -5.01727e+00_rt, -4.87108e+00_rt, -4.72489e+00_rt, +-4.57870e+00_rt, -4.43251e+00_rt, -4.28632e+00_rt, -4.14013e+00_rt, +-3.99395e+00_rt, -3.84778e+00_rt, -3.70161e+00_rt, -3.55545e+00_rt, +-3.40930e+00_rt, -3.26317e+00_rt, -3.11706e+00_rt, -2.97098e+00_rt, +-2.82493e+00_rt, -2.67894e+00_rt, -2.53302e+00_rt, -2.38720e+00_rt, +-2.24150e+00_rt, -2.09597e+00_rt, -1.95067e+00_rt, -1.80567e+00_rt, +-1.66109e+00_rt, -1.51706e+00_rt, -1.37378e+00_rt, -1.23150e+00_rt, +-1.09060e+00_rt, -9.51540e-01_rt, -8.15002e-01_rt, -6.81898e-01_rt, +-5.53491e-01_rt, -4.31519e-01_rt, -3.18353e-01_rt, -2.17146e-01_rt, +-1.31847e-01_rt, -6.67538e-02_rt, -2.49772e-02_rt, -5.41331e-03_rt, +-3.87555e-04_rt, -1.77093e-06_rt, 0.00000e+00_rt, 0.00000e+00_rt, +-8.14478e+00_rt, -7.99858e+00_rt, -7.85239e+00_rt, -7.70619e+00_rt, +-7.55999e+00_rt, -7.41380e+00_rt, -7.26760e+00_rt, -7.12141e+00_rt, +-6.97521e+00_rt, -6.82901e+00_rt, -6.68282e+00_rt, -6.53662e+00_rt, +-6.39043e+00_rt, -6.24423e+00_rt, -6.09804e+00_rt, -5.95184e+00_rt, +-5.80564e+00_rt, -5.65945e+00_rt, -5.51325e+00_rt, -5.36706e+00_rt, +-5.22086e+00_rt, -5.07467e+00_rt, -4.92848e+00_rt, -4.78228e+00_rt, +-4.63609e+00_rt, -4.48990e+00_rt, -4.34371e+00_rt, -4.19753e+00_rt, +-4.05134e+00_rt, -3.90517e+00_rt, -3.75899e+00_rt, -3.61283e+00_rt, +-3.46667e+00_rt, -3.32053e+00_rt, -3.17441e+00_rt, -3.02832e+00_rt, +-2.88226e+00_rt, -2.73624e+00_rt, -2.59029e+00_rt, -2.44442e+00_rt, +-2.29866e+00_rt, -2.15305e+00_rt, -2.00764e+00_rt, -1.86250e+00_rt, +-1.71772e+00_rt, -1.57343e+00_rt, -1.42980e+00_rt, -1.28705e+00_rt, +-1.14550e+00_rt, -1.00558e+00_rt, -8.67872e-01_rt, -7.33176e-01_rt, +-6.02602e-01_rt, -4.77680e-01_rt, -3.60513e-01_rt, -2.53947e-01_rt, +-1.61676e-01_rt, -8.80563e-02_rt, -3.70951e-02_rt, -9.86840e-03_rt, +-1.03910e-03_rt, -1.10564e-05_rt, -9.50173e-12_rt, 0.00000e+00_rt, +-8.20104e+00_rt, -8.05484e+00_rt, -7.90865e+00_rt, -7.76245e+00_rt, +-7.61626e+00_rt, -7.47006e+00_rt, -7.32387e+00_rt, -7.17767e+00_rt, +-7.03147e+00_rt, -6.88528e+00_rt, -6.73908e+00_rt, -6.59289e+00_rt, +-6.44669e+00_rt, -6.30049e+00_rt, -6.15430e+00_rt, -6.00810e+00_rt, +-5.86191e+00_rt, -5.71571e+00_rt, -5.56952e+00_rt, -5.42332e+00_rt, +-5.27713e+00_rt, -5.13093e+00_rt, -4.98474e+00_rt, -4.83855e+00_rt, +-4.69235e+00_rt, -4.54616e+00_rt, -4.39997e+00_rt, -4.25379e+00_rt, +-4.10760e+00_rt, -3.96142e+00_rt, -3.81525e+00_rt, -3.66908e+00_rt, +-3.52292e+00_rt, -3.37677e+00_rt, -3.23064e+00_rt, -3.08453e+00_rt, +-2.93846e+00_rt, -2.79242e+00_rt, -2.64644e+00_rt, -2.50053e+00_rt, +-2.35472e+00_rt, -2.20904e+00_rt, -2.06353e+00_rt, -1.91826e+00_rt, +-1.77331e+00_rt, -1.62879e+00_rt, -1.48485e+00_rt, -1.34169e+00_rt, +-1.19958e+00_rt, -1.05890e+00_rt, -9.20170e-01_rt, -7.84083e-01_rt, +-6.51617e-01_rt, -5.24118e-01_rt, -4.03448e-01_rt, -2.92155e-01_rt, +-1.93635e-01_rt, -1.12128e-01_rt, -5.21283e-02_rt, -1.64271e-02_rt, +-2.37796e-03_rt, -5.08169e-05_rt, -6.19466e-10_rt, 0.00000e+00_rt, +-8.25605e+00_rt, -8.10985e+00_rt, -7.96366e+00_rt, -7.81746e+00_rt, +-7.67126e+00_rt, -7.52507e+00_rt, -7.37887e+00_rt, -7.23268e+00_rt, +-7.08648e+00_rt, -6.94029e+00_rt, -6.79409e+00_rt, -6.64789e+00_rt, +-6.50170e+00_rt, -6.35550e+00_rt, -6.20931e+00_rt, -6.06311e+00_rt, +-5.91692e+00_rt, -5.77072e+00_rt, -5.62452e+00_rt, -5.47833e+00_rt, +-5.33213e+00_rt, -5.18594e+00_rt, -5.03975e+00_rt, -4.89355e+00_rt, +-4.74736e+00_rt, -4.60117e+00_rt, -4.45498e+00_rt, -4.30879e+00_rt, +-4.16260e+00_rt, -4.01642e+00_rt, -3.87024e+00_rt, -3.72407e+00_rt, +-3.57791e+00_rt, -3.43175e+00_rt, -3.28562e+00_rt, -3.13950e+00_rt, +-2.99341e+00_rt, -2.84735e+00_rt, -2.70134e+00_rt, -2.55540e+00_rt, +-2.40954e+00_rt, -2.26380e+00_rt, -2.11821e+00_rt, -1.97283e+00_rt, +-1.82773e+00_rt, -1.68301e+00_rt, -1.53880e+00_rt, -1.39527e+00_rt, +-1.25267e+00_rt, -1.11133e+00_rt, -9.71702e-01_rt, -8.34400e-01_rt, +-7.00282e-01_rt, -5.70534e-01_rt, -4.46800e-01_rt, -3.31354e-01_rt, +-2.27280e-01_rt, -1.38590e-01_rt, -6.99533e-02_rt, -2.53641e-02_rt, +-4.77509e-03_rt, -1.81428e-04_rt, -1.40005e-08_rt, 0.00000e+00_rt, +-8.30968e+00_rt, -8.16348e+00_rt, -8.01729e+00_rt, -7.87109e+00_rt, +-7.72490e+00_rt, -7.57870e+00_rt, -7.43250e+00_rt, -7.28631e+00_rt, +-7.14011e+00_rt, -6.99392e+00_rt, -6.84772e+00_rt, -6.70152e+00_rt, +-6.55533e+00_rt, -6.40913e+00_rt, -6.26294e+00_rt, -6.11674e+00_rt, +-5.97055e+00_rt, -5.82435e+00_rt, -5.67816e+00_rt, -5.53196e+00_rt, +-5.38576e+00_rt, -5.23957e+00_rt, -5.09338e+00_rt, -4.94718e+00_rt, +-4.80099e+00_rt, -4.65480e+00_rt, -4.50861e+00_rt, -4.36242e+00_rt, +-4.21623e+00_rt, -4.07005e+00_rt, -3.92387e+00_rt, -3.77769e+00_rt, +-3.63152e+00_rt, -3.48537e+00_rt, -3.33922e+00_rt, -3.19309e+00_rt, +-3.04699e+00_rt, -2.90092e+00_rt, -2.75489e+00_rt, -2.60891e+00_rt, +-2.46301e+00_rt, -2.31722e+00_rt, -2.17155e+00_rt, -2.02608e+00_rt, +-1.88085e+00_rt, -1.73595e+00_rt, -1.59150e+00_rt, -1.44765e+00_rt, +-1.30462e+00_rt, -1.16270e+00_rt, -1.02229e+00_rt, -8.83923e-01_rt, +-7.48362e-01_rt, -6.16650e-01_rt, -4.90241e-01_rt, -3.71154e-01_rt, +-2.62173e-01_rt, -1.67019e-01_rt, -9.03181e-02_rt, -3.67951e-02_rt, +-8.60747e-03_rt, -5.25902e-04_rt, -1.83903e-07_rt, 0.00000e+00_rt, +-8.36182e+00_rt, -8.21562e+00_rt, -8.06943e+00_rt, -7.92323e+00_rt, +-7.77703e+00_rt, -7.63084e+00_rt, -7.48464e+00_rt, -7.33845e+00_rt, +-7.19225e+00_rt, -7.04606e+00_rt, -6.89986e+00_rt, -6.75366e+00_rt, +-6.60747e+00_rt, -6.46127e+00_rt, -6.31508e+00_rt, -6.16888e+00_rt, +-6.02269e+00_rt, -5.87649e+00_rt, -5.73029e+00_rt, -5.58410e+00_rt, +-5.43790e+00_rt, -5.29171e+00_rt, -5.14551e+00_rt, -4.99932e+00_rt, +-4.85313e+00_rt, -4.70693e+00_rt, -4.56074e+00_rt, -4.41455e+00_rt, +-4.26836e+00_rt, -4.12218e+00_rt, -3.97600e+00_rt, -3.82982e+00_rt, +-3.68365e+00_rt, -3.53749e+00_rt, -3.39134e+00_rt, -3.24520e+00_rt, +-3.09908e+00_rt, -2.95300e+00_rt, -2.80695e+00_rt, -2.66095e+00_rt, +-2.51501e+00_rt, -2.36917e+00_rt, -2.22344e+00_rt, -2.07788e+00_rt, +-1.93253e+00_rt, -1.78748e+00_rt, -1.64282e+00_rt, -1.49869e+00_rt, +-1.35529e+00_rt, -1.21286e+00_rt, -1.07176e+00_rt, -9.32463e-01_rt, +-7.95642e-01_rt, -6.62215e-01_rt, -5.33469e-01_rt, -4.11198e-01_rt, +-2.97898e-01_rt, -1.96981e-01_rt, -1.12883e-01_rt, -5.06787e-02_rt, +-1.41908e-02_rt, -1.28359e-03_rt, -1.56011e-06_rt, 0.00000e+00_rt, +-8.41236e+00_rt, -8.26616e+00_rt, -8.11996e+00_rt, -7.97377e+00_rt, +-7.82757e+00_rt, -7.68138e+00_rt, -7.53518e+00_rt, -7.38899e+00_rt, +-7.24279e+00_rt, -7.09659e+00_rt, -6.95040e+00_rt, -6.80420e+00_rt, +-6.65801e+00_rt, -6.51181e+00_rt, -6.36561e+00_rt, -6.21942e+00_rt, +-6.07322e+00_rt, -5.92703e+00_rt, -5.78083e+00_rt, -5.63464e+00_rt, +-5.48844e+00_rt, -5.34225e+00_rt, -5.19605e+00_rt, -5.04986e+00_rt, +-4.90366e+00_rt, -4.75747e+00_rt, -4.61128e+00_rt, -4.46509e+00_rt, +-4.31890e+00_rt, -4.17271e+00_rt, -4.02653e+00_rt, -3.88035e+00_rt, +-3.73417e+00_rt, -3.58801e+00_rt, -3.44185e+00_rt, -3.29571e+00_rt, +-3.14958e+00_rt, -3.00348e+00_rt, -2.85742e+00_rt, -2.71139e+00_rt, +-2.56543e+00_rt, -2.41954e+00_rt, -2.27376e+00_rt, -2.12812e+00_rt, +-1.98267e+00_rt, -1.83748e+00_rt, -1.69264e+00_rt, -1.54827e+00_rt, +-1.40453e+00_rt, -1.26166e+00_rt, -1.11995e+00_rt, -9.79846e-01_rt, +-8.41924e-01_rt, -7.07000e-01_rt, -5.76214e-01_rt, -4.51160e-01_rt, +-3.34072e-01_rt, -2.28051e-01_rt, -1.37260e-01_rt, -6.68403e-02_rt, +-2.17291e-02_rt, -2.71824e-03_rt, -9.23596e-06_rt, 0.00000e+00_rt, +-8.46119e+00_rt, -8.31500e+00_rt, -8.16880e+00_rt, -8.02261e+00_rt, +-7.87641e+00_rt, -7.73021e+00_rt, -7.58402e+00_rt, -7.43782e+00_rt, +-7.29163e+00_rt, -7.14543e+00_rt, -6.99923e+00_rt, -6.85304e+00_rt, +-6.70684e+00_rt, -6.56065e+00_rt, -6.41445e+00_rt, -6.26826e+00_rt, +-6.12206e+00_rt, -5.97586e+00_rt, -5.82967e+00_rt, -5.68347e+00_rt, +-5.53728e+00_rt, -5.39108e+00_rt, -5.24489e+00_rt, -5.09869e+00_rt, +-4.95250e+00_rt, -4.80631e+00_rt, -4.66011e+00_rt, -4.51392e+00_rt, +-4.36773e+00_rt, -4.22154e+00_rt, -4.07536e+00_rt, -3.92918e+00_rt, +-3.78300e+00_rt, -3.63683e+00_rt, -3.49067e+00_rt, -3.34452e+00_rt, +-3.19839e+00_rt, -3.05228e+00_rt, -2.90619e+00_rt, -2.76015e+00_rt, +-2.61415e+00_rt, -2.46823e+00_rt, -2.32240e+00_rt, -2.17669e+00_rt, +-2.03116e+00_rt, -1.88585e+00_rt, -1.74084e+00_rt, -1.59626e+00_rt, +-1.45223e+00_rt, -1.30897e+00_rt, -1.16674e+00_rt, -1.02592e+00_rt, +-8.87032e-01_rt, -7.50800e-01_rt, -6.18233e-01_rt, -4.90749e-01_rt, +-3.70346e-01_rt, -2.59826e-01_rt, -1.63042e-01_rt, -8.50082e-02_rt, +-3.12914e-02_rt, -5.11928e-03_rt, -4.06448e-05_rt, 0.00000e+00_rt, +-8.50824e+00_rt, -8.36205e+00_rt, -8.21585e+00_rt, -8.06965e+00_rt, +-7.92346e+00_rt, -7.77726e+00_rt, -7.63107e+00_rt, -7.48487e+00_rt, +-7.33868e+00_rt, -7.19248e+00_rt, -7.04628e+00_rt, -6.90009e+00_rt, +-6.75389e+00_rt, -6.60770e+00_rt, -6.46150e+00_rt, -6.31530e+00_rt, +-6.16911e+00_rt, -6.02291e+00_rt, -5.87672e+00_rt, -5.73052e+00_rt, +-5.58433e+00_rt, -5.43813e+00_rt, -5.29194e+00_rt, -5.14574e+00_rt, +-4.99955e+00_rt, -4.85335e+00_rt, -4.70716e+00_rt, -4.56097e+00_rt, +-4.41478e+00_rt, -4.26859e+00_rt, -4.12240e+00_rt, -3.97622e+00_rt, +-3.83004e+00_rt, -3.68387e+00_rt, -3.53770e+00_rt, -3.39155e+00_rt, +-3.24541e+00_rt, -3.09928e+00_rt, -2.95319e+00_rt, -2.80712e+00_rt, +-2.66111e+00_rt, -2.51515e+00_rt, -2.36928e+00_rt, -2.22351e+00_rt, +-2.07790e+00_rt, -1.93248e+00_rt, -1.78734e+00_rt, -1.64256e+00_rt, +-1.49828e+00_rt, -1.35467e+00_rt, -1.21198e+00_rt, -1.07054e+00_rt, +-9.30813e-01_rt, -7.93438e-01_rt, -6.59315e-01_rt, -5.29710e-01_rt, +-4.06408e-01_rt, -2.91936e-01_rt, -1.89828e-01_rt, -1.04850e-01_rt, +-4.28169e-02_rt, -8.74979e-03_rt, -1.40043e-04_rt, 0.00000e+00_rt, +-8.55343e+00_rt, -8.40723e+00_rt, -8.26104e+00_rt, -8.11484e+00_rt, +-7.96864e+00_rt, -7.82245e+00_rt, -7.67625e+00_rt, -7.53006e+00_rt, +-7.38386e+00_rt, -7.23766e+00_rt, -7.09147e+00_rt, -6.94527e+00_rt, +-6.79908e+00_rt, -6.65288e+00_rt, -6.50668e+00_rt, -6.36049e+00_rt, +-6.21429e+00_rt, -6.06810e+00_rt, -5.92190e+00_rt, -5.77571e+00_rt, +-5.62951e+00_rt, -5.48332e+00_rt, -5.33712e+00_rt, -5.19093e+00_rt, +-5.04473e+00_rt, -4.89854e+00_rt, -4.75234e+00_rt, -4.60615e+00_rt, +-4.45996e+00_rt, -4.31377e+00_rt, -4.16758e+00_rt, -4.02140e+00_rt, +-3.87522e+00_rt, -3.72904e+00_rt, -3.58287e+00_rt, -3.43671e+00_rt, +-3.29057e+00_rt, -3.14443e+00_rt, -2.99833e+00_rt, -2.85225e+00_rt, +-2.70621e+00_rt, -2.56022e+00_rt, -2.41431e+00_rt, -2.26849e+00_rt, +-2.12281e+00_rt, -1.97730e+00_rt, -1.83204e+00_rt, -1.68709e+00_rt, +-1.54259e+00_rt, -1.39868e+00_rt, -1.25559e+00_rt, -1.11360e+00_rt, +-9.73133e-01_rt, -8.34759e-01_rt, -6.99278e-01_rt, -5.67819e-01_rt, +-4.41985e-01_rt, -3.24052e-01_rt, -2.17240e-01_rt, -1.26006e-01_rt, +-5.61371e-02_rt, -1.38001e-02_rt, -3.94274e-04_rt, 0.00000e+00_rt, +-8.59669e+00_rt, -8.45049e+00_rt, -8.30429e+00_rt, -8.15810e+00_rt, +-8.01190e+00_rt, -7.86571e+00_rt, -7.71951e+00_rt, -7.57332e+00_rt, +-7.42712e+00_rt, -7.28092e+00_rt, -7.13473e+00_rt, -6.98853e+00_rt, +-6.84234e+00_rt, -6.69614e+00_rt, -6.54994e+00_rt, -6.40375e+00_rt, +-6.25755e+00_rt, -6.11136e+00_rt, -5.96516e+00_rt, -5.81897e+00_rt, +-5.67277e+00_rt, -5.52657e+00_rt, -5.38038e+00_rt, -5.23418e+00_rt, +-5.08799e+00_rt, -4.94180e+00_rt, -4.79560e+00_rt, -4.64941e+00_rt, +-4.50322e+00_rt, -4.35703e+00_rt, -4.21084e+00_rt, -4.06465e+00_rt, +-3.91847e+00_rt, -3.77229e+00_rt, -3.62612e+00_rt, -3.47996e+00_rt, +-3.33380e+00_rt, -3.18766e+00_rt, -3.04154e+00_rt, -2.89545e+00_rt, +-2.74939e+00_rt, -2.60338e+00_rt, -2.45744e+00_rt, -2.31158e+00_rt, +-2.16583e+00_rt, -2.02024e+00_rt, -1.87487e+00_rt, -1.72978e+00_rt, +-1.58508e+00_rt, -1.44091e+00_rt, -1.29746e+00_rt, -1.15499e+00_rt, +-1.01388e+00_rt, -8.74633e-01_rt, -7.37964e-01_rt, -6.04888e-01_rt, +-4.76843e-01_rt, -3.55882e-01_rt, -2.44931e-01_rt, -1.48114e-01_rt, +-7.10067e-02_rt, -2.03603e-02_rt, -9.39472e-04_rt, 0.00000e+00_rt, +-8.63797e+00_rt, -8.49178e+00_rt, -8.34558e+00_rt, -8.19939e+00_rt, +-8.05319e+00_rt, -7.90699e+00_rt, -7.76080e+00_rt, -7.61460e+00_rt, +-7.46841e+00_rt, -7.32221e+00_rt, -7.17602e+00_rt, -7.02982e+00_rt, +-6.88362e+00_rt, -6.73743e+00_rt, -6.59123e+00_rt, -6.44504e+00_rt, +-6.29884e+00_rt, -6.15264e+00_rt, -6.00645e+00_rt, -5.86025e+00_rt, +-5.71406e+00_rt, -5.56786e+00_rt, -5.42167e+00_rt, -5.27547e+00_rt, +-5.12928e+00_rt, -4.98308e+00_rt, -4.83689e+00_rt, -4.69070e+00_rt, +-4.54450e+00_rt, -4.39831e+00_rt, -4.25212e+00_rt, -4.10594e+00_rt, +-3.95975e+00_rt, -3.81357e+00_rt, -3.66740e+00_rt, -3.52123e+00_rt, +-3.37507e+00_rt, -3.22892e+00_rt, -3.08280e+00_rt, -2.93669e+00_rt, +-2.79062e+00_rt, -2.64458e+00_rt, -2.49861e+00_rt, -2.35271e+00_rt, +-2.20691e+00_rt, -2.06125e+00_rt, -1.91578e+00_rt, -1.77056e+00_rt, +-1.62569e+00_rt, -1.48129e+00_rt, -1.33753e+00_rt, -1.19464e+00_rt, +-1.05297e+00_rt, -9.12953e-01_rt, -7.75246e-01_rt, -6.40757e-01_rt, +-5.10782e-01_rt, -3.87174e-01_rt, -2.72594e-01_rt, -1.70827e-01_rt, +-8.71357e-02_rt, -2.84153e-02_rt, -1.95018e-03_rt, 0.00000e+00_rt, +-8.67726e+00_rt, -8.53106e+00_rt, -8.38486e+00_rt, -8.23867e+00_rt, +-8.09247e+00_rt, -7.94628e+00_rt, -7.80008e+00_rt, -7.65389e+00_rt, +-7.50769e+00_rt, -7.36149e+00_rt, -7.21530e+00_rt, -7.06910e+00_rt, +-6.92291e+00_rt, -6.77671e+00_rt, -6.63051e+00_rt, -6.48432e+00_rt, +-6.33812e+00_rt, -6.19193e+00_rt, -6.04573e+00_rt, -5.89954e+00_rt, +-5.75334e+00_rt, -5.60714e+00_rt, -5.46095e+00_rt, -5.31475e+00_rt, +-5.16856e+00_rt, -5.02236e+00_rt, -4.87617e+00_rt, -4.72998e+00_rt, +-4.58378e+00_rt, -4.43759e+00_rt, -4.29140e+00_rt, -4.14521e+00_rt, +-3.99903e+00_rt, -3.85285e+00_rt, -3.70667e+00_rt, -3.56050e+00_rt, +-3.41433e+00_rt, -3.26818e+00_rt, -3.12205e+00_rt, -2.97593e+00_rt, +-2.82984e+00_rt, -2.68379e+00_rt, -2.53779e+00_rt, -2.39185e+00_rt, +-2.24601e+00_rt, -2.10029e+00_rt, -1.95473e+00_rt, -1.80940e+00_rt, +-1.66438e+00_rt, -1.51978e+00_rt, -1.37574e+00_rt, -1.23249e+00_rt, +-1.09032e+00_rt, -9.49633e-01_rt, -8.11019e-01_rt, -6.75295e-01_rt, +-5.43634e-01_rt, -4.17716e-01_rt, -2.99957e-01_rt, -1.93826e-01_rt, +-1.04216e-01_rt, -3.78569e-02_rt, -3.61159e-03_rt, 0.00000e+00_rt, +-8.71451e+00_rt, -8.56832e+00_rt, -8.42212e+00_rt, -8.27593e+00_rt, +-8.12973e+00_rt, -7.98354e+00_rt, -7.83734e+00_rt, -7.69114e+00_rt, +-7.54495e+00_rt, -7.39875e+00_rt, -7.25256e+00_rt, -7.10636e+00_rt, +-6.96016e+00_rt, -6.81397e+00_rt, -6.66777e+00_rt, -6.52158e+00_rt, +-6.37538e+00_rt, -6.22918e+00_rt, -6.08299e+00_rt, -5.93679e+00_rt, +-5.79060e+00_rt, -5.64440e+00_rt, -5.49821e+00_rt, -5.35201e+00_rt, +-5.20582e+00_rt, -5.05962e+00_rt, -4.91343e+00_rt, -4.76723e+00_rt, +-4.62104e+00_rt, -4.47485e+00_rt, -4.32866e+00_rt, -4.18247e+00_rt, +-4.03628e+00_rt, -3.89010e+00_rt, -3.74392e+00_rt, -3.59774e+00_rt, +-3.45158e+00_rt, -3.30542e+00_rt, -3.15928e+00_rt, -3.01315e+00_rt, +-2.86705e+00_rt, -2.72098e+00_rt, -2.57496e+00_rt, -2.42899e+00_rt, +-2.28311e+00_rt, -2.13733e+00_rt, -1.99170e+00_rt, -1.84627e+00_rt, +-1.70112e+00_rt, -1.55633e+00_rt, -1.41206e+00_rt, -1.26849e+00_rt, +-1.12588e+00_rt, -9.84610e-01_rt, -8.45201e-01_rt, -7.08398e-01_rt, +-5.75265e-01_rt, -4.47328e-01_rt, -3.26792e-01_rt, -2.16825e-01_rt, +-1.21941e-01_rt, -4.85071e-02_rt, -6.08473e-03_rt, 0.00000e+00_rt, +-8.74974e+00_rt, -8.60355e+00_rt, -8.45735e+00_rt, -8.31115e+00_rt, +-8.16496e+00_rt, -8.01876e+00_rt, -7.87257e+00_rt, -7.72637e+00_rt, +-7.58018e+00_rt, -7.43398e+00_rt, -7.28778e+00_rt, -7.14159e+00_rt, +-6.99539e+00_rt, -6.84920e+00_rt, -6.70300e+00_rt, -6.55680e+00_rt, +-6.41061e+00_rt, -6.26441e+00_rt, -6.11822e+00_rt, -5.97202e+00_rt, +-5.82583e+00_rt, -5.67963e+00_rt, -5.53343e+00_rt, -5.38724e+00_rt, +-5.24104e+00_rt, -5.09485e+00_rt, -4.94865e+00_rt, -4.80246e+00_rt, +-4.65627e+00_rt, -4.51007e+00_rt, -4.36388e+00_rt, -4.21769e+00_rt, +-4.07151e+00_rt, -3.92532e+00_rt, -3.77914e+00_rt, -3.63296e+00_rt, +-3.48679e+00_rt, -3.34063e+00_rt, -3.19448e+00_rt, -3.04835e+00_rt, +-2.90223e+00_rt, -2.75615e+00_rt, -2.61010e+00_rt, -2.46411e+00_rt, +-2.31819e+00_rt, -2.17237e+00_rt, -2.02667e+00_rt, -1.88116e+00_rt, +-1.73589e+00_rt, -1.59095e+00_rt, -1.44646e+00_rt, -1.30261e+00_rt, +-1.15962e+00_rt, -1.01784e+00_rt, -8.77732e-01_rt, -7.39986e-01_rt, +-6.05565e-01_rt, -4.75867e-01_rt, -3.52903e-01_rt, -2.39575e-01_rt, +-1.40023e-01_rt, -6.01435e-02_rt, -9.47701e-03_rt, 0.00000e+00_rt, +-8.78295e+00_rt, -8.63675e+00_rt, -8.49055e+00_rt, -8.34436e+00_rt, +-8.19816e+00_rt, -8.05197e+00_rt, -7.90577e+00_rt, -7.75957e+00_rt, +-7.61338e+00_rt, -7.46718e+00_rt, -7.32099e+00_rt, -7.17479e+00_rt, +-7.02860e+00_rt, -6.88240e+00_rt, -6.73620e+00_rt, -6.59001e+00_rt, +-6.44381e+00_rt, -6.29762e+00_rt, -6.15142e+00_rt, -6.00522e+00_rt, +-5.85903e+00_rt, -5.71283e+00_rt, -5.56664e+00_rt, -5.42044e+00_rt, +-5.27425e+00_rt, -5.12805e+00_rt, -4.98186e+00_rt, -4.83566e+00_rt, +-4.68947e+00_rt, -4.54328e+00_rt, -4.39708e+00_rt, -4.25089e+00_rt, +-4.10470e+00_rt, -3.95852e+00_rt, -3.81233e+00_rt, -3.66616e+00_rt, +-3.51998e+00_rt, -3.37382e+00_rt, -3.22766e+00_rt, -3.08152e+00_rt, +-2.93540e+00_rt, -2.78930e+00_rt, -2.64324e+00_rt, -2.49722e+00_rt, +-2.35127e+00_rt, -2.20540e+00_rt, -2.05965e+00_rt, -1.91406e+00_rt, +-1.76869e+00_rt, -1.62361e+00_rt, -1.47894e+00_rt, -1.33484e+00_rt, +-1.19152e+00_rt, -1.04928e+00_rt, -9.08571e-01_rt, -7.69999e-01_rt, +-6.34454e-01_rt, -5.03216e-01_rt, -3.78133e-01_rt, -2.61864e-01_rt, +-1.58201e-01_rt, -7.25228e-02_rt, -1.38263e-02_rt, 0.00000e+00_rt, +-8.81414e+00_rt, -8.66795e+00_rt, -8.52175e+00_rt, -8.37556e+00_rt, +-8.22936e+00_rt, -8.08316e+00_rt, -7.93697e+00_rt, -7.79077e+00_rt, +-7.64458e+00_rt, -7.49838e+00_rt, -7.35218e+00_rt, -7.20599e+00_rt, +-7.05979e+00_rt, -6.91360e+00_rt, -6.76740e+00_rt, -6.62121e+00_rt, +-6.47501e+00_rt, -6.32881e+00_rt, -6.18262e+00_rt, -6.03642e+00_rt, +-5.89023e+00_rt, -5.74403e+00_rt, -5.59783e+00_rt, -5.45164e+00_rt, +-5.30544e+00_rt, -5.15925e+00_rt, -5.01305e+00_rt, -4.86686e+00_rt, +-4.72067e+00_rt, -4.57447e+00_rt, -4.42828e+00_rt, -4.28209e+00_rt, +-4.13590e+00_rt, -3.98971e+00_rt, -3.84353e+00_rt, -3.69735e+00_rt, +-3.55117e+00_rt, -3.40500e+00_rt, -3.25884e+00_rt, -3.11269e+00_rt, +-2.96656e+00_rt, -2.82045e+00_rt, -2.67438e+00_rt, -2.52834e+00_rt, +-2.38236e+00_rt, -2.23645e+00_rt, -2.09065e+00_rt, -1.94500e+00_rt, +-1.79953e+00_rt, -1.65433e+00_rt, -1.50951e+00_rt, -1.36518e+00_rt, +-1.22157e+00_rt, -1.07894e+00_rt, -9.37694e-01_rt, -7.98399e-01_rt, +-6.61871e-01_rt, -5.29289e-01_rt, -4.02354e-01_rt, -2.83515e-01_rt, +-1.76243e-01_rt, -8.54001e-02_rt, -1.90999e-02_rt, 0.00000e+00_rt, +-8.84336e+00_rt, -8.69717e+00_rt, -8.55097e+00_rt, -8.40477e+00_rt, +-8.25858e+00_rt, -8.11238e+00_rt, -7.96619e+00_rt, -7.81999e+00_rt, +-7.67380e+00_rt, -7.52760e+00_rt, -7.38140e+00_rt, -7.23521e+00_rt, +-7.08901e+00_rt, -6.94282e+00_rt, -6.79662e+00_rt, -6.65042e+00_rt, +-6.50423e+00_rt, -6.35803e+00_rt, -6.21184e+00_rt, -6.06564e+00_rt, +-5.91945e+00_rt, -5.77325e+00_rt, -5.62705e+00_rt, -5.48086e+00_rt, +-5.33466e+00_rt, -5.18847e+00_rt, -5.04227e+00_rt, -4.89608e+00_rt, +-4.74988e+00_rt, -4.60369e+00_rt, -4.45750e+00_rt, -4.31131e+00_rt, +-4.16512e+00_rt, -4.01893e+00_rt, -3.87274e+00_rt, -3.72656e+00_rt, +-3.58038e+00_rt, -3.43421e+00_rt, -3.28804e+00_rt, -3.14189e+00_rt, +-2.99575e+00_rt, -2.84963e+00_rt, -2.70354e+00_rt, -2.55749e+00_rt, +-2.41148e+00_rt, -2.26554e+00_rt, -2.11970e+00_rt, -1.97398e+00_rt, +-1.82844e+00_rt, -1.68314e+00_rt, -1.53817e+00_rt, -1.39365e+00_rt, +-1.24978e+00_rt, -1.10681e+00_rt, -9.65094e-01_rt, -8.25166e-01_rt, +-6.87778e-01_rt, -5.54020e-01_rt, -4.25469e-01_rt, -3.04385e-01_rt, +-1.93953e-01_rt, -9.85428e-02_rt, -2.52054e-02_rt, 0.00000e+00_rt, +-8.87064e+00_rt, -8.72445e+00_rt, -8.57825e+00_rt, -8.43206e+00_rt, +-8.28586e+00_rt, -8.13966e+00_rt, -7.99347e+00_rt, -7.84727e+00_rt, +-7.70108e+00_rt, -7.55488e+00_rt, -7.40868e+00_rt, -7.26249e+00_rt, +-7.11629e+00_rt, -6.97010e+00_rt, -6.82390e+00_rt, -6.67770e+00_rt, +-6.53151e+00_rt, -6.38531e+00_rt, -6.23912e+00_rt, -6.09292e+00_rt, +-5.94673e+00_rt, -5.80053e+00_rt, -5.65433e+00_rt, -5.50814e+00_rt, +-5.36194e+00_rt, -5.21575e+00_rt, -5.06955e+00_rt, -4.92336e+00_rt, +-4.77716e+00_rt, -4.63097e+00_rt, -4.48478e+00_rt, -4.33858e+00_rt, +-4.19239e+00_rt, -4.04620e+00_rt, -3.90002e+00_rt, -3.75383e+00_rt, +-3.60765e+00_rt, -3.46148e+00_rt, -3.31531e+00_rt, -3.16915e+00_rt, +-3.02301e+00_rt, -2.87688e+00_rt, -2.73077e+00_rt, -2.58470e+00_rt, +-2.43868e+00_rt, -2.29271e+00_rt, -2.14683e+00_rt, -2.00106e+00_rt, +-1.85545e+00_rt, -1.71005e+00_rt, -1.56496e+00_rt, -1.42028e+00_rt, +-1.27618e+00_rt, -1.13290e+00_rt, -9.90777e-01_rt, -8.50295e-01_rt, +-7.12155e-01_rt, -5.77370e-01_rt, -4.47406e-01_rt, -3.24362e-01_rt, +-2.11168e-01_rt, -1.11740e-01_rt, -3.20077e-02_rt, 0.00000e+00_rt, +-8.89603e+00_rt, -8.74984e+00_rt, -8.60364e+00_rt, -8.45745e+00_rt, +-8.31125e+00_rt, -8.16505e+00_rt, -8.01886e+00_rt, -7.87266e+00_rt, +-7.72647e+00_rt, -7.58027e+00_rt, -7.43407e+00_rt, -7.28788e+00_rt, +-7.14168e+00_rt, -6.99549e+00_rt, -6.84929e+00_rt, -6.70309e+00_rt, +-6.55690e+00_rt, -6.41070e+00_rt, -6.26451e+00_rt, -6.11831e+00_rt, +-5.97212e+00_rt, -5.82592e+00_rt, -5.67972e+00_rt, -5.53353e+00_rt, +-5.38733e+00_rt, -5.24114e+00_rt, -5.09494e+00_rt, -4.94875e+00_rt, +-4.80255e+00_rt, -4.65636e+00_rt, -4.51017e+00_rt, -4.36397e+00_rt, +-4.21778e+00_rt, -4.07159e+00_rt, -3.92540e+00_rt, -3.77922e+00_rt, +-3.63303e+00_rt, -3.48686e+00_rt, -3.34069e+00_rt, -3.19452e+00_rt, +-3.04837e+00_rt, -2.90224e+00_rt, -2.75612e+00_rt, -2.61004e+00_rt, +-2.46399e+00_rt, -2.31800e+00_rt, -2.17209e+00_rt, -2.02627e+00_rt, +-1.88060e+00_rt, -1.73512e+00_rt, -1.58992e+00_rt, -1.44509e+00_rt, +-1.30079e+00_rt, -1.15725e+00_rt, -1.01477e+00_rt, -8.73798e-01_rt, +-7.34999e-01_rt, -5.99315e-01_rt, -4.68118e-01_rt, -3.43362e-01_rt, +-2.27755e-01_rt, -1.24807e-01_rt, -3.93469e-02_rt, 0.00000e+00_rt, +-8.91959e+00_rt, -8.77339e+00_rt, -8.62720e+00_rt, -8.48100e+00_rt, +-8.33481e+00_rt, -8.18861e+00_rt, -8.04241e+00_rt, -7.89622e+00_rt, +-7.75002e+00_rt, -7.60383e+00_rt, -7.45763e+00_rt, -7.31143e+00_rt, +-7.16524e+00_rt, -7.01904e+00_rt, -6.87285e+00_rt, -6.72665e+00_rt, +-6.58046e+00_rt, -6.43426e+00_rt, -6.28806e+00_rt, -6.14187e+00_rt, +-5.99567e+00_rt, -5.84948e+00_rt, -5.70328e+00_rt, -5.55709e+00_rt, +-5.41089e+00_rt, -5.26469e+00_rt, -5.11850e+00_rt, -4.97230e+00_rt, +-4.82611e+00_rt, -4.67991e+00_rt, -4.53372e+00_rt, -4.38753e+00_rt, +-4.24134e+00_rt, -4.09514e+00_rt, -3.94896e+00_rt, -3.80277e+00_rt, +-3.65658e+00_rt, -3.51040e+00_rt, -3.36423e+00_rt, -3.21806e+00_rt, +-3.07191e+00_rt, -2.92577e+00_rt, -2.77964e+00_rt, -2.63355e+00_rt, +-2.48748e+00_rt, -2.34147e+00_rt, -2.19553e+00_rt, -2.04967e+00_rt, +-1.90394e+00_rt, -1.75840e+00_rt, -1.61309e+00_rt, -1.46814e+00_rt, +-1.32367e+00_rt, -1.17989e+00_rt, -1.03709e+00_rt, -8.95699e-01_rt, +-7.56322e-01_rt, -6.19852e-01_rt, -4.87576e-01_rt, -3.61326e-01_rt, +-2.43612e-01_rt, -1.37588e-01_rt, -4.70555e-02_rt, 0.00000e+00_rt, +-8.94138e+00_rt, -8.79518e+00_rt, -8.64898e+00_rt, -8.50279e+00_rt, +-8.35659e+00_rt, -8.21040e+00_rt, -8.06420e+00_rt, -7.91801e+00_rt, +-7.77181e+00_rt, -7.62561e+00_rt, -7.47942e+00_rt, -7.33322e+00_rt, +-7.18703e+00_rt, -7.04083e+00_rt, -6.89463e+00_rt, -6.74844e+00_rt, +-6.60224e+00_rt, -6.45605e+00_rt, -6.30985e+00_rt, -6.16366e+00_rt, +-6.01746e+00_rt, -5.87126e+00_rt, -5.72507e+00_rt, -5.57887e+00_rt, +-5.43268e+00_rt, -5.28648e+00_rt, -5.14029e+00_rt, -4.99409e+00_rt, +-4.84790e+00_rt, -4.70170e+00_rt, -4.55551e+00_rt, -4.40931e+00_rt, +-4.26312e+00_rt, -4.11693e+00_rt, -3.97074e+00_rt, -3.82455e+00_rt, +-3.67837e+00_rt, -3.53218e+00_rt, -3.38601e+00_rt, -3.23984e+00_rt, +-3.09368e+00_rt, -2.94753e+00_rt, -2.80140e+00_rt, -2.65529e+00_rt, +-2.50921e+00_rt, -2.36318e+00_rt, -2.21721e+00_rt, -2.07132e+00_rt, +-1.92555e+00_rt, -1.77993e+00_rt, -1.63455e+00_rt, -1.48948e+00_rt, +-1.34486e+00_rt, -1.20087e+00_rt, -1.05780e+00_rt, -9.16033e-01_rt, +-7.76151e-01_rt, -6.38993e-01_rt, -5.05773e-01_rt, -3.78216e-01_rt, +-2.58665e-01_rt, -1.49958e-01_rt, -5.49705e-02_rt, 0.00000e+00_rt, +-8.96147e+00_rt, -8.81527e+00_rt, -8.66907e+00_rt, -8.52288e+00_rt, +-8.37668e+00_rt, -8.23049e+00_rt, -8.08429e+00_rt, -7.93809e+00_rt, +-7.79190e+00_rt, -7.64570e+00_rt, -7.49951e+00_rt, -7.35331e+00_rt, +-7.20712e+00_rt, -7.06092e+00_rt, -6.91472e+00_rt, -6.76853e+00_rt, +-6.62233e+00_rt, -6.47614e+00_rt, -6.32994e+00_rt, -6.18374e+00_rt, +-6.03755e+00_rt, -5.89135e+00_rt, -5.74516e+00_rt, -5.59896e+00_rt, +-5.45277e+00_rt, -5.30657e+00_rt, -5.16037e+00_rt, -5.01418e+00_rt, +-4.86798e+00_rt, -4.72179e+00_rt, -4.57560e+00_rt, -4.42940e+00_rt, +-4.28321e+00_rt, -4.13702e+00_rt, -3.99083e+00_rt, -3.84464e+00_rt, +-3.69845e+00_rt, -3.55227e+00_rt, -3.40609e+00_rt, -3.25992e+00_rt, +-3.11375e+00_rt, -2.96760e+00_rt, -2.82146e+00_rt, -2.67534e+00_rt, +-2.52925e+00_rt, -2.38320e+00_rt, -2.23721e+00_rt, -2.09129e+00_rt, +-1.94547e+00_rt, -1.79981e+00_rt, -1.65434e+00_rt, -1.50918e+00_rt, +-1.36442e+00_rt, -1.22026e+00_rt, -1.07694e+00_rt, -9.34847e-01_rt, +-7.94522e-01_rt, -6.56761e-01_rt, -5.22715e-01_rt, -3.94018e-01_rt, +-2.72862e-01_rt, -1.61817e-01_rt, -6.29425e-02_rt, 0.00000e+00_rt, +-8.97993e+00_rt, -8.83374e+00_rt, -8.68754e+00_rt, -8.54135e+00_rt, +-8.39515e+00_rt, -8.24895e+00_rt, -8.10276e+00_rt, -7.95656e+00_rt, +-7.81037e+00_rt, -7.66417e+00_rt, -7.51797e+00_rt, -7.37178e+00_rt, +-7.22558e+00_rt, -7.07939e+00_rt, -6.93319e+00_rt, -6.78700e+00_rt, +-6.64080e+00_rt, -6.49460e+00_rt, -6.34841e+00_rt, -6.20221e+00_rt, +-6.05602e+00_rt, -5.90982e+00_rt, -5.76362e+00_rt, -5.61743e+00_rt, +-5.47123e+00_rt, -5.32504e+00_rt, -5.17884e+00_rt, -5.03265e+00_rt, +-4.88645e+00_rt, -4.74026e+00_rt, -4.59406e+00_rt, -4.44787e+00_rt, +-4.30168e+00_rt, -4.15548e+00_rt, -4.00929e+00_rt, -3.86310e+00_rt, +-3.71691e+00_rt, -3.57073e+00_rt, -3.42455e+00_rt, -3.27837e+00_rt, +-3.13220e+00_rt, -2.98605e+00_rt, -2.83990e+00_rt, -2.69378e+00_rt, +-2.54768e+00_rt, -2.40161e+00_rt, -2.25560e+00_rt, -2.10965e+00_rt, +-1.96380e+00_rt, -1.81808e+00_rt, -1.67256e+00_rt, -1.52730e+00_rt, +-1.38243e+00_rt, -1.23810e+00_rt, -1.09458e+00_rt, -9.52195e-01_rt, +-8.11482e-01_rt, -6.73192e-01_rt, -5.38425e-01_rt, -4.08729e-01_rt, +-2.86173e-01_rt, -1.73092e-01_rt, -7.08416e-02_rt, 0.00000e+00_rt, +-8.99686e+00_rt, -8.85067e+00_rt, -8.70447e+00_rt, -8.55827e+00_rt, +-8.41208e+00_rt, -8.26588e+00_rt, -8.11969e+00_rt, -7.97349e+00_rt, +-7.82729e+00_rt, -7.68110e+00_rt, -7.53490e+00_rt, -7.38871e+00_rt, +-7.24251e+00_rt, -7.09631e+00_rt, -6.95012e+00_rt, -6.80392e+00_rt, +-6.65773e+00_rt, -6.51153e+00_rt, -6.36534e+00_rt, -6.21914e+00_rt, +-6.07294e+00_rt, -5.92675e+00_rt, -5.78055e+00_rt, -5.63436e+00_rt, +-5.48816e+00_rt, -5.34197e+00_rt, -5.19577e+00_rt, -5.04957e+00_rt, +-4.90338e+00_rt, -4.75718e+00_rt, -4.61099e+00_rt, -4.46480e+00_rt, +-4.31860e+00_rt, -4.17241e+00_rt, -4.02622e+00_rt, -3.88003e+00_rt, +-3.73384e+00_rt, -3.58765e+00_rt, -3.44147e+00_rt, -3.29529e+00_rt, +-3.14912e+00_rt, -3.00296e+00_rt, -2.85681e+00_rt, -2.71067e+00_rt, +-2.56456e+00_rt, -2.41849e+00_rt, -2.27245e+00_rt, -2.12648e+00_rt, +-1.98060e+00_rt, -1.83484e+00_rt, -1.68926e+00_rt, -1.54392e+00_rt, +-1.39895e+00_rt, -1.25449e+00_rt, -1.11077e+00_rt, -9.68140e-01_rt, +-8.27086e-01_rt, -6.88334e-01_rt, -5.52934e-01_rt, -4.22365e-01_rt, +-2.98586e-01_rt, -1.83733e-01_rt, -7.85591e-02_rt, 0.00000e+00_rt, +-9.01233e+00_rt, -8.86614e+00_rt, -8.71994e+00_rt, -8.57375e+00_rt, +-8.42755e+00_rt, -8.28135e+00_rt, -8.13516e+00_rt, -7.98896e+00_rt, +-7.84277e+00_rt, -7.69657e+00_rt, -7.55037e+00_rt, -7.40418e+00_rt, +-7.25798e+00_rt, -7.11179e+00_rt, -6.96559e+00_rt, -6.81939e+00_rt, +-6.67320e+00_rt, -6.52700e+00_rt, -6.38081e+00_rt, -6.23461e+00_rt, +-6.08842e+00_rt, -5.94222e+00_rt, -5.79602e+00_rt, -5.64983e+00_rt, +-5.50363e+00_rt, -5.35744e+00_rt, -5.21124e+00_rt, -5.06505e+00_rt, +-4.91885e+00_rt, -4.77266e+00_rt, -4.62646e+00_rt, -4.48027e+00_rt, +-4.33407e+00_rt, -4.18788e+00_rt, -4.04169e+00_rt, -3.89550e+00_rt, +-3.74931e+00_rt, -3.60312e+00_rt, -3.45694e+00_rt, -3.31076e+00_rt, +-3.16458e+00_rt, -3.01842e+00_rt, -2.87226e+00_rt, -2.72612e+00_rt, +-2.58000e+00_rt, -2.43391e+00_rt, -2.28786e+00_rt, -2.14187e+00_rt, +-1.99596e+00_rt, -1.85017e+00_rt, -1.70453e+00_rt, -1.55913e+00_rt, +-1.41406e+00_rt, -1.26948e+00_rt, -1.12560e+00_rt, -9.82749e-01_rt, +-8.41396e-01_rt, -7.02238e-01_rt, -5.66284e-01_rt, -4.34952e-01_rt, +-3.10105e-01_rt, -1.93707e-01_rt, -8.60082e-02_rt, 0.00000e+00_rt, +-9.02644e+00_rt, -8.88024e+00_rt, -8.73404e+00_rt, -8.58785e+00_rt, +-8.44165e+00_rt, -8.29546e+00_rt, -8.14926e+00_rt, -8.00306e+00_rt, +-7.85687e+00_rt, -7.71067e+00_rt, -7.56448e+00_rt, -7.41828e+00_rt, +-7.27209e+00_rt, -7.12589e+00_rt, -6.97969e+00_rt, -6.83350e+00_rt, +-6.68730e+00_rt, -6.54111e+00_rt, -6.39491e+00_rt, -6.24871e+00_rt, +-6.10252e+00_rt, -5.95632e+00_rt, -5.81013e+00_rt, -5.66393e+00_rt, +-5.51774e+00_rt, -5.37154e+00_rt, -5.22534e+00_rt, -5.07915e+00_rt, +-4.93295e+00_rt, -4.78676e+00_rt, -4.64056e+00_rt, -4.49437e+00_rt, +-4.34817e+00_rt, -4.20198e+00_rt, -4.05579e+00_rt, -3.90960e+00_rt, +-3.76341e+00_rt, -3.61722e+00_rt, -3.47103e+00_rt, -3.32485e+00_rt, +-3.17867e+00_rt, -3.03251e+00_rt, -2.88635e+00_rt, -2.74020e+00_rt, +-2.59407e+00_rt, -2.44798e+00_rt, -2.30191e+00_rt, -2.15590e+00_rt, +-2.00997e+00_rt, -1.86414e+00_rt, -1.71846e+00_rt, -1.57300e+00_rt, +-1.42785e+00_rt, -1.28316e+00_rt, -1.13914e+00_rt, -9.96094e-01_rt, +-8.54478e-01_rt, -7.14965e-01_rt, -5.78526e-01_rt, -4.46526e-01_rt, +-3.20745e-01_rt, -2.03002e-01_rt, -9.31232e-02_rt, 0.00000e+00_rt, +-9.03926e+00_rt, -8.89306e+00_rt, -8.74686e+00_rt, -8.60067e+00_rt, +-8.45447e+00_rt, -8.30828e+00_rt, -8.16208e+00_rt, -8.01589e+00_rt, +-7.86969e+00_rt, -7.72349e+00_rt, -7.57730e+00_rt, -7.43110e+00_rt, +-7.28491e+00_rt, -7.13871e+00_rt, -6.99251e+00_rt, -6.84632e+00_rt, +-6.70012e+00_rt, -6.55393e+00_rt, -6.40773e+00_rt, -6.26153e+00_rt, +-6.11534e+00_rt, -5.96914e+00_rt, -5.82295e+00_rt, -5.67675e+00_rt, +-5.53056e+00_rt, -5.38436e+00_rt, -5.23816e+00_rt, -5.09197e+00_rt, +-4.94577e+00_rt, -4.79958e+00_rt, -4.65338e+00_rt, -4.50719e+00_rt, +-4.36099e+00_rt, -4.21480e+00_rt, -4.06861e+00_rt, -3.92241e+00_rt, +-3.77622e+00_rt, -3.63004e+00_rt, -3.48385e+00_rt, -3.33767e+00_rt, +-3.19149e+00_rt, -3.04532e+00_rt, -2.89915e+00_rt, -2.75300e+00_rt, +-2.60687e+00_rt, -2.46076e+00_rt, -2.31469e+00_rt, -2.16866e+00_rt, +-2.02270e+00_rt, -1.87684e+00_rt, -1.73113e+00_rt, -1.58561e+00_rt, +-1.44040e+00_rt, -1.29562e+00_rt, -1.15147e+00_rt, -1.00825e+00_rt, +-8.66403e-01_rt, -7.26578e-01_rt, -5.89713e-01_rt, -4.57128e-01_rt, +-3.30531e-01_rt, -2.11616e-01_rt, -9.98571e-02_rt, 0.00000e+00_rt, +-9.05088e+00_rt, -8.90469e+00_rt, -8.75849e+00_rt, -8.61229e+00_rt, +-8.46610e+00_rt, -8.31990e+00_rt, -8.17371e+00_rt, -8.02751e+00_rt, +-7.88132e+00_rt, -7.73512e+00_rt, -7.58892e+00_rt, -7.44273e+00_rt, +-7.29653e+00_rt, -7.15034e+00_rt, -7.00414e+00_rt, -6.85794e+00_rt, +-6.71175e+00_rt, -6.56555e+00_rt, -6.41936e+00_rt, -6.27316e+00_rt, +-6.12696e+00_rt, -5.98077e+00_rt, -5.83457e+00_rt, -5.68838e+00_rt, +-5.54218e+00_rt, -5.39599e+00_rt, -5.24979e+00_rt, -5.10359e+00_rt, +-4.95740e+00_rt, -4.81120e+00_rt, -4.66501e+00_rt, -4.51881e+00_rt, +-4.37262e+00_rt, -4.22643e+00_rt, -4.08023e+00_rt, -3.93404e+00_rt, +-3.78785e+00_rt, -3.64166e+00_rt, -3.49547e+00_rt, -3.34929e+00_rt, +-3.20311e+00_rt, -3.05693e+00_rt, -2.91077e+00_rt, -2.76461e+00_rt, +-2.61847e+00_rt, -2.47236e+00_rt, -2.32627e+00_rt, -2.18023e+00_rt, +-2.03425e+00_rt, -1.88837e+00_rt, -1.74262e+00_rt, -1.59706e+00_rt, +-1.45178e+00_rt, -1.30692e+00_rt, -1.16266e+00_rt, -1.01929e+00_rt, +-8.77243e-01_rt, -7.37144e-01_rt, -5.99906e-01_rt, -4.66808e-01_rt, +-3.39497e-01_rt, -2.19560e-01_rt, -1.06179e-01_rt, 0.00000e+00_rt, +-9.06140e+00_rt, -8.91520e+00_rt, -8.76901e+00_rt, -8.62281e+00_rt, +-8.47662e+00_rt, -8.33042e+00_rt, -8.18422e+00_rt, -8.03803e+00_rt, +-7.89183e+00_rt, -7.74564e+00_rt, -7.59944e+00_rt, -7.45324e+00_rt, +-7.30705e+00_rt, -7.16085e+00_rt, -7.01466e+00_rt, -6.86846e+00_rt, +-6.72227e+00_rt, -6.57607e+00_rt, -6.42987e+00_rt, -6.28368e+00_rt, +-6.13748e+00_rt, -5.99129e+00_rt, -5.84509e+00_rt, -5.69889e+00_rt, +-5.55270e+00_rt, -5.40650e+00_rt, -5.26031e+00_rt, -5.11411e+00_rt, +-4.96792e+00_rt, -4.82172e+00_rt, -4.67553e+00_rt, -4.52933e+00_rt, +-4.38314e+00_rt, -4.23694e+00_rt, -4.09075e+00_rt, -3.94455e+00_rt, +-3.79836e+00_rt, -3.65217e+00_rt, -3.50598e+00_rt, -3.35980e+00_rt, +-3.21362e+00_rt, -3.06744e+00_rt, -2.92127e+00_rt, -2.77511e+00_rt, +-2.62897e+00_rt, -2.48285e+00_rt, -2.33675e+00_rt, -2.19070e+00_rt, +-2.04471e+00_rt, -1.89880e+00_rt, -1.75302e+00_rt, -1.60742e+00_rt, +-1.46209e+00_rt, -1.31715e+00_rt, -1.17279e+00_rt, -1.02929e+00_rt, +-8.87070e-01_rt, -7.46731e-01_rt, -6.09166e-01_rt, -4.75619e-01_rt, +-3.47682e-01_rt, -2.26853e-01_rt, -1.12073e-01_rt, 0.00000e+00_rt, +-9.07089e+00_rt, -8.92470e+00_rt, -8.77850e+00_rt, -8.63230e+00_rt, +-8.48611e+00_rt, -8.33991e+00_rt, -8.19372e+00_rt, -8.04752e+00_rt, +-7.90133e+00_rt, -7.75513e+00_rt, -7.60893e+00_rt, -7.46274e+00_rt, +-7.31654e+00_rt, -7.17035e+00_rt, -7.02415e+00_rt, -6.87795e+00_rt, +-6.73176e+00_rt, -6.58556e+00_rt, -6.43937e+00_rt, -6.29317e+00_rt, +-6.14697e+00_rt, -6.00078e+00_rt, -5.85458e+00_rt, -5.70839e+00_rt, +-5.56219e+00_rt, -5.41600e+00_rt, -5.26980e+00_rt, -5.12360e+00_rt, +-4.97741e+00_rt, -4.83121e+00_rt, -4.68502e+00_rt, -4.53882e+00_rt, +-4.39263e+00_rt, -4.24643e+00_rt, -4.10024e+00_rt, -3.95405e+00_rt, +-3.80785e+00_rt, -3.66166e+00_rt, -3.51547e+00_rt, -3.36929e+00_rt, +-3.22310e+00_rt, -3.07693e+00_rt, -2.93075e+00_rt, -2.78459e+00_rt, +-2.63844e+00_rt, -2.49231e+00_rt, -2.34621e+00_rt, -2.20015e+00_rt, +-2.05414e+00_rt, -1.90821e+00_rt, -1.76241e+00_rt, -1.61677e+00_rt, +-1.47139e+00_rt, -1.32639e+00_rt, -1.18195e+00_rt, -1.03834e+00_rt, +-8.95956e-01_rt, -7.55406e-01_rt, -6.17555e-01_rt, -4.83614e-01_rt, +-3.55130e-01_rt, -2.33523e-01_rt, -1.17533e-01_rt, 0.00000e+00_rt, +-9.07944e+00_rt, -8.93325e+00_rt, -8.78705e+00_rt, -8.64085e+00_rt, +-8.49466e+00_rt, -8.34846e+00_rt, -8.20227e+00_rt, -8.05607e+00_rt, +-7.90988e+00_rt, -7.76368e+00_rt, -7.61748e+00_rt, -7.47129e+00_rt, +-7.32509e+00_rt, -7.17890e+00_rt, -7.03270e+00_rt, -6.88650e+00_rt, +-6.74031e+00_rt, -6.59411e+00_rt, -6.44792e+00_rt, -6.30172e+00_rt, +-6.15552e+00_rt, -6.00933e+00_rt, -5.86313e+00_rt, -5.71694e+00_rt, +-5.57074e+00_rt, -5.42455e+00_rt, -5.27835e+00_rt, -5.13215e+00_rt, +-4.98596e+00_rt, -4.83976e+00_rt, -4.69357e+00_rt, -4.54737e+00_rt, +-4.40118e+00_rt, -4.25498e+00_rt, -4.10879e+00_rt, -3.96260e+00_rt, +-3.81640e+00_rt, -3.67021e+00_rt, -3.52402e+00_rt, -3.37783e+00_rt, +-3.23165e+00_rt, -3.08547e+00_rt, -2.93930e+00_rt, -2.79313e+00_rt, +-2.64698e+00_rt, -2.50084e+00_rt, -2.35473e+00_rt, -2.20866e+00_rt, +-2.06264e+00_rt, -1.91670e+00_rt, -1.77087e+00_rt, -1.62520e+00_rt, +-1.47978e+00_rt, -1.33472e+00_rt, -1.19020e+00_rt, -1.04649e+00_rt, +-9.03973e-01_rt, -7.63238e-01_rt, -6.25135e-01_rt, -4.90849e-01_rt, +-3.61886e-01_rt, -2.39599e-01_rt, -1.22564e-01_rt, 0.00000e+00_rt, +-9.08713e+00_rt, -8.94093e+00_rt, -8.79474e+00_rt, -8.64854e+00_rt, +-8.50234e+00_rt, -8.35615e+00_rt, -8.20995e+00_rt, -8.06376e+00_rt, +-7.91756e+00_rt, -7.77136e+00_rt, -7.62517e+00_rt, -7.47897e+00_rt, +-7.33278e+00_rt, -7.18658e+00_rt, -7.04038e+00_rt, -6.89419e+00_rt, +-6.74799e+00_rt, -6.60180e+00_rt, -6.45560e+00_rt, -6.30941e+00_rt, +-6.16321e+00_rt, -6.01701e+00_rt, -5.87082e+00_rt, -5.72462e+00_rt, +-5.57843e+00_rt, -5.43223e+00_rt, -5.28603e+00_rt, -5.13984e+00_rt, +-4.99364e+00_rt, -4.84745e+00_rt, -4.70125e+00_rt, -4.55506e+00_rt, +-4.40886e+00_rt, -4.26267e+00_rt, -4.11647e+00_rt, -3.97028e+00_rt, +-3.82409e+00_rt, -3.67789e+00_rt, -3.53170e+00_rt, -3.38552e+00_rt, +-3.23933e+00_rt, -3.09315e+00_rt, -2.94697e+00_rt, -2.80080e+00_rt, +-2.65465e+00_rt, -2.50851e+00_rt, -2.36239e+00_rt, -2.21631e+00_rt, +-2.07028e+00_rt, -1.92432e+00_rt, -1.77847e+00_rt, -1.63278e+00_rt, +-1.48732e+00_rt, -1.34221e+00_rt, -1.19763e+00_rt, -1.05383e+00_rt, +-9.11190e-01_rt, -7.70293e-01_rt, -6.31969e-01_rt, -4.97380e-01_rt, +-3.67997e-01_rt, -2.45116e-01_rt, -1.27177e-01_rt, 0.00000e+00_rt, +-9.09402e+00_rt, -8.94783e+00_rt, -8.80163e+00_rt, -8.65543e+00_rt, +-8.50924e+00_rt, -8.36304e+00_rt, -8.21685e+00_rt, -8.07065e+00_rt, +-7.92445e+00_rt, -7.77826e+00_rt, -7.63206e+00_rt, -7.48587e+00_rt, +-7.33967e+00_rt, -7.19347e+00_rt, -7.04728e+00_rt, -6.90108e+00_rt, +-6.75489e+00_rt, -6.60869e+00_rt, -6.46250e+00_rt, -6.31630e+00_rt, +-6.17010e+00_rt, -6.02391e+00_rt, -5.87771e+00_rt, -5.73152e+00_rt, +-5.58532e+00_rt, -5.43912e+00_rt, -5.29293e+00_rt, -5.14673e+00_rt, +-5.00054e+00_rt, -4.85434e+00_rt, -4.70815e+00_rt, -4.56195e+00_rt, +-4.41576e+00_rt, -4.26956e+00_rt, -4.12337e+00_rt, -3.97717e+00_rt, +-3.83098e+00_rt, -3.68479e+00_rt, -3.53860e+00_rt, -3.39241e+00_rt, +-3.24622e+00_rt, -3.10004e+00_rt, -2.95386e+00_rt, -2.80769e+00_rt, +-2.66153e+00_rt, -2.51539e+00_rt, -2.36927e+00_rt, -2.22318e+00_rt, +-2.07714e+00_rt, -1.93116e+00_rt, -1.78529e+00_rt, -1.63958e+00_rt, +-1.49409e+00_rt, -1.34894e+00_rt, -1.20430e+00_rt, -1.06041e+00_rt, +-9.17673e-01_rt, -7.76634e-01_rt, -6.38116e-01_rt, -5.03261e-01_rt, +-3.73511e-01_rt, -2.50110e-01_rt, -1.31387e-01_rt, 0.00000e+00_rt, +-9.10020e+00_rt, -8.95400e+00_rt, -8.80780e+00_rt, -8.66161e+00_rt, +-8.51541e+00_rt, -8.36922e+00_rt, -8.22302e+00_rt, -8.07682e+00_rt, +-7.93063e+00_rt, -7.78443e+00_rt, -7.63824e+00_rt, -7.49204e+00_rt, +-7.34585e+00_rt, -7.19965e+00_rt, -7.05345e+00_rt, -6.90726e+00_rt, +-6.76106e+00_rt, -6.61487e+00_rt, -6.46867e+00_rt, -6.32247e+00_rt, +-6.17628e+00_rt, -6.03008e+00_rt, -5.88389e+00_rt, -5.73769e+00_rt, +-5.59149e+00_rt, -5.44530e+00_rt, -5.29910e+00_rt, -5.15291e+00_rt, +-5.00671e+00_rt, -4.86052e+00_rt, -4.71432e+00_rt, -4.56813e+00_rt, +-4.42193e+00_rt, -4.27574e+00_rt, -4.12954e+00_rt, -3.98335e+00_rt, +-3.83715e+00_rt, -3.69096e+00_rt, -3.54477e+00_rt, -3.39858e+00_rt, +-3.25239e+00_rt, -3.10621e+00_rt, -2.96003e+00_rt, -2.81386e+00_rt, +-2.66769e+00_rt, -2.52155e+00_rt, -2.37542e+00_rt, -2.22933e+00_rt, +-2.08327e+00_rt, -1.93729e+00_rt, -1.79141e+00_rt, -1.64567e+00_rt, +-1.50015e+00_rt, -1.35496e+00_rt, -1.21027e+00_rt, -1.06632e+00_rt, +-9.23486e-01_rt, -7.82322e-01_rt, -6.43634e-01_rt, -5.08546e-01_rt, +-3.78473e-01_rt, -2.54618e-01_rt, -1.35215e-01_rt, 0.00000e+00_rt, +-9.10572e+00_rt, -8.95952e+00_rt, -8.81333e+00_rt, -8.66713e+00_rt, +-8.52093e+00_rt, -8.37474e+00_rt, -8.22854e+00_rt, -8.08235e+00_rt, +-7.93615e+00_rt, -7.78995e+00_rt, -7.64376e+00_rt, -7.49756e+00_rt, +-7.35137e+00_rt, -7.20517e+00_rt, -7.05897e+00_rt, -6.91278e+00_rt, +-6.76658e+00_rt, -6.62039e+00_rt, -6.47419e+00_rt, -6.32799e+00_rt, +-6.18180e+00_rt, -6.03560e+00_rt, -5.88941e+00_rt, -5.74321e+00_rt, +-5.59702e+00_rt, -5.45082e+00_rt, -5.30462e+00_rt, -5.15843e+00_rt, +-5.01223e+00_rt, -4.86604e+00_rt, -4.71984e+00_rt, -4.57365e+00_rt, +-4.42745e+00_rt, -4.28126e+00_rt, -4.13506e+00_rt, -3.98887e+00_rt, +-3.84267e+00_rt, -3.69648e+00_rt, -3.55029e+00_rt, -3.40410e+00_rt, +-3.25791e+00_rt, -3.11172e+00_rt, -2.96554e+00_rt, -2.81937e+00_rt, +-2.67321e+00_rt, -2.52706e+00_rt, -2.38092e+00_rt, -2.23482e+00_rt, +-2.08877e+00_rt, -1.94277e+00_rt, -1.79687e+00_rt, -1.65112e+00_rt, +-1.50557e+00_rt, -1.36035e+00_rt, -1.21562e+00_rt, -1.07160e+00_rt, +-9.28689e-01_rt, -7.87415e-01_rt, -6.48578e-01_rt, -5.13286e-01_rt, +-3.82930e-01_rt, -2.58677e-01_rt, -1.38684e-01_rt, 0.00000e+00_rt, +-9.11065e+00_rt, -8.96445e+00_rt, -8.81825e+00_rt, -8.67206e+00_rt, +-8.52586e+00_rt, -8.37967e+00_rt, -8.23347e+00_rt, -8.08727e+00_rt, +-7.94108e+00_rt, -7.79488e+00_rt, -7.64869e+00_rt, -7.50249e+00_rt, +-7.35629e+00_rt, -7.21010e+00_rt, -7.06390e+00_rt, -6.91771e+00_rt, +-6.77151e+00_rt, -6.62531e+00_rt, -6.47912e+00_rt, -6.33292e+00_rt, +-6.18673e+00_rt, -6.04053e+00_rt, -5.89434e+00_rt, -5.74814e+00_rt, +-5.60194e+00_rt, -5.45575e+00_rt, -5.30955e+00_rt, -5.16336e+00_rt, +-5.01716e+00_rt, -4.87096e+00_rt, -4.72477e+00_rt, -4.57857e+00_rt, +-4.43238e+00_rt, -4.28618e+00_rt, -4.13999e+00_rt, -3.99379e+00_rt, +-3.84760e+00_rt, -3.70141e+00_rt, -3.55521e+00_rt, -3.40902e+00_rt, +-3.26283e+00_rt, -3.11665e+00_rt, -2.97047e+00_rt, -2.82429e+00_rt, +-2.67813e+00_rt, -2.53197e+00_rt, -2.38584e+00_rt, -2.23973e+00_rt, +-2.09367e+00_rt, -1.94766e+00_rt, -1.80175e+00_rt, -1.65598e+00_rt, +-1.51042e+00_rt, -1.36517e+00_rt, -1.22039e+00_rt, -1.07633e+00_rt, +-9.33339e-01_rt, -7.91968e-01_rt, -6.53000e-01_rt, -5.17528e-01_rt, +-3.86924e-01_rt, -2.62323e-01_rt, -1.41817e-01_rt, 0.00000e+00_rt, +-9.11504e+00_rt, -8.96884e+00_rt, -8.82265e+00_rt, -8.67645e+00_rt, +-8.53025e+00_rt, -8.38406e+00_rt, -8.23786e+00_rt, -8.09167e+00_rt, +-7.94547e+00_rt, -7.79928e+00_rt, -7.65308e+00_rt, -7.50688e+00_rt, +-7.36069e+00_rt, -7.21449e+00_rt, -7.06830e+00_rt, -6.92210e+00_rt, +-6.77590e+00_rt, -6.62971e+00_rt, -6.48351e+00_rt, -6.33732e+00_rt, +-6.19112e+00_rt, -6.04492e+00_rt, -5.89873e+00_rt, -5.75253e+00_rt, +-5.60634e+00_rt, -5.46014e+00_rt, -5.31395e+00_rt, -5.16775e+00_rt, +-5.02155e+00_rt, -4.87536e+00_rt, -4.72916e+00_rt, -4.58297e+00_rt, +-4.43677e+00_rt, -4.29058e+00_rt, -4.14438e+00_rt, -3.99819e+00_rt, +-3.85199e+00_rt, -3.70580e+00_rt, -3.55961e+00_rt, -3.41342e+00_rt, +-3.26723e+00_rt, -3.12104e+00_rt, -2.97486e+00_rt, -2.82868e+00_rt, +-2.68251e+00_rt, -2.53636e+00_rt, -2.39022e+00_rt, -2.24411e+00_rt, +-2.09804e+00_rt, -1.95203e+00_rt, -1.80610e+00_rt, -1.66032e+00_rt, +-1.51474e+00_rt, -1.36946e+00_rt, -1.22465e+00_rt, -1.08054e+00_rt, +-9.37487e-01_rt, -7.96032e-01_rt, -6.56948e-01_rt, -5.21319e-01_rt, +-3.90498e-01_rt, -2.65591e-01_rt, -1.44639e-01_rt, 0.00000e+00_rt, +-9.11895e+00_rt, -8.97275e+00_rt, -8.82656e+00_rt, -8.68036e+00_rt, +-8.53417e+00_rt, -8.38797e+00_rt, -8.24177e+00_rt, -8.09558e+00_rt, +-7.94938e+00_rt, -7.80319e+00_rt, -7.65699e+00_rt, -7.51079e+00_rt, +-7.36460e+00_rt, -7.21840e+00_rt, -7.07221e+00_rt, -6.92601e+00_rt, +-6.77982e+00_rt, -6.63362e+00_rt, -6.48742e+00_rt, -6.34123e+00_rt, +-6.19503e+00_rt, -6.04884e+00_rt, -5.90264e+00_rt, -5.75644e+00_rt, +-5.61025e+00_rt, -5.46405e+00_rt, -5.31786e+00_rt, -5.17166e+00_rt, +-5.02547e+00_rt, -4.87927e+00_rt, -4.73307e+00_rt, -4.58688e+00_rt, +-4.44068e+00_rt, -4.29449e+00_rt, -4.14829e+00_rt, -4.00210e+00_rt, +-3.85590e+00_rt, -3.70971e+00_rt, -3.56352e+00_rt, -3.41733e+00_rt, +-3.27114e+00_rt, -3.12495e+00_rt, -2.97876e+00_rt, -2.83259e+00_rt, +-2.68642e+00_rt, -2.54026e+00_rt, -2.39412e+00_rt, -2.24800e+00_rt, +-2.10193e+00_rt, -1.95591e+00_rt, -1.80998e+00_rt, -1.66418e+00_rt, +-1.51858e+00_rt, -1.37328e+00_rt, -1.22844e+00_rt, -1.08429e+00_rt, +-9.41184e-01_rt, -7.99654e-01_rt, -6.60469e-01_rt, -5.24702e-01_rt, +-3.93689e-01_rt, -2.68515e-01_rt, -1.47175e-01_rt, 0.00000e+00_rt, +-9.12243e+00_rt, -8.97623e+00_rt, -8.83004e+00_rt, -8.68384e+00_rt, +-8.53764e+00_rt, -8.39145e+00_rt, -8.24525e+00_rt, -8.09906e+00_rt, +-7.95286e+00_rt, -7.80666e+00_rt, -7.66047e+00_rt, -7.51427e+00_rt, +-7.36808e+00_rt, -7.22188e+00_rt, -7.07569e+00_rt, -6.92949e+00_rt, +-6.78329e+00_rt, -6.63710e+00_rt, -6.49090e+00_rt, -6.34471e+00_rt, +-6.19851e+00_rt, -6.05231e+00_rt, -5.90612e+00_rt, -5.75992e+00_rt, +-5.61373e+00_rt, -5.46753e+00_rt, -5.32133e+00_rt, -5.17514e+00_rt, +-5.02894e+00_rt, -4.88275e+00_rt, -4.73655e+00_rt, -4.59036e+00_rt, +-4.44416e+00_rt, -4.29797e+00_rt, -4.15177e+00_rt, -4.00558e+00_rt, +-3.85938e+00_rt, -3.71319e+00_rt, -3.56699e+00_rt, -3.42080e+00_rt, +-3.27461e+00_rt, -3.12842e+00_rt, -2.98224e+00_rt, -2.83606e+00_rt, +-2.68989e+00_rt, -2.54373e+00_rt, -2.39759e+00_rt, -2.25147e+00_rt, +-2.10539e+00_rt, -1.95936e+00_rt, -1.81343e+00_rt, -1.66762e+00_rt, +-1.52200e+00_rt, -1.37668e+00_rt, -1.23181e+00_rt, -1.08763e+00_rt, +-9.44473e-01_rt, -8.02878e-01_rt, -6.63604e-01_rt, -5.27715e-01_rt, +-3.96535e-01_rt, -2.71126e-01_rt, -1.49447e-01_rt, 0.00000e+00_rt, +-9.12552e+00_rt, -8.97932e+00_rt, -8.83313e+00_rt, -8.68693e+00_rt, +-8.54073e+00_rt, -8.39454e+00_rt, -8.24834e+00_rt, -8.10215e+00_rt, +-7.95595e+00_rt, -7.80975e+00_rt, -7.66356e+00_rt, -7.51736e+00_rt, +-7.37117e+00_rt, -7.22497e+00_rt, -7.07877e+00_rt, -6.93258e+00_rt, +-6.78638e+00_rt, -6.64019e+00_rt, -6.49399e+00_rt, -6.34780e+00_rt, +-6.20160e+00_rt, -6.05540e+00_rt, -5.90921e+00_rt, -5.76301e+00_rt, +-5.61682e+00_rt, -5.47062e+00_rt, -5.32442e+00_rt, -5.17823e+00_rt, +-5.03203e+00_rt, -4.88584e+00_rt, -4.73964e+00_rt, -4.59345e+00_rt, +-4.44725e+00_rt, -4.30105e+00_rt, -4.15486e+00_rt, -4.00866e+00_rt, +-3.86247e+00_rt, -3.71628e+00_rt, -3.57008e+00_rt, -3.42389e+00_rt, +-3.27770e+00_rt, -3.13151e+00_rt, -2.98533e+00_rt, -2.83915e+00_rt, +-2.69297e+00_rt, -2.54681e+00_rt, -2.40067e+00_rt, -2.25455e+00_rt, +-2.10846e+00_rt, -1.96243e+00_rt, -1.81649e+00_rt, -1.67067e+00_rt, +-1.52504e+00_rt, -1.37970e+00_rt, -1.23481e+00_rt, -1.09059e+00_rt, +-9.47396e-01_rt, -8.05743e-01_rt, -6.66391e-01_rt, -5.30396e-01_rt, +-3.99069e-01_rt, -2.73455e-01_rt, -1.51480e-01_rt, 0.00000e+00_rt, +-9.12826e+00_rt, -8.98206e+00_rt, -8.83587e+00_rt, -8.68967e+00_rt, +-8.54348e+00_rt, -8.39728e+00_rt, -8.25108e+00_rt, -8.10489e+00_rt, +-7.95869e+00_rt, -7.81250e+00_rt, -7.66630e+00_rt, -7.52010e+00_rt, +-7.37391e+00_rt, -7.22771e+00_rt, -7.08152e+00_rt, -6.93532e+00_rt, +-6.78912e+00_rt, -6.64293e+00_rt, -6.49673e+00_rt, -6.35054e+00_rt, +-6.20434e+00_rt, -6.05814e+00_rt, -5.91195e+00_rt, -5.76575e+00_rt, +-5.61956e+00_rt, -5.47336e+00_rt, -5.32717e+00_rt, -5.18097e+00_rt, +-5.03477e+00_rt, -4.88858e+00_rt, -4.74238e+00_rt, -4.59619e+00_rt, +-4.44999e+00_rt, -4.30380e+00_rt, -4.15760e+00_rt, -4.01141e+00_rt, +-3.86521e+00_rt, -3.71902e+00_rt, -3.57282e+00_rt, -3.42663e+00_rt, +-3.28044e+00_rt, -3.13425e+00_rt, -2.98807e+00_rt, -2.84188e+00_rt, +-2.69571e+00_rt, -2.54955e+00_rt, -2.40340e+00_rt, -2.25728e+00_rt, +-2.11119e+00_rt, -1.96515e+00_rt, -1.81920e+00_rt, -1.67337e+00_rt, +-1.52773e+00_rt, -1.38238e+00_rt, -1.23747e+00_rt, -1.09322e+00_rt, +-9.49991e-01_rt, -8.08288e-01_rt, -6.68868e-01_rt, -5.32779e-01_rt, +-4.01323e-01_rt, -2.75528e-01_rt, -1.53296e-01_rt, 0.00000e+00_rt, +-9.13069e+00_rt, -8.98449e+00_rt, -8.83830e+00_rt, -8.69210e+00_rt, +-8.54591e+00_rt, -8.39971e+00_rt, -8.25351e+00_rt, -8.10732e+00_rt, +-7.96112e+00_rt, -7.81493e+00_rt, -7.66873e+00_rt, -7.52253e+00_rt, +-7.37634e+00_rt, -7.23014e+00_rt, -7.08395e+00_rt, -6.93775e+00_rt, +-6.79155e+00_rt, -6.64536e+00_rt, -6.49916e+00_rt, -6.35297e+00_rt, +-6.20677e+00_rt, -6.06058e+00_rt, -5.91438e+00_rt, -5.76818e+00_rt, +-5.62199e+00_rt, -5.47579e+00_rt, -5.32960e+00_rt, -5.18340e+00_rt, +-5.03720e+00_rt, -4.89101e+00_rt, -4.74481e+00_rt, -4.59862e+00_rt, +-4.45242e+00_rt, -4.30623e+00_rt, -4.16003e+00_rt, -4.01384e+00_rt, +-3.86764e+00_rt, -3.72145e+00_rt, -3.57525e+00_rt, -3.42906e+00_rt, +-3.28287e+00_rt, -3.13668e+00_rt, -2.99049e+00_rt, -2.84431e+00_rt, +-2.69814e+00_rt, -2.55197e+00_rt, -2.40582e+00_rt, -2.25970e+00_rt, +-2.11361e+00_rt, -1.96757e+00_rt, -1.82161e+00_rt, -1.67577e+00_rt, +-1.53013e+00_rt, -1.38476e+00_rt, -1.23983e+00_rt, -1.09556e+00_rt, +-9.52293e-01_rt, -8.10546e-01_rt, -6.71065e-01_rt, -5.34894e-01_rt, +-4.03325e-01_rt, -2.77372e-01_rt, -1.54914e-01_rt, 0.00000e+00_rt, +-9.13284e+00_rt, -8.98665e+00_rt, -8.84045e+00_rt, -8.69425e+00_rt, +-8.54806e+00_rt, -8.40186e+00_rt, -8.25567e+00_rt, -8.10947e+00_rt, +-7.96327e+00_rt, -7.81708e+00_rt, -7.67088e+00_rt, -7.52469e+00_rt, +-7.37849e+00_rt, -7.23229e+00_rt, -7.08610e+00_rt, -6.93990e+00_rt, +-6.79371e+00_rt, -6.64751e+00_rt, -6.50132e+00_rt, -6.35512e+00_rt, +-6.20892e+00_rt, -6.06273e+00_rt, -5.91653e+00_rt, -5.77034e+00_rt, +-5.62414e+00_rt, -5.47794e+00_rt, -5.33175e+00_rt, -5.18555e+00_rt, +-5.03936e+00_rt, -4.89316e+00_rt, -4.74697e+00_rt, -4.60077e+00_rt, +-4.45457e+00_rt, -4.30838e+00_rt, -4.16218e+00_rt, -4.01599e+00_rt, +-3.86979e+00_rt, -3.72360e+00_rt, -3.57740e+00_rt, -3.43121e+00_rt, +-3.28502e+00_rt, -3.13883e+00_rt, -2.99264e+00_rt, -2.84646e+00_rt, +-2.70029e+00_rt, -2.55412e+00_rt, -2.40797e+00_rt, -2.26184e+00_rt, +-2.11575e+00_rt, -1.96971e+00_rt, -1.82374e+00_rt, -1.67790e+00_rt, +-1.53224e+00_rt, -1.38687e+00_rt, -1.24192e+00_rt, -1.09763e+00_rt, +-9.54333e-01_rt, -8.12547e-01_rt, -6.73013e-01_rt, -5.36770e-01_rt, +-4.05101e-01_rt, -2.79009e-01_rt, -1.56354e-01_rt, 0.00000e+00_rt}; + + amrex::Gpu::synchronize(); + + m_phot_em_table = QS_phot_em_table{phot_em_params, vals}; +} + //============================================ diff --git a/Source/Particles/ElementaryProcess/QEDInternals/SchwingerProcessWrapper.H b/Source/Particles/ElementaryProcess/QEDInternals/SchwingerProcessWrapper.H index 46960fa0036..e7466985a3d 100644 --- a/Source/Particles/ElementaryProcess/QEDInternals/SchwingerProcessWrapper.H +++ b/Source/Particles/ElementaryProcess/QEDInternals/SchwingerProcessWrapper.H @@ -10,10 +10,7 @@ #include "QedWrapperCommons.H" -//#define PXRMP_CORE_ONLY allows importing only the 'core functions' of the -//Schwinger process engine of the QED PICSAR library. -#define PXRMP_CORE_ONLY -#include +#include #include #include @@ -22,7 +19,8 @@ * the number of Schwinger pairs created at a given timestep and in a given * cell as a function of the EM field in that cell. * - * @param[in] dVdt Volume of the cell multiplied by temporal step. + * @param[in] dV Volume of the cell. + * @param[in] dt temporal step. * @param[in] Ex x-component of the electric field on the cell. * @param[in] Ey y-component of the electric field on the cell. * @param[in] Ez z-component of the electric field on the cell. @@ -33,33 +31,31 @@ * pairs is below this parameter, a Poisson distribution is used * to draw the number of created pairs. Otherwise a Gaussian * distribution is used. + * @return the number of pairs generated via the Schwinger process */ -AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real -getSchwingerProductionNumber (const amrex::Real dVdt, +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real +getSchwingerProductionNumber (const amrex::Real dV, const amrex::Real dt, const amrex::Real Ex, const amrex::Real Ey, const amrex::Real Ez, const amrex::Real Bx, const amrex::Real By, const amrex::Real Bz, - const amrex::Real PoissonToGaussianThreshold) + const amrex::Real PoissonToGaussianThreshold, + amrex::RandomEngine const& engine) { using namespace amrex; - using namespace picsar::multi_physics; + namespace pxr_p = picsar::multi_physics::phys; + namespace pxr_sh = picsar::multi_physics::phys::schwinger; - //If SI units are used, PICSAR QED does not use a reference length. - // However, a "lambda" parameter must still be passed to some functions. - const auto dummy_lambda = 1._rt; - - const auto expectedPairNumber = dVdt * - schwinger_pair_engine:: - internal_compute_schwinger_pair_production_rate( - Ex, Ey, Ez, Bx, By, Bz, dummy_lambda); + const auto expectedPairNumber = + pxr_sh::expected_pair_number( + Ex, Ey, Ez, Bx, By, Bz, dV, dt); if (expectedPairNumber <= PoissonToGaussianThreshold) { - return RandomPoisson(expectedPairNumber); - } - else { - const auto numpairs = RandomNormal(expectedPairNumber,sqrt(expectedPairNumber)); + return amrex::RandomPoisson(expectedPairNumber, engine); + } else { + const auto numpairs = + amrex::RandomNormal(expectedPairNumber, std::sqrt(expectedPairNumber), engine); return numpairs > 0._rt ? numpairs : 0._rt; } - } #endif // WARPX_schwinger_process_wrapper_h_ diff --git a/Source/Particles/ElementaryProcess/QEDPairGeneration.H b/Source/Particles/ElementaryProcess/QEDPairGeneration.H index 45e71a5837a..439cde9755a 100644 --- a/Source/Particles/ElementaryProcess/QEDPairGeneration.H +++ b/Source/Particles/ElementaryProcess/QEDPairGeneration.H @@ -48,7 +48,7 @@ public: */ template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - bool operator() (const PData& ptd, int const i) const noexcept + bool operator() (const PData& ptd, int const i, amrex::RandomEngine const&) const noexcept { using namespace amrex; @@ -106,14 +106,6 @@ public: { using namespace amrex; - constexpr ParticleReal me = PhysConst::m_e; - constexpr ParticleReal one_over_me = 1._prt/me; - - const ParticleReal w = src.m_rdata[PIdx::w][i_src]; - const ParticleReal ux = src.m_rdata[PIdx::ux][i_src]; - const ParticleReal uy = src.m_rdata[PIdx::uy][i_src]; - const ParticleReal uz = src.m_rdata[PIdx::uz][i_src]; - // gather E and B amrex::ParticleReal xp, yp, zp; m_get_position(i_src, xp, yp, zp); @@ -130,43 +122,26 @@ public: m_dx_arr, m_xyzmin_arr, m_lo, m_n_rz_azimuthal_modes, m_nox, m_galerkin_interpolation); - const auto px = ux*me; - const auto py = uy*me; - const auto pz = uz*me; - - auto e_w = 0.0_rt; - auto p_w = 0.0_rt; - auto e_px = 0.0_rt; - auto e_py = 0.0_rt; - auto e_pz = 0.0_rt; - auto p_px = 0.0_rt; - auto p_py = 0.0_rt; - auto p_pz = 0.0_rt; - //Despite the names of the variables, positrons and electrons //can be exchanged, since the physical process is completely //symmetric with respect to this exchange. - m_generate_functor.operator()<1>( - px, py, pz, + const auto& ux = src.m_rdata[PIdx::ux][i_src]; + const auto& uy = src.m_rdata[PIdx::uy][i_src]; + const auto& uz = src.m_rdata[PIdx::uz][i_src]; + auto& e_ux = dst1.m_rdata[PIdx::ux][i_dst1]; + auto& e_uy = dst1.m_rdata[PIdx::uy][i_dst1]; + auto& e_uz = dst1.m_rdata[PIdx::uz][i_dst1]; + auto& p_ux = dst2.m_rdata[PIdx::ux][i_dst2]; + auto& p_uy = dst2.m_rdata[PIdx::uy][i_dst2]; + auto& p_uz = dst2.m_rdata[PIdx::uz][i_dst2]; + m_generate_functor( + ux, uy, uz, ex, ey, ez, bx, by, bz, - w, - &e_px, &e_py, &e_pz, - &p_px, &p_py, &p_pz, - &e_w, &p_w); - - dst1.m_rdata[PIdx::w][i_dst1] = e_w; - dst1.m_rdata[PIdx::ux][i_dst1] = e_px*one_over_me; - dst1.m_rdata[PIdx::uy][i_dst1] = e_py*one_over_me; - dst1.m_rdata[PIdx::uz][i_dst1] = e_pz*one_over_me; - - dst2.m_rdata[PIdx::w][i_dst2] = p_w; - dst2.m_rdata[PIdx::ux][i_dst2] = p_px*one_over_me; - dst2.m_rdata[PIdx::uy][i_dst2] = p_py*one_over_me; - dst2.m_rdata[PIdx::uz][i_dst2] = p_pz*one_over_me; + e_ux, e_uy, e_uz, + p_ux, p_uy, p_uz); src.m_aos[i_src].id() = -1; //destroy photon after pair generation - } private: diff --git a/Source/Particles/ElementaryProcess/QEDPhotonEmission.H b/Source/Particles/ElementaryProcess/QEDPhotonEmission.H index 73cd87b0b95..1233c16fbac 100644 --- a/Source/Particles/ElementaryProcess/QEDPhotonEmission.H +++ b/Source/Particles/ElementaryProcess/QEDPhotonEmission.H @@ -15,6 +15,8 @@ #include "Particles/Pusher/GetAndSetPosition.H" #include "QEDInternals/QuantumSyncEngineWrapper.H" +#include + /** @file * * This file contains the implementation of the elementary process @@ -48,7 +50,7 @@ public: */ template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - bool operator() (const PData& ptd, int const i) const noexcept + bool operator() (const PData& ptd, int const i, amrex::RandomEngine const&) const noexcept { using namespace amrex; @@ -86,7 +88,7 @@ public: PhotonEmissionTransformFunc ( QuantumSynchrotronGetOpticalDepth opt_depth_functor, int const opt_depth_runtime_comp, - QuantumSynchrotronGeneratePhotonAndUpdateMomentum const emission_functor, + QuantumSynchrotronPhotonEmission const emission_functor, const WarpXParIter& a_pti, int lev, int ngE, amrex::FArrayBox const& exfab, amrex::FArrayBox const& eyfab, @@ -112,14 +114,6 @@ public: { using namespace amrex; - constexpr ParticleReal me = PhysConst::m_e; - constexpr ParticleReal one_over_me = 1._prt/me; - - const ParticleReal w = src.m_rdata[PIdx::w][i_src]; - const ParticleReal ux = src.m_rdata[PIdx::ux][i_src]; - const ParticleReal uy = src.m_rdata[PIdx::uy][i_src]; - const ParticleReal uz = src.m_rdata[PIdx::uz][i_src]; - // gather E and B amrex::ParticleReal xp, yp, zp; m_get_position(i_src, xp, yp, zp); @@ -136,34 +130,17 @@ public: m_dx_arr, m_xyzmin_arr, m_lo, m_n_rz_azimuthal_modes, m_nox, m_galerkin_interpolation); - // Particle momentum is stored as gamma * velocity. - // Convert to m * gamma * velocity before applying the emission functor. - auto px = ux*me; - auto py = uy*me; - auto pz = uz*me; - - auto g_w = 0.0_rt; - auto g_px = 0.0_rt; - auto g_py = 0.0_rt; - auto g_pz = 0.0_rt; - - m_emission_functor.operator()<1>( - &px, &py, &pz, + auto& ux = src.m_rdata[PIdx::ux][i_src]; + auto& uy = src.m_rdata[PIdx::uy][i_src]; + auto& uz = src.m_rdata[PIdx::uz][i_src]; + auto& g_ux = dst.m_rdata[PIdx::ux][i_dst]; + auto& g_uy = dst.m_rdata[PIdx::uy][i_dst]; + auto& g_uz = dst.m_rdata[PIdx::uz][i_dst]; + m_emission_functor( + ux, uy, uz, ex, ey, ez, bx, by, bz, - w, - &g_px, &g_py, &g_pz, - &g_w); - - // Then convert back to WarpX convention. - src.m_rdata[PIdx::ux][i_src] = px*one_over_me; - src.m_rdata[PIdx::uy][i_src] = py*one_over_me; - src.m_rdata[PIdx::uz][i_src] = pz*one_over_me; - - dst.m_rdata[PIdx::w][i_dst] = g_w; - dst.m_rdata[PIdx::ux][i_dst] = g_px*one_over_me; - dst.m_rdata[PIdx::uy][i_dst] = g_py*one_over_me; - dst.m_rdata[PIdx::uz][i_dst] = g_pz*one_over_me; + g_ux, g_uy, g_uz); //Initialize the optical depth component of the source species. src.m_runtime_rdata[m_opt_depth_runtime_comp][i_src] = @@ -173,10 +150,12 @@ public: private: const QuantumSynchrotronGetOpticalDepth m_opt_depth_functor; /*!< A copy of the functor to initialize the optical depth of the source species. */ - const QuantumSynchrotronGeneratePhotonAndUpdateMomentum - m_emission_functor; /*!< A copy of the functor to generate photons. It contains only pointers to the lookup tables.*/ + const int m_opt_depth_runtime_comp = 0; /*!< Index of the optical depth component of source species*/ + const QuantumSynchrotronPhotonEmission + m_emission_functor; /*!< A copy of the functor to generate photons. It contains only pointers to the lookup tables.*/ + GetParticlePosition m_get_position; GetExternalEField m_get_externalE; GetExternalBField m_get_externalB; @@ -209,6 +188,8 @@ private: /** * \brief Free function to call to remove immediately * low energy photons by setting their ID to -1. +* Photons with extremely small energy are removed regardless of +* the value of the energy_threshold * * @tparam PTile particle tile type * @param[in,out] ptile a particle tile @@ -230,7 +211,9 @@ void cleanLowEnergyPhotons( const auto p_uz = soa.GetRealData(PIdx::uz).data() + old_size; //The square of the energy threshold - const auto energy_threshold2 = energy_threshold*energy_threshold; + const auto energy_threshold2 = std::max( + energy_threshold*energy_threshold, + std::numeric_limits::min()); amrex::ParallelFor(num_added, [=] AMREX_GPU_DEVICE (int ip) noexcept { diff --git a/Source/Particles/ElementaryProcess/QEDPhotonEmission.cpp b/Source/Particles/ElementaryProcess/QEDPhotonEmission.cpp index eb7ef074b59..97c64f62a88 100644 --- a/Source/Particles/ElementaryProcess/QEDPhotonEmission.cpp +++ b/Source/Particles/ElementaryProcess/QEDPhotonEmission.cpp @@ -12,7 +12,7 @@ PhotonEmissionTransformFunc:: PhotonEmissionTransformFunc (QuantumSynchrotronGetOpticalDepth opt_depth_functor, int const opt_depth_runtime_comp, - QuantumSynchrotronGeneratePhotonAndUpdateMomentum const emission_functor, + QuantumSynchrotronPhotonEmission const emission_functor, const WarpXParIter& a_pti, int lev, int ngE, amrex::FArrayBox const& exfab, amrex::FArrayBox const& eyfab, diff --git a/Source/Particles/ElementaryProcess/QEDSchwingerProcess.H b/Source/Particles/ElementaryProcess/QEDSchwingerProcess.H index 7f39e8ed672..664736a3de9 100644 --- a/Source/Particles/ElementaryProcess/QEDSchwingerProcess.H +++ b/Source/Particles/ElementaryProcess/QEDSchwingerProcess.H @@ -17,7 +17,8 @@ struct SchwingerFilterFunc { const int m_threshold_poisson_gaussian; - const amrex::Real m_dVdt; + const amrex::Real m_dV; + const amrex::Real m_dt; /** Get the number of created pairs in a given cell at a given timestep. * @@ -30,8 +31,9 @@ struct SchwingerFilterFunc */ template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - amrex::Real operator() (const FABs& src_FABs, const int i, - const int j, const int k) const noexcept + amrex::Real operator() (const FABs& src_FABs, + const int i, const int j, const int k, + amrex::RandomEngine const& engine) const noexcept { const auto& arrEx = src_FABs[0]; const auto& arrEy = src_FABs[1]; @@ -40,10 +42,10 @@ struct SchwingerFilterFunc const auto& arrBy = src_FABs[4]; const auto& arrBz = src_FABs[5]; - return getSchwingerProductionNumber( m_dVdt, + return getSchwingerProductionNumber( m_dV, m_dt, arrEx(i,j,k),arrEy(i,j,k),arrEz(i,j,k), arrBx(i,j,k),arrBy(i,j,k),arrBz(i,j,k), - m_threshold_poisson_gaussian); + m_threshold_poisson_gaussian,engine); } }; diff --git a/Source/Particles/Filter/FilterFunctors.H b/Source/Particles/Filter/FilterFunctors.H index 44240f3fc86..5b6a3f3235f 100644 --- a/Source/Particles/Filter/FilterFunctors.H +++ b/Source/Particles/Filter/FilterFunctors.H @@ -34,10 +34,10 @@ struct RandomFilter * \return whether or not the particle is selected */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - bool operator () (const SuperParticleType& p) const noexcept + bool operator () (const SuperParticleType& /*p*/, const amrex::RandomEngine& engine) const noexcept { if ( !m_is_active ) return 1; - else if ( amrex::Random() < m_fraction ) return 1; + else if ( amrex::Random(engine) < m_fraction ) return 1; else return 0; } private: @@ -63,7 +63,7 @@ struct UniformFilter * \return whether or not the particle is selected */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - bool operator () (const SuperParticleType& p) const noexcept + bool operator () (const SuperParticleType& p, const amrex::RandomEngine&) const noexcept { if ( !m_is_active ) return 1; else if ( p.id()%m_stride == 0 ) return 1; @@ -82,7 +82,7 @@ struct ParserFilter /** constructor * \param a_is_active whether the test is active */ - ParserFilter(bool a_is_active, ParserWrapper<7>* a_filter_parser) + ParserFilter(bool a_is_active, HostDeviceParser<7> const& a_filter_parser) : m_is_active(a_is_active), m_function_partparser(a_filter_parser) { m_t = WarpX::GetInstance().gett_new(0); @@ -94,7 +94,7 @@ struct ParserFilter * \return whether or not the particle is selected */ AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - bool operator () (const SuperParticleType& p) const noexcept + bool operator () (const SuperParticleType& p, const amrex::RandomEngine&) const noexcept { if ( !m_is_active ) return 1; amrex::Real const x = p.pos(0); @@ -103,7 +103,7 @@ struct ParserFilter amrex::Real const ux = p.rdata(PIdx::ux)/PhysConst::c; amrex::Real const uy = p.rdata(PIdx::uy)/PhysConst::c; amrex::Real const uz = p.rdata(PIdx::uz)/PhysConst::c; - if ( (*m_function_partparser)(m_t,x,y,z,ux,uy,uz) > 0.5 ) return 1; + if ( m_function_partparser(m_t,x,y,z,ux,uy,uz) > 0.5 ) return 1; else return 0; } private: @@ -111,9 +111,40 @@ private: const bool m_is_active; public: /** Parser function with 7 input variables, t,x,y,z,ux,uy,uz */ - ParserWrapper<7> const * const m_function_partparser; + HostDeviceParser<7> const m_function_partparser; /** Store physical time. */ amrex::Real m_t; }; + + +/** + * \brief Functor that returns 1 if the particle is inside a given axis-aligned region + * defined by amrex::RealBox, 0 otherwise. + */ +struct GeometryFilter +{ + GeometryFilter(bool a_is_active, amrex::RealBox a_domain) + : m_is_active(a_is_active), m_domain(a_domain) {} + /** + * \brief return 1 if the partcile is within the region described by the RealBox + * \param p one particle + * \param rb RealBox + * \return whether or not the particle is inside the region defined by m_domain + */ + AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE + bool operator () (const SuperParticleType& p, const amrex::RandomEngine&) const noexcept + { + if ( !m_is_active ) return 1; + return ! (AMREX_D_TERM( (p.pos(0) < m_domain.lo(0)) || (p.pos(0) > m_domain.hi(0) ), + || (p.pos(1) < m_domain.lo(1)) || (p.pos(1) > m_domain.hi(1) ), + || (p.pos(2) < m_domain.lo(2)) || (p.pos(2) > m_domain.hi(2) ))); + } +private: + /** Whether this diagnostics is activated. Select all particles if false */ + const bool m_is_active; + /** Physical extent of the axis-aligned region used for particle check */ + const amrex::RealBox m_domain; +}; + #endif // FILTERFUNCTORS_H diff --git a/Source/Particles/Gather/FieldGather.H b/Source/Particles/Gather/FieldGather.H index 4a8399c5665..4050a6a3622 100644 --- a/Source/Particles/Gather/FieldGather.H +++ b/Source/Particles/Gather/FieldGather.H @@ -13,22 +13,27 @@ #include "Particles/ShapeFactors.H" #include "Utils/WarpX_Complex.H" +#include + /** * \brief Field gather for a single particle * - * \param xp, yp, zp : Particle position coordinates - * \param Exp, Eyp, Ezp : Electric field on particles. - * \param Bxp, Byp, Bzp : Magnetic field on particles. - * \param ex_arr ey_arr ez_arr : Array4 of the electric field, either full array or tile. - * \param bx_arr by_arr bz_arr : Array4 of the magnetic field, either full array or tile. - * \param ex_type, ey_type, ez_type : IndexType of the electric field - * \param bx_type, by_type, bz_type : IndexType of the magnetic field - * \param dx : 3D cell spacing - * \param xyzmin : Physical lower bounds of domain in x, y, z. - * \param lo : Index lower bounds of domain. - * \param n_rz_azimuthal_modes : Number of azimuthal modes when using RZ geometry + * \tparam depos_order Particle shape order + * \tparam galerkin_interpolation Lower the order of the particle shape by + * this value (0/1) for the parallel field component + * \param xp, yp, zp Particle position coordinates + * \param Exp, Eyp, Ezp Electric field on particles. + * \param Bxp, Byp, Bzp Magnetic field on particles. + * \param ex_arr ey_arr ez_arr Array4 of the electric field, either full array or tile. + * \param bx_arr by_arr bz_arr Array4 of the magnetic field, either full array or tile. + * \param ex_type, ey_type, ez_type IndexType of the electric field + * \param bx_type, by_type, bz_type IndexType of the magnetic field + * \param dx 3D cell spacing + * \param xyzmin Physical lower bounds of domain in x, y, z. + * \param lo Index lower bounds of domain. + * \param n_rz_azimuthal_modes Number of azimuthal modes when using RZ geometry */ -template +template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void doGatherShapeN (const amrex::ParticleReal xp, const amrex::ParticleReal yp, @@ -58,6 +63,14 @@ void doGatherShapeN (const amrex::ParticleReal xp, { using namespace amrex; +#if defined(WARPX_DIM_XZ) + amrex::ignore_unused(yp); +#endif + +#ifndef WARPX_DIM_RZ + amrex::ignore_unused(n_rz_azimuthal_modes); +#endif + const amrex::Real dxi = 1.0/dx[0]; const amrex::Real dzi = 1.0/dx[2]; #if (AMREX_SPACEDIM == 3) @@ -90,14 +103,14 @@ void doGatherShapeN (const amrex::ParticleReal xp, // arrays will be needed. amrex::Real sx_node[depos_order + 1]; amrex::Real sx_cell[depos_order + 1]; - amrex::Real sx_node_v[depos_order + 1 - lower_in_v]; - amrex::Real sx_cell_v[depos_order + 1 - lower_in_v]; - int j_node; - int j_cell; - int j_node_v; - int j_cell_v; + amrex::Real sx_node_galerkin[depos_order + 1 - galerkin_interpolation]; + amrex::Real sx_cell_galerkin[depos_order + 1 - galerkin_interpolation]; + int j_node = 0; + int j_cell = 0; + int j_node_v = 0; + int j_cell_v = 0; Compute_shape_factor< depos_order > const compute_shape_factor; - Compute_shape_factor< depos_order-lower_in_v > const compute_shape_factor_lower_in_v; + Compute_shape_factor const compute_shape_factor_galerkin; if ((ey_type[0] == NODE) || (ez_type[0] == NODE) || (bx_type[0] == NODE)) { j_node = compute_shape_factor(sx_node, x); } @@ -105,17 +118,17 @@ void doGatherShapeN (const amrex::ParticleReal xp, j_cell = compute_shape_factor(sx_cell, x - 0.5_rt); } if ((ex_type[0] == NODE) || (by_type[0] == NODE) || (bz_type[0] == NODE)) { - j_node_v = compute_shape_factor_lower_in_v(sx_node_v, x); + j_node_v = compute_shape_factor_galerkin(sx_node_galerkin, x); } if ((ex_type[0] == CELL) || (by_type[0] == CELL) || (bz_type[0] == CELL)) { - j_cell_v = compute_shape_factor_lower_in_v(sx_cell_v, x - 0.5_rt); + j_cell_v = compute_shape_factor_galerkin(sx_cell_galerkin, x - 0.5_rt); } - const amrex::Real (&sx_ex)[depos_order + 1 - lower_in_v] = ((ex_type[0] == NODE) ? sx_node_v : sx_cell_v); + const amrex::Real (&sx_ex)[depos_order + 1 - galerkin_interpolation] = ((ex_type[0] == NODE) ? sx_node_galerkin : sx_cell_galerkin); const amrex::Real (&sx_ey)[depos_order + 1 ] = ((ey_type[0] == NODE) ? sx_node : sx_cell ); const amrex::Real (&sx_ez)[depos_order + 1 ] = ((ez_type[0] == NODE) ? sx_node : sx_cell ); const amrex::Real (&sx_bx)[depos_order + 1 ] = ((bx_type[0] == NODE) ? sx_node : sx_cell ); - const amrex::Real (&sx_by)[depos_order + 1 - lower_in_v] = ((by_type[0] == NODE) ? sx_node_v : sx_cell_v); - const amrex::Real (&sx_bz)[depos_order + 1 - lower_in_v] = ((bz_type[0] == NODE) ? sx_node_v : sx_cell_v); + const amrex::Real (&sx_by)[depos_order + 1 - galerkin_interpolation] = ((by_type[0] == NODE) ? sx_node_galerkin : sx_cell_galerkin); + const amrex::Real (&sx_bz)[depos_order + 1 - galerkin_interpolation] = ((bz_type[0] == NODE) ? sx_node_galerkin : sx_cell_galerkin); int const j_ex = ((ex_type[0] == NODE) ? j_node_v : j_cell_v); int const j_ey = ((ey_type[0] == NODE) ? j_node : j_cell ); int const j_ez = ((ez_type[0] == NODE) ? j_node : j_cell ); @@ -128,12 +141,12 @@ void doGatherShapeN (const amrex::ParticleReal xp, const amrex::Real y = (yp-ymin)*dyi; amrex::Real sy_node[depos_order + 1]; amrex::Real sy_cell[depos_order + 1]; - amrex::Real sy_node_v[depos_order + 1 - lower_in_v]; - amrex::Real sy_cell_v[depos_order + 1 - lower_in_v]; - int k_node; - int k_cell; - int k_node_v; - int k_cell_v; + amrex::Real sy_node_v[depos_order + 1 - galerkin_interpolation]; + amrex::Real sy_cell_v[depos_order + 1 - galerkin_interpolation]; + int k_node = 0; + int k_cell = 0; + int k_node_v = 0; + int k_cell_v = 0; if ((ex_type[1] == NODE) || (ez_type[1] == NODE) || (by_type[1] == NODE)) { k_node = compute_shape_factor(sy_node, y); } @@ -141,17 +154,17 @@ void doGatherShapeN (const amrex::ParticleReal xp, k_cell = compute_shape_factor(sy_cell, y - 0.5_rt); } if ((ey_type[1] == NODE) || (bx_type[1] == NODE) || (bz_type[1] == NODE)) { - k_node_v = compute_shape_factor_lower_in_v(sy_node_v, y); + k_node_v = compute_shape_factor_galerkin(sy_node_v, y); } if ((ey_type[1] == CELL) || (bx_type[1] == CELL) || (bz_type[1] == CELL)) { - k_cell_v = compute_shape_factor_lower_in_v(sy_cell_v, y - 0.5_rt); + k_cell_v = compute_shape_factor_galerkin(sy_cell_v, y - 0.5_rt); } const amrex::Real (&sy_ex)[depos_order + 1 ] = ((ex_type[1] == NODE) ? sy_node : sy_cell ); - const amrex::Real (&sy_ey)[depos_order + 1 - lower_in_v] = ((ey_type[1] == NODE) ? sy_node_v : sy_cell_v); + const amrex::Real (&sy_ey)[depos_order + 1 - galerkin_interpolation] = ((ey_type[1] == NODE) ? sy_node_v : sy_cell_v); const amrex::Real (&sy_ez)[depos_order + 1 ] = ((ez_type[1] == NODE) ? sy_node : sy_cell ); - const amrex::Real (&sy_bx)[depos_order + 1 - lower_in_v] = ((bx_type[1] == NODE) ? sy_node_v : sy_cell_v); + const amrex::Real (&sy_bx)[depos_order + 1 - galerkin_interpolation] = ((bx_type[1] == NODE) ? sy_node_v : sy_cell_v); const amrex::Real (&sy_by)[depos_order + 1 ] = ((by_type[1] == NODE) ? sy_node : sy_cell ); - const amrex::Real (&sy_bz)[depos_order + 1 - lower_in_v] = ((bz_type[1] == NODE) ? sy_node_v : sy_cell_v); + const amrex::Real (&sy_bz)[depos_order + 1 - galerkin_interpolation] = ((bz_type[1] == NODE) ? sy_node_v : sy_cell_v); int const k_ex = ((ex_type[1] == NODE) ? k_node : k_cell ); int const k_ey = ((ey_type[1] == NODE) ? k_node_v : k_cell_v); int const k_ez = ((ez_type[1] == NODE) ? k_node : k_cell ); @@ -164,12 +177,12 @@ void doGatherShapeN (const amrex::ParticleReal xp, const amrex::Real z = (zp-zmin)*dzi; amrex::Real sz_node[depos_order + 1]; amrex::Real sz_cell[depos_order + 1]; - amrex::Real sz_node_v[depos_order + 1 - lower_in_v]; - amrex::Real sz_cell_v[depos_order + 1 - lower_in_v]; - int l_node; - int l_cell; - int l_node_v; - int l_cell_v; + amrex::Real sz_node_v[depos_order + 1 - galerkin_interpolation]; + amrex::Real sz_cell_v[depos_order + 1 - galerkin_interpolation]; + int l_node = 0; + int l_cell = 0; + int l_node_v = 0; + int l_cell_v = 0; if ((ex_type[zdir] == NODE) || (ey_type[zdir] == NODE) || (bz_type[zdir] == NODE)) { l_node = compute_shape_factor(sz_node, z); } @@ -177,16 +190,16 @@ void doGatherShapeN (const amrex::ParticleReal xp, l_cell = compute_shape_factor(sz_cell, z - 0.5_rt); } if ((ez_type[zdir] == NODE) || (bx_type[zdir] == NODE) || (by_type[zdir] == NODE)) { - l_node_v = compute_shape_factor_lower_in_v(sz_node_v, z); + l_node_v = compute_shape_factor_galerkin(sz_node_v, z); } if ((ez_type[zdir] == CELL) || (bx_type[zdir] == CELL) || (by_type[zdir] == CELL)) { - l_cell_v = compute_shape_factor_lower_in_v(sz_cell_v, z - 0.5_rt); + l_cell_v = compute_shape_factor_galerkin(sz_cell_v, z - 0.5_rt); } const amrex::Real (&sz_ex)[depos_order + 1 ] = ((ex_type[zdir] == NODE) ? sz_node : sz_cell ); const amrex::Real (&sz_ey)[depos_order + 1 ] = ((ey_type[zdir] == NODE) ? sz_node : sz_cell ); - const amrex::Real (&sz_ez)[depos_order + 1 - lower_in_v] = ((ez_type[zdir] == NODE) ? sz_node_v : sz_cell_v); - const amrex::Real (&sz_bx)[depos_order + 1 - lower_in_v] = ((bx_type[zdir] == NODE) ? sz_node_v : sz_cell_v); - const amrex::Real (&sz_by)[depos_order + 1 - lower_in_v] = ((by_type[zdir] == NODE) ? sz_node_v : sz_cell_v); + const amrex::Real (&sz_ez)[depos_order + 1 - galerkin_interpolation] = ((ez_type[zdir] == NODE) ? sz_node_v : sz_cell_v); + const amrex::Real (&sz_bx)[depos_order + 1 - galerkin_interpolation] = ((bx_type[zdir] == NODE) ? sz_node_v : sz_cell_v); + const amrex::Real (&sz_by)[depos_order + 1 - galerkin_interpolation] = ((by_type[zdir] == NODE) ? sz_node_v : sz_cell_v); const amrex::Real (&sz_bz)[depos_order + 1 ] = ((bz_type[zdir] == NODE) ? sz_node : sz_cell ); int const l_ex = ((ex_type[zdir] == NODE) ? l_node : l_cell ); int const l_ey = ((ey_type[zdir] == NODE) ? l_node : l_cell ); @@ -199,7 +212,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, // Each field is gathered in a separate block of // AMREX_SPACEDIM nested loops because the deposition // order can differ for each component of each field - // when lower_in_v is set to 1 + // when galerkin_interpolation is set to 1 #if (AMREX_SPACEDIM == 2) // Gather field on particle Eyp from field on grid ey_arr for (int iz=0; iz<=depos_order; iz++){ @@ -211,7 +224,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, // Gather field on particle Exp from field on grid ex_arr // Gather field on particle Bzp from field on grid bz_arr for (int iz=0; iz<=depos_order; iz++){ - for (int ix=0; ix<=depos_order-lower_in_v; ix++){ + for (int ix=0; ix<=depos_order-galerkin_interpolation; ix++){ Exp += sx_ex[ix]*sz_ex[iz]* ex_arr(lo.x+j_ex+ix, lo.y+l_ex+iz, 0, 0); Bzp += sx_bz[ix]*sz_bz[iz]* @@ -220,7 +233,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } // Gather field on particle Ezp from field on grid ez_arr // Gather field on particle Bxp from field on grid bx_arr - for (int iz=0; iz<=depos_order-lower_in_v; iz++){ + for (int iz=0; iz<=depos_order-galerkin_interpolation; iz++){ for (int ix=0; ix<=depos_order; ix++){ Ezp += sx_ez[ix]*sz_ez[iz]* ez_arr(lo.x+j_ez+ix, lo.y+l_ez+iz, 0, 0); @@ -229,8 +242,8 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } // Gather field on particle Byp from field on grid by_arr - for (int iz=0; iz<=depos_order-lower_in_v; iz++){ - for (int ix=0; ix<=depos_order-lower_in_v; ix++){ + for (int iz=0; iz<=depos_order-galerkin_interpolation; iz++){ + for (int ix=0; ix<=depos_order-galerkin_interpolation; ix++){ Byp += sx_by[ix]*sz_by[iz]* by_arr(lo.x+j_by+ix, lo.y+l_by+iz, 0, 0); } @@ -263,7 +276,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, // Gather field on particle Exp from field on grid ex_arr // Gather field on particle Bzp from field on grid bz_arr for (int iz=0; iz<=depos_order; iz++){ - for (int ix=0; ix<=depos_order-lower_in_v; ix++){ + for (int ix=0; ix<=depos_order-galerkin_interpolation; ix++){ const amrex::Real dEx = (+ ex_arr(lo.x+j_ex+ix, lo.y+l_ex+iz, 0, 2*imode-1)*xy.real() - ex_arr(lo.x+j_ex+ix, lo.y+l_ex+iz, 0, 2*imode)*xy.imag()); Exp += sx_ex[ix]*sz_ex[iz]*dEx; @@ -274,7 +287,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } // Gather field on particle Ezp from field on grid ez_arr // Gather field on particle Bxp from field on grid bx_arr - for (int iz=0; iz<=depos_order-lower_in_v; iz++){ + for (int iz=0; iz<=depos_order-galerkin_interpolation; iz++){ for (int ix=0; ix<=depos_order; ix++){ const amrex::Real dEz = (+ ez_arr(lo.x+j_ez+ix, lo.y+l_ez+iz, 0, 2*imode-1)*xy.real() - ez_arr(lo.x+j_ez+ix, lo.y+l_ez+iz, 0, 2*imode)*xy.imag()); @@ -285,8 +298,8 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } // Gather field on particle Byp from field on grid by_arr - for (int iz=0; iz<=depos_order-lower_in_v; iz++){ - for (int ix=0; ix<=depos_order-lower_in_v; ix++){ + for (int iz=0; iz<=depos_order-galerkin_interpolation; iz++){ + for (int ix=0; ix<=depos_order-galerkin_interpolation; ix++){ const amrex::Real dBy = (+ by_arr(lo.x+j_by+ix, lo.y+l_by+iz, 0, 2*imode-1)*xy.real() - by_arr(lo.x+j_by+ix, lo.y+l_by+iz, 0, 2*imode)*xy.imag()); Byp += sx_by[ix]*sz_by[iz]*dBy; @@ -308,7 +321,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, // Gather field on particle Exp from field on grid ex_arr for (int iz=0; iz<=depos_order; iz++){ for (int iy=0; iy<=depos_order; iy++){ - for (int ix=0; ix<=depos_order-lower_in_v; ix++){ + for (int ix=0; ix<= depos_order - galerkin_interpolation; ix++){ Exp += sx_ex[ix]*sy_ex[iy]*sz_ex[iz]* ex_arr(lo.x+j_ex+ix, lo.y+k_ex+iy, lo.z+l_ex+iz); } @@ -316,7 +329,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } // Gather field on particle Eyp from field on grid ey_arr for (int iz=0; iz<=depos_order; iz++){ - for (int iy=0; iy<=depos_order-lower_in_v; iy++){ + for (int iy=0; iy<= depos_order - galerkin_interpolation; iy++){ for (int ix=0; ix<=depos_order; ix++){ Eyp += sx_ey[ix]*sy_ey[iy]*sz_ey[iz]* ey_arr(lo.x+j_ey+ix, lo.y+k_ey+iy, lo.z+l_ey+iz); @@ -324,7 +337,7 @@ void doGatherShapeN (const amrex::ParticleReal xp, } } // Gather field on particle Ezp from field on grid ez_arr - for (int iz=0; iz<=depos_order-lower_in_v; iz++){ + for (int iz=0; iz<= depos_order - galerkin_interpolation; iz++){ for (int iy=0; iy<=depos_order; iy++){ for (int ix=0; ix<=depos_order; ix++){ Ezp += sx_ez[ix]*sy_ez[iy]*sz_ez[iz]* @@ -334,25 +347,25 @@ void doGatherShapeN (const amrex::ParticleReal xp, } // Gather field on particle Bzp from field on grid bz_arr for (int iz=0; iz<=depos_order; iz++){ - for (int iy=0; iy<=depos_order-lower_in_v; iy++){ - for (int ix=0; ix<=depos_order-lower_in_v; ix++){ + for (int iy=0; iy<= depos_order - galerkin_interpolation; iy++){ + for (int ix=0; ix<= depos_order - galerkin_interpolation; ix++){ Bzp += sx_bz[ix]*sy_bz[iy]*sz_bz[iz]* bz_arr(lo.x+j_bz+ix, lo.y+k_bz+iy, lo.z+l_bz+iz); } } } // Gather field on particle Byp from field on grid by_arr - for (int iz=0; iz<=depos_order-lower_in_v; iz++){ + for (int iz=0; iz<= depos_order - galerkin_interpolation; iz++){ for (int iy=0; iy<=depos_order; iy++){ - for (int ix=0; ix<=depos_order-lower_in_v; ix++){ + for (int ix=0; ix<= depos_order - galerkin_interpolation; ix++){ Byp += sx_by[ix]*sy_by[iy]*sz_by[iz]* by_arr(lo.x+j_by+ix, lo.y+k_by+iy, lo.z+l_by+iz); } } } // Gather field on particle Bxp from field on grid bx_arr - for (int iz=0; iz<=depos_order-lower_in_v; iz++){ - for (int iy=0; iy<=depos_order-lower_in_v; iy++){ + for (int iz=0; iz<= depos_order - galerkin_interpolation; iz++){ + for (int iy=0; iy<= depos_order - galerkin_interpolation; iy++){ for (int ix=0; ix<=depos_order; ix++){ Bxp += sx_bx[ix]*sy_bx[iy]*sz_bx[iz]* bx_arr(lo.x+j_bx+ix, lo.y+k_bx+iy, lo.z+l_bx+iz); diff --git a/Source/Particles/Gather/GetExternalFields.H b/Source/Particles/Gather/GetExternalFields.H index 594198e810a..91f868b8942 100644 --- a/Source/Particles/Gather/GetExternalFields.H +++ b/Source/Particles/Gather/GetExternalFields.H @@ -26,9 +26,9 @@ struct GetExternalField amrex::GpuArray m_field_value; - ParserWrapper<4>* m_xfield_partparser = nullptr; - ParserWrapper<4>* m_yfield_partparser = nullptr; - ParserWrapper<4>* m_zfield_partparser = nullptr; + HostDeviceParser<4> m_xfield_partparser; + HostDeviceParser<4> m_yfield_partparser; + HostDeviceParser<4> m_zfield_partparser; GetParticlePosition m_get_position; amrex::Real m_time; @@ -40,21 +40,17 @@ struct GetExternalField { if (m_type == Constant) { - field_x = m_field_value[0]; - field_y = m_field_value[1]; - field_z = m_field_value[2]; + field_x += m_field_value[0]; + field_y += m_field_value[1]; + field_z += m_field_value[2]; } else if (m_type == Parser) { - AMREX_ASSERT(m_xfield_partparser != nullptr); - AMREX_ASSERT(m_yfield_partparser != nullptr); - AMREX_ASSERT(m_zfield_partparser != nullptr); - amrex::ParticleReal x, y, z; m_get_position(i, x, y, z); - field_x = (*m_xfield_partparser)(x, y, z, m_time); - field_y = (*m_yfield_partparser)(x, y, z, m_time); - field_z = (*m_zfield_partparser)(x, y, z, m_time); + field_x += m_xfield_partparser(x, y, z, m_time); + field_y += m_yfield_partparser(x, y, z, m_time); + field_z += m_zfield_partparser(x, y, z, m_time); } #ifdef PULSAR else if (m_type == Pulsar_Efield) diff --git a/Source/Particles/Gather/GetExternalFields.cpp b/Source/Particles/Gather/GetExternalFields.cpp index 6a7d0f59706..e8fbdd6a82b 100644 --- a/Source/Particles/Gather/GetExternalFields.cpp +++ b/Source/Particles/Gather/GetExternalFields.cpp @@ -17,9 +17,9 @@ GetExternalEField::GetExternalEField (const WarpXParIter& a_pti, int a_offset) n m_type = Parser; m_time = warpx.gett_new(a_pti.GetLevel()); m_get_position = GetParticlePosition(a_pti, a_offset); - m_xfield_partparser = mypc.m_Ex_particle_parser.get(); - m_yfield_partparser = mypc.m_Ey_particle_parser.get(); - m_zfield_partparser = mypc.m_Ez_particle_parser.get(); + m_xfield_partparser = getParser(mypc.m_Ex_particle_parser); + m_yfield_partparser = getParser(mypc.m_Ey_particle_parser); + m_zfield_partparser = getParser(mypc.m_Ez_particle_parser); } #ifdef PULSAR if (PulsarParm::EB_external == 1) @@ -47,9 +47,9 @@ GetExternalBField::GetExternalBField (const WarpXParIter& a_pti, int a_offset) n m_type = Parser; m_time = warpx.gett_new(a_pti.GetLevel()); m_get_position = GetParticlePosition(a_pti, a_offset); - m_xfield_partparser = mypc.m_Bx_particle_parser.get(); - m_yfield_partparser = mypc.m_By_particle_parser.get(); - m_zfield_partparser = mypc.m_Bz_particle_parser.get(); + m_xfield_partparser = getParser(mypc.m_Bx_particle_parser); + m_yfield_partparser = getParser(mypc.m_By_particle_parser); + m_zfield_partparser = getParser(mypc.m_Bz_particle_parser); } #ifdef PULSAR if (PulsarParm::EB_external == 1) diff --git a/Source/Particles/Gather/ScaleFields.H b/Source/Particles/Gather/ScaleFields.H index cc94fac2934..cf96ec2c11c 100644 --- a/Source/Particles/Gather/ScaleFields.H +++ b/Source/Particles/Gather/ScaleFields.H @@ -12,7 +12,7 @@ */ struct ScaleFields { - amrex::Real m_do_scale; + bool m_do_scale; amrex::Real m_dt; amrex::Real m_z_plane_previous; amrex::Real m_vz_ave_boosted; @@ -41,7 +41,7 @@ struct ScaleFields { using namespace amrex::literals; - if (not m_do_scale) return; + if (m_do_scale != 0.0_rt) return; // Scale the fields of particles about to cross the injection plane. // This only approximates what should be happening. The particles diff --git a/Source/Particles/Make.package b/Source/Particles/Make.package index 5537fe8196e..31f49c1ec5e 100644 --- a/Source/Particles/Make.package +++ b/Source/Particles/Make.package @@ -13,5 +13,6 @@ include $(WARPX_HOME)/Source/Particles/ParticleCreation/Make.package include $(WARPX_HOME)/Source/Particles/ElementaryProcess/Make.package include $(WARPX_HOME)/Source/Particles/Collision/Make.package include $(WARPX_HOME)/Source/Particles/Filter/Make.package +include $(WARPX_HOME)/Source/Particles/Resampling/Make.package VPATH_LOCATIONS += $(WARPX_HOME)/Source/Particles diff --git a/Source/Particles/MultiParticleContainer.H b/Source/Particles/MultiParticleContainer.H index cec6678877a..7b34a0458cf 100644 --- a/Source/Particles/MultiParticleContainer.H +++ b/Source/Particles/MultiParticleContainer.H @@ -125,7 +125,14 @@ public: const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez, const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz); - void doCoulombCollisions (); + void doCoulombCollisions (amrex::Real cur_time); + + /** + * \brief This function loops over all species and performs resampling if appropriate. + * + * @param[in] timestep the current timestep. + */ + void doResampling (const int timestep); #ifdef WARPX_QED /** If Schwinger process is activated, this function is called at every @@ -138,10 +145,6 @@ public: void doQEDSchwinger (); #endif - void Checkpoint (const std::string& dir) const; - - void WritePlotFile (const std::string& dir) const; - void Restart (const std::string& dir); void PostRestart (); @@ -156,6 +159,10 @@ public: void RedistributeLocal (const int num_ghost); + /** Apply BC. For now, just discard particles outside the domain, regardless + * of the whole simulation BC. */ + void ApplyBoundaryConditions (); + amrex::Vector NumberOfParticlesInGrid(int lev) const; void Increment (amrex::MultiFab& mf, int lev); @@ -279,6 +286,9 @@ protected: std::vector species_types; + /** Whether to absorb particles exiting the domain */ + ParticleBC m_boundary_conditions = ParticleBC::none; + template amrex::MFItInfo getMFItInfo (const WarpXParticleContainer& pc_src, Args const&... pc_dsts) const noexcept @@ -316,9 +326,13 @@ protected: int m_nspecies_breit_wheeler = 0; //________ + static constexpr auto m_default_quantum_sync_photon_creation_energy_threshold = + static_cast( + 2.0 * PhysConst::m_e * PhysConst::c * PhysConst::c ); /*!< Default value of the energy threshold for photon creation in Quantum Synchrotron process.*/ + + amrex::ParticleReal m_quantum_sync_photon_creation_energy_threshold = - static_cast( - 2.0 * PhysConst::m_e * PhysConst::c * PhysConst::c ); /*!< Energy threshold for photon creation in Quantum Synchrotron process.*/ + m_default_quantum_sync_photon_creation_energy_threshold; /*!< Energy threshold for photon creation in Quantum Synchrotron process.*/ /** * Returns the number of species having Quantum Synchrotron process enabled @@ -390,7 +404,7 @@ private: std::vector map_species_back_transformed_diagnostics; int do_back_transformed_diagnostics = 0; - void MFItInfoCheckTiling(const WarpXParticleContainer& pc_src) const noexcept + void MFItInfoCheckTiling(const WarpXParticleContainer& /*pc_src*/) const noexcept { return; } diff --git a/Source/Particles/MultiParticleContainer.cpp b/Source/Particles/MultiParticleContainer.cpp index e93b56c6fb1..a672b068381 100644 --- a/Source/Particles/MultiParticleContainer.cpp +++ b/Source/Particles/MultiParticleContainer.cpp @@ -24,7 +24,7 @@ #include #include #include - +#include using namespace amrex; @@ -33,8 +33,8 @@ MultiParticleContainer::MultiParticleContainer (AmrCore* amr_core) ReadParameters(); - auto const nspecies = species_names.size(); - auto const nlasers = lasers_names.size(); + auto const nspecies = static_cast(species_names.size()); + auto const nlasers = static_cast(lasers_names.size()); allcontainers.resize(nspecies + nlasers); for (int i = 0; i < nspecies; ++i) { @@ -74,7 +74,7 @@ MultiParticleContainer::MultiParticleContainer (AmrCore* amr_core) // collision auto const ncollisions = collision_names.size(); allcollisions.resize(ncollisions); - for (int i = 0; i < ncollisions; ++i) { + for (int i = 0; i < static_cast(ncollisions); ++i) { allcollisions[i].reset (new CollisionType(species_names, collision_names[i])); } @@ -256,10 +256,23 @@ MultiParticleContainer::ReadParameters () pc.queryarr("collision_names", collision_names); } - pp.query("use_fdtd_nci_corr", WarpX::use_fdtd_nci_corr); +#ifdef WARPX_DIM_RZ + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(WarpX::use_fdtd_nci_corr==0, + "ERROR: use_fdtd_nci_corr is not supported in RZ"); +#endif pp.query("galerkin_interpolation", WarpX::galerkin_interpolation); + std::string boundary_conditions = "none"; + pp.query("boundary_conditions", boundary_conditions); + if (boundary_conditions == "none"){ + m_boundary_conditions = ParticleBC::none; + } else if (boundary_conditions == "absorbing"){ + m_boundary_conditions = ParticleBC::absorbing; + } else { + amrex::Abort("unknown particle BC type"); + } + ParmParse ppl("lasers"); ppl.queryarr("names", lasers_names); @@ -395,6 +408,14 @@ MultiParticleContainer::RedistributeLocal (const int num_ghost) } } +void +MultiParticleContainer::ApplyBoundaryConditions () +{ + for (auto& pc : allcontainers) { + pc->ApplyBoundaryConditions(m_boundary_conditions); + } +} + Vector MultiParticleContainer::NumberOfParticlesInGrid (int lev) const { @@ -452,7 +473,7 @@ ::GetLabFrameData (const std::string& /*snapshot_name*/, Vector& parts) const { - WARPX_PROFILE("MultiParticleContainer::GetLabFrameData"); + WARPX_PROFILE("MultiParticleContainer::GetLabFrameData()"); // Loop over particle species for (int i = 0; i < nspecies_back_transformed_diagnostics; ++i){ @@ -554,7 +575,7 @@ MultiParticleContainer::doContinuousInjection () const void MultiParticleContainer::mapSpeciesProduct () { - for (int i=0; i(species_names.size()); i++){ auto& pc = allcontainers[i]; // If species pc has ionization on, find species with name // pc->ionization_product_name and store its ID into @@ -599,10 +620,10 @@ MultiParticleContainer::mapSpeciesProduct () int MultiParticleContainer::getSpeciesID (std::string product_str) const { - int i_product; + int i_product = 0; bool found = 0; // Loop over species - for (int i=0; i(species_names.size()); i++){ // If species name matches, store its ID // into i_product if (species_names[i] == product_str){ @@ -629,7 +650,7 @@ MultiParticleContainer::doFieldIonization (int lev, const MultiFab& By, const MultiFab& Bz) { - WARPX_PROFILE("MPC::doFieldIonization"); + WARPX_PROFILE("MultiParticleContainer::doFieldIonization()"); // Loop over all species. // Ionized particles in pc_source create particles in pc_product @@ -672,12 +693,16 @@ MultiParticleContainer::doFieldIonization (int lev, } void -MultiParticleContainer::doCoulombCollisions () +MultiParticleContainer::doCoulombCollisions ( Real cur_time ) { - WARPX_PROFILE("MPC::doCoulombCollisions"); + WARPX_PROFILE("MultiParticleContainer::doCoulombCollisions()"); for( auto const& collision : allcollisions ) { + + const Real dt = WarpX::GetInstance().getdt(0); + if ( int(std::floor(cur_time/dt)) % collision->m_ndt != 0 ) continue; + auto& species1 = allcontainers[ collision->m_species1_index ]; auto& species2 = allcontainers[ collision->m_species2_index ]; @@ -698,16 +723,28 @@ MultiParticleContainer::doCoulombCollisions () CollisionType::doCoulombCollisionsWithinTile ( lev, mfi, species1, species2, collision->m_isSameSpecies, - collision->m_CoulombLog ); + collision->m_CoulombLog, + collision->m_ndt ); } } } } +void MultiParticleContainer::doResampling (const int timestep) +{ + for (auto& pc : allcontainers) + { + // do_resampling can only be true for PhysicalParticleContainers + if (!pc->do_resampling){ continue; } + + pc->resample(timestep); + } +} + void MultiParticleContainer::CheckIonizationProductSpecies() { - for (int i=0; i(species_names.size()); i++){ if (allcontainers[i]->do_field_ionization){ AMREX_ALWAYS_ASSERT_WITH_MESSAGE( i != allcontainers[i]->ionization_product, @@ -753,7 +790,9 @@ void MultiParticleContainer::InitQuantumSync () //If specified, use a user-defined energy threshold for photon creaction ParticleReal temp; + constexpr auto mec2 = PhysConst::c * PhysConst::c * PhysConst::m_e; if(pp.query("photon_creation_energy_threshold", temp)){ + temp *= mec2; m_quantum_sync_photon_creation_energy_threshold = temp; } else{ @@ -761,6 +800,13 @@ void MultiParticleContainer::InitQuantumSync () " for photon energy creaction threshold \n" ; } + // qs_minimum_chi_part is the minimum chi parameter to be + // considered for Synchrotron emission. If a lepton has chi < chi_min, + // the optical depth is not evolved and photon generation is ignored + amrex::Real qs_minimum_chi_part; + pp.get("chi_min", qs_minimum_chi_part); + + pp.query("lookup_table_mode", lookup_table_mode); if(lookup_table_mode.empty()){ amrex::Abort("Quantum Synchrotron table mode should be provided"); @@ -784,11 +830,12 @@ void MultiParticleContainer::InitQuantumSync () Vector table_data; ParallelDescriptor::ReadAndBcastFile(load_table_name, table_data); ParallelDescriptor::Barrier(); - m_shr_p_qs_engine->init_lookup_tables_from_raw_data(table_data); + m_shr_p_qs_engine->init_lookup_tables_from_raw_data(table_data, + qs_minimum_chi_part); } - else if(lookup_table_mode == "dummy_builtin"){ - amrex::Print() << "Built-in Quantum Synchrotron dummy table will be used. \n" ; - m_shr_p_qs_engine->init_dummy_tables(); + else if(lookup_table_mode == "builtin"){ + amrex::Print() << "Built-in Quantum Synchrotron table will be used. \n" ; + m_shr_p_qs_engine->init_builtin_tables(qs_minimum_chi_part); } else{ amrex::Abort("Unknown Quantum Synchrotron table mode"); @@ -803,6 +850,14 @@ void MultiParticleContainer::InitBreitWheeler () { std::string lookup_table_mode; ParmParse pp("qed_bw"); + + // bw_minimum_chi_phot is the minimum chi parameter to be + // considered for pair production. If a photon has chi < chi_min, + // the optical depth is not evolved and photon generation is ignored + amrex::Real bw_minimum_chi_part; + if(!pp.query("chi_min", bw_minimum_chi_part)) + amrex::Abort("qed_bw.chi_min should be provided!"); + pp.query("lookup_table_mode", lookup_table_mode); if(lookup_table_mode.empty()){ amrex::Abort("Breit Wheeler table mode should be provided"); @@ -826,11 +881,12 @@ void MultiParticleContainer::InitBreitWheeler () Vector table_data; ParallelDescriptor::ReadAndBcastFile(load_table_name, table_data); ParallelDescriptor::Barrier(); - m_shr_p_bw_engine->init_lookup_tables_from_raw_data(table_data); + m_shr_p_bw_engine->init_lookup_tables_from_raw_data( + table_data, bw_minimum_chi_part); } - else if(lookup_table_mode == "dummy_builtin"){ - amrex::Print() << "Built-in Breit Wheeler dummy table will be used. \n" ; - m_shr_p_bw_engine->init_dummy_tables(); + else if(lookup_table_mode == "builtin"){ + amrex::Print() << "Built-in Breit Wheeler table will be used. \n" ; + m_shr_p_bw_engine->init_builtin_tables(bw_minimum_chi_part); } else{ amrex::Abort("Unknown Breit Wheeler table mode"); @@ -850,15 +906,14 @@ MultiParticleContainer::QuantumSyncGenerateTable () if(table_name.empty()) amrex::Abort("qed_qs.save_table_in should be provided!"); - if(ParallelDescriptor::IOProcessor()){ - PicsarQuantumSynchrotronCtrl ctrl; - int t_int; + // qs_minimum_chi_part is the minimum chi parameter to be + // considered for Synchrotron emission. If a lepton has chi < chi_min, + // the optical depth is not evolved and photon generation is ignored + amrex::Real qs_minimum_chi_part; + pp.get("chi_min", qs_minimum_chi_part); - // Engine paramenter: chi_part_min is the minium chi parameter to be - // considered by the engine. If a lepton has chi < chi_part_min, - // the optical depth is not evolved and photon generation is ignored - if(!pp.query("chi_min", ctrl.chi_part_min)) - amrex::Abort("qed_qs.chi_min should be provided!"); + if(ParallelDescriptor::IOProcessor()){ + PicsarQuantumSyncCtrl ctrl; //==Table parameters== @@ -866,20 +921,16 @@ MultiParticleContainer::QuantumSyncGenerateTable () //These parameters are used to pre-compute a function //which appears in the evolution of the optical depth - //Minimun chi for the table. If a lepton has chi < chi_part_tdndt_min, - //chi is considered as it were equal to chi_part_tdndt_min - if(!pp.query("tab_dndt_chi_min", ctrl.chi_part_tdndt_min)) - amrex::Abort("qed_qs.tab_dndt_chi_min should be provided!"); + //Minimun chi for the table. If a lepton has chi < tab_dndt_chi_min, + //chi is considered as if it were equal to tab_dndt_chi_min + pp.get("tab_dndt_chi_min", ctrl.dndt_params.chi_part_min); - //Maximum chi for the table. If a lepton has chi > chi_part_tdndt_max, - //chi is considered as it were equal to chi_part_tdndt_max - if(!pp.query("tab_dndt_chi_max", ctrl.chi_part_tdndt_max)) - amrex::Abort("qed_qs.tab_dndt_chi_max should be provided!"); + //Maximum chi for the table. If a lepton has chi > tab_dndt_chi_max, + //chi is considered as if it were equal to tab_dndt_chi_max + pp.get("tab_dndt_chi_max", ctrl.dndt_params.chi_part_max); //How many points should be used for chi in the table - if(!pp.query("tab_dndt_how_many", t_int)) - amrex::Abort("qed_qs.tab_dndt_how_many should be provided!"); - ctrl.chi_part_tdndt_how_many = t_int; + pp.get("tab_dndt_how_many", ctrl.dndt_params.chi_part_how_many); //------ //--- sub-table 2 (2D) @@ -887,32 +938,31 @@ MultiParticleContainer::QuantumSyncGenerateTable () //which is used to extract the properties of the generated //photons. - //Minimun chi for the table. If a lepton has chi < chi_part_tem_min, - //chi is considered as it were equal to chi_part_tem_min - if(!pp.query("tab_em_chi_min", ctrl.chi_part_tem_min)) - amrex::Abort("qed_qs.tab_em_chi_min should be provided!"); + //Minimun chi for the table. If a lepton has chi < tab_em_chi_min, + //chi is considered as if it were equal to tab_em_chi_min + pp.get("tab_em_chi_min", ctrl.phot_em_params.chi_part_min); - //Maximum chi for the table. If a lepton has chi > chi_part_tem_max, - //chi is considered as it were equal to chi_part_tem_max - if(!pp.query("tab_em_chi_max", ctrl.chi_part_tem_max)) - amrex::Abort("qed_qs.tab_em_chi_max should be provided!"); + //Maximum chi for the table. If a lepton has chi > tab_em_chi_max, + //chi is considered as if it were equal to tab_em_chi_max + pp.get("tab_em_chi_max", ctrl.phot_em_params.chi_part_max); //How many points should be used for chi in the table - if(!pp.query("tab_em_chi_how_many", t_int)) - amrex::Abort("qed_qs.tab_em_chi_how_many should be provided!"); - ctrl.chi_part_tem_how_many = t_int; - - //The other axis of the table is a cumulative probability distribution - //(corresponding to different energies of the generated particles) - //This parameter is the number of different points to consider - if(!pp.query("tab_em_prob_how_many", t_int)) - amrex::Abort("qed_qs.tab_em_prob_how_many should be provided!"); - ctrl.prob_tem_how_many = t_int; + pp.get("tab_em_chi_how_many", ctrl.phot_em_params.chi_part_how_many); + + //The other axis of the table is the ratio between the quantum + //parameter of the emitted photon and the quantum parameter of the + //lepton. This parameter is the minimum ratio to consider for the table. + pp.get("tab_em_frac_min", ctrl.phot_em_params.frac_min); + + //This parameter is the number of different points to consider for the second + //axis + pp.get("tab_em_frac_how_many", ctrl.phot_em_params.frac_how_many); //==================== - m_shr_p_qs_engine->compute_lookup_tables(ctrl); + m_shr_p_qs_engine->compute_lookup_tables(ctrl, qs_minimum_chi_part); + const auto data = m_shr_p_qs_engine->export_lookup_tables_data(); WarpXUtilIO::WriteBinaryDataOnFile(table_name, - m_shr_p_qs_engine->export_lookup_tables_data()); + Vector{data.begin(), data.end()}); } ParallelDescriptor::Barrier(); @@ -923,7 +973,8 @@ MultiParticleContainer::QuantumSyncGenerateTable () //No need to initialize from raw data for the processor that //has just generated the table if(!ParallelDescriptor::IOProcessor()){ - m_shr_p_qs_engine->init_lookup_tables_from_raw_data(table_data); + m_shr_p_qs_engine->init_lookup_tables_from_raw_data( + table_data, qs_minimum_chi_part); } } @@ -936,15 +987,14 @@ MultiParticleContainer::BreitWheelerGenerateTable () if(table_name.empty()) amrex::Abort("qed_bw.save_table_in should be provided!"); + // bw_minimum_chi_phot is the minimum chi parameter to be + // considered for pair production. If a photon has chi < chi_min, + // the optical depth is not evolved and photon generation is ignored + amrex::Real bw_minimum_chi_part; + pp.get("chi_min", bw_minimum_chi_part); + if(ParallelDescriptor::IOProcessor()){ PicsarBreitWheelerCtrl ctrl; - int t_int; - - // Engine paramenter: chi_phot_min is the minium chi parameter to be - // considered by the engine. If a photon has chi < chi_phot_min, - // the optical depth is not evolved and pair generation is ignored - if(!pp.query("chi_min", ctrl.chi_phot_min)) - amrex::Abort("qed_bw.chi_min should be provided!"); //==Table parameters== @@ -952,20 +1002,16 @@ MultiParticleContainer::BreitWheelerGenerateTable () //These parameters are used to pre-compute a function //which appears in the evolution of the optical depth - //Minimun chi for the table. If a photon has chi < chi_phot_tdndt_min, + //Minimun chi for the table. If a photon has chi < tab_dndt_chi_min, //an analytical approximation is used. - if(!pp.query("tab_dndt_chi_min", ctrl.chi_phot_tdndt_min)) - amrex::Abort("qed_bw.tab_dndt_chi_min should be provided!"); + pp.get("tab_dndt_chi_min", ctrl.dndt_params.chi_phot_min); - //Maximum chi for the table. If a photon has chi > chi_phot_tdndt_min, + //Maximum chi for the table. If a photon has chi > tab_dndt_chi_max, //an analytical approximation is used. - if(!pp.query("tab_dndt_chi_max", ctrl.chi_phot_tdndt_max)) - amrex::Abort("qed_bw.tab_dndt_chi_max should be provided!"); + pp.get("tab_dndt_chi_max", ctrl.dndt_params.chi_phot_max); //How many points should be used for chi in the table - if(!pp.query("tab_dndt_how_many", t_int)) - amrex::Abort("qed_bw.tab_dndt_how_many should be provided!"); - ctrl.chi_phot_tdndt_how_many = t_int; + pp.get("tab_dndt_how_many", ctrl.dndt_params.chi_phot_how_many); //------ //--- sub-table 2 (2D) @@ -973,32 +1019,27 @@ MultiParticleContainer::BreitWheelerGenerateTable () //which is used to extract the properties of the generated //particles. - //Minimun chi for the table. If a photon has chi < chi_phot_tpair_min + //Minimun chi for the table. If a photon has chi < tab_pair_chi_min //chi is considered as it were equal to chi_phot_tpair_min - if(!pp.query("tab_pair_chi_min", ctrl.chi_phot_tpair_min)) - amrex::Abort("qed_bw.tab_pair_chi_min should be provided!"); + pp.get("tab_pair_chi_min", ctrl.pair_prod_params.chi_phot_min); - //Maximum chi for the table. If a photon has chi > chi_phot_tpair_max + //Maximum chi for the table. If a photon has chi > tab_pair_chi_max //chi is considered as it were equal to chi_phot_tpair_max - if(!pp.query("tab_pair_chi_max", ctrl.chi_phot_tpair_max)) - amrex::Abort("qed_bw.tab_pair_chi_max should be provided!"); + pp.get("tab_pair_chi_max", ctrl.pair_prod_params.chi_phot_max); //How many points should be used for chi in the table - if(!pp.query("tab_pair_chi_how_many", t_int)) - amrex::Abort("qed_bw.tab_pair_chi_how_many should be provided!"); - ctrl.chi_phot_tpair_how_many = t_int; + pp.get("tab_pair_chi_how_many", ctrl.pair_prod_params.chi_phot_how_many); //The other axis of the table is the fraction of the initial energy //'taken away' by the most energetic particle of the pair. //This parameter is the number of different fractions to consider - if(!pp.query("tab_pair_frac_how_many", t_int)) - amrex::Abort("qed_bw.tab_pair_frac_how_many should be provided!"); - ctrl.chi_frac_tpair_how_many = t_int; + pp.get("tab_pair_frac_how_many", ctrl.pair_prod_params.frac_how_many); //==================== - m_shr_p_bw_engine->compute_lookup_tables(ctrl); + m_shr_p_bw_engine->compute_lookup_tables(ctrl, bw_minimum_chi_part); + const auto data = m_shr_p_bw_engine->export_lookup_tables_data(); WarpXUtilIO::WriteBinaryDataOnFile(table_name, - m_shr_p_bw_engine->export_lookup_tables_data()); + Vector{data.begin(), data.end()}); } ParallelDescriptor::Barrier(); @@ -1009,14 +1050,15 @@ MultiParticleContainer::BreitWheelerGenerateTable () //No need to initialize from raw data for the processor that //has just generated the table if(!ParallelDescriptor::IOProcessor()){ - m_shr_p_bw_engine->init_lookup_tables_from_raw_data(table_data); + m_shr_p_bw_engine->init_lookup_tables_from_raw_data( + table_data, bw_minimum_chi_part); } } void MultiParticleContainer::doQEDSchwinger () { - WARPX_PROFILE("MPC::doQEDSchwinger"); + WARPX_PROFILE("MultiParticleContainer::doQEDSchwinger()"); if (!m_do_qed_schwinger) {return;} @@ -1036,22 +1078,20 @@ MultiParticleContainer::doQEDSchwinger () amrex::Abort("Schwinger process not implemented in rz geometry"); #endif -#ifdef AMREX_USE_FLOAT - amrex::Abort("Schwinger process not implemented in single precision"); -#endif - -// Get cell volume multiplied by temporal step. In 2D the transverse size is +// Get cell volume. In 2D the transverse size is // chosen by the user in the input file. amrex::Geometry const & geom = warpx.Geom(level_0); - auto domain_box = geom.Domain(); #if (AMREX_SPACEDIM == 2) - const auto dVdt = geom.CellSize(0) * geom.CellSize(1) - * m_qed_schwinger_y_size * warpx.getdt(level_0); + const auto dV = geom.CellSize(0) * geom.CellSize(1) + * m_qed_schwinger_y_size; #elif (AMREX_SPACEDIM == 3) - const auto dVdt = geom.CellSize(0) * geom.CellSize(1) - * geom.CellSize(2) * warpx.getdt(level_0); + const auto dV = geom.CellSize(0) * geom.CellSize(1) + * geom.CellSize(2); #endif + // Get the temporal step + const auto dt = warpx.getdt(level_0); + auto& pc_product_ele = allcontainers[m_qed_schwinger_ele_product]; auto& pc_product_pos = @@ -1064,17 +1104,11 @@ MultiParticleContainer::doQEDSchwinger () const MultiFab & By = warpx.getBfield(level_0,1); const MultiFab & Bz = warpx.getBfield(level_0,2); - MFItInfo info; - if (TilingIfNotGPU()) { - info.EnableTiling(); - } #ifdef _OPENMP - info.SetDynamic(WarpX::do_dynamic_scheduling); -#pragma omp parallel +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) #endif - - for (MFIter mfi(Ex, info); mfi.isValid(); ++mfi ) - { + for (MFIter mfi(Ex, TilingIfNotGPU()); mfi.isValid(); ++mfi ) + { // Make the box cell centered to avoid creating particles twice on the tile edges const Box& box = enclosedCells(mfi.nodaltilebox()); @@ -1098,7 +1132,8 @@ MultiParticleContainer::doQEDSchwinger () const auto np_pos_dst = dst_pos_tile.numParticles(); const auto Filter = SchwingerFilterFunc{ - m_qed_schwinger_threshold_poisson_gaussian,dVdt}; + m_qed_schwinger_threshold_poisson_gaussian, + dV, dt}; const SmartCreateFactory create_factory_ele(*pc_product_ele); const SmartCreateFactory create_factory_pos(*pc_product_pos); @@ -1127,7 +1162,7 @@ void MultiParticleContainer::doQedEvents (int lev, const MultiFab& By, const MultiFab& Bz) { - WARPX_PROFILE("MPC::doQedEvents"); + WARPX_PROFILE("MultiParticleContainer::doQedEvents()"); doQedBreitWheeler(lev, Ex, Ey, Ez, Bx, By, Bz); doQedQuantumSync(lev, Ex, Ey, Ez, Bx, By, Bz); @@ -1141,7 +1176,7 @@ void MultiParticleContainer::doQedBreitWheeler (int lev, const MultiFab& By, const MultiFab& Bz) { - WARPX_PROFILE("MPC::doQedBreitWheeler"); + WARPX_PROFILE("MultiParticleContainer::doQedBreitWheeler()"); // Loop over all species. // Photons undergoing Breit Wheeler process create electrons @@ -1208,7 +1243,7 @@ void MultiParticleContainer::doQedQuantumSync (int lev, const MultiFab& By, const MultiFab& Bz) { - WARPX_PROFILE("MPC::doQedEvents::doQedQuantumSync"); + WARPX_PROFILE("MultiParticleContainer::doQedQuantumSync()"); // Loop over all species. // Electrons or positrons undergoing Quantum photon emission process @@ -1267,7 +1302,7 @@ void MultiParticleContainer::doQedQuantumSync (int lev, void MultiParticleContainer::CheckQEDProductSpecies() { - auto const nspecies = species_names.size(); + auto const nspecies = static_cast(species_names.size()); for (int i=0; ihas_breit_wheeler()){ diff --git a/Source/Particles/ParticleCreation/DefaultInitialization.H b/Source/Particles/ParticleCreation/DefaultInitialization.H index 5763253e08d..026aac7b33e 100644 --- a/Source/Particles/ParticleCreation/DefaultInitialization.H +++ b/Source/Particles/ParticleCreation/DefaultInitialization.H @@ -53,13 +53,13 @@ static std::map initialization_policies = { }; AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE -amrex::ParticleReal initializeRealValue (const InitializationPolicy policy) noexcept +amrex::ParticleReal initializeRealValue (const InitializationPolicy policy, amrex::RandomEngine const& engine) noexcept { switch (policy) { case InitializationPolicy::Zero : return 0.0; case InitializationPolicy::One : return 1.0; case InitializationPolicy::RandomExp : { - return -log(amrex::Random()); + return -log(amrex::Random(engine)); } default : { amrex::Abort("Initialization Policy not recognized"); diff --git a/Source/Particles/ParticleCreation/FilterCopyTransform.H b/Source/Particles/ParticleCreation/FilterCopyTransform.H index 0cc8a7fe5ae..842bfad4cf4 100644 --- a/Source/Particles/ParticleCreation/FilterCopyTransform.H +++ b/Source/Particles/ParticleCreation/FilterCopyTransform.H @@ -59,14 +59,8 @@ Index filterCopyTransformParticles (DstTile& dst, SrcTile& src, Index* mask, Ind if (np == 0) return 0; Gpu::DeviceVector offsets(np); - Gpu::exclusive_scan(mask, mask+np, offsets.begin()); - - Index last_mask, last_offset; - Gpu::copyAsync(Gpu::deviceToHost, mask+np-1, mask + np, &last_mask); - Gpu::copyAsync(Gpu::deviceToHost, offsets.data()+np-1, offsets.data()+np, &last_offset); - - Gpu::streamSynchronize(); - const Index num_added = N * (last_mask + last_offset); + auto total = amrex::Scan::ExclusiveSum(np, mask, offsets.data()); + const Index num_added = N * total; dst.resize(std::max(dst_index + num_added, dst.numParticles())); const auto p_offsets = offsets.dataPtr(); @@ -74,17 +68,19 @@ Index filterCopyTransformParticles (DstTile& dst, SrcTile& src, Index* mask, Ind const auto src_data = src.getParticleTileData(); const auto dst_data = dst.getParticleTileData(); - AMREX_HOST_DEVICE_FOR_1D( np, i, + amrex::ParallelForRNG(np, + [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept { if (mask[i]) { - for (int j = 0; j < N; ++j) - copy(dst_data, src_data, i, N*p_offsets[i] + dst_index + j); + for (int j = 0; j < N; ++j) { + copy(dst_data, src_data, i, N*p_offsets[i] + dst_index + j, engine); + } transform(dst_data, src_data, i, N*p_offsets[i] + dst_index); } - }) + }); - Gpu::streamSynchronize(); + Gpu::synchronize(); return num_added; } @@ -140,10 +136,11 @@ Index filterCopyTransformParticles (DstTile& dst, SrcTile& src, Index dst_index, auto p_mask = mask.dataPtr(); const auto src_data = src.getParticleTileData(); - AMREX_HOST_DEVICE_FOR_1D(np, i, + amrex::ParallelForRNG(np, + [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept { - p_mask[i] = filter(src_data, i); - }) + p_mask[i] = filter(src_data, i, engine); + }); return filterCopyTransformParticles(dst, src, mask.dataPtr(), dst_index, std::forward(copy), @@ -205,14 +202,8 @@ Index filterCopyTransformParticles (DstTile& dst1, DstTile& dst2, SrcTile& src, if (np == 0) return 0; Gpu::DeviceVector offsets(np); - Gpu::exclusive_scan(mask, mask+np, offsets.begin()); - - Index last_mask, last_offset; - Gpu::copyAsync(Gpu::deviceToHost, mask+np-1, mask + np, &last_mask); - Gpu::copyAsync(Gpu::deviceToHost, offsets.data()+np-1, offsets.data()+np, &last_offset); - - Gpu::streamSynchronize(); - const Index num_added = N*(last_mask + last_offset); + auto total = amrex::Scan::ExclusiveSum(np, mask, offsets.data()); + const Index num_added = N * total; dst1.resize(std::max(dst1_index + num_added, dst1.numParticles())); dst2.resize(std::max(dst2_index + num_added, dst2.numParticles())); @@ -222,22 +213,23 @@ Index filterCopyTransformParticles (DstTile& dst1, DstTile& dst2, SrcTile& src, const auto dst1_data = dst1.getParticleTileData(); const auto dst2_data = dst2.getParticleTileData(); - AMREX_HOST_DEVICE_FOR_1D( np, i, + amrex::ParallelForRNG(np, + [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept { if (mask[i]) { for (int j = 0; j < N; ++j) { - copy1(dst1_data, src_data, i, N*p_offsets[i] + dst1_index + j); - copy2(dst2_data, src_data, i, N*p_offsets[i] + dst2_index + j); + copy1(dst1_data, src_data, i, N*p_offsets[i] + dst1_index + j, engine); + copy2(dst2_data, src_data, i, N*p_offsets[i] + dst2_index + j, engine); } transform(dst1_data, dst2_data, src_data, i, N*p_offsets[i] + dst1_index, N*p_offsets[i] + dst2_index); } - }) + }); - Gpu::streamSynchronize(); + Gpu::synchronize(); return num_added; } @@ -300,10 +292,11 @@ Index filterCopyTransformParticles (DstTile& dst1, DstTile& dst2, SrcTile& src, auto p_mask = mask.dataPtr(); const auto src_data = src.getParticleTileData(); - AMREX_HOST_DEVICE_FOR_1D(np, i, + amrex::ParallelForRNG(np, + [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) { - p_mask[i] = filter(src_data, i); - }) + p_mask[i] = filter(src_data, i, engine); + }); return filterCopyTransformParticles(dst1, dst2, src, mask.dataPtr(), dst1_index, dst2_index, diff --git a/Source/Particles/ParticleCreation/FilterCreateTransformFromFAB.H b/Source/Particles/ParticleCreation/FilterCreateTransformFromFAB.H index 3f7fe38703d..c249b95ec63 100644 --- a/Source/Particles/ParticleCreation/FilterCreateTransformFromFAB.H +++ b/Source/Particles/ParticleCreation/FilterCreateTransformFromFAB.H @@ -8,6 +8,9 @@ #ifndef FILTER_CREATE_TRANSFORM_FROM_FAB_H_ #define FILTER_CREATE_TRANSFORM_FROM_FAB_H_ +#include +#include + /** * \brief Apply a filter on a list of FABs, then create and apply a transform * operation to the particles depending on the output of the filter. @@ -64,20 +67,15 @@ Index filterCreateTransformFromFAB (DstTile& dst1, DstTile& dst2, const amrex::B #if (AMREX_SPACEDIM == 3) const Real zlo_global = geom.ProbLo(2); const Real dz = geom.CellSize(2); +#else + const Real zlo_global = 0.0; + const Real dz = 0.0; #endif const auto arrNumPartCreation = src_FAB->array(); Gpu::DeviceVector offsets(ncells); - Gpu::exclusive_scan(mask, mask+ncells, offsets.begin()); - - Index last_mask, last_offset; - Gpu::copyAsync(Gpu::deviceToHost, mask+ncells-1, mask + ncells, &last_mask); - Gpu::copyAsync(Gpu::deviceToHost, offsets.data()+ncells-1, - offsets.data()+ncells, &last_offset); - - Gpu::streamSynchronize(); - - const Index num_added = N*(last_mask + last_offset); + auto total = amrex::Scan::ExclusiveSum(ncells, mask, offsets.data()); + const Index num_added = N*total; dst1.resize(std::max(dst1_index + num_added, dst1.numParticles())); dst2.resize(std::max(dst2_index + num_added, dst2.numParticles())); @@ -86,39 +84,38 @@ Index filterCreateTransformFromFAB (DstTile& dst1, DstTile& dst2, const amrex::B const auto dst1_data = dst1.getParticleTileData(); const auto dst2_data = dst2.getParticleTileData(); + constexpr int spacedim = AMREX_SPACEDIM; + // For loop over all cells in the box. If mask is true in the given cell, // we create the particles in the cell and apply a transform function to the // created particles. - AMREX_HOST_DEVICE_FOR_1D( ncells, i, + amrex::ParallelForRNG(ncells, + [=] AMREX_GPU_DEVICE (int i, amrex::RandomEngine const& engine) noexcept { if (mask[i]) { const IntVect iv = box.atOffset(i); const int j = iv[0]; const int k = iv[1]; - // Currently all particles are created on nodes. This makes it useless - // to use N>1 (for now). + const int l = (spacedim == 3) ? iv[2] : 0; + + // Currently all particles are created on nodes. This makes it useless + // to use N>1 (for now). const Real x = xlo_global + j*dx; -#if (AMREX_SPACEDIM == 3) - const int l = iv[2]; - const Real y = ylo_global + k*dy; - const Real z = zlo_global + l*dz; -#else - const int l = 0; - const int y = 0; - const Real z = ylo_global + k*dy; -#endif + const Real y = (spacedim == 3) ? ylo_global + k*dy : 0.0_rt; + const Real z = (spacedim == 3) ? zlo_global + l*dz : ylo_global + k*dy; + for (int n = 0; n < N; ++n) { - create1(dst1_data, N*p_offsets[i] + dst1_index + n, x, y, z); - create2(dst2_data, N*p_offsets[i] + dst2_index + n, x, y, z); + create1(dst1_data, N*p_offsets[i] + dst1_index + n, engine, x, y, z); + create2(dst2_data, N*p_offsets[i] + dst2_index + n, engine, x, y, z); } transform(dst1_data, dst2_data, N*p_offsets[i] + dst1_index, N*p_offsets[i] + dst2_index, N, arrNumPartCreation(j,k,l)); } }); - Gpu::streamSynchronize(); + Gpu::synchronize(); return num_added; } @@ -181,8 +178,9 @@ Index filterCreateTransformFromFAB (DstTile& dst1, DstTile& dst2, const amrex::B // for loop over all cells in the box. We apply the filter function to each cell // and store the result in arrNumPartCreation. If the result is strictly greater than // 0, the mask is set to true at the given cell position. - amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k){ - arrNumPartCreation(i,j,k) = filter(src_FABs,i,j,k); + amrex::ParallelForRNG(box, + [=] AMREX_GPU_DEVICE (int i, int j, int k, amrex::RandomEngine const& engine){ + arrNumPartCreation(i,j,k) = filter(src_FABs,i,j,k,engine); const IntVect iv(AMREX_D_DECL(i,j,k)); const auto mask_position = box.index(iv); p_mask[mask_position] = (arrNumPartCreation(i,j,k) > 0); diff --git a/Source/Particles/ParticleCreation/SmartCopy.H b/Source/Particles/ParticleCreation/SmartCopy.H index 735d5b6fbc4..c946e265a4d 100644 --- a/Source/Particles/ParticleCreation/SmartCopy.H +++ b/Source/Particles/ParticleCreation/SmartCopy.H @@ -45,16 +45,17 @@ struct SmartCopy template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - void operator() (DstData& dst, const SrcData& src, int i_src, int i_dst) const noexcept + void operator() (DstData& dst, const SrcData& src, int i_src, int i_dst, + amrex::RandomEngine const& engine) const noexcept { // the particle struct is always copied over dst.m_aos[i_dst] = src.m_aos[i_src]; // initialize the real components for (int j = 0; j < DstData::NAR; ++j) - dst.m_rdata[j][i_dst] = initializeRealValue(m_policy_real[j]); + dst.m_rdata[j][i_dst] = initializeRealValue(m_policy_real[j], engine); for (int j = 0; j < dst.m_num_runtime_real; ++j) - dst.m_runtime_rdata[j][i_dst] = initializeRealValue(m_policy_real[j+DstData::NAR]); + dst.m_runtime_rdata[j][i_dst] = initializeRealValue(m_policy_real[j+DstData::NAR], engine); // initialize the int components for (int j = 0; j < DstData::NAI; ++j) diff --git a/Source/Particles/ParticleCreation/SmartCreate.H b/Source/Particles/ParticleCreation/SmartCreate.H index 39408f403dd..4f395f9b04f 100644 --- a/Source/Particles/ParticleCreation/SmartCreate.H +++ b/Source/Particles/ParticleCreation/SmartCreate.H @@ -13,6 +13,7 @@ #include #include #include +#include /** * \brief This is a functor for performing a "smart create" that works @@ -32,12 +33,13 @@ struct SmartCreate { const InitializationPolicy* m_policy_real; const InitializationPolicy* m_policy_int; - const int m_weight_index; + const int m_weight_index = 0; template AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void operator() ( PartData& prt, const int i_prt, + amrex::RandomEngine const& engine, const amrex::Real x = 0.0, const amrex::Real y = 0.0, const amrex::Real z = 0.0, @@ -50,6 +52,7 @@ struct SmartCreate prt.m_aos[i_prt].pos(2) = z; #elif (AMREX_SPACEDIM == 2) prt.m_aos[i_prt].pos(1) = z; + amrex::ignore_unused(y); #endif prt.m_aos[i_prt].cpu() = cpu; @@ -57,9 +60,9 @@ struct SmartCreate // initialize the real components for (int j = 0; j < PartData::NAR; ++j) - prt.m_rdata[j][i_prt] = initializeRealValue(m_policy_real[j]); + prt.m_rdata[j][i_prt] = initializeRealValue(m_policy_real[j], engine); for (int j = 0; j < prt.m_num_runtime_real; ++j) - prt.m_runtime_rdata[j][i_prt] = initializeRealValue(m_policy_real[j+PartData::NAR]); + prt.m_runtime_rdata[j][i_prt] = initializeRealValue(m_policy_real[j+PartData::NAR], engine); // initialize the int components for (int j = 0; j < PartData::NAI; ++j) diff --git a/Source/Particles/ParticleCreation/SmartUtils.H b/Source/Particles/ParticleCreation/SmartUtils.H index 48988201c6b..84f5354228a 100644 --- a/Source/Particles/ParticleCreation/SmartUtils.H +++ b/Source/Particles/ParticleCreation/SmartUtils.H @@ -39,7 +39,7 @@ SmartCopyTag getSmartCopyTag (const NameMap& src, const NameMap& dst) noexcept; template void setNewParticleIDs (PTile& ptile, int old_size, int num_added) { - int pid; + amrex::Long pid; #ifdef _OPENMP #pragma omp critical (ionization_nextid) #endif diff --git a/Source/Particles/PhotonParticleContainer.H b/Source/Particles/PhotonParticleContainer.H index 1291f2cf944..52e973f2b90 100644 --- a/Source/Particles/PhotonParticleContainer.H +++ b/Source/Particles/PhotonParticleContainer.H @@ -75,32 +75,32 @@ public: DtType a_dt_type) override; // Do nothing - virtual void PushP (int lev, - amrex::Real dt, - const amrex::MultiFab& Ex, - const amrex::MultiFab& Ey, - const amrex::MultiFab& Ez, - const amrex::MultiFab& Bx, - const amrex::MultiFab& By, - const amrex::MultiFab& Bz) override {} + virtual void PushP (int /*lev*/, + amrex::Real /*dt*/, + const amrex::MultiFab& /*Ex*/, + const amrex::MultiFab& /*Ey*/, + const amrex::MultiFab& /*Ez*/, + const amrex::MultiFab& /*Bx*/, + const amrex::MultiFab& /*By*/, + const amrex::MultiFab& /*Bz*/) override {} // DepositCurrent should do nothing for photons - virtual void DepositCurrent(WarpXParIter& pti, - RealVector& wp, - RealVector& uxp, - RealVector& uyp, - RealVector& uzp, - const int * const ion_lev, - amrex::MultiFab* jx, - amrex::MultiFab* jy, - amrex::MultiFab* jz, - const long offset, - const long np_to_depose, - int thread_num, - int lev, - int depos_lev, - amrex::Real dt) override {} + virtual void DepositCurrent(WarpXParIter& /*pti*/, + RealVector& /*wp*/, + RealVector& /*uxp*/, + RealVector& /*uyp*/, + RealVector& /*uzp*/, + const int * const /*ion_lev*/, + amrex::MultiFab* /*jx*/, + amrex::MultiFab* /*jy*/, + amrex::MultiFab* /*jz*/, + const long /*offset*/, + const long /*np_to_depose*/, + int /*thread_num*/, + int /*lev*/, + int /*depos_lev*/, + amrex::Real /*dt*/) override {} }; #endif // #ifndef WARPX_PhotonParticleContainer_H_ diff --git a/Source/Particles/PhotonParticleContainer.cpp b/Source/Particles/PhotonParticleContainer.cpp index 0e3d8073330..9eee508f10e 100644 --- a/Source/Particles/PhotonParticleContainer.cpp +++ b/Source/Particles/PhotonParticleContainer.cpp @@ -102,8 +102,6 @@ PhotonParticleContainer::PushPX (WarpXParIter& pti, ParticleReal* const AMREX_RESTRICT uz = attribs[PIdx::uz].dataPtr(); #ifdef WARPX_QED - AMREX_ASSERT(has_breit_wheeler()); - BreitWheelerEvolveOpticalDepth evolve_opt; amrex::Real* AMREX_RESTRICT p_optical_depth_BW = nullptr; const bool local_has_breit_wheeler = has_breit_wheeler(); @@ -111,8 +109,6 @@ PhotonParticleContainer::PushPX (WarpXParIter& pti, evolve_opt = m_shr_p_bw_engine->build_evolve_functor(); p_optical_depth_BW = pti.GetAttribs(particle_comps["optical_depth_BW"]).dataPtr(); } - - const auto me = PhysConst::m_e; #endif auto copyAttribs = CopyParticleAttribs(pti, tmp_particle_data); @@ -129,7 +125,10 @@ PhotonParticleContainer::PushPX (WarpXParIter& pti, amrex::Real cur_time = WarpX::GetInstance().gett_new(lev); const auto& time_of_last_gal_shift = WarpX::GetInstance().time_of_last_gal_shift; amrex::Real time_shift = (cur_time - time_of_last_gal_shift); - amrex::Array galilean_shift = { v_galilean[0]*time_shift, v_galilean[1]*time_shift, v_galilean[2]*time_shift }; + amrex::Array galilean_shift = { + m_v_galilean[0]*time_shift, + m_v_galilean[1]*time_shift, + m_v_galilean[2]*time_shift }; const std::array& xyzmin = WarpX::LowerCorner(box, galilean_shift, gather_lev); const Dim3 lo = lbound(box); @@ -164,11 +163,8 @@ PhotonParticleContainer::PushPX (WarpXParIter& pti, ParticleReal x, y, z; GetPosition(i, x, y, z); - amrex::ParticleReal Exp, Eyp, Ezp; - getExternalE(i, Exp, Eyp, Ezp); - - amrex::ParticleReal Bxp, Byp, Bzp; - getExternalB(i, Bxp, Byp, Bzp); + amrex::ParticleReal Exp=0, Eyp=0, Ezp=0; + amrex::ParticleReal Bxp=0, Byp=0, Bzp=0; if(!t_do_not_gather){ // first gather E and B to the particle positions @@ -178,15 +174,13 @@ PhotonParticleContainer::PushPX (WarpXParIter& pti, dx_arr, xyzmin_arr, lo, n_rz_azimuthal_modes, nox, galerkin_interpolation); } + getExternalE(i, Exp, Eyp, Ezp); + getExternalB(i, Bxp, Byp, Bzp); #ifdef WARPX_QED if (local_has_breit_wheeler) { - const ParticleReal px = me * ux[i]; - const ParticleReal py = me * uy[i]; - const ParticleReal pz = me * uz[i]; - - bool has_event_happened = evolve_opt(px, py, pz, Exp, Eyp, Ezp, Bxp, Byp, Bzp, - dt, p_optical_depth_BW[i]); + evolve_opt(ux[i], uy[i], uz[i], Exp, Eyp, Ezp, Bxp, Byp, Bzp, + dt, p_optical_depth_BW[i]); } #endif @@ -210,8 +204,7 @@ PhotonParticleContainer::Evolve (int lev, Real t, Real dt, DtType /*a_dt_type*/) { // This does gather, push and depose. - // Push and depose have been re-written for photon, - // so they do not do anything. + // Push and depose have been re-written for photons PhysicalParticleContainer::Evolve (lev, Ex, Ey, Ez, Bx, By, Bz, diff --git a/Source/Particles/PhysicalParticleContainer.H b/Source/Particles/PhysicalParticleContainer.H index 3035602854e..036edf6d217 100644 --- a/Source/Particles/PhysicalParticleContainer.H +++ b/Source/Particles/PhysicalParticleContainer.H @@ -280,6 +280,15 @@ public: amrex::FArrayBox const * & ezfab, amrex::FArrayBox const * & bxfab, amrex::FArrayBox const * & byfab, amrex::FArrayBox const * & bzfab); + /** + * \brief This function determines if resampling should be done for the current species, and + * if so, performs the resampling. + * + * @param[in] resampler the Resampling object used for resampling. + * @param[in] timestep the current timestep. + */ + void resample (const int timestep) override final; + #ifdef WARPX_QED //Functions decleared in WarpXParticleContainer.H //containers for which QED processes could be relevant @@ -334,6 +343,8 @@ protected: bool boost_adjust_transverse_positions = false; bool do_backward_propagation = false; + Resampling m_resampler; + // Inject particles during the whole simulation void ContinuousInjection (const amrex::RealBox& injection_box) override; @@ -356,7 +367,6 @@ protected: // A smart pointer to an instance of a Breit Wheeler engine [photons only!] std::shared_ptr m_shr_p_bw_engine; - #endif }; diff --git a/Source/Particles/PhysicalParticleContainer.cpp b/Source/Particles/PhysicalParticleContainer.cpp index 4315f56e3ed..86254bca19e 100644 --- a/Source/Particles/PhysicalParticleContainer.cpp +++ b/Source/Particles/PhysicalParticleContainer.cpp @@ -27,6 +27,7 @@ #endif #include +#include #ifdef WARPX_USE_OPENPMD # include @@ -124,6 +125,9 @@ PhysicalParticleContainer::PhysicalParticleContainer (AmrCore* amr_core, int isp pp.query("do_field_ionization", do_field_ionization); + pp.query("do_resampling", do_resampling); + if (do_resampling) m_resampler = Resampling(species_name); + //check if Radiation Reaction is enabled and do consistency checks pp.query("do_classical_radiation_reaction", do_classical_radiation_reaction); //if the species is not a lepton, do_classical_radiation_reaction @@ -165,9 +169,9 @@ PhysicalParticleContainer::PhysicalParticleContainer (AmrCore* amr_core, int isp // Parse galilean velocity ParmParse ppsatd("psatd"); - ppsatd.query("v_galilean", v_galilean); + ppsatd.query("v_galilean", m_v_galilean); // Scale the velocity by the speed of light - for (int i=0; i<3; i++) v_galilean[i] *= PhysConst::c; + for (int i=0; i<3; i++) m_v_galilean[i] *= PhysConst::c; // build filter functors m_do_random_filter = pp.query("random_fraction", m_random_fraction); @@ -444,6 +448,9 @@ PhysicalParticleContainer::AddPlasmaFromFile(ParticleReal q_tot, particle_ux.dataPtr(), particle_uy.dataPtr(), particle_uz.dataPtr(), 1, particle_w.dataPtr(),1); #endif // WARPX_USE_OPENPMD + + ignore_unused(q_tot, z_shift); + return; } @@ -521,7 +528,7 @@ PhysicalParticleContainer::AddParticles (int lev) void PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) { - WARPX_PROFILE("PhysicalParticleContainer::AddPlasma"); + WARPX_PROFILE("PhysicalParticleContainer::AddPlasma()"); // If no part_realbox is provided, initialize particles in the whole domain const Geometry& geom = Geom(lev); @@ -648,8 +655,8 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) overlap_realbox.lo(2))}; // count the number of particles that each cell in overlap_box could add - Gpu::DeviceVector counts(overlap_box.numPts()+1, 0); - Gpu::DeviceVector offset(overlap_box.numPts()+1, 0); + Gpu::DeviceVector counts(overlap_box.numPts(), 0); + Gpu::DeviceVector offset(overlap_box.numPts()); auto pcounts = counts.data(); int lrrfac = rrfac; int lrefine_injection = refine_injection; @@ -691,20 +698,17 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) pcounts[index] = num_ppc; } } +#if (AMREX_SPACEDIM != 3) + amrex::ignore_unused(k); +#endif }); - Gpu::exclusive_scan(counts.begin(), counts.end(), offset.begin()); // Max number of new particles. All of them are created, // and invalid ones are then discarded - int max_new_particles; -#ifdef AMREX_USE_GPU - Gpu::dtoh_memcpy(&max_new_particles, offset.dataPtr()+overlap_box.numPts(), sizeof(int)); -#else - std::memcpy(&max_new_particles, offset.dataPtr()+overlap_box.numPts(), sizeof(int)); -#endif + int max_new_particles = Scan::ExclusiveSum(counts.size(), counts.data(), offset.data()); // Update NextID to include particles created in this function - int pid; + Long pid; #ifdef _OPENMP #pragma omp critical (add_plasma_nextid) #endif @@ -712,7 +716,7 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) pid = ParticleType::NextID(); ParticleType::NextID(pid+max_new_particles); } - WarpXUtilMsg::AlwaysAssert(static_cast(pid + max_new_particles) > 0, + WarpXUtilMsg::AlwaysAssert(static_cast(pid + max_new_particles) < LastParticleID, "ERROR: overflow on particle id numbers"); const int cpuid = ParallelDescriptor::MyProc(); @@ -734,15 +738,15 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) pa[ia] = soa.GetRealData(ia).data() + old_size; } - int* pi; + int* pi = nullptr; if (do_field_ionization) { pi = soa.GetIntData(particle_icomps["ionization_level"]).data() + old_size; } #ifdef WARPX_QED //Pointer to the optical depth component - amrex::Real* p_optical_depth_QSR; - amrex::Real* p_optical_depth_BW; + amrex::Real* p_optical_depth_QSR = nullptr; + amrex::Real* p_optical_depth_BW = nullptr; // If a QED effect is enabled, the corresponding optical depth // has to be initialized @@ -794,7 +798,8 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) // The invalid ones are given negative ID and are deleted during the // next redistribute. const auto poffset = offset.data(); - amrex::For(overlap_box, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept + amrex::ParallelForRNG(overlap_box, + [=] AMREX_GPU_DEVICE (int i, int j, int k, amrex::RandomEngine const& engine) noexcept { amrex::IntVect iv = amrex::IntVect(AMREX_D_DECL(i, j, k)); const auto index = overlap_box.index(iv); @@ -806,7 +811,7 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) p.cpu() = cpuid; const XDim3 r = - inj_pos->getPositionUnitBox(i_part, lrrfac); + inj_pos->getPositionUnitBox(i_part, lrrfac, engine); auto pos = getCellCoords(overlap_corner, dx, r, iv); #if (AMREX_SPACEDIM == 3) @@ -815,6 +820,7 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) continue; } #else + amrex::ignore_unused(k); if (!tile_realbox.contains(XDim3{pos.x,pos.z,0.0_rt})) { p.id() = -1; continue; @@ -833,7 +839,7 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) if (nmodes == 1) { // With only 1 mode, the angle doesn't matter so // choose it randomly. - theta = 2._rt*MathConst::pi*amrex::Random(); + theta = 2._rt*MathConst::pi*amrex::Random(engine); } else { theta = 2._rt*MathConst::pi*r.y; } @@ -953,7 +959,7 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) #endif - u = inj_mom->getMomentum(pos.x, pos.y, z0); + u = inj_mom->getMomentum(pos.x, pos.y, z0, engine); dens = inj_rho->getDensity(pos.x, pos.y, z0); // Remove particle if density below threshold @@ -985,7 +991,7 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) dens = amrex::min(dens, density_max); // get the full momentum, including thermal motion - u = inj_mom->getMomentum(pos.x, pos.y, 0._rt); + u = inj_mom->getMomentum(pos.x, pos.y, 0._rt, engine); const Real gamma_lab = std::sqrt( 1._rt+(u.x*u.x+u.y*u.y+u.z*u.z) ); const Real betaz_lab = u.z/(gamma_lab); @@ -1005,7 +1011,7 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) } if(loc_has_breit_wheeler){ - p_optical_depth_BW[ip] = breit_wheeler_get_opt(); + p_optical_depth_BW[ip] = breit_wheeler_get_opt(engine); } #endif @@ -1044,13 +1050,13 @@ PhysicalParticleContainer::AddPlasma (int lev, RealBox part_realbox) } }); + amrex::Gpu::synchronize(); + if (cost && WarpX::load_balance_costs_update_algo == LoadBalanceCostsUpdateAlgo::Timers) { - amrex::Gpu::synchronize(); wt = amrex::second() - wt; amrex::HostDevice::Atomic::Add( &(*cost)[mfi.index()], wt); } - amrex::Gpu::synchronize(); } // The function that calls this is responsible for redistributing particles. @@ -1221,8 +1227,8 @@ PhysicalParticleContainer::Evolve (int lev, Real /*t*/, Real dt, DtType a_dt_type) { - WARPX_PROFILE("PPC::Evolve()"); - WARPX_PROFILE_VAR_NS("PPC::GatherAndPush", blp_fg); + WARPX_PROFILE("PhysicalParticleContainer::Evolve()"); + WARPX_PROFILE_VAR_NS("PhysicalParticleContainer::Evolve::GatherAndPush", blp_fg); BL_ASSERT(OnSameGrids(lev,jx)); @@ -1427,9 +1433,10 @@ PhysicalParticleContainer::Evolve (int lev, } } + amrex::Gpu::synchronize(); + if (cost && WarpX::load_balance_costs_update_algo == LoadBalanceCostsUpdateAlgo::Timers) { - amrex::Gpu::synchronize(); wt = amrex::second() - wt; amrex::HostDevice::Atomic::Add( &(*cost)[pti.index()], wt); } @@ -1513,6 +1520,10 @@ PhysicalParticleContainer::applyNCIFilter ( bzeli = filtered_Bz.elixir(); nci_godfrey_filter_exeybz[lev]->ApplyStencil(filtered_Bz, Bz, filtered_Bz.box()); bz_ptr = &filtered_Bz; +#else + amrex::ignore_unused(eyeli, bxeli, bzeli, + filtered_Ey, filtered_Bx, filtered_Bz, + Ey, Bx, Bz, ey_ptr, bx_ptr, bz_ptr); #endif } @@ -1529,7 +1540,7 @@ PhysicalParticleContainer::SplitParticles (int lev) long np_split; if(split_type==0) { - np_split = pow(2, AMREX_SPACEDIM); + np_split = (AMREX_SPACEDIM == 3) ? 8 : 4; } else { np_split = 2*AMREX_SPACEDIM; } @@ -1689,7 +1700,8 @@ PhysicalParticleContainer::PushP (int lev, Real dt, const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez, const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz) { - WARPX_PROFILE("PhysicalParticleContainer::PushP"); + WARPX_PROFILE("PhysicalParticleContainer::PushP()"); + if (do_not_push) return; const std::array& dx = WarpX::CellSize(std::max(lev,0)); @@ -1713,12 +1725,11 @@ PhysicalParticleContainer::PushP (int lev, Real dt, const FArrayBox& bzfab = Bz[pti]; const auto getPosition = GetParticlePosition(pti); - auto setPosition = SetParticlePosition(pti); const auto getExternalE = GetExternalEField(pti); const auto getExternalB = GetExternalBField(pti); - const auto& xyzmin = WarpX::GetInstance().LowerCornerWithGalilean(box,v_galilean,lev); + const auto& xyzmin = WarpX::GetInstance().LowerCornerWithGalilean(box,m_v_galilean,lev); const Dim3 lo = lbound(box); @@ -1768,10 +1779,7 @@ PhysicalParticleContainer::PushP (int lev, Real dt, getPosition(ip, xp, yp, zp); amrex::ParticleReal Exp = 0._rt, Eyp = 0._rt, Ezp = 0._rt; - getExternalE(ip, Exp, Eyp, Ezp); - amrex::ParticleReal Bxp = 0._rt, Byp = 0._rt, Bzp = 0._rt; - getExternalB(ip, Bxp, Byp, Bzp); if (!t_do_not_gather){ // first gather E and B to the particle positions @@ -1781,6 +1789,10 @@ PhysicalParticleContainer::PushP (int lev, Real dt, dx_arr, xyzmin_arr, lo, n_rz_azimuthal_modes, nox, galerkin_interpolation); } + // Externally applied E-field in Cartesian co-ordinates + getExternalE(ip, Exp, Eyp, Ezp); + // Externally applied B-field in Cartesian co-ordinates + getExternalB(ip, Bxp, Byp, Bzp); if (do_crr) { amrex::Real qp = q; @@ -1821,7 +1833,7 @@ PhysicalParticleContainer::GetParticleSlice ( const Real t_lab, const Real dt, DiagnosticParticles& diagnostic_particles) { - WARPX_PROFILE("PhysicalParticleContainer::GetParticleSlice"); + WARPX_PROFILE("PhysicalParticleContainer::GetParticleSlice()"); // Assume that the boost in the positive z direction. #if (AMREX_SPACEDIM == 2) @@ -1871,8 +1883,8 @@ PhysicalParticleContainer::GetParticleSlice ( // from going out of scope after each iteration, while the kernels // may still need access to them. // Note that the destructor for WarpXParIter is synchronized. - amrex::Gpu::ManagedDeviceVector FlagForPartCopy; - amrex::Gpu::ManagedDeviceVector IndexForPartCopy; + amrex::Gpu::DeviceVector FlagForPartCopy; + amrex::Gpu::DeviceVector IndexForPartCopy; for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) { const Box& box = pti.validbox(); @@ -1930,9 +1942,7 @@ PhysicalParticleContainer::GetParticleSlice ( // exclusive scan to obtain location indices using flag values // These location indices are used to copy data from // src to dst when the copy-flag is set to 1. - amrex::Gpu::exclusive_scan(Flag,Flag+np,IndexLocation); - - const int total_partdiag_size = IndexLocation[np-1] + Flag[np-1]; + const int total_partdiag_size = amrex::Scan::ExclusiveSum(np,Flag,IndexLocation); // allocate array size for diagnostic particle array diagnostic_particles[lev][index].resize(total_partdiag_size); @@ -2012,6 +2022,7 @@ PhysicalParticleContainer::GetParticleSlice ( diag_uzp[loc] = uzp; } }); + Gpu::synchronize(); // because of FlagForPartCopy & IndexForPartCopy } } } @@ -2077,7 +2088,10 @@ PhysicalParticleContainer::PushPX (WarpXParIter& pti, Real cur_time = WarpX::GetInstance().gett_new(lev); const auto& time_of_last_gal_shift = WarpX::GetInstance().time_of_last_gal_shift; Real time_shift = (cur_time - time_of_last_gal_shift); - amrex::Array galilean_shift = { v_galilean[0]*time_shift, v_galilean[1]*time_shift, v_galilean[2]*time_shift }; + amrex::Array galilean_shift ={ + m_v_galilean[0]*time_shift, + m_v_galilean[1]*time_shift, + m_v_galilean[2]*time_shift }; const std::array& xyzmin = WarpX::LowerCorner(box, galilean_shift, gather_lev); const Dim3 lo = lbound(box); @@ -2127,7 +2141,7 @@ PhysicalParticleContainer::PushPX (WarpXParIter& pti, #ifdef WARPX_QED const auto do_sync = m_do_qed_quantum_sync; amrex::Real t_chi_max = 0.0; - if (do_sync) t_chi_max = m_shr_p_qs_engine->get_ref_ctrl().chi_part_min; + if (do_sync) t_chi_max = m_shr_p_qs_engine->get_minimum_chi_part(); QuantumSynchrotronEvolveOpticalDepth evolve_opt; amrex::ParticleReal* AMREX_RESTRICT p_optical_depth_QSR = nullptr; @@ -2146,10 +2160,7 @@ PhysicalParticleContainer::PushPX (WarpXParIter& pti, getPosition(ip, xp, yp, zp); amrex::ParticleReal Exp = 0._rt, Eyp = 0._rt, Ezp = 0._rt; - getExternalE(ip, Exp, Eyp, Ezp); - amrex::ParticleReal Bxp = 0._rt, Byp = 0._rt, Bzp = 0._rt; - getExternalB(ip, Bxp, Byp, Bzp); if(!t_do_not_gather){ // first gather E and B to the particle positions @@ -2159,22 +2170,13 @@ PhysicalParticleContainer::PushPX (WarpXParIter& pti, dx_arr, xyzmin_arr, lo, n_rz_azimuthal_modes, nox, galerkin_interpolation); } + // Externally applied E-field in Cartesian co-ordinates + getExternalE(ip, Exp, Eyp, Ezp); + // Externally applied B-field in Cartesian co-ordinates + getExternalB(ip, Bxp, Byp, Bzp); scaleFields(xp, yp, zp, Exp, Eyp, Ezp, Bxp, Byp, Bzp); -#ifdef WARPX_QED - if (local_has_quantum_sync) { - const ParticleReal px = m * ux[ip]; - const ParticleReal py = m * uy[ip]; - const ParticleReal pz = m * uz[ip]; - - bool has_event_happened = evolve_opt(px, py, pz, - Exp, Eyp, Ezp, - Bxp, Byp, Bzp, - dt, p_optical_depth_QSR[ip]); - } -#endif - doParticlePush(getPosition, setPosition, copyAttribs, ip, ux[ip+offset], uy[ip+offset], uz[ip+offset], Exp, Eyp, Ezp, Bxp, Byp, Bzp, @@ -2185,6 +2187,15 @@ PhysicalParticleContainer::PushPX (WarpXParIter& pti, t_chi_max, #endif dt); + +#ifdef WARPX_QED + if (local_has_quantum_sync) { + evolve_opt(ux[ip], uy[ip], uz[ip], + Exp, Eyp, Ezp,Bxp, Byp, Bzp, + dt, p_optical_depth_QSR[ip]); + } +#endif + }); } @@ -2206,10 +2217,10 @@ PhysicalParticleContainer::InitIonizationModule () // Get atomic number and ionization energies from file int ion_element_id = ion_map_ids[physical_element]; ion_atomic_number = ion_atomic_numbers[ion_element_id]; - ionization_energies.resize(ion_atomic_number); + Vector h_ionization_energies(ion_atomic_number); int offset = ion_energy_offsets[ion_element_id]; for(int i=0; i ptr) PhotonEmissionFilterFunc PhysicalParticleContainer::getPhotonEmissionFilterFunc () { - WARPX_PROFILE("PPC::getPhotonEmissionFunc"); + WARPX_PROFILE("PhysicalParticleContainer::getPhotonEmissionFunc()"); return PhotonEmissionFilterFunc{particle_runtime_comps["optical_depth_QSR"]}; } PairGenerationFilterFunc PhysicalParticleContainer::getPairGenerationFilterFunc () { - WARPX_PROFILE("PPC::getPairGenerationFunc"); + WARPX_PROFILE("PhysicalParticleContainer::getPairGenerationFunc()"); return PairGenerationFilterFunc{particle_runtime_comps["optical_depth_BW"]}; } diff --git a/Source/Particles/Pusher/GetAndSetPosition.H b/Source/Particles/Pusher/GetAndSetPosition.H index 7f9886d7974..dba4c0a3460 100644 --- a/Source/Particles/Pusher/GetAndSetPosition.H +++ b/Source/Particles/Pusher/GetAndSetPosition.H @@ -10,6 +10,7 @@ #include "Particles/WarpXParticleContainer.H" +#include #include #include @@ -26,9 +27,9 @@ struct GetParticlePosition using PType = WarpXParticleContainer::ParticleType; using RType = amrex::ParticleReal; - const PType* AMREX_RESTRICT m_structs; + const PType* AMREX_RESTRICT m_structs = nullptr; #if (defined WARPX_DIM_RZ) - const RType* m_theta; + const RType* m_theta = nullptr; #elif (AMREX_SPACEDIM == 2) static constexpr RType m_snan = std::numeric_limits::quiet_NaN(); #endif @@ -98,6 +99,9 @@ struct SetParticlePosition AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void operator() (const int i, RType x, RType y, RType z) const noexcept { +#if defined(WARPX_DIM_XZ) + amrex::ignore_unused(y); +#endif #ifdef WARPX_DIM_RZ m_theta[i] = std::atan2(y, x); m_structs[i].pos(0) = std::sqrt(x*x + y*y); diff --git a/Source/Particles/Pusher/PushSelector.H b/Source/Particles/Pusher/PushSelector.H index 149342b74e9..bd469b51b4b 100644 --- a/Source/Particles/Pusher/PushSelector.H +++ b/Source/Particles/Pusher/PushSelector.H @@ -69,7 +69,7 @@ void doParticlePush(const GetParticlePosition& GetPosition, if (do_crr) { #ifdef WARPX_QED if (do_sync) { - auto chi = QedUtils::chi_lepton(m*ux, m*uy, m*uz, + auto chi = QedUtils::chi_ele_pos(m*ux, m*uy, m*uz, Ex, Ey, Ez, Bx, By, Bz); if (chi < t_chi_max) { diff --git a/Source/Particles/Pusher/UpdatePosition.H b/Source/Particles/Pusher/UpdatePosition.H index 39321027b3c..d6189256cec 100644 --- a/Source/Particles/Pusher/UpdatePosition.H +++ b/Source/Particles/Pusher/UpdatePosition.H @@ -12,6 +12,7 @@ #include #include +#include /** \brief Push the particle's positions over one timestep, @@ -31,6 +32,8 @@ void UpdatePosition(amrex::ParticleReal& x, amrex::ParticleReal& y, amrex::Parti x += ux * inv_gamma * dt; #if (AMREX_SPACEDIM == 3) || (defined WARPX_DIM_RZ) // RZ pushes particles in 3D y += uy * inv_gamma * dt; +#else + amrex::ignore_unused(y); #endif z += uz * inv_gamma * dt; } diff --git a/Source/Particles/Pusher/UpdatePositionPhoton.H b/Source/Particles/Pusher/UpdatePositionPhoton.H index 08827e40796..4787c67c03a 100644 --- a/Source/Particles/Pusher/UpdatePositionPhoton.H +++ b/Source/Particles/Pusher/UpdatePositionPhoton.H @@ -12,7 +12,7 @@ #include #include - +#include /** * \brief Push the position of a photon particle over one timestep, @@ -31,6 +31,8 @@ void UpdatePositionPhoton( x += ux * c_over_umod * dt; #if (defined WARPX_DIM_3D) || (defined WARPX_DIM_RZ) // RZ pushes particles in 3D y += uy * c_over_umod * dt; +#else + amrex::ignore_unused(y); #endif z += uz * c_over_umod * dt; } diff --git a/Source/Particles/Resampling/CMakeLists.txt b/Source/Particles/Resampling/CMakeLists.txt new file mode 100644 index 00000000000..0b44f7a5d76 --- /dev/null +++ b/Source/Particles/Resampling/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(WarpX + PRIVATE + Resampling.cpp + ResamplingTrigger.cpp + LevelingThinning.cpp +) diff --git a/Source/Particles/Resampling/LevelingThinning.H b/Source/Particles/Resampling/LevelingThinning.H new file mode 100644 index 00000000000..ecfd727c4c4 --- /dev/null +++ b/Source/Particles/Resampling/LevelingThinning.H @@ -0,0 +1,50 @@ +/* Copyright 2019-2020 Neil Zaim + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_LEVELING_THINNING_H_ +#define WARPX_LEVELING_THINNING_H_ + +#include "Resampling.H" + +/** + * \brief This class implements the leveling thinning algorithm as defined in Muraviev, A., et al. + * arXiv:2006.08593 (2020). + * The main steps of the algorithm are the following: for every cell we calculate a level weight, + * defined by the average weight of the species particles in that cell multiplied by the target + * ratio. Then, particles with a weight lower than the level weight are either removed, with a + * probability 1 - particle_weight/level_weight, or have their weight set to the level weight. + */ +class LevelingThinning: public ResamplingAlgorithm { +public: + + /** + * \brief Default constructor of the LevelingThinning class. + */ + LevelingThinning () = default; + + /** + * \brief Constructor of the LevelingThinning class + * + * @param[in] species_name the name of the resampled species + */ + LevelingThinning (const std::string species_name); + + /** + * \brief A method that performs leveling thinning for the considered species. + * + * @param[in] pti WarpX particle iterator of the particles to resample. + * @param[in] lev the index of the refinement level. + * @param[in] pc a pointer to the particle container. + */ + void operator() (WarpXParIter& pti, const int lev, WarpXParticleContainer * const pc) const override final; + +private: + amrex::Real m_target_ratio = amrex::Real(1.5); + int m_min_ppc = 1; +}; + + +#endif //WARPX_LEVELING_THINNING_H_ diff --git a/Source/Particles/Resampling/LevelingThinning.cpp b/Source/Particles/Resampling/LevelingThinning.cpp new file mode 100644 index 00000000000..ec1205fa830 --- /dev/null +++ b/Source/Particles/Resampling/LevelingThinning.cpp @@ -0,0 +1,102 @@ +/* Copyright 2019-2020 Neil Zaim + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "LevelingThinning.H" +#include "Utils/ParticleUtils.H" + +#include + +LevelingThinning::LevelingThinning (const std::string species_name) +{ + using namespace amrex::literals; + + amrex::ParmParse pp(species_name); + pp.query("resampling_algorithm_target_ratio", m_target_ratio); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( m_target_ratio > 0._rt, + "Resampling target ratio should be strictly greater than 0"); + if (m_target_ratio <= 1._rt) + { + amrex::Warning("WARNING: target ratio for leveling thinning is smaller or equal to one." + " It is possible that no particle will be removed during resampling"); + } + + pp.query("resampling_algorithm_min_ppc", m_min_ppc); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_min_ppc >= 1, + "Resampling min_ppc should be greater than or equal to 1"); +} + +void LevelingThinning::operator() (WarpXParIter& pti, const int lev, + WarpXParticleContainer * const pc) const +{ + using namespace amrex::literals; + + auto& ptile = pc->ParticlesAt(lev, pti); + auto& soa = ptile.GetStructOfArrays(); + amrex::ParticleReal * const AMREX_RESTRICT w = soa.GetRealData(PIdx::w).data(); + WarpXParticleContainer::ParticleType * const AMREX_RESTRICT + particle_ptr = ptile.GetArrayOfStructs()().data(); + + // Using this function means that we must loop over the cells in the ParallelFor. In the case + // of the leveling thinning algorithm, it would have possibly been more natural and more + // efficient to directly loop over the particles. Nevertheless, this structure with a loop over + // the cells is more general and can be readily used to implement almost any other resampling + // algorithm. + auto bins = ParticleUtils::findParticlesInEachCell(lev, pti, ptile); + + const int n_cells = bins.numBins(); + const auto indices = bins.permutationPtr(); + const auto cell_offsets = bins.offsetsPtr(); + + const amrex::Real target_ratio = m_target_ratio; + const int min_ppc = m_min_ppc; + + // Loop over cells + amrex::ParallelForRNG( n_cells, + [=] AMREX_GPU_DEVICE (int i_cell, amrex::RandomEngine const& engine) noexcept + { + // The particles that are in the cell `i_cell` are + // given by the `indices[cell_start:cell_stop]` + const auto cell_start = cell_offsets[i_cell]; + const auto cell_stop = static_cast(cell_offsets[i_cell+1]); + const int cell_numparts = cell_stop - cell_start; + + // do nothing for cells with less particles than min_ppc + // (this intentionally includes skipping empty cells, too) + if (cell_numparts < min_ppc) + return; + amrex::Real average_weight = 0._rt; + + // First loop over cell particles to compute average particle weight in the cell + for (int i = cell_start; i < cell_stop; ++i) + { + average_weight += w[indices[i]]; + } + average_weight /= cell_numparts; + + const amrex::Real level_weight = average_weight*target_ratio; + + + // Second loop over cell particles to perform the thinning + for (int i = cell_start; i < cell_stop; ++i) + { + // Particles with weight greater than level_weight are left unchanged + if (w[indices[i]] > level_weight) {continue;} + + amrex::Real const random_number = amrex::Random(engine); + // Remove particle with probability 1 - particle_weight/level_weight + if (random_number > w[indices[i]]/level_weight) + { + particle_ptr[indices[i]].id() = -1; + } + // Set particle weight to level weight otherwise + else + { + w[indices[i]] = level_weight; + } + } + } + ); +} diff --git a/Source/Particles/Resampling/Make.package b/Source/Particles/Resampling/Make.package new file mode 100644 index 00000000000..e70e3446c33 --- /dev/null +++ b/Source/Particles/Resampling/Make.package @@ -0,0 +1,5 @@ +CEXE_sources += Resampling.cpp +CEXE_sources += ResamplingTrigger.cpp +CEXE_sources += LevelingThinning.cpp + +VPATH_LOCATIONS += $(WARPX_HOME)/Source/Particles/Resampling/ diff --git a/Source/Particles/Resampling/Resampling.H b/Source/Particles/Resampling/Resampling.H new file mode 100644 index 00000000000..b381771f83f --- /dev/null +++ b/Source/Particles/Resampling/Resampling.H @@ -0,0 +1,77 @@ +/* Copyright 2019-2020 Neil Zaim + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_RESAMPLING_H_ +#define WARPX_RESAMPLING_H_ + +#include "ResamplingTrigger.H" + +class WarpXParIter; // forward declaration +class WarpXParticleContainer; // forward declaration + +/** + * \brief An empty base class from which specific resampling algorithms are derived. + */ +struct ResamplingAlgorithm +{ + /** + * \brief Virtual operator() of the abstract ResamplingAlgorithm class + */ + virtual void operator() (WarpXParIter& /*pti*/, const int /*lev*/, WarpXParticleContainer */*pc*/) const = 0; + + /** + * \brief Virtual destructor of the abstract ResamplingAlgorithm class + */ + virtual ~ResamplingAlgorithm () = default; +}; + +/** + * \brief This is a general class used for resampling that is instantiated as a member of + * MultiParticleContainer. It contains a ResamplingTrigger object used to determine if resampling + * should be done at a given timestep for a given species and a pointer to a ResamplingAlgorithm + * object used to carry out the resampling. + */ +class Resampling +{ +public: + + /** + * \brief Default constructor of the Resampling class. + */ + Resampling () = default; + + /** + * \brief Constructor of the Resampling class. Reads the chosen resampling algorithm from the + * input file. + * + * @param[in] species_name the name of the resampled species + */ + Resampling (const std::string species_name); + + /** + * \brief A method that returns true if resampling should be done for the considered species + * at the considered timestep. + * + * @param[in] timestep the current timestep + * @param[in] global_numparts the total number of particles of the considered species + */ + bool triggered (const int timestep, const amrex::Real global_numparts) const; + + /** + * \brief A method that uses the ResamplingAlgorithm object to perform resampling. + * + * @param[in] pti WarpX particle iterator of the particles to resample. + * @param[in] lev the index of the refinement level. + * @param[in] pc a pointer to the particle container. + */ + void operator() (WarpXParIter& pti, const int lev, WarpXParticleContainer * const pc) const; + +private: + ResamplingTrigger m_resampling_trigger; + std::unique_ptr m_resampling_algorithm; +}; + +#endif //WARPX_RESAMPLING_H_ diff --git a/Source/Particles/Resampling/Resampling.cpp b/Source/Particles/Resampling/Resampling.cpp new file mode 100644 index 00000000000..b5c2d8b4f98 --- /dev/null +++ b/Source/Particles/Resampling/Resampling.cpp @@ -0,0 +1,34 @@ +/* Copyright 2019-2020 Neil Zaim + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "Resampling.H" +#include "LevelingThinning.H" + +Resampling::Resampling (const std::string species_name) +{ + amrex::ParmParse pp(species_name); + std::string resampling_algorithm_string = "leveling_thinning"; // default resampling algorithm + pp.query("resampling_algorithm", resampling_algorithm_string); + + if (resampling_algorithm_string.compare("leveling_thinning") == 0) + { + m_resampling_algorithm = std::make_unique(species_name); + } + else + { amrex::Abort("Unknown resampling algorithm."); } + + m_resampling_trigger = ResamplingTrigger(species_name); +} + +bool Resampling::triggered (const int timestep, const amrex::Real global_numparts) const +{ + return m_resampling_trigger.triggered(timestep, global_numparts); +} + +void Resampling::operator() (WarpXParIter& pti, const int lev, WarpXParticleContainer * const pc) const +{ + (*m_resampling_algorithm)(pti, lev, pc); +} diff --git a/Source/Particles/Resampling/ResamplingTrigger.H b/Source/Particles/Resampling/ResamplingTrigger.H new file mode 100644 index 00000000000..830b511f2bd --- /dev/null +++ b/Source/Particles/Resampling/ResamplingTrigger.H @@ -0,0 +1,65 @@ +/* Copyright 2019-2020 Neil Zaim + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_RESAMPLING_TRIGGER_H_ +#define WARPX_RESAMPLING_TRIGGER_H_ + +#include "Utils/IntervalsParser.H" + +#include + +/** + * \brief This class is used to determine if resampling should be done at a given timestep for + * a given species. Specifically resampling is performed if the current timestep is included in + * the IntervalsParser m_resampling_intervals or if the average number of particles per cell of + * the considered species exceeds the threshold m_max_avg_ppc. + */ +class ResamplingTrigger +{ +public: + + /** + * \brief Default constructor of the ResamplingTrigger class. + */ + ResamplingTrigger () = default; + + /** + * \brief Constructor of the ResamplingTrigger class. Reads the resampling trigger parameters + * from the input file. + */ + ResamplingTrigger (const std::string species_name); + + /** + * \brief A method that returns true if resampling should be done for the considered species + * at the considered timestep. + * + * @param[in] timestep the current timestep + * @param[in] global_numparts the total number of particles of the considered species + */ + bool triggered (const int timestep, const amrex::Real global_numparts) const; + + /** + * \brief A method that initializes the member m_global_numcells. It is only called once (the + * first time triggered() is called) and is needed because warpx.boxArray(lev) is not yet + * initialized when the constructor of this class is called. + */ + void initialize_global_numcells () const; + +private: + // Intervals that define predetermined timesteps at which resampling is performed for all + // species. + IntervalsParser m_resampling_intervals; + + // Average number of particles per cell above which resampling is performed for a given species + amrex::Real m_max_avg_ppc = std::numeric_limits::max(); + + //Total number of simulated cells, summed over all mesh refinement levels. + mutable amrex::Real m_global_numcells = amrex::Real(0.0); + + mutable bool m_initialized = false; +}; + +#endif //WARPX_RESAMPLING_TRIGGER_H_ diff --git a/Source/Particles/Resampling/ResamplingTrigger.cpp b/Source/Particles/Resampling/ResamplingTrigger.cpp new file mode 100644 index 00000000000..97407d51a10 --- /dev/null +++ b/Source/Particles/Resampling/ResamplingTrigger.cpp @@ -0,0 +1,38 @@ +/* Copyright 2019-2020 Neil Zaim + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "ResamplingTrigger.H" +#include "WarpX.H" + +ResamplingTrigger::ResamplingTrigger (const std::string species_name) +{ + amrex::ParmParse pprt(species_name); + + std::vector resampling_trigger_int_string_vec = {"0"}; + pprt.queryarr("resampling_trigger_intervals", resampling_trigger_int_string_vec); + m_resampling_intervals = IntervalsParser(resampling_trigger_int_string_vec); + + pprt.query("resampling_trigger_max_avg_ppc", m_max_avg_ppc); +} + +bool ResamplingTrigger::triggered (const int timestep, const amrex::Real global_numparts) const +{ + if (!m_initialized) {initialize_global_numcells();}; + + const amrex::Real avg_ppc = global_numparts/m_global_numcells; + return (m_resampling_intervals.contains(timestep) || + avg_ppc > m_max_avg_ppc); +} + +void ResamplingTrigger::initialize_global_numcells () const +{ + auto & warpx = WarpX::GetInstance(); + for (int lev = 0; lev <= warpx.maxLevel(); lev++) + { + m_global_numcells += warpx.boxArray(lev).numPts(); + } + m_initialized = true; +} diff --git a/Source/Particles/RigidInjectedParticleContainer.cpp b/Source/Particles/RigidInjectedParticleContainer.cpp index 6cdd9993802..b27b67dc9ef 100644 --- a/Source/Particles/RigidInjectedParticleContainer.cpp +++ b/Source/Particles/RigidInjectedParticleContainer.cpp @@ -246,7 +246,7 @@ RigidInjectedParticleContainer::PushPX (WarpXParIter& pti, auto& uzp = attribs[PIdx::uz]; // Save the position and momenta, making copies - Gpu::ManagedDeviceVector xp_save, yp_save, zp_save; + Gpu::DeviceVector xp_save, yp_save, zp_save; RealVector uxp_save, uyp_save, uzp_save; const auto GetPosition = GetParticlePosition(pti); @@ -410,12 +410,11 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, const FArrayBox& bzfab = Bz[pti]; const auto getPosition = GetParticlePosition(pti); - auto setPosition = SetParticlePosition(pti); const auto getExternalE = GetExternalEField(pti); const auto getExternalB = GetExternalBField(pti); - const auto& xyzmin = WarpX::GetInstance().LowerCornerWithGalilean(box,v_galilean,lev); + const auto& xyzmin = WarpX::GetInstance().LowerCornerWithGalilean(box,m_v_galilean,lev); const Dim3 lo = lbound(box); @@ -441,9 +440,6 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, amrex::IndexType const bz_type = bzfab.box().ixType(); auto& attribs = pti.GetAttribs(); - auto& uxp = attribs[PIdx::ux]; - auto& uyp = attribs[PIdx::uy]; - auto& uzp = attribs[PIdx::uz]; amrex::ParticleReal* const AMREX_RESTRICT uxpp = attribs[PIdx::ux].dataPtr(); amrex::ParticleReal* const AMREX_RESTRICT uypp = attribs[PIdx::uy].dataPtr(); amrex::ParticleReal* const AMREX_RESTRICT uzpp = attribs[PIdx::uz].dataPtr(); @@ -454,9 +450,12 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, } // Save the position and momenta, making copies - auto uxp_save = uxp; - auto uyp_save = uyp; - auto uzp_save = uzp; + amrex::Gpu::DeviceVector uxp_save(np); + amrex::Gpu::DeviceVector uyp_save(np); + amrex::Gpu::DeviceVector uzp_save(np); + ParticleReal* const AMREX_RESTRICT ux_save = uxp_save.dataPtr(); + ParticleReal* const AMREX_RESTRICT uy_save = uyp_save.dataPtr(); + ParticleReal* const AMREX_RESTRICT uz_save = uzp_save.dataPtr(); // Loop over the particles and update their momentum const amrex::Real q = this->charge; @@ -467,14 +466,15 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, amrex::ParallelFor( np, [=] AMREX_GPU_DEVICE (long ip) { + ux_save[ip] = uxpp[ip]; + uy_save[ip] = uypp[ip]; + uz_save[ip] = uzpp[ip]; + amrex::ParticleReal xp, yp, zp; getPosition(ip, xp, yp, zp); amrex::ParticleReal Exp = 0._rt, Eyp = 0._rt, Ezp = 0._rt; - getExternalE(ip, Exp, Eyp, Ezp); - amrex::ParticleReal Bxp = 0._rt, Byp = 0._rt, Bzp = 0._rt; - getExternalB(ip, Bxp, Byp, Bzp); // first gather E and B to the particle positions doGatherShapeN(xp, yp, zp, Exp, Eyp, Ezp, Bxp, Byp, Bzp, @@ -482,6 +482,8 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, ex_type, ey_type, ez_type, bx_type, by_type, bz_type, dx_arr, xyzmin_arr, lo, n_rz_azimuthal_modes, nox, galerkin_interpolation); + getExternalE(ip, Exp, Eyp, Ezp); + getExternalB(ip, Bxp, Byp, Bzp); if (do_crr) { amrex::Real qp = q; @@ -515,9 +517,6 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, // Undo the push for particles not injected yet. // It is assumed that PushP will only be called on the first and last steps // and that no particles will cross zinject_plane. - const ParticleReal* const AMREX_RESTRICT ux_save = uxp_save.dataPtr(); - const ParticleReal* const AMREX_RESTRICT uy_save = uyp_save.dataPtr(); - const ParticleReal* const AMREX_RESTRICT uz_save = uzp_save.dataPtr(); const ParticleReal zz = zinject_plane_levels[lev]; amrex::ParallelFor( pti.numParticles(), [=] AMREX_GPU_DEVICE (long i) { @@ -529,6 +528,8 @@ RigidInjectedParticleContainer::PushP (int lev, Real dt, uzpp[i] = uz_save[i]; } }); + + amrex::Gpu::synchronize(); } } } diff --git a/Source/Particles/ShapeFactors.H b/Source/Particles/ShapeFactors.H index 917f0872dd8..835d1237ea6 100644 --- a/Source/Particles/ShapeFactors.H +++ b/Source/Particles/ShapeFactors.H @@ -23,7 +23,7 @@ struct Compute_shape_factor { template< typename T > AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE - int operator()(T* const sx, T xint) const { return 0; } + int operator()(T* const /*sx*/, T /*xint*/) const { return 0; } }; /** diff --git a/Source/Particles/Sorting/Partition.cpp b/Source/Particles/Sorting/Partition.cpp index b78cffa6cbc..455c3ffb81a 100644 --- a/Source/Particles/Sorting/Partition.cpp +++ b/Source/Particles/Sorting/Partition.cpp @@ -48,7 +48,7 @@ PhysicalParticleContainer::PartitionParticlesInBuffers( iMultiFab const* gather_masks, RealVector& uxp, RealVector& uyp, RealVector& uzp, RealVector& wp) { - WARPX_PROFILE("PPC::Evolve::partition"); + WARPX_PROFILE("PhysicalParticleContainer::PartitionParticlesInBuffers"); // Initialize temporary arrays Gpu::DeviceVector inexflag; diff --git a/Source/Particles/WarpXParticleContainer.H b/Source/Particles/WarpXParticleContainer.H index 2ff57fd0edf..5e41646c716 100644 --- a/Source/Particles/WarpXParticleContainer.H +++ b/Source/Particles/WarpXParticleContainer.H @@ -14,6 +14,7 @@ #include "Utils/WarpXConst.H" #include "SpeciesPhysicalProperties.H" #include "Evolve/WarpXDtType.H" +#include "Particles/Resampling/Resampling.H" #ifdef WARPX_QED # include "ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.H" @@ -25,6 +26,7 @@ #include +enum struct ParticleBC { none=0, absorbing }; enum struct ConvertDirection{WarpX_to_SI, SI_to_WarpX}; @@ -168,10 +170,10 @@ public: virtual void PostRestart () = 0; - virtual void GetParticleSlice(const int direction, const amrex::Real z_old, - const amrex::Real z_new, const amrex::Real t_boost, - const amrex::Real t_lab, const amrex::Real dt, - DiagnosticParticles& diagnostic_particles) {} + virtual void GetParticleSlice(const int /*direction*/, const amrex::Real /*z_old*/, + const amrex::Real /*z_new*/, const amrex::Real /*t_boost*/, + const amrex::Real /*t_lab*/, const amrex::Real /*dt*/, + DiagnosticParticles& /*diagnostic_particles*/) {} void AllocData (); @@ -233,9 +235,9 @@ public: // PhysicalParticleContainer: implemented. // LaserParticleContainer: implemented. // RigidInjectedParticleContainer: not implemented. - virtual void ContinuousInjection(const amrex::RealBox& injection_box) {} + virtual void ContinuousInjection(const amrex::RealBox& /*injection_box*/) {} // Update optional sub-class-specific injection location. - virtual void UpdateContinuousInjectionPosition(amrex::Real dt) {} + virtual void UpdateContinuousInjectionPosition(amrex::Real /*dt*/) {} /// /// This returns the total charge for all the particles in this ParticleContainer. @@ -250,21 +252,24 @@ public: void AddNParticles (int lev, int n, const amrex::ParticleReal* x, const amrex::ParticleReal* y, const amrex::ParticleReal* z, const amrex::ParticleReal* vx, const amrex::ParticleReal* vy, const amrex::ParticleReal* vz, - int nattr, const amrex::ParticleReal* attr, int uniqueparticles, int id=-1); + int nattr, const amrex::ParticleReal* attr, int uniqueparticles, amrex::Long id=-1); virtual void ReadHeader (std::istream& is); virtual void WriteHeader (std::ostream& os) const; - virtual void ConvertUnits (ConvertDirection convert_dir){} + virtual void ConvertUnits (ConvertDirection /*convert_dir*/){} static void ReadParameters (); static void BackwardCompatibility (); - static int NextID () { return ParticleType::NextID(); } - - void setNextID(int next_id) { ParticleType::NextID(next_id); } + /** \brief Apply particle BC. + * + * \param[in] boundary_conditions Type of boundary conditions. For now, only absorbing or none + * are supported + */ + void ApplyBoundaryConditions (ParticleBC boundary_conditions); bool do_splitting = false; bool initialize_self_fields = false; @@ -334,10 +339,18 @@ public: template bool AmIA () const noexcept {return (physical_species == PhysSpec);} - amrex::Array get_v_galilean () {return v_galilean;} + amrex::Array get_v_galilean () {return m_v_galilean;} + + /** + * \brief Virtual method to resample the species. Overriden by PhysicalParticleContainer only. + * Empty body is here because making the method purely virtual would mean that we need to + * override the method for every derived class. Note that in practice this function is never + * called because resample() is only called for PhysicalParticleContainers. + */ + virtual void resample (const int /*timestep*/) {} protected: - amrex::Array v_galilean = {{0}}; + amrex::Array m_v_galilean = {{0}}; std::map particle_comps; std::map particle_icomps; std::map particle_runtime_comps; @@ -370,12 +383,14 @@ protected: std::string ionization_product_name; int ion_atomic_number; int ionization_initial_level = 0; - amrex::Gpu::ManagedVector ionization_energies; - amrex::Gpu::ManagedVector adk_power; - amrex::Gpu::ManagedVector adk_prefactor; - amrex::Gpu::ManagedVector adk_exp_prefactor; + amrex::Gpu::DeviceVector ionization_energies; + amrex::Gpu::DeviceVector adk_power; + amrex::Gpu::DeviceVector adk_prefactor; + amrex::Gpu::DeviceVector adk_exp_prefactor; std::string physical_element; + int do_resampling = 0; + int do_back_transformed_diagnostics = 1; #ifdef WARPX_QED @@ -402,9 +417,9 @@ protected: amrex::Vector local_jz; public: - using DataContainer = amrex::Gpu::ManagedDeviceVector; using PairIndex = std::pair; - using TmpParticleTile = std::array; + using TmpParticleTile = std::array, + TmpIdx::nattribs>; using TmpParticles = amrex::Vector >; protected: diff --git a/Source/Particles/WarpXParticleContainer.cpp b/Source/Particles/WarpXParticleContainer.cpp index d7cf02620fe..ba1e2b43858 100644 --- a/Source/Particles/WarpXParticleContainer.cpp +++ b/Source/Particles/WarpXParticleContainer.cpp @@ -19,6 +19,7 @@ #include "Deposition/ChargeDeposition.H" #include +#include #include @@ -98,9 +99,13 @@ void WarpXParticleContainer::AddNParticles (int /*lev*/, int n, const ParticleReal* x, const ParticleReal* y, const ParticleReal* z, const ParticleReal* vx, const ParticleReal* vy, const ParticleReal* vz, - int nattr, const ParticleReal* attr, int uniqueparticles, int id) + int nattr, const ParticleReal* attr, int uniqueparticles, amrex::Long id) { - BL_ASSERT(nattr == 1); //! @fixme nattr is unused below: false sense of safety + // nattr is unused below but needed in the BL_ASSERT + amrex::ignore_unused(nattr); + + BL_ASSERT(nattr == 1); + const ParticleReal* weight = attr; int ibegin, iend; @@ -146,6 +151,7 @@ WarpXParticleContainer::AddNParticles (int /*lev*/, p.pos(1) = y[i]; p.pos(2) = z[i]; #elif (AMREX_SPACEDIM == 2) + amrex::ignore_unused(y); #ifdef WARPX_DIM_RZ theta[i-ibegin] = std::atan2(y[i], x[i]); p.pos(0) = std::sqrt(x[i]*x[i] + y[i]*y[i]); @@ -226,19 +232,34 @@ WarpXParticleContainer::DepositCurrent(WarpXParIter& pti, AMREX_ALWAYS_ASSERT_WITH_MESSAGE((depos_lev==(lev-1)) || (depos_lev==(lev )), "Deposition buffers only work for lev-1"); + // If no particles, do not do anything if (np_to_depose == 0) return; // If user decides not to deposit if (do_not_deposit) return; - const long ngJ = jx->nGrow(); + // Number of guard cells for local deposition of J + WarpX& warpx = WarpX::GetInstance(); + const int ng_J = warpx.get_ng_depos_J().max(); + + // Extract deposition order (same order along all directions) and check that + // particles shape fits within the guard cells. + // NOTE: In specific situations where the staggering of J and the current + // deposition algorithm are not trivial, this check might be too relaxed + // and we might include a particle that should deposit part of its current + // in a neighboring box. However, this should catch particles traveling many + // cells away, for example with algorithms that allow for large time steps. + const int shape_extent = static_cast(WarpX::nox / 2); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + amrex::numParticlesOutOfRange(pti, ng_J - shape_extent) == 0, + "Particles shape does not fit within guard cells used for local current deposition"); + const std::array& dx = WarpX::CellSize(std::max(depos_lev,0)); Real q = this->charge; - WARPX_PROFILE_VAR_NS("PPC::Evolve::Accumulate", blp_accumulate); - WARPX_PROFILE_VAR_NS("PPC::CurrentDeposition", blp_deposit); - + WARPX_PROFILE_VAR_NS("WarpXParticleContainer::DepositCurrent::CurrentDeposition", blp_deposit); + WARPX_PROFILE_VAR_NS("WarpXParticleContainer::DepositCurrent::Accumulate", blp_accumulate); // Get tile box where current is deposited. // The tile box is different when depositing in the buffers (depos_levixType().toIntVect() ); Box tby = convert( tilebox, jy->ixType().toIntVect() ); Box tbz = convert( tilebox, jz->ixType().toIntVect() ); - tilebox.grow(ngJ); + + tilebox.grow(ng_J); #ifdef AMREX_USE_GPU - // No tiling on GPU: jx_ptr points to the full - // jx array (same for jy_ptr and jz_ptr). + // GPU, no tiling: j_arr point to the full j arrays auto & jx_fab = jx->get(pti); auto & jy_fab = jy->get(pti); auto & jz_fab = jz->get(pti); @@ -267,12 +288,11 @@ WarpXParticleContainer::DepositCurrent(WarpXParIter& pti, Array4 const& jy_arr = jy->array(pti); Array4 const& jz_arr = jz->array(pti); #else - // Tiling is on: jx_ptr points to local_jx[thread_num] - // (same for jy_ptr and jz_ptr) - tbx.grow(ngJ); - tby.grow(ngJ); - tbz.grow(ngJ); + tbx.grow(ng_J); + tby.grow(ng_J); + tbz.grow(ng_J); + // CPU, tiling: j_arr point to the local_j[thread_num] arrays local_jx[thread_num].resize(tbx, jx->nComp()); local_jy[thread_num].resize(tby, jy->nComp()); local_jz[thread_num].resize(tbz, jz->nComp()); @@ -289,9 +309,6 @@ WarpXParticleContainer::DepositCurrent(WarpXParIter& pti, Array4 const& jy_arr = local_jy[thread_num].array(); Array4 const& jz_arr = local_jz[thread_num].array(); #endif - // GPU, no tiling: deposit directly in jx - // CPU, tiling: deposit into local_jx - // (same for jx and jz) const auto GetPosition = GetParticlePosition(pti, offset); @@ -299,18 +316,20 @@ WarpXParticleContainer::DepositCurrent(WarpXParIter& pti, // Note that this includes guard cells since it is after tilebox.ngrow const Dim3 lo = lbound(tilebox); // Take into account Galilean shift - auto& warpx_instance = WarpX::GetInstance(); - Real cur_time = warpx_instance.gett_new(lev); - const auto& time_of_last_gal_shift = warpx_instance.time_of_last_gal_shift; + Real cur_time = warpx.gett_new(lev); + const auto& time_of_last_gal_shift = warpx.time_of_last_gal_shift; Real time_shift = (cur_time + 0.5*dt - time_of_last_gal_shift); - amrex::Array galilean_shift = { v_galilean[0]* time_shift, v_galilean[1]*time_shift, v_galilean[2]*time_shift }; + amrex::Array galilean_shift = { + m_v_galilean[0]* time_shift, + m_v_galilean[1]*time_shift, + m_v_galilean[2]*time_shift }; const std::array& xyzmin = WarpX::LowerCorner(tilebox, galilean_shift, depos_lev); if (WarpX::current_deposition_algo == CurrentDepositionAlgo::Esirkepov) { if (WarpX::do_nodal==1) { amrex::Abort("The Esirkepov algorithm cannot be used with a nodal grid."); } - if ( (v_galilean[0]!=0) or (v_galilean[1]!=0) or (v_galilean[2]!=0)){ + if ( (m_v_galilean[0]!=0) or (m_v_galilean[1]!=0) or (m_v_galilean[2]!=0)){ amrex::Abort("The Esirkepov algorithm cannot be used with the Galilean algorithm."); } } @@ -380,9 +399,8 @@ WarpXParticleContainer::DepositCurrent(WarpXParIter& pti, WARPX_PROFILE_VAR_STOP(blp_deposit); #ifndef AMREX_USE_GPU + // CPU, tiling: atomicAdd local_j into j WARPX_PROFILE_VAR_START(blp_accumulate); - // CPU, tiling: atomicAdd local_jx into jx - // (same for jx and jz) (*jx)[pti].atomicAdd(local_jx[thread_num], tbx, tbx, 0, 0, jx->nComp()); (*jy)[pti].atomicAdd(local_jy[thread_num], tby, tby, 0, 0, jy->nComp()); (*jz)[pti].atomicAdd(local_jz[thread_num], tbz, tbz, 0, 0, jz->nComp()); @@ -425,12 +443,25 @@ WarpXParticleContainer::DepositCharge (WarpXParIter& pti, RealVector& wp, // If user decides not to deposit if (do_not_deposit) return; - const long ngRho = rho->nGrow(); + // Number of guard cells for local deposition of rho + WarpX& warpx = WarpX::GetInstance(); + const int ng_rho = warpx.get_ng_depos_rho().max(); + + // Extract deposition order (same order along all directions) and check that + // particles shape fits within the guard cells. + // NOTE: In specific situations where the staggering of rho and the charge + // deposition algorithm are not trivial, this check might be too strict and + // we might need to relax it, as currently done for the current deposition. + const int shape_extent = static_cast(WarpX::nox / 2 + 1); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + amrex::numParticlesOutOfRange(pti, ng_rho - shape_extent) == 0, + "Particles shape does not fit within guard cells used for local charge deposition"); + const std::array& dx = WarpX::CellSize(std::max(depos_lev,0)); const Real q = this->charge; - WARPX_PROFILE_VAR_NS("PPC::ChargeDeposition", blp_ppc_chd); - WARPX_PROFILE_VAR_NS("PPC::Evolve::Accumulate", blp_accumulate); + WARPX_PROFILE_VAR_NS("WarpXParticleContainer::DepositCharge::ChargeDeposition", blp_ppc_chd); + WARPX_PROFILE_VAR_NS("WarpXParticleContainer::DepositCharge::Accumulate", blp_accumulate); // Get tile box where charge is deposited. // The tile box is different when depositing in the buffers (depos_levixType().toIntVect() ); + // Staggered tile box + Box tb = amrex::convert( tilebox, rho->ixType().toIntVect() ); + + tilebox.grow(ng_rho); const int nc = WarpX::ncomps; #ifdef AMREX_USE_GPU - // No tiling on GPU: rho_fab points to the full rho array. + // GPU, no tiling: rho_fab points to the full rho array MultiFab rhoi(*rho, amrex::make_alias, icomp*nc, nc); auto & rho_fab = rhoi.get(pti); #else - // Tiling is on: rho_fab points to local_rho[thread_num] + tb.grow(ng_rho); + // CPU, tiling: rho_fab points to local_rho[thread_num] local_rho[thread_num].resize(tb, nc); // local_rho[thread_num] is set to zero @@ -462,25 +496,28 @@ WarpXParticleContainer::DepositCharge (WarpXParIter& pti, RealVector& wp, auto & rho_fab = local_rho[thread_num]; #endif - // GPU, no tiling: deposit directly in rho - // CPU, tiling: deposit into local_rho const auto GetPosition = GetParticlePosition(pti, offset); // Lower corner of tile box physical domain // Note that this includes guard cells since it is after tilebox.ngrow - const auto& warpx_instance = WarpX::GetInstance(); - Real cur_time = warpx_instance.gett_new(lev); - Real dt = warpx_instance.getdt(lev); - const auto& time_of_last_gal_shift = warpx_instance.time_of_last_gal_shift; + Real cur_time = warpx.gett_new(lev); + Real dt = warpx.getdt(lev); + const auto& time_of_last_gal_shift = warpx.time_of_last_gal_shift; // Take into account Galilean shift Real time_shift_rho_old = (cur_time - time_of_last_gal_shift); Real time_shift_rho_new = (cur_time + dt - time_of_last_gal_shift); amrex::Array galilean_shift; if (icomp==0){ - galilean_shift = { v_galilean[0]*time_shift_rho_old, v_galilean[1]*time_shift_rho_old, v_galilean[2]*time_shift_rho_old }; + galilean_shift = { + m_v_galilean[0]*time_shift_rho_old, + m_v_galilean[1]*time_shift_rho_old, + m_v_galilean[2]*time_shift_rho_old }; } else{ - galilean_shift = { v_galilean[0]*time_shift_rho_new, v_galilean[1]*time_shift_rho_new, v_galilean[2]*time_shift_rho_new }; + galilean_shift = { + m_v_galilean[0]*time_shift_rho_new, + m_v_galilean[1]*time_shift_rho_new, + m_v_galilean[2]*time_shift_rho_new }; } const std::array& xyzmin = WarpX::LowerCorner(tilebox, galilean_shift, depos_lev); @@ -504,10 +541,9 @@ WarpXParticleContainer::DepositCharge (WarpXParIter& pti, RealVector& wp, WARPX_PROFILE_VAR_STOP(blp_ppc_chd); #ifndef AMREX_USE_GPU + // CPU, tiling: atomicAdd local_rho into rho WARPX_PROFILE_VAR_START(blp_accumulate); - (*rho)[pti].atomicAdd(local_rho[thread_num], tb, tb, 0, icomp*nc, nc); - WARPX_PROFILE_VAR_STOP(blp_accumulate); #endif } @@ -557,6 +593,8 @@ WarpXParticleContainer::DepositCharge (amrex::VectorParticleBoxArray(lev); const auto& dm = m_gdb->DistributionMap(lev); BoxArray nba = ba; +#if (!defined WARPX_DIM_RZ) || (!defined WARPX_USE_PSATD) nba.surroundingNodes(); +#endif - const int ng = WarpX::nox; + // Number of guard cells for local deposition of rho + WarpX& warpx = WarpX::GetInstance(); + const int ng_rho = warpx.get_ng_depos_rho().max(); - auto rho = std::unique_ptr(new MultiFab(nba,dm,WarpX::ncomps,ng)); + auto rho = std::unique_ptr(new MultiFab(nba,dm,WarpX::ncomps,ng_rho)); rho->setVal(0.0); #ifdef _OPENMP @@ -782,7 +824,7 @@ WarpXParticleContainer::PushX (amrex::Real dt) void WarpXParticleContainer::PushX (int lev, amrex::Real dt) { - WARPX_PROFILE("WPC::PushX()"); + WARPX_PROFILE("WarpXParticleContainer::PushX()"); if (do_not_push) return; @@ -859,6 +901,8 @@ WarpXParticleContainer::particlePostLocate(ParticleType& p, const ParticleLocData& pld, const int lev) { + if (not do_splitting) return; + // Tag particle if goes to higher level. // It will be split later in the loop if (pld.m_lev == lev+1 @@ -873,3 +917,45 @@ WarpXParticleContainer::particlePostLocate(ParticleType& p, // to lower level. } } + +void +WarpXParticleContainer::ApplyBoundaryConditions (ParticleBC boundary_conditions){ + WARPX_PROFILE("WarpXParticleContainer::ApplyBoundaryConditions()"); + for (int lev = 0; lev <= finestLevel(); ++lev) + { + for (WarpXParIter pti(*this, lev); pti.isValid(); ++pti) + { + auto GetPosition = GetParticlePosition(pti); + const Real xmin = Geom(lev).ProbLo(0); + const Real xmax = Geom(lev).ProbHi(0); +#ifdef WARPX_DIM_3D + const Real ymin = Geom(lev).ProbLo(1); + const Real ymax = Geom(lev).ProbHi(1); +#endif + const Real zmin = Geom(lev).ProbLo(AMREX_SPACEDIM-1); + const Real zmax = Geom(lev).ProbHi(AMREX_SPACEDIM-1); + + ParticleTileType& ptile = ParticlesAt(lev, pti); + ParticleType * const pp = ptile.GetArrayOfStructs()().data(); + + // Loop over particles and apply BC to each particle + amrex::ParallelFor( + pti.numParticles(), + [=] AMREX_GPU_DEVICE (long i) { + ParticleType& p = pp[i]; + ParticleReal x, y, z; + GetPosition(i, x, y, z); +#ifdef WARPX_DIM_3D + if (x < xmin || x > xmax || y < ymin || y > ymax || z < zmin || z > zmax){ + if (boundary_conditions == ParticleBC::absorbing) p.id() = -1; + } +#else + if (x < xmin || x > xmax || z < zmin || z > zmax){ + if (boundary_conditions == ParticleBC::absorbing) p.id() = -1; + } +#endif + } + ); + } + } +} diff --git a/Source/Python/WarpXWrappers.cpp b/Source/Python/WarpXWrappers.cpp index db1f03e4a72..0944e1c06e1 100644 --- a/Source/Python/WarpXWrappers.cpp +++ b/Source/Python/WarpXWrappers.cpp @@ -89,7 +89,7 @@ extern "C" int warpx_nSpecies() { - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); return mypc.nSpecies(); } @@ -239,8 +239,8 @@ extern "C" } long warpx_getNumParticles(int speciesnumber) { - auto & mypc = WarpX::GetInstance().GetPartContainer(); - auto & myspc = mypc.GetParticleContainer(speciesnumber); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & myspc = mypc.GetParticleContainer(speciesnumber); return myspc.TotalNumberOfParticles(); } @@ -258,29 +258,29 @@ extern "C" return getMultiFabLoVects(mf, return_size, ngrow); \ } - WARPX_GET_FIELD(warpx_getEfield, WarpX::GetInstance().getEfield); - WARPX_GET_FIELD(warpx_getEfieldCP, WarpX::GetInstance().getEfield_cp); - WARPX_GET_FIELD(warpx_getEfieldFP, WarpX::GetInstance().getEfield_fp); + WARPX_GET_FIELD(warpx_getEfield, WarpX::GetInstance().getEfield) + WARPX_GET_FIELD(warpx_getEfieldCP, WarpX::GetInstance().getEfield_cp) + WARPX_GET_FIELD(warpx_getEfieldFP, WarpX::GetInstance().getEfield_fp) - WARPX_GET_FIELD(warpx_getBfield, WarpX::GetInstance().getBfield); - WARPX_GET_FIELD(warpx_getBfieldCP, WarpX::GetInstance().getBfield_cp); - WARPX_GET_FIELD(warpx_getBfieldFP, WarpX::GetInstance().getBfield_fp); + WARPX_GET_FIELD(warpx_getBfield, WarpX::GetInstance().getBfield) + WARPX_GET_FIELD(warpx_getBfieldCP, WarpX::GetInstance().getBfield_cp) + WARPX_GET_FIELD(warpx_getBfieldFP, WarpX::GetInstance().getBfield_fp) - WARPX_GET_FIELD(warpx_getCurrentDensity, WarpX::GetInstance().getcurrent); - WARPX_GET_FIELD(warpx_getCurrentDensityCP, WarpX::GetInstance().getcurrent_cp); - WARPX_GET_FIELD(warpx_getCurrentDensityFP, WarpX::GetInstance().getcurrent_fp); + WARPX_GET_FIELD(warpx_getCurrentDensity, WarpX::GetInstance().getcurrent) + WARPX_GET_FIELD(warpx_getCurrentDensityCP, WarpX::GetInstance().getcurrent_cp) + WARPX_GET_FIELD(warpx_getCurrentDensityFP, WarpX::GetInstance().getcurrent_fp) - WARPX_GET_LOVECTS(warpx_getEfieldLoVects, WarpX::GetInstance().getEfield); - WARPX_GET_LOVECTS(warpx_getEfieldCPLoVects, WarpX::GetInstance().getEfield_cp); - WARPX_GET_LOVECTS(warpx_getEfieldFPLoVects, WarpX::GetInstance().getEfield_fp); + WARPX_GET_LOVECTS(warpx_getEfieldLoVects, WarpX::GetInstance().getEfield) + WARPX_GET_LOVECTS(warpx_getEfieldCPLoVects, WarpX::GetInstance().getEfield_cp) + WARPX_GET_LOVECTS(warpx_getEfieldFPLoVects, WarpX::GetInstance().getEfield_fp) - WARPX_GET_LOVECTS(warpx_getBfieldLoVects, WarpX::GetInstance().getBfield); - WARPX_GET_LOVECTS(warpx_getBfieldCPLoVects, WarpX::GetInstance().getBfield_cp); - WARPX_GET_LOVECTS(warpx_getBfieldFPLoVects, WarpX::GetInstance().getBfield_fp); + WARPX_GET_LOVECTS(warpx_getBfieldLoVects, WarpX::GetInstance().getBfield) + WARPX_GET_LOVECTS(warpx_getBfieldCPLoVects, WarpX::GetInstance().getBfield_cp) + WARPX_GET_LOVECTS(warpx_getBfieldFPLoVects, WarpX::GetInstance().getBfield_fp) - WARPX_GET_LOVECTS(warpx_getCurrentDensityLoVects, WarpX::GetInstance().getcurrent); - WARPX_GET_LOVECTS(warpx_getCurrentDensityCPLoVects, WarpX::GetInstance().getcurrent_cp); - WARPX_GET_LOVECTS(warpx_getCurrentDensityFPLoVects, WarpX::GetInstance().getcurrent_fp); + WARPX_GET_LOVECTS(warpx_getCurrentDensityLoVects, WarpX::GetInstance().getcurrent) + WARPX_GET_LOVECTS(warpx_getCurrentDensityCPLoVects, WarpX::GetInstance().getcurrent_cp) + WARPX_GET_LOVECTS(warpx_getCurrentDensityFPLoVects, WarpX::GetInstance().getcurrent_fp) int* warpx_getEx_nodal_flag() {return getFieldNodalFlagData( WarpX::GetInstance().getEfield(0,0) );} int* warpx_getEy_nodal_flag() {return getFieldNodalFlagData( WarpX::GetInstance().getEfield(0,1) );} @@ -307,11 +307,11 @@ extern "C" return getMultiFabLoVects(mf, return_size, ngrow); \ } - WARPX_GET_SCALAR(warpx_getChargeDensityCP, WarpX::GetInstance().getrho_cp); - WARPX_GET_SCALAR(warpx_getChargeDensityFP, WarpX::GetInstance().getrho_fp); + WARPX_GET_SCALAR(warpx_getChargeDensityCP, WarpX::GetInstance().getrho_cp) + WARPX_GET_SCALAR(warpx_getChargeDensityFP, WarpX::GetInstance().getrho_fp) - WARPX_GET_LOVECTS_SCALAR(warpx_getChargeDensityCPLoVects, WarpX::GetInstance().getrho_cp); - WARPX_GET_LOVECTS_SCALAR(warpx_getChargeDensityFPLoVects, WarpX::GetInstance().getrho_fp); + WARPX_GET_LOVECTS_SCALAR(warpx_getChargeDensityCPLoVects, WarpX::GetInstance().getrho_cp) + WARPX_GET_LOVECTS_SCALAR(warpx_getChargeDensityFPLoVects, WarpX::GetInstance().getrho_fp) #define WARPX_GET_FIELD_PML(FIELD, GETTER) \ amrex::Real** FIELD(int lev, int direction, \ @@ -337,22 +337,22 @@ extern "C" } \ } - WARPX_GET_FIELD_PML(warpx_getEfieldCP_PML, GetE_cp); - WARPX_GET_FIELD_PML(warpx_getEfieldFP_PML, GetE_fp); - WARPX_GET_FIELD_PML(warpx_getBfieldCP_PML, GetB_cp); - WARPX_GET_FIELD_PML(warpx_getBfieldFP_PML, GetB_fp); - WARPX_GET_FIELD_PML(warpx_getCurrentDensityCP_PML, Getj_cp); - WARPX_GET_FIELD_PML(warpx_getCurrentDensityFP_PML, Getj_fp); - WARPX_GET_LOVECTS_PML(warpx_getEfieldCPLoVects_PML, GetE_cp); - WARPX_GET_LOVECTS_PML(warpx_getEfieldFPLoVects_PML, GetE_fp); - WARPX_GET_LOVECTS_PML(warpx_getBfieldCPLoVects_PML, GetB_cp); - WARPX_GET_LOVECTS_PML(warpx_getBfieldFPLoVects_PML, GetB_fp); - WARPX_GET_LOVECTS_PML(warpx_getCurrentDensityCPLoVects_PML, Getj_cp); - WARPX_GET_LOVECTS_PML(warpx_getCurrentDensityFPLoVects_PML, Getj_fp); + WARPX_GET_FIELD_PML(warpx_getEfieldCP_PML, GetE_cp) + WARPX_GET_FIELD_PML(warpx_getEfieldFP_PML, GetE_fp) + WARPX_GET_FIELD_PML(warpx_getBfieldCP_PML, GetB_cp) + WARPX_GET_FIELD_PML(warpx_getBfieldFP_PML, GetB_fp) + WARPX_GET_FIELD_PML(warpx_getCurrentDensityCP_PML, Getj_cp) + WARPX_GET_FIELD_PML(warpx_getCurrentDensityFP_PML, Getj_fp) + WARPX_GET_LOVECTS_PML(warpx_getEfieldCPLoVects_PML, GetE_cp) + WARPX_GET_LOVECTS_PML(warpx_getEfieldFPLoVects_PML, GetE_fp) + WARPX_GET_LOVECTS_PML(warpx_getBfieldCPLoVects_PML, GetB_cp) + WARPX_GET_LOVECTS_PML(warpx_getBfieldFPLoVects_PML, GetB_fp) + WARPX_GET_LOVECTS_PML(warpx_getCurrentDensityCPLoVects_PML, Getj_cp) + WARPX_GET_LOVECTS_PML(warpx_getCurrentDensityFPLoVects_PML, Getj_fp) amrex::ParticleReal** warpx_getParticleStructs(int speciesnumber, int lev, int* num_tiles, int** particles_per_tile) { - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); auto & myspc = mypc.GetParticleContainer(speciesnumber); int i = 0; @@ -374,7 +374,7 @@ extern "C" amrex::ParticleReal** warpx_getParticleArrays(int speciesnumber, int comp, int lev, int* num_tiles, int** particles_per_tile) { - auto & mypc = WarpX::GetInstance().GetPartContainer(); + const auto & mypc = WarpX::GetInstance().GetPartContainer(); auto & myspc = mypc.GetParticleContainer(speciesnumber); int i = 0; diff --git a/Source/Utils/CMakeLists.txt b/Source/Utils/CMakeLists.txt index bd500e4e5eb..40d3a2c31e9 100644 --- a/Source/Utils/CMakeLists.txt +++ b/Source/Utils/CMakeLists.txt @@ -4,6 +4,9 @@ target_sources(WarpX CoarsenMR.cpp Interpolate.cpp IntervalsParser.cpp + MPIInitHelpers.cpp + ParticleUtils.cpp + RelativeCellPosition.cpp WarpXAlgorithmSelection.cpp WarpXMovingWindow.cpp WarpXTagging.cpp diff --git a/Source/Utils/CoarsenIO.H b/Source/Utils/CoarsenIO.H index 670fcb1300b..594af2a5bfc 100644 --- a/Source/Utils/CoarsenIO.H +++ b/Source/Utils/CoarsenIO.H @@ -28,9 +28,9 @@ namespace CoarsenIO{ AMREX_GPU_DEVICE AMREX_FORCE_INLINE Real Interp ( Array4 const& arr_src, - int const* const AMREX_RESTRICT sf, - int const* const AMREX_RESTRICT sc, - int const* const AMREX_RESTRICT cr, + GpuArray const& sf, + GpuArray const& sc, + GpuArray const& cr, const int i, const int j, const int k, diff --git a/Source/Utils/CoarsenIO.cpp b/Source/Utils/CoarsenIO.cpp index b75d054b401..93cac152f2b 100644 --- a/Source/Utils/CoarsenIO.cpp +++ b/Source/Utils/CoarsenIO.cpp @@ -22,39 +22,34 @@ CoarsenIO::Loop ( MultiFab& mf_dst, "source fine MultiFab does not have enough guard cells for this interpolation" ); // Auxiliary integer arrays (always 3D) - Gpu::ManagedVector sf_gpuarr, sc_gpuarr, cr_gpuarr; - sf_gpuarr.resize( 3 ); // staggering of source fine MultiFab - sc_gpuarr.resize( 3 ); // staggering of destination coarse MultiFab - cr_gpuarr.resize( 3 ); // coarsening ratio + GpuArray sf; // staggering of source fine MultiFab + GpuArray sc; // staggering of destination coarse MultiFab + GpuArray cr; // coarsening ratio - sf_gpuarr[0] = stag_src[0]; - sf_gpuarr[1] = stag_src[1]; + sf[0] = stag_src[0]; + sf[1] = stag_src[1]; #if (AMREX_SPACEDIM == 2) - sf_gpuarr[2] = 0; + sf[2] = 0; #elif (AMREX_SPACEDIM == 3) - sf_gpuarr[2] = stag_src[2]; + sf[2] = stag_src[2]; #endif - sc_gpuarr[0] = stag_dst[0]; - sc_gpuarr[1] = stag_dst[1]; + sc[0] = stag_dst[0]; + sc[1] = stag_dst[1]; #if (AMREX_SPACEDIM == 2) - sc_gpuarr[2] = 0; + sc[2] = 0; #elif (AMREX_SPACEDIM == 3) - sc_gpuarr[2] = stag_dst[2]; + sc[2] = stag_dst[2]; #endif - cr_gpuarr[0] = crse_ratio[0]; - cr_gpuarr[1] = crse_ratio[1]; + cr[0] = crse_ratio[0]; + cr[1] = crse_ratio[1]; #if (AMREX_SPACEDIM == 2) - cr_gpuarr[2] = 1; + cr[2] = 1; #elif (AMREX_SPACEDIM == 3) - cr_gpuarr[2] = crse_ratio[2]; + cr[2] = crse_ratio[2]; #endif - int const* const AMREX_RESTRICT sf = sf_gpuarr.data(); - int const* const AMREX_RESTRICT sc = sc_gpuarr.data(); - int const* const AMREX_RESTRICT cr = cr_gpuarr.data(); - #ifdef _OPENMP #pragma omp parallel if (Gpu::notInLaunchRegion()) #endif @@ -83,7 +78,7 @@ CoarsenIO::Coarsen ( MultiFab& mf_dst, const int ngrow, const IntVect crse_ratio ) { - BL_PROFILE( "CoarsenIO::Coarsen" ); + BL_PROFILE("CoarsenIO::Coarsen()"); // Convert BoxArray of source MultiFab to staggering of destination MultiFab and coarsen it BoxArray ba_tmp = amrex::convert( mf_src.boxArray(), mf_dst.ixType().toIntVect() ); diff --git a/Source/Utils/CoarsenMR.H b/Source/Utils/CoarsenMR.H index 4a64a62c853..0ece65a57cc 100644 --- a/Source/Utils/CoarsenMR.H +++ b/Source/Utils/CoarsenMR.H @@ -28,9 +28,9 @@ namespace CoarsenMR{ AMREX_GPU_DEVICE AMREX_FORCE_INLINE Real Interp ( Array4 const& arr_src, - int const* const AMREX_RESTRICT sf, - int const* const AMREX_RESTRICT sc, - int const* const AMREX_RESTRICT cr, + GpuArray const& sf, + GpuArray const& sc, + GpuArray const& cr, const int i, const int j, const int k, diff --git a/Source/Utils/CoarsenMR.cpp b/Source/Utils/CoarsenMR.cpp index 52f2dfa3ff8..9228f471e45 100644 --- a/Source/Utils/CoarsenMR.cpp +++ b/Source/Utils/CoarsenMR.cpp @@ -14,39 +14,34 @@ CoarsenMR::Loop ( MultiFab& mf_dst, const IntVect stag_dst = mf_dst.boxArray().ixType().toIntVect(); // Auxiliary integer arrays (always 3D) - Gpu::ManagedVector sf_gpuarr, sc_gpuarr, cr_gpuarr; - sf_gpuarr.resize( 3 ); // staggering of source fine MultiFab - sc_gpuarr.resize( 3 ); // staggering of destination coarse MultiFab - cr_gpuarr.resize( 3 ); // coarsening ratio + GpuArray sf; // staggering of source fine MultiFab + GpuArray sc; // staggering of destination coarse MultiFab + GpuArray cr; // coarsening ratio - sf_gpuarr[0] = stag_src[0]; - sf_gpuarr[1] = stag_src[1]; + sf[0] = stag_src[0]; + sf[1] = stag_src[1]; #if (AMREX_SPACEDIM == 2) - sf_gpuarr[2] = 0; + sf[2] = 0; #elif (AMREX_SPACEDIM == 3) - sf_gpuarr[2] = stag_src[2]; + sf[2] = stag_src[2]; #endif - sc_gpuarr[0] = stag_dst[0]; - sc_gpuarr[1] = stag_dst[1]; + sc[0] = stag_dst[0]; + sc[1] = stag_dst[1]; #if (AMREX_SPACEDIM == 2) - sc_gpuarr[2] = 0; + sc[2] = 0; #elif (AMREX_SPACEDIM == 3) - sc_gpuarr[2] = stag_dst[2]; + sc[2] = stag_dst[2]; #endif - cr_gpuarr[0] = crse_ratio[0]; - cr_gpuarr[1] = crse_ratio[1]; + cr[0] = crse_ratio[0]; + cr[1] = crse_ratio[1]; #if (AMREX_SPACEDIM == 2) - cr_gpuarr[2] = 1; + cr[2] = 1; #elif (AMREX_SPACEDIM == 3) - cr_gpuarr[2] = crse_ratio[2]; + cr[2] = crse_ratio[2]; #endif - int const* const AMREX_RESTRICT sf = sf_gpuarr.data(); - int const* const AMREX_RESTRICT sc = sc_gpuarr.data(); - int const* const AMREX_RESTRICT cr = cr_gpuarr.data(); - #ifdef _OPENMP #pragma omp parallel if (Gpu::notInLaunchRegion()) #endif @@ -71,7 +66,7 @@ CoarsenMR::Coarsen ( MultiFab& mf_dst, const MultiFab& mf_src, const IntVect crse_ratio ) { - BL_PROFILE( "CoarsenMR::Coarsen" ); + BL_PROFILE("CoarsenMR::Coarsen()"); AMREX_ALWAYS_ASSERT_WITH_MESSAGE( mf_src.ixType() == mf_dst.ixType(), "source MultiFab and destination MultiFab have different IndexType" ); diff --git a/Source/Utils/Interpolate.cpp b/Source/Utils/Interpolate.cpp index b5d713b7524..11d6989a05b 100644 --- a/Source/Utils/Interpolate.cpp +++ b/Source/Utils/Interpolate.cpp @@ -56,7 +56,7 @@ namespace Interpolate const MultiFab* Fy_fp, const MultiFab* Fz_fp, const DistributionMapping& dm, const int r_ratio, - const Real* dx, const int ngrow ) + const Real* /*dx*/, const int ngrow ) { // Prepare the structure that will contain the returned fields diff --git a/Source/Utils/IntervalsParser.H b/Source/Utils/IntervalsParser.H index 96cbca7cbf9..b89d8502b45 100644 --- a/Source/Utils/IntervalsParser.H +++ b/Source/Utils/IntervalsParser.H @@ -1,6 +1,10 @@ #ifndef WARPX_INTERVALSPARSER_H_ #define WARPX_INTERVALSPARSER_H_ +#include +#include +#include + /** * \brief This class is a parser for slices of the form i:j:k where i, j and k are integers * representing respectively the starting point, the stopping point and the period. @@ -86,10 +90,11 @@ public: /** * \brief Constructor of the IntervalsParser class. * - * @param[in] instr an input string of the form "x,y,z,...". This will call the constructor of - * SliceParser using x, y and z as input arguments. + * @param[in] instr_vec an input vector string, which when concatenated is of the form + * "x,y,z,...". This will call the constructor of SliceParser using x, y and z as input + * arguments. */ - IntervalsParser (const std::string& instr); + IntervalsParser (const std::vector& instr_vec); /** * \brief A method that returns true if the input integer is contained in any of the slices diff --git a/Source/Utils/IntervalsParser.cpp b/Source/Utils/IntervalsParser.cpp index 657911524af..a98bf57a286 100644 --- a/Source/Utils/IntervalsParser.cpp +++ b/Source/Utils/IntervalsParser.cpp @@ -58,11 +58,14 @@ int SliceParser::getStart () const {return m_start;} int SliceParser::getStop () const {return m_stop;} -IntervalsParser::IntervalsParser (const std::string& instr) +IntervalsParser::IntervalsParser (const std::vector& instr_vec) { - auto insplit = WarpXUtilStr::split>(instr, m_separator); + std::string inconcatenated; + for (const auto& instr_element : instr_vec) inconcatenated +=instr_element; - for(int i=0; i>(inconcatenated, m_separator); + + for(int i=0; i(insplit.size()); i++) { SliceParser temp_slice(insplit[i]); m_slices.push_back(temp_slice); @@ -73,7 +76,7 @@ IntervalsParser::IntervalsParser (const std::string& instr) bool IntervalsParser::contains (const int n) const { - for(int i=0; i(m_slices.size()); i++){ if (m_slices[i].contains(n)) return true; } return false; @@ -82,7 +85,7 @@ bool IntervalsParser::contains (const int n) const int IntervalsParser::nextContains (const int n) const { int next = std::numeric_limits::max(); - for(int i=0; i(m_slices.size()); i++){ next = std::min(m_slices[i].nextContains(n),next); } return next; @@ -91,7 +94,7 @@ int IntervalsParser::nextContains (const int n) const int IntervalsParser::previousContains (const int n) const { int previous = 0; - for(int i=0; i(m_slices.size()); i++){ previous = std::max(m_slices[i].previousContains(n),previous); } return previous; diff --git a/Source/Utils/MPIInitHelpers.H b/Source/Utils/MPIInitHelpers.H new file mode 100644 index 00000000000..34d98bcb28f --- /dev/null +++ b/Source/Utils/MPIInitHelpers.H @@ -0,0 +1,34 @@ +/* Copyright 2020 Axel Huebl + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_MPI_INIT_HELPERS_H_ +#define WARPX_MPI_INIT_HELPERS_H_ + +#include +#include + + +namespace utils +{ + /** Initialize MPI + * + * @return pair(required, provided) of MPI thread level from MPI_Init_thread + */ + std::pair< int, int > + warpx_mpi_init (int argc, char* argv[]); + + /** Check if the requested MPI thread level is valid + * + * Prints warnings and notes otherwise. + * + * @param mpi_thread_levels pair(required, provided) of MPI thread level from MPI_Init_thread + */ + void + warpx_check_mpi_thread_level (std::pair< int, int > const mpi_thread_levels); + +} // namespace utils + +#endif // WARPX_MPI_INIT_HELPERS_H_ diff --git a/Source/Utils/MPIInitHelpers.cpp b/Source/Utils/MPIInitHelpers.cpp new file mode 100644 index 00000000000..ccf28134ca4 --- /dev/null +++ b/Source/Utils/MPIInitHelpers.cpp @@ -0,0 +1,58 @@ +/* Copyright 2020 Axel Huebl + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "MPIInitHelpers.H" + +#include +#include +#include + +#include +#include + + +namespace utils +{ + std::pair< int, int > + warpx_mpi_init (int argc, char* argv[]) + { + int thread_required = -1; + int thread_provided = -1; +#ifdef AMREX_USE_MPI + thread_required = MPI_THREAD_SINGLE; // equiv. to MPI_Init +# ifdef AMREX_USE_OMP + thread_required = MPI_THREAD_FUNNELED; +# endif +# ifdef AMREX_MPI_THREAD_MULTIPLE // i.e. for async_io + thread_required = MPI_THREAD_MULTIPLE; +# endif + MPI_Init_thread(&argc, &argv, thread_required, &thread_provided); +#endif + return std::make_pair(thread_required, thread_provided); + } + + void + warpx_check_mpi_thread_level (std::pair< int, int > const mpi_thread_levels) + { +#ifdef AMREX_USE_MPI + auto const thread_required = mpi_thread_levels.first; + auto const thread_provided = mpi_thread_levels.second; + auto mtn = amrex::ParallelDescriptor::mpi_level_to_string; + + if( thread_provided < thread_required ) + amrex::Print() << "WARNING: Provided MPI thread safety level (" + << mtn(thread_provided) << ") is LOWER than requested " + << mtn(thread_required) << "). This might lead to undefined " + << "results in asynchronous operations (e.g. async_io)."; + if( thread_provided > thread_required ) + amrex::Print() << "NOTE: Provided MPI thread safety level (" + << mtn(thread_provided) << ") is stricter than requested " + << mtn(thread_required) << "). This might reduce multi-node " + << "communication performance."; +#endif + } + +} // namespace utils diff --git a/Source/Utils/Make.package b/Source/Utils/Make.package index 8abad53fdc2..3b1ff6a84e8 100644 --- a/Source/Utils/Make.package +++ b/Source/Utils/Make.package @@ -6,5 +6,8 @@ CEXE_sources += CoarsenIO.cpp CEXE_sources += CoarsenMR.cpp CEXE_sources += Interpolate.cpp CEXE_sources += IntervalsParser.cpp +CEXE_sources += MPIInitHelpers.cpp +CEXE_sources += RelativeCellPosition.cpp +CEXE_sources += ParticleUtils.cpp VPATH_LOCATIONS += $(WARPX_HOME)/Source/Utils diff --git a/Source/Utils/NCIGodfreyTables.H b/Source/Utils/NCIGodfreyTables.H index 0403b10c320..8f69ae9fd72 100644 --- a/Source/Utils/NCIGodfreyTables.H +++ b/Source/Utils/NCIGodfreyTables.H @@ -19,214 +19,214 @@ const int tab_length = 101; // Each line stands for one value of c*dt/dz, between 0 and 1. // We typically interpolate between two lines. const amrex::Real table_nci_godfrey_galerkin_Ex_Ey_Bz[tab_length][tab_width]{ - -2.47536,2.04288,-0.598163,0.0314711, - -2.47536,2.04288,-0.598163,0.0314711, - -2.47545,2.04309,-0.598307,0.0315029, - -2.4756,2.04342,-0.598549,0.0315558, - -2.47581,2.0439,-0.598886,0.0316298, - -2.47608,2.0445,-0.59932,0.031725, - -2.47641,2.04525,-0.59985,0.0318412, - -2.4768,2.04612,-0.600477,0.0319785, - -2.47725,2.04714,-0.6012,0.0321367, - -2.47776,2.04829,-0.602019,0.0323158, - -2.47833,2.04957,-0.602934,0.0325158, - -2.47896,2.05099,-0.603944,0.0327364, - -2.47965,2.05254,-0.605051,0.0329777, - -2.4804,2.05423,-0.606253,0.0332396, - -2.48121,2.05606,-0.60755,0.0335218, - -2.48208,2.05802,-0.608942,0.0338243, - -2.48301,2.06012,-0.610429,0.0341469, - -2.48401,2.06235,-0.61201,0.0344895, - -2.48506,2.06471,-0.613685,0.0348519, - -2.48618,2.06721,-0.615453,0.0352339, - -2.48735,2.06984,-0.617314,0.0356353, - -2.48859,2.07261,-0.619268,0.0360559, - -2.48988,2.0755,-0.621312,0.0364954, - -2.49123,2.07853,-0.623447,0.0369536, - -2.49265,2.08169,-0.625672,0.0374302, - -2.49412,2.08498,-0.627986,0.0379248, - -2.49565,2.0884,-0.630386,0.0384372, - -2.49724,2.09194,-0.632873,0.0389669, - -2.49888,2.09561,-0.635443,0.0395135, - -2.50058,2.09939,-0.638096,0.0400766, - -2.50234,2.1033,-0.640829,0.0406557, - -2.50415,2.10732,-0.64364,0.0412502, - -2.50601,2.11145,-0.646526,0.0418594, - -2.50791,2.1157,-0.649485,0.0424828, - -2.50987,2.12004,-0.652512,0.0431196, - -2.51187,2.12448,-0.655604,0.0437688, - -2.51392,2.12901,-0.658756,0.0444297, - -2.516,2.13363,-0.661964,0.0451011, - -2.51812,2.13832,-0.665221,0.0457818, - -2.52027,2.14308,-0.668521,0.0464705, - -2.52244,2.14789,-0.671856,0.0471658, - -2.52464,2.15274,-0.675218,0.0478658, - -2.52684,2.15762,-0.678596,0.0485687, - -2.52906,2.16251,-0.68198,0.0492723, - -2.53126,2.16738,-0.685355,0.049974, - -2.53345,2.17222,-0.688706,0.0506708, - -2.53561,2.177,-0.692015,0.0513594, - -2.53773,2.18168,-0.69526,0.0520359, - -2.53978,2.18623,-0.698416,0.0526955, - -2.54175,2.19059,-0.701452,0.053333, - -2.5436,2.19471,-0.704331,0.0539417, - -2.54531,2.19852,-0.70701,0.0545141, - -2.54683,2.20193,-0.709433,0.0550409, - -2.5481,2.20483,-0.711533,0.0555106, - -2.54906,2.20709,-0.713224,0.0559094, - -2.54963,2.20852,-0.714397,0.0562198, - -2.54968,2.20888,-0.714907,0.0564196, - -2.54905,2.20785,-0.714562,0.0564797, - -2.54751,2.20496,-0.713094,0.0563618, - -2.54472,2.19955,-0.710118,0.0560124, - -2.54014,2.19058,-0.705048,0.0553544, - -2.53286,2.1763,-0.69693,0.0542684, - -2.52115,2.15344,-0.684027,0.05255, - -2.50098,2.11466,-0.66255,0.0497817, - -2.45797,2.03459,-0.620099,0.0446889, - -2.28371,1.72254,-0.465905,0.0283268, - -2.4885,2.04899,-0.599292,0.0390466, - -2.1433,1.36735,-0.220924,-0.00215633, - -2.4943,2.07019,-0.610552,0.035166, - -2.84529,2.77303,-1.00018,0.0724884, - -2.72242,2.51888,-0.847226,0.0509964, - -2.65633,2.3744,-0.750392,0.0326366, - -2.59601,2.23412,-0.646421,0.00868027, - -2.51477,2.0369,-0.491066,-0.0306397, - -2.35935,1.65155,-0.178971,-0.112713, - -1.84315,0.361693,0.876104,-0.393844, - -2.65422,2.39262,-0.789663,0.0516265, - -3.46529,4.42354,-2.45543,0.497097, - -3.15747,3.65311,-1.824,0.328432, - -3.04694,3.37613,-1.59668,0.267631, - -2.99205,3.23814,-1.48302,0.237103, - -2.96075,3.15894,-1.41733,0.219317, - -2.94172,3.11028,-1.37649,0.20811, - -2.92994,3.07962,-1.35025,0.200755, - -2.92283,3.06054,-1.33338,0.195859, - -2.91894,3.04938,-1.3229,0.192637, - -2.91736,3.04394,-1.31702,0.190612, - -2.91753,3.04278,-1.31456,0.189477, - -2.91905,3.04494,-1.31475,0.189026, - -2.92165,3.04973,-1.31705,0.189117, - -2.92512,3.05667,-1.32105,0.189646, - -2.92933,3.06539,-1.32646,0.190538, - -2.93416,3.07562,-1.33308,0.191735, - -2.93952,3.08715,-1.34072,0.193194, - -2.94535,3.09982,-1.34925,0.194881, - -2.95159,3.11349,-1.35858,0.196769, - -2.9582,3.12805,-1.36861,0.198838, - -2.96514,3.14342,-1.37929,0.201068, - -2.97239,3.15953,-1.39055,0.203448, - -2.97991,3.17632,-1.40234,0.205964, - -2.98769,3.19374,-1.41463,0.208607 + {-2.47536,2.04288,-0.598163,0.0314711}, + {-2.47536,2.04288,-0.598163,0.0314711}, + {-2.47545,2.04309,-0.598307,0.0315029}, + {-2.4756,2.04342,-0.598549,0.0315558}, + {-2.47581,2.0439,-0.598886,0.0316298}, + {-2.47608,2.0445,-0.59932,0.031725}, + {-2.47641,2.04525,-0.59985,0.0318412}, + {-2.4768,2.04612,-0.600477,0.0319785}, + {-2.47725,2.04714,-0.6012,0.0321367}, + {-2.47776,2.04829,-0.602019,0.0323158}, + {-2.47833,2.04957,-0.602934,0.0325158}, + {-2.47896,2.05099,-0.603944,0.0327364}, + {-2.47965,2.05254,-0.605051,0.0329777}, + {-2.4804,2.05423,-0.606253,0.0332396}, + {-2.48121,2.05606,-0.60755,0.0335218}, + {-2.48208,2.05802,-0.608942,0.0338243}, + {-2.48301,2.06012,-0.610429,0.0341469}, + {-2.48401,2.06235,-0.61201,0.0344895}, + {-2.48506,2.06471,-0.613685,0.0348519}, + {-2.48618,2.06721,-0.615453,0.0352339}, + {-2.48735,2.06984,-0.617314,0.0356353}, + {-2.48859,2.07261,-0.619268,0.0360559}, + {-2.48988,2.0755,-0.621312,0.0364954}, + {-2.49123,2.07853,-0.623447,0.0369536}, + {-2.49265,2.08169,-0.625672,0.0374302}, + {-2.49412,2.08498,-0.627986,0.0379248}, + {-2.49565,2.0884,-0.630386,0.0384372}, + {-2.49724,2.09194,-0.632873,0.0389669}, + {-2.49888,2.09561,-0.635443,0.0395135}, + {-2.50058,2.09939,-0.638096,0.0400766}, + {-2.50234,2.1033,-0.640829,0.0406557}, + {-2.50415,2.10732,-0.64364,0.0412502}, + {-2.50601,2.11145,-0.646526,0.0418594}, + {-2.50791,2.1157,-0.649485,0.0424828}, + {-2.50987,2.12004,-0.652512,0.0431196}, + {-2.51187,2.12448,-0.655604,0.0437688}, + {-2.51392,2.12901,-0.658756,0.0444297}, + {-2.516,2.13363,-0.661964,0.0451011}, + {-2.51812,2.13832,-0.665221,0.0457818}, + {-2.52027,2.14308,-0.668521,0.0464705}, + {-2.52244,2.14789,-0.671856,0.0471658}, + {-2.52464,2.15274,-0.675218,0.0478658}, + {-2.52684,2.15762,-0.678596,0.0485687}, + {-2.52906,2.16251,-0.68198,0.0492723}, + {-2.53126,2.16738,-0.685355,0.049974}, + {-2.53345,2.17222,-0.688706,0.0506708}, + {-2.53561,2.177,-0.692015,0.0513594}, + {-2.53773,2.18168,-0.69526,0.0520359}, + {-2.53978,2.18623,-0.698416,0.0526955}, + {-2.54175,2.19059,-0.701452,0.053333}, + {-2.5436,2.19471,-0.704331,0.0539417}, + {-2.54531,2.19852,-0.70701,0.0545141}, + {-2.54683,2.20193,-0.709433,0.0550409}, + {-2.5481,2.20483,-0.711533,0.0555106}, + {-2.54906,2.20709,-0.713224,0.0559094}, + {-2.54963,2.20852,-0.714397,0.0562198}, + {-2.54968,2.20888,-0.714907,0.0564196}, + {-2.54905,2.20785,-0.714562,0.0564797}, + {-2.54751,2.20496,-0.713094,0.0563618}, + {-2.54472,2.19955,-0.710118,0.0560124}, + {-2.54014,2.19058,-0.705048,0.0553544}, + {-2.53286,2.1763,-0.69693,0.0542684}, + {-2.52115,2.15344,-0.684027,0.05255}, + {-2.50098,2.11466,-0.66255,0.0497817}, + {-2.45797,2.03459,-0.620099,0.0446889}, + {-2.28371,1.72254,-0.465905,0.0283268}, + {-2.4885,2.04899,-0.599292,0.0390466}, + {-2.1433,1.36735,-0.220924,-0.00215633}, + {-2.4943,2.07019,-0.610552,0.035166}, + {-2.84529,2.77303,-1.00018,0.0724884}, + {-2.72242,2.51888,-0.847226,0.0509964}, + {-2.65633,2.3744,-0.750392,0.0326366}, + {-2.59601,2.23412,-0.646421,0.00868027}, + {-2.51477,2.0369,-0.491066,-0.0306397}, + {-2.35935,1.65155,-0.178971,-0.112713}, + {-1.84315,0.361693,0.876104,-0.393844}, + {-2.65422,2.39262,-0.789663,0.0516265}, + {-3.46529,4.42354,-2.45543,0.497097}, + {-3.15747,3.65311,-1.824,0.328432}, + {-3.04694,3.37613,-1.59668,0.267631}, + {-2.99205,3.23814,-1.48302,0.237103}, + {-2.96075,3.15894,-1.41733,0.219317}, + {-2.94172,3.11028,-1.37649,0.20811}, + {-2.92994,3.07962,-1.35025,0.200755}, + {-2.92283,3.06054,-1.33338,0.195859}, + {-2.91894,3.04938,-1.3229,0.192637}, + {-2.91736,3.04394,-1.31702,0.190612}, + {-2.91753,3.04278,-1.31456,0.189477}, + {-2.91905,3.04494,-1.31475,0.189026}, + {-2.92165,3.04973,-1.31705,0.189117}, + {-2.92512,3.05667,-1.32105,0.189646}, + {-2.92933,3.06539,-1.32646,0.190538}, + {-2.93416,3.07562,-1.33308,0.191735}, + {-2.93952,3.08715,-1.34072,0.193194}, + {-2.94535,3.09982,-1.34925,0.194881}, + {-2.95159,3.11349,-1.35858,0.196769}, + {-2.9582,3.12805,-1.36861,0.198838}, + {-2.96514,3.14342,-1.37929,0.201068}, + {-2.97239,3.15953,-1.39055,0.203448}, + {-2.97991,3.17632,-1.40234,0.205964}, + {-2.98769,3.19374,-1.41463,0.208607} }; // Table of coefficient for Bx, By and Ez for Galerkin gather. // Each line stands for one value of c*dt/dz, between 0 and 1. // We typically interpolate between two lines. const amrex::Real table_nci_godfrey_galerkin_Bx_By_Ez[tab_length][tab_width]{ - -2.80862,2.80104,-1.14615,0.154077, - -2.80862,2.80104,-1.14615,0.154077, - -2.80851,2.80078,-1.14595,0.154027, - -2.80832,2.80034,-1.14561,0.153945, - -2.80807,2.79973,-1.14514,0.153829, - -2.80774,2.79894,-1.14454,0.15368, - -2.80733,2.79798,-1.1438,0.153498, - -2.80685,2.79685,-1.14292,0.153284, - -2.8063,2.79554,-1.14192,0.153036, - -2.80568,2.79405,-1.14077,0.152756, - -2.80498,2.79239,-1.1395,0.152443, - -2.80421,2.79056,-1.13809,0.152098, - -2.80337,2.78856,-1.13656,0.151721, - -2.80246,2.78638,-1.13488,0.151312, - -2.80147,2.78404,-1.13308,0.150871, - -2.80041,2.78152,-1.13115,0.150397, - -2.79927,2.77882,-1.12908,0.149893, - -2.79807,2.77596,-1.12689,0.149356, - -2.79679,2.77292,-1.12456,0.148789, - -2.79543,2.76972,-1.12211,0.14819, - -2.79401,2.76634,-1.11953,0.14756, - -2.79251,2.76279,-1.11681,0.1469, - -2.79094,2.75907,-1.11397,0.146208, - -2.78929,2.75517,-1.111,0.145486, - -2.78757,2.7511,-1.10789,0.144733, - -2.78578,2.74686,-1.10466,0.14395, - -2.78391,2.74245,-1.1013,0.143137, - -2.78196,2.73786,-1.09781,0.142293, - -2.77994,2.73309,-1.09419,0.141419, - -2.77784,2.72814,-1.09043,0.140514, - -2.77566,2.72301,-1.08654,0.139578, - -2.7734,2.7177,-1.08252,0.138612, - -2.77106,2.7122,-1.07836,0.137614, - -2.76864,2.70651,-1.07406,0.136586, - -2.76613,2.70062,-1.06962,0.135525, - -2.76353,2.69453,-1.06503,0.134432, - -2.76084,2.68824,-1.0603,0.133307, - -2.75806,2.68173,-1.05541,0.132148, - -2.75518,2.675,-1.05037,0.130954, - -2.75219,2.66804,-1.04516,0.129725, - -2.7491,2.66084,-1.03978,0.12846, - -2.7459,2.65339,-1.03423,0.127156, - -2.74257,2.64566,-1.02848,0.125813, - -2.73912,2.63765,-1.02254,0.124428, - -2.73552,2.62934,-1.01638,0.122999, - -2.73178,2.62069,-1.01,0.121523, - -2.72787,2.61169,-1.00337,0.119996, - -2.72379,2.6023,-0.996479,0.118417, - -2.71951,2.59248,-0.989294,0.116778, - -2.71501,2.58218,-0.981786,0.115076, - -2.71026,2.57135,-0.97392,0.113303, - -2.70524,2.55991,-0.965651,0.111453, - -2.69989,2.54778,-0.956922,0.109514, - -2.69416,2.53484,-0.947666,0.107476, - -2.68799,2.52096,-0.937795,0.105324, - -2.68129,2.50596,-0.927197,0.103039, - -2.67394,2.48959,-0.915724,0.100597, - -2.66578,2.47153,-0.903179,0.097968, - -2.65657,2.4513,-0.889283,0.0951084, - -2.64598,2.42824,-0.873638,0.0919592, - -2.63347,2.40127,-0.855632,0.0884325, - -2.61813,2.36864,-0.834261,0.0843898, - -2.59821,2.32701,-0.807691,0.0795876, - -2.56971,2.26887,-0.77188,0.0735132, - -2.51823,2.16823,-0.713448,0.0645399, - -2.33537,1.8294,-0.533852,0.0409941, - -2.53143,2.14818,-0.670502,0.053982, - -2.17737,1.43641,-0.259095,0.00101255, - -2.51929,2.12931,-0.654743,0.0452381, - -2.86122,2.82221,-1.05039,0.0894636, - -2.72908,2.54506,-0.87834,0.0626188, - -2.6536,2.37954,-0.7665,0.0409117, - -2.58374,2.21923,-0.649738,0.0146791, - -2.49284,2.00346,-0.48457,-0.0255348, - -2.32762,1.60337,-0.1698,-0.105287, - -1.80149,0.316787,0.855414,-0.369652, - -2.60242,2.28418,-0.721378,0.040091, - -3.40335,4.25157,-2.29817,0.449834, - -3.0852,3.47341,-1.67791,0.28982, - -2.9642,3.17856,-1.44399,0.229852, - -2.89872,3.01966,-1.31861,0.197945, - -2.85668,2.91811,-1.23894,0.17783, - -2.82679,2.84621,-1.18287,0.163785, - -2.80401,2.79167,-1.14058,0.153278, - -2.78577,2.74819,-1.10706,0.145015, - -2.77061,2.7122,-1.07946,0.138267, - -2.75764,2.68152,-1.05606,0.132589, - -2.74627,2.65475,-1.03575,0.127695, - -2.73612,2.63093,-1.01777,0.123395, - -2.72692,2.6094,-1.00159,0.119553, - -2.71846,2.58968,-0.986841,0.116074, - -2.71061,2.57142,-0.973239,0.112887, - -2.70323,2.55434,-0.960573,0.109937, - -2.69626,2.53824,-0.948678,0.107185, - -2.68962,2.52294,-0.937429,0.104598, - -2.68327,2.50833,-0.926722,0.102151, - -2.67714,2.4943,-0.916477,0.0998223, - -2.67122,2.48076,-0.906627,0.0975966, - -2.66546,2.46764,-0.897118,0.0954599, - -2.65985,2.45489,-0.887903,0.0934011, - -2.65437,2.44244,-0.878945,0.0914107 + {-2.80862,2.80104,-1.14615,0.154077}, + {-2.80862,2.80104,-1.14615,0.154077}, + {-2.80851,2.80078,-1.14595,0.154027}, + {-2.80832,2.80034,-1.14561,0.153945}, + {-2.80807,2.79973,-1.14514,0.153829}, + {-2.80774,2.79894,-1.14454,0.15368}, + {-2.80733,2.79798,-1.1438,0.153498}, + {-2.80685,2.79685,-1.14292,0.153284}, + {-2.8063,2.79554,-1.14192,0.153036}, + {-2.80568,2.79405,-1.14077,0.152756}, + {-2.80498,2.79239,-1.1395,0.152443}, + {-2.80421,2.79056,-1.13809,0.152098}, + {-2.80337,2.78856,-1.13656,0.151721}, + {-2.80246,2.78638,-1.13488,0.151312}, + {-2.80147,2.78404,-1.13308,0.150871}, + {-2.80041,2.78152,-1.13115,0.150397}, + {-2.79927,2.77882,-1.12908,0.149893}, + {-2.79807,2.77596,-1.12689,0.149356}, + {-2.79679,2.77292,-1.12456,0.148789}, + {-2.79543,2.76972,-1.12211,0.14819}, + {-2.79401,2.76634,-1.11953,0.14756}, + {-2.79251,2.76279,-1.11681,0.1469}, + {-2.79094,2.75907,-1.11397,0.146208}, + {-2.78929,2.75517,-1.111,0.145486}, + {-2.78757,2.7511,-1.10789,0.144733}, + {-2.78578,2.74686,-1.10466,0.14395}, + {-2.78391,2.74245,-1.1013,0.143137}, + {-2.78196,2.73786,-1.09781,0.142293}, + {-2.77994,2.73309,-1.09419,0.141419}, + {-2.77784,2.72814,-1.09043,0.140514}, + {-2.77566,2.72301,-1.08654,0.139578}, + {-2.7734,2.7177,-1.08252,0.138612}, + {-2.77106,2.7122,-1.07836,0.137614}, + {-2.76864,2.70651,-1.07406,0.136586}, + {-2.76613,2.70062,-1.06962,0.135525}, + {-2.76353,2.69453,-1.06503,0.134432}, + {-2.76084,2.68824,-1.0603,0.133307}, + {-2.75806,2.68173,-1.05541,0.132148}, + {-2.75518,2.675,-1.05037,0.130954}, + {-2.75219,2.66804,-1.04516,0.129725}, + {-2.7491,2.66084,-1.03978,0.12846}, + {-2.7459,2.65339,-1.03423,0.127156}, + {-2.74257,2.64566,-1.02848,0.125813}, + {-2.73912,2.63765,-1.02254,0.124428}, + {-2.73552,2.62934,-1.01638,0.122999}, + {-2.73178,2.62069,-1.01,0.121523}, + {-2.72787,2.61169,-1.00337,0.119996}, + {-2.72379,2.6023,-0.996479,0.118417}, + {-2.71951,2.59248,-0.989294,0.116778}, + {-2.71501,2.58218,-0.981786,0.115076}, + {-2.71026,2.57135,-0.97392,0.113303}, + {-2.70524,2.55991,-0.965651,0.111453}, + {-2.69989,2.54778,-0.956922,0.109514}, + {-2.69416,2.53484,-0.947666,0.107476}, + {-2.68799,2.52096,-0.937795,0.105324}, + {-2.68129,2.50596,-0.927197,0.103039}, + {-2.67394,2.48959,-0.915724,0.100597}, + {-2.66578,2.47153,-0.903179,0.097968}, + {-2.65657,2.4513,-0.889283,0.0951084}, + {-2.64598,2.42824,-0.873638,0.0919592}, + {-2.63347,2.40127,-0.855632,0.0884325}, + {-2.61813,2.36864,-0.834261,0.0843898}, + {-2.59821,2.32701,-0.807691,0.0795876}, + {-2.56971,2.26887,-0.77188,0.0735132}, + {-2.51823,2.16823,-0.713448,0.0645399}, + {-2.33537,1.8294,-0.533852,0.0409941}, + {-2.53143,2.14818,-0.670502,0.053982}, + {-2.17737,1.43641,-0.259095,0.00101255}, + {-2.51929,2.12931,-0.654743,0.0452381}, + {-2.86122,2.82221,-1.05039,0.0894636}, + {-2.72908,2.54506,-0.87834,0.0626188}, + {-2.6536,2.37954,-0.7665,0.0409117}, + {-2.58374,2.21923,-0.649738,0.0146791}, + {-2.49284,2.00346,-0.48457,-0.0255348}, + {-2.32762,1.60337,-0.1698,-0.105287}, + {-1.80149,0.316787,0.855414,-0.369652}, + {-2.60242,2.28418,-0.721378,0.040091}, + {-3.40335,4.25157,-2.29817,0.449834}, + {-3.0852,3.47341,-1.67791,0.28982}, + {-2.9642,3.17856,-1.44399,0.229852}, + {-2.89872,3.01966,-1.31861,0.197945}, + {-2.85668,2.91811,-1.23894,0.17783}, + {-2.82679,2.84621,-1.18287,0.163785}, + {-2.80401,2.79167,-1.14058,0.153278}, + {-2.78577,2.74819,-1.10706,0.145015}, + {-2.77061,2.7122,-1.07946,0.138267}, + {-2.75764,2.68152,-1.05606,0.132589}, + {-2.74627,2.65475,-1.03575,0.127695}, + {-2.73612,2.63093,-1.01777,0.123395}, + {-2.72692,2.6094,-1.00159,0.119553}, + {-2.71846,2.58968,-0.986841,0.116074}, + {-2.71061,2.57142,-0.973239,0.112887}, + {-2.70323,2.55434,-0.960573,0.109937}, + {-2.69626,2.53824,-0.948678,0.107185}, + {-2.68962,2.52294,-0.937429,0.104598}, + {-2.68327,2.50833,-0.926722,0.102151}, + {-2.67714,2.4943,-0.916477,0.0998223}, + {-2.67122,2.48076,-0.906627,0.0975966}, + {-2.66546,2.46764,-0.897118,0.0954599}, + {-2.65985,2.45489,-0.887903,0.0934011}, + {-2.65437,2.44244,-0.878945,0.0914107} }; // Table of coefficient for Ex, Ey and Bz for momentum-conserving gather @@ -234,107 +234,107 @@ const amrex::Real table_nci_godfrey_galerkin_Bx_By_Ez[tab_length][tab_width]{ // Each line stands for one value of c*dt/dz, between 0 and 1. // We typically interpolate between two lines. const amrex::Real table_nci_godfrey_momentum_Ex_Ey_Bz[tab_length][tab_width]{ - -2.98767,3.19368,-1.41458,0.208594, - -2.98767,3.19368,-1.41458,0.208594, - -2.9876,3.19351,-1.41444,0.208555, - -2.98749,3.19323,-1.41421,0.208491, - -2.98734,3.19285,-1.41388,0.208402, - -2.98716,3.19237,-1.41348,0.20829, - -2.98693,3.19179,-1.41299,0.208157, - -2.98668,3.19114,-1.41244,0.208006, - -2.98639,3.19041,-1.41183,0.207837, - -2.98608,3.18962,-1.41116,0.207655, - -2.98576,3.18878,-1.41046,0.207463, - -2.98542,3.18791,-1.40972,0.207262, - -2.98507,3.18701,-1.40898,0.207058, - -2.98472,3.18612,-1.40822,0.206852, - -2.98437,3.18523,-1.40748,0.20665, - -2.98403,3.18437,-1.40676,0.206454, - -2.98371,3.18355,-1.40608,0.206269, - -2.98341,3.18279,-1.40544,0.206097, - -2.98314,3.1821,-1.40487,0.205943, - -2.98291,3.1815,-1.40437,0.20581, - -2.98271,3.181,-1.40397,0.205702, - -2.98256,3.18062,-1.40366,0.205622, - -2.98246,3.18037,-1.40346,0.205572, - -2.98241,3.18027,-1.40339,0.205557, - -2.98243,3.18033,-1.40345,0.20558, - -2.98251,3.18056,-1.40366,0.205642, - -2.98267,3.18097,-1.40402,0.205747, - -2.9829,3.18157,-1.40455,0.205896, - -2.98321,3.18238,-1.40524,0.206093, - -2.9836,3.1834,-1.40612,0.206339, - -2.98407,3.18465,-1.40718,0.206637, - -2.98464,3.18612,-1.40844,0.206987, - -2.9853,3.18783,-1.40989,0.207392, - -2.98606,3.18979,-1.41156,0.207853, - -2.98691,3.19199,-1.41343,0.208372, - -2.98787,3.19446,-1.41551,0.208948, - -2.98892,3.19718,-1.41782,0.209585, - -2.99008,3.20017,-1.42034,0.210281, - -2.99135,3.20343,-1.42309,0.211039, - -2.99273,3.20697,-1.42608,0.211859, - -2.99421,3.21078,-1.42929,0.212741, - -2.9958,3.21487,-1.43273,0.213686, - -2.99751,3.21925,-1.43641,0.214695, - -2.99933,3.22391,-1.44033,0.215767, - -3.00126,3.22886,-1.44449,0.216904, - -3.0033,3.2341,-1.44888,0.218105, - -3.00546,3.23964,-1.45352,0.219371, - -3.00774,3.24546,-1.4584,0.220701, - -3.01013,3.25158,-1.46352,0.222096, - -3.01264,3.25799,-1.46888,0.223557, - -3.01526,3.26469,-1.47449,0.225082, - -3.018,3.2717,-1.48034,0.226672, - -3.02086,3.279,-1.48644,0.228327, - -3.02384,3.28659,-1.49278,0.230048, - -3.02694,3.29449,-1.49936,0.231833, - -3.03015,3.30268,-1.50619,0.233684, - -3.03348,3.31117,-1.51327,0.2356, - -3.03693,3.31996,-1.52059,0.237581, - -3.0405,3.32904,-1.52815,0.239627, - -3.04418,3.33843,-1.53596,0.241738, - -3.04799,3.34812,-1.54402,0.243915, - -3.05191,3.3581,-1.55232,0.246156, - -3.05596,3.36838,-1.56087,0.248463, - -3.06012,3.37896,-1.56966,0.250835, - -3.0644,3.38984,-1.5787,0.253272, - -3.0688,3.40102,-1.58798,0.255774, - -3.07332,3.4125,-1.59751,0.258342, - -3.07795,3.42428,-1.60729,0.260976, - -3.08271,3.43636,-1.61731,0.263675, - -3.08758,3.44874,-1.62758,0.26644, - -3.09257,3.46142,-1.6381,0.269271, - -3.09769,3.47439,-1.64886,0.272168, - -3.10292,3.48767,-1.65987,0.275131, - -3.10826,3.50125,-1.67113,0.278161, - -3.11373,3.51513,-1.68264,0.281258, - -3.11932,3.52931,-1.6944,0.284421, - -3.12502,3.54379,-1.7064,0.287652, - -3.13084,3.55857,-1.71866,0.29095, - -3.13678,3.57365,-1.73117,0.294316, - -3.14284,3.58903,-1.74393,0.297751, - -3.14902,3.60472,-1.75694,0.301254, - -3.15531,3.6207,-1.7702,0.304825, - -3.16173,3.63699,-1.78372,0.308466, - -3.16826,3.65359,-1.79749,0.312177, - -3.17491,3.67048,-1.81152,0.315958, - -3.18168,3.68768,-1.82581,0.31981, - -3.18856,3.70519,-1.84035,0.323732, - -3.19557,3.72299,-1.85515,0.327726, - -3.20269,3.74111,-1.8702,0.331792, - -3.20993,3.75953,-1.88552,0.33593, - -3.21729,3.77825,-1.9011,0.340142, - -3.22476,3.79729,-1.91694,0.344427, - -3.23236,3.81662,-1.93305,0.348786, - -3.24007,3.83627,-1.94942,0.353221, - -3.2479,3.85623,-1.96606,0.35773, - -3.25584,3.87649,-1.98296,0.362316, - -3.26391,3.89706,-2.00013,0.366978, - -3.27209,3.91795,-2.01758,0.371718, - -3.28039,3.93914,-2.03529,0.376536, - -3.2888,3.96065,-2.05328,0.381432, - -3.2888,3.96065,-2.05328,0.381432 + {-2.98767,3.19368,-1.41458,0.208594}, + {-2.98767,3.19368,-1.41458,0.208594}, + {-2.9876,3.19351,-1.41444,0.208555}, + {-2.98749,3.19323,-1.41421,0.208491}, + {-2.98734,3.19285,-1.41388,0.208402}, + {-2.98716,3.19237,-1.41348,0.20829}, + {-2.98693,3.19179,-1.41299,0.208157}, + {-2.98668,3.19114,-1.41244,0.208006}, + {-2.98639,3.19041,-1.41183,0.207837}, + {-2.98608,3.18962,-1.41116,0.207655}, + {-2.98576,3.18878,-1.41046,0.207463}, + {-2.98542,3.18791,-1.40972,0.207262}, + {-2.98507,3.18701,-1.40898,0.207058}, + {-2.98472,3.18612,-1.40822,0.206852}, + {-2.98437,3.18523,-1.40748,0.20665}, + {-2.98403,3.18437,-1.40676,0.206454}, + {-2.98371,3.18355,-1.40608,0.206269}, + {-2.98341,3.18279,-1.40544,0.206097}, + {-2.98314,3.1821,-1.40487,0.205943}, + {-2.98291,3.1815,-1.40437,0.20581}, + {-2.98271,3.181,-1.40397,0.205702}, + {-2.98256,3.18062,-1.40366,0.205622}, + {-2.98246,3.18037,-1.40346,0.205572}, + {-2.98241,3.18027,-1.40339,0.205557}, + {-2.98243,3.18033,-1.40345,0.20558}, + {-2.98251,3.18056,-1.40366,0.205642}, + {-2.98267,3.18097,-1.40402,0.205747}, + {-2.9829,3.18157,-1.40455,0.205896}, + {-2.98321,3.18238,-1.40524,0.206093}, + {-2.9836,3.1834,-1.40612,0.206339}, + {-2.98407,3.18465,-1.40718,0.206637}, + {-2.98464,3.18612,-1.40844,0.206987}, + {-2.9853,3.18783,-1.40989,0.207392}, + {-2.98606,3.18979,-1.41156,0.207853}, + {-2.98691,3.19199,-1.41343,0.208372}, + {-2.98787,3.19446,-1.41551,0.208948}, + {-2.98892,3.19718,-1.41782,0.209585}, + {-2.99008,3.20017,-1.42034,0.210281}, + {-2.99135,3.20343,-1.42309,0.211039}, + {-2.99273,3.20697,-1.42608,0.211859}, + {-2.99421,3.21078,-1.42929,0.212741}, + {-2.9958,3.21487,-1.43273,0.213686}, + {-2.99751,3.21925,-1.43641,0.214695}, + {-2.99933,3.22391,-1.44033,0.215767}, + {-3.00126,3.22886,-1.44449,0.216904}, + {-3.0033,3.2341,-1.44888,0.218105}, + {-3.00546,3.23964,-1.45352,0.219371}, + {-3.00774,3.24546,-1.4584,0.220701}, + {-3.01013,3.25158,-1.46352,0.222096}, + {-3.01264,3.25799,-1.46888,0.223557}, + {-3.01526,3.26469,-1.47449,0.225082}, + {-3.018,3.2717,-1.48034,0.226672}, + {-3.02086,3.279,-1.48644,0.228327}, + {-3.02384,3.28659,-1.49278,0.230048}, + {-3.02694,3.29449,-1.49936,0.231833}, + {-3.03015,3.30268,-1.50619,0.233684}, + {-3.03348,3.31117,-1.51327,0.2356}, + {-3.03693,3.31996,-1.52059,0.237581}, + {-3.0405,3.32904,-1.52815,0.239627}, + {-3.04418,3.33843,-1.53596,0.241738}, + {-3.04799,3.34812,-1.54402,0.243915}, + {-3.05191,3.3581,-1.55232,0.246156}, + {-3.05596,3.36838,-1.56087,0.248463}, + {-3.06012,3.37896,-1.56966,0.250835}, + {-3.0644,3.38984,-1.5787,0.253272}, + {-3.0688,3.40102,-1.58798,0.255774}, + {-3.07332,3.4125,-1.59751,0.258342}, + {-3.07795,3.42428,-1.60729,0.260976}, + {-3.08271,3.43636,-1.61731,0.263675}, + {-3.08758,3.44874,-1.62758,0.26644}, + {-3.09257,3.46142,-1.6381,0.269271}, + {-3.09769,3.47439,-1.64886,0.272168}, + {-3.10292,3.48767,-1.65987,0.275131}, + {-3.10826,3.50125,-1.67113,0.278161}, + {-3.11373,3.51513,-1.68264,0.281258}, + {-3.11932,3.52931,-1.6944,0.284421}, + {-3.12502,3.54379,-1.7064,0.287652}, + {-3.13084,3.55857,-1.71866,0.29095}, + {-3.13678,3.57365,-1.73117,0.294316}, + {-3.14284,3.58903,-1.74393,0.297751}, + {-3.14902,3.60472,-1.75694,0.301254}, + {-3.15531,3.6207,-1.7702,0.304825}, + {-3.16173,3.63699,-1.78372,0.308466}, + {-3.16826,3.65359,-1.79749,0.312177}, + {-3.17491,3.67048,-1.81152,0.315958}, + {-3.18168,3.68768,-1.82581,0.31981}, + {-3.18856,3.70519,-1.84035,0.323732}, + {-3.19557,3.72299,-1.85515,0.327726}, + {-3.20269,3.74111,-1.8702,0.331792}, + {-3.20993,3.75953,-1.88552,0.33593}, + {-3.21729,3.77825,-1.9011,0.340142}, + {-3.22476,3.79729,-1.91694,0.344427}, + {-3.23236,3.81662,-1.93305,0.348786}, + {-3.24007,3.83627,-1.94942,0.353221}, + {-3.2479,3.85623,-1.96606,0.35773}, + {-3.25584,3.87649,-1.98296,0.362316}, + {-3.26391,3.89706,-2.00013,0.366978}, + {-3.27209,3.91795,-2.01758,0.371718}, + {-3.28039,3.93914,-2.03529,0.376536}, + {-3.2888,3.96065,-2.05328,0.381432}, + {-3.2888,3.96065,-2.05328,0.381432} }; // Table of coefficient for Bx, By and Ez for momentum-conserving gather @@ -342,107 +342,107 @@ const amrex::Real table_nci_godfrey_momentum_Ex_Ey_Bz[tab_length][tab_width]{ // Each line stands for one value of c*dt/dz, between 0 and 1. // We typically interpolate between two lines. const amrex::Real table_nci_godfrey_momentum_Bx_By_Ez[tab_length][tab_width]{ - -2.65428,2.44224,-0.878796,0.0913764, - -2.65428,2.44224,-0.878796,0.0913764, - -2.65401,2.44163,-0.878347,0.0912737, - -2.65357,2.44061,-0.877602,0.0911031, - -2.65296,2.43919,-0.876563,0.0908654, - -2.65217,2.43738,-0.875236,0.0905616, - -2.65121,2.43518,-0.873624,0.0901933, - -2.65009,2.43261,-0.871736,0.089762, - -2.6488,2.42966,-0.869579,0.0892697, - -2.64736,2.42635,-0.86716,0.0887186, - -2.64577,2.4227,-0.86449,0.088111, - -2.64403,2.41871,-0.861579,0.0874494, - -2.64215,2.4144,-0.858436,0.0867364, - -2.64013,2.40979,-0.855074,0.0859749, - -2.63798,2.40488,-0.851503,0.0851676, - -2.63571,2.39969,-0.847734,0.0843175, - -2.63333,2.39425,-0.84378,0.0834274, - -2.63083,2.38855,-0.839652,0.0825004, - -2.62822,2.38262,-0.835361,0.0815392, - -2.62552,2.37647,-0.830918,0.0805468, - -2.62272,2.37011,-0.826336,0.0795259, - -2.61984,2.36357,-0.821624,0.0784793, - -2.61687,2.35684,-0.816793,0.0774095, - -2.61383,2.34995,-0.811854,0.076319, - -2.61071,2.34291,-0.806815,0.0752104, - -2.60753,2.33573,-0.801685,0.0740857, - -2.60428,2.32842,-0.796474,0.0729473, - -2.60098,2.32098,-0.791189,0.0717971, - -2.59762,2.31344,-0.785838,0.070637, - -2.59421,2.3058,-0.780428,0.0694688, - -2.59076,2.29806,-0.774966,0.0682941, - -2.58726,2.29024,-0.769458,0.0671146, - -2.58372,2.28234,-0.76391,0.0659314, - -2.58014,2.27437,-0.758326,0.064746, - -2.57653,2.26633,-0.752711,0.0635596, - -2.57288,2.25824,-0.74707,0.0623731, - -2.5692,2.25009,-0.741407,0.0611876, - -2.5655,2.24189,-0.735726,0.060004, - -2.56177,2.23365,-0.730029,0.058823, - -2.55801,2.22537,-0.724319,0.0576454, - -2.55422,2.21704,-0.7186,0.0564718, - -2.55042,2.20869,-0.712873,0.0553027, - -2.54659,2.2003,-0.707141,0.0541387, - -2.54274,2.19188,-0.701405,0.0529802, - -2.53887,2.18343,-0.695668,0.0518276, - -2.53498,2.17496,-0.68993,0.0506813, - -2.53108,2.16647,-0.684193,0.0495415, - -2.52715,2.15795,-0.678458,0.0484085, - -2.52321,2.14941,-0.672726,0.0472826, - -2.51925,2.14085,-0.666998,0.0461638, - -2.51528,2.13227,-0.661275,0.0450525, - -2.51129,2.12367,-0.655557,0.0439488, - -2.50728,2.11506,-0.649845,0.0428526, - -2.50326,2.10642,-0.644139,0.0417643, - -2.49922,2.09778,-0.63844,0.0406838, - -2.49516,2.08911,-0.632749,0.0396112, - -2.4911,2.08043,-0.627065,0.0385466, - -2.48701,2.07174,-0.621388,0.03749, - -2.48291,2.06303,-0.61572,0.0364415, - -2.4788,2.05431,-0.610059,0.0354011, - -2.47467,2.04557,-0.604408,0.0343687, - -2.47053,2.03682,-0.598764,0.0333446, - -2.46637,2.02805,-0.593129,0.0323286, - -2.4622,2.01927,-0.587503,0.0313207, - -2.45802,2.01047,-0.581886,0.0303211, - -2.45381,2.00167,-0.576277,0.0293296, - -2.4496,1.99285,-0.570678,0.0283464, - -2.44537,1.98401,-0.565088,0.0273714, - -2.44112,1.97516,-0.559506,0.0264046, - -2.43686,1.9663,-0.553934,0.0254461, - -2.43259,1.95742,-0.548372,0.0244959, - -2.4283,1.94853,-0.542818,0.0235539, - -2.424,1.93963,-0.537274,0.0226202, - -2.41968,1.93072,-0.53174,0.0216949, - -2.41534,1.92179,-0.526216,0.0207778, - -2.411,1.91284,-0.520701,0.0198692, - -2.40663,1.90389,-0.515196,0.0189689, - -2.40225,1.89492,-0.5097,0.0180771, - -2.39786,1.88594,-0.504215,0.0171938, - -2.39345,1.87694,-0.49874,0.0163189, - -2.38903,1.86793,-0.493276,0.0154526, - -2.38459,1.85891,-0.487822,0.0145948, - -2.38014,1.84988,-0.482378,0.0137456, - -2.37567,1.84083,-0.476946,0.0129052, - -2.37119,1.83177,-0.471524,0.0120734, - -2.36669,1.8227,-0.466113,0.0112504, - -2.36217,1.81361,-0.460713,0.0104362, - -2.35764,1.80451,-0.455325,0.00963088, - -2.3531,1.7954,-0.449949,0.0088345, - -2.34854,1.78628,-0.444584,0.00804712, - -2.34396,1.77714,-0.439231,0.00726881, - -2.33937,1.768,-0.43389,0.00649963, - -2.33476,1.75884,-0.428562,0.00573964, - -2.33014,1.74966,-0.423246,0.00498893, - -2.3255,1.74048,-0.417943,0.00424756, - -2.32085,1.73128,-0.412654,0.00351561, - -2.31618,1.72208,-0.407377,0.00279317, - -2.31149,1.71286,-0.402114,0.0020803, - -2.30679,1.70363,-0.396865,0.00137709, - -2.30207,1.69438,-0.391629,0.000683629, - -2.30207,1.69438,-0.391629,0.000683629 + {-2.65428,2.44224,-0.878796,0.0913764}, + {-2.65428,2.44224,-0.878796,0.0913764}, + {-2.65401,2.44163,-0.878347,0.0912737}, + {-2.65357,2.44061,-0.877602,0.0911031}, + {-2.65296,2.43919,-0.876563,0.0908654}, + {-2.65217,2.43738,-0.875236,0.0905616}, + {-2.65121,2.43518,-0.873624,0.0901933}, + {-2.65009,2.43261,-0.871736,0.089762}, + {-2.6488,2.42966,-0.869579,0.0892697}, + {-2.64736,2.42635,-0.86716,0.0887186}, + {-2.64577,2.4227,-0.86449,0.088111}, + {-2.64403,2.41871,-0.861579,0.0874494}, + {-2.64215,2.4144,-0.858436,0.0867364}, + {-2.64013,2.40979,-0.855074,0.0859749}, + {-2.63798,2.40488,-0.851503,0.0851676}, + {-2.63571,2.39969,-0.847734,0.0843175}, + {-2.63333,2.39425,-0.84378,0.0834274}, + {-2.63083,2.38855,-0.839652,0.0825004}, + {-2.62822,2.38262,-0.835361,0.0815392}, + {-2.62552,2.37647,-0.830918,0.0805468}, + {-2.62272,2.37011,-0.826336,0.0795259}, + {-2.61984,2.36357,-0.821624,0.0784793}, + {-2.61687,2.35684,-0.816793,0.0774095}, + {-2.61383,2.34995,-0.811854,0.076319}, + {-2.61071,2.34291,-0.806815,0.0752104}, + {-2.60753,2.33573,-0.801685,0.0740857}, + {-2.60428,2.32842,-0.796474,0.0729473}, + {-2.60098,2.32098,-0.791189,0.0717971}, + {-2.59762,2.31344,-0.785838,0.070637}, + {-2.59421,2.3058,-0.780428,0.0694688}, + {-2.59076,2.29806,-0.774966,0.0682941}, + {-2.58726,2.29024,-0.769458,0.0671146}, + {-2.58372,2.28234,-0.76391,0.0659314}, + {-2.58014,2.27437,-0.758326,0.064746}, + {-2.57653,2.26633,-0.752711,0.0635596}, + {-2.57288,2.25824,-0.74707,0.0623731}, + {-2.5692,2.25009,-0.741407,0.0611876}, + {-2.5655,2.24189,-0.735726,0.060004}, + {-2.56177,2.23365,-0.730029,0.058823}, + {-2.55801,2.22537,-0.724319,0.0576454}, + {-2.55422,2.21704,-0.7186,0.0564718}, + {-2.55042,2.20869,-0.712873,0.0553027}, + {-2.54659,2.2003,-0.707141,0.0541387}, + {-2.54274,2.19188,-0.701405,0.0529802}, + {-2.53887,2.18343,-0.695668,0.0518276}, + {-2.53498,2.17496,-0.68993,0.0506813}, + {-2.53108,2.16647,-0.684193,0.0495415}, + {-2.52715,2.15795,-0.678458,0.0484085}, + {-2.52321,2.14941,-0.672726,0.0472826}, + {-2.51925,2.14085,-0.666998,0.0461638}, + {-2.51528,2.13227,-0.661275,0.0450525}, + {-2.51129,2.12367,-0.655557,0.0439488}, + {-2.50728,2.11506,-0.649845,0.0428526}, + {-2.50326,2.10642,-0.644139,0.0417643}, + {-2.49922,2.09778,-0.63844,0.0406838}, + {-2.49516,2.08911,-0.632749,0.0396112}, + {-2.4911,2.08043,-0.627065,0.0385466}, + {-2.48701,2.07174,-0.621388,0.03749}, + {-2.48291,2.06303,-0.61572,0.0364415}, + {-2.4788,2.05431,-0.610059,0.0354011}, + {-2.47467,2.04557,-0.604408,0.0343687}, + {-2.47053,2.03682,-0.598764,0.0333446}, + {-2.46637,2.02805,-0.593129,0.0323286}, + {-2.4622,2.01927,-0.587503,0.0313207}, + {-2.45802,2.01047,-0.581886,0.0303211}, + {-2.45381,2.00167,-0.576277,0.0293296}, + {-2.4496,1.99285,-0.570678,0.0283464}, + {-2.44537,1.98401,-0.565088,0.0273714}, + {-2.44112,1.97516,-0.559506,0.0264046}, + {-2.43686,1.9663,-0.553934,0.0254461}, + {-2.43259,1.95742,-0.548372,0.0244959}, + {-2.4283,1.94853,-0.542818,0.0235539}, + {-2.424,1.93963,-0.537274,0.0226202}, + {-2.41968,1.93072,-0.53174,0.0216949}, + {-2.41534,1.92179,-0.526216,0.0207778}, + {-2.411,1.91284,-0.520701,0.0198692}, + {-2.40663,1.90389,-0.515196,0.0189689}, + {-2.40225,1.89492,-0.5097,0.0180771}, + {-2.39786,1.88594,-0.504215,0.0171938}, + {-2.39345,1.87694,-0.49874,0.0163189}, + {-2.38903,1.86793,-0.493276,0.0154526}, + {-2.38459,1.85891,-0.487822,0.0145948}, + {-2.38014,1.84988,-0.482378,0.0137456}, + {-2.37567,1.84083,-0.476946,0.0129052}, + {-2.37119,1.83177,-0.471524,0.0120734}, + {-2.36669,1.8227,-0.466113,0.0112504}, + {-2.36217,1.81361,-0.460713,0.0104362}, + {-2.35764,1.80451,-0.455325,0.00963088}, + {-2.3531,1.7954,-0.449949,0.0088345}, + {-2.34854,1.78628,-0.444584,0.00804712}, + {-2.34396,1.77714,-0.439231,0.00726881}, + {-2.33937,1.768,-0.43389,0.00649963}, + {-2.33476,1.75884,-0.428562,0.00573964}, + {-2.33014,1.74966,-0.423246,0.00498893}, + {-2.3255,1.74048,-0.417943,0.00424756}, + {-2.32085,1.73128,-0.412654,0.00351561}, + {-2.31618,1.72208,-0.407377,0.00279317}, + {-2.31149,1.71286,-0.402114,0.0020803}, + {-2.30679,1.70363,-0.396865,0.00137709}, + {-2.30207,1.69438,-0.391629,0.000683629}, + {-2.30207,1.69438,-0.391629,0.000683629} }; #endif // #ifndef WARPX_GODFREY_COEFF_TABLE_H_ diff --git a/Source/Utils/ParticleUtils.H b/Source/Utils/ParticleUtils.H new file mode 100644 index 00000000000..cb25607a339 --- /dev/null +++ b/Source/Utils/ParticleUtils.H @@ -0,0 +1,31 @@ +/* Copyright 2019-2020 Neil Zaim, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_PARTICLE_UTILS_H_ +#define WARPX_PARTICLE_UTILS_H_ + +#include "Particles/WarpXParticleContainer.H" +#include + +namespace ParticleUtils { + + /** + * \brief Find the particles and count the particles that are in each cell. More specifically + * this function returns an amrex::DenseBins object containing an offset array and a permutation + * array which can be used to loop over all the cells in a tile and apply an algorithm to + * particles of a given species present in each cell. + * Note that this does *not* rearrange particle arrays. + * + * @param[in] lev the index of the refinement level. + * @param[in] mfi the MultiFAB iterator. + * @param[in] ptile the particle tile. + */ + amrex::DenseBins + findParticlesInEachCell( int const lev, amrex::MFIter const& mfi, + WarpXParticleContainer::ParticleTileType const& ptile); +} + +#endif // WARPX_PARTICLE_UTILS_H_ diff --git a/Source/Utils/ParticleUtils.cpp b/Source/Utils/ParticleUtils.cpp new file mode 100644 index 00000000000..92c03bc306e --- /dev/null +++ b/Source/Utils/ParticleUtils.cpp @@ -0,0 +1,52 @@ +/* Copyright 2019-2020 Neil Zaim, Yinjian Zhao + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "ParticleUtils.H" +#include "WarpX.H" + +namespace ParticleUtils { + + using namespace amrex; + // Define shortcuts for frequently-used type names + using ParticleType = WarpXParticleContainer::ParticleType; + using ParticleTileType = WarpXParticleContainer::ParticleTileType; + using ParticleBins = DenseBins; + using index_type = ParticleBins::index_type; + + /* Find the particles and count the particles that are in each cell. + Note that this does *not* rearrange particle arrays */ + ParticleBins + findParticlesInEachCell( int const lev, MFIter const& mfi, + ParticleTileType const& ptile) { + + // Extract particle structures for this tile + int const np = ptile.numParticles(); + ParticleType const* particle_ptr = ptile.GetArrayOfStructs()().data(); + + // Extract box properties + Geometry const& geom = WarpX::GetInstance().Geom(lev); + Box const& cbx = mfi.tilebox(IntVect::TheZeroVector()); //Cell-centered box + const auto lo = lbound(cbx); + const auto dxi = geom.InvCellSizeArray(); + const auto plo = geom.ProbLoArray(); + + // Find particles that are in each cell; + // results are stored in the object `bins`. + ParticleBins bins; + bins.build(np, particle_ptr, cbx, + // Pass lambda function that returns the cell index + [=] AMREX_GPU_HOST_DEVICE (const ParticleType& p) noexcept -> IntVect + { + return IntVect(AMREX_D_DECL( + static_cast((p.pos(0)-plo[0])*dxi[0] - lo.x), + static_cast((p.pos(1)-plo[1])*dxi[1] - lo.y), + static_cast((p.pos(2)-plo[2])*dxi[2] - lo.z))); + }); + + return bins; + } + +} diff --git a/Source/Utils/RelativeCellPosition.H b/Source/Utils/RelativeCellPosition.H new file mode 100644 index 00000000000..15ec0670740 --- /dev/null +++ b/Source/Utils/RelativeCellPosition.H @@ -0,0 +1,31 @@ +/* Copyright 2020 Axel Huebl + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_RELATIVE_CELL_POSITION_H_ +#define WARPX_RELATIVE_CELL_POSITION_H_ + +#include "WarpX.H" + +#include + +#include + + +namespace utils +{ + /** Get the Relative Cell Position of Values in an MultiFab + * + * Translate the IndexType of a given MultiFab into a position relative to + * the lower corner of a cell. + * + * @param[in] mf the amrex::MultiFab to get relative cell positions for + * @return relative position to the lower corner, scaled to cell size [0.0:1.0) + */ + std::vector< double > + getRelativeCellPosition (amrex::MultiFab const& mf); +} + +#endif // WARPX_RELATIVE_CELL_POSITION_H_ diff --git a/Source/Utils/RelativeCellPosition.cpp b/Source/Utils/RelativeCellPosition.cpp new file mode 100644 index 00000000000..58c35fa52f1 --- /dev/null +++ b/Source/Utils/RelativeCellPosition.cpp @@ -0,0 +1,29 @@ +/* Copyright 2020 Axel Huebl + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "RelativeCellPosition.H" + +#include + + +std::vector< double > +utils::getRelativeCellPosition(amrex::MultiFab const& mf) +{ + amrex::IndexType const idx_type = mf.ixType(); + + std::vector< double > relative_position(AMREX_SPACEDIM, 0.0); + + // amrex::CellIndex::CELL means: 0.5 from lower corner for that index/direction + // amrex::CellIndex::NODE means: at corner for that index/direction + // WarpX::do_nodal means: all indices/directions on CellIndex::NODE + for (int d = 0; d < AMREX_SPACEDIM; d++) + { + if (idx_type.cellCentered(d)) + relative_position.at(d) = 0.5; + } + + return relative_position; +} diff --git a/Source/Utils/WarpXMovingWindow.cpp b/Source/Utils/WarpXMovingWindow.cpp index 8435864b8f3..f1ee00b13ef 100644 --- a/Source/Utils/WarpXMovingWindow.cpp +++ b/Source/Utils/WarpXMovingWindow.cpp @@ -112,21 +112,21 @@ WarpX::MoveWindow (bool move_j) // Shift each component of vector fields (E, B, j) for (int dim = 0; dim < 3; ++dim) { // Fine grid - ParserWrapper<3> *Bfield_parser; - ParserWrapper<3> *Efield_parser; + HostDeviceParser<3> Bfield_parser; + HostDeviceParser<3> Efield_parser; bool use_Bparser = false; bool use_Eparser = false; if (B_ext_grid_s == "parse_b_ext_grid_function") { use_Bparser = true; - if (dim == 0) Bfield_parser = Bxfield_parser.get(); - if (dim == 1) Bfield_parser = Byfield_parser.get(); - if (dim == 2) Bfield_parser = Bzfield_parser.get(); + if (dim == 0) Bfield_parser = getParser(Bxfield_parser); + if (dim == 1) Bfield_parser = getParser(Byfield_parser); + if (dim == 2) Bfield_parser = getParser(Bzfield_parser); } if (E_ext_grid_s == "parse_e_ext_grid_function") { use_Eparser = true; - if (dim == 0) Efield_parser = Exfield_parser.get(); - if (dim == 1) Efield_parser = Eyfield_parser.get(); - if (dim == 2) Efield_parser = Ezfield_parser.get(); + if (dim == 0) Efield_parser = getParser(Exfield_parser); + if (dim == 1) Efield_parser = getParser(Eyfield_parser); + if (dim == 2) Efield_parser = getParser(Ezfield_parser); } shiftMF(*Bfield_fp[lev][dim], geom[lev], num_shift, dir, ng_extra, B_external_grid[dim], use_Bparser, Bfield_parser); shiftMF(*Efield_fp[lev][dim], geom[lev], num_shift, dir, ng_extra, E_external_grid[dim], use_Eparser, Efield_parser); @@ -244,7 +244,7 @@ WarpX::MoveWindow (bool move_j) void WarpX::shiftMF (MultiFab& mf, const Geometry& geom, int num_shift, int dir, IntVect ng_extra, amrex::Real external_field, bool useparser, - ParserWrapper<3> *field_parser) + HostDeviceParser<3> const& field_parser) { WARPX_PROFILE("WarpX::shiftMF()"); const BoxArray& ba = mf.boxArray(); @@ -345,7 +345,7 @@ WarpX::shiftMF (MultiFab& mf, const Geometry& geom, int num_shift, int dir, Real fac_z = (1.0 - mf_type[2]) * dx[2]*0.5; Real z = k*dx[2] + real_box.lo(2) + fac_z; #endif - srcfab(i,j,k,n) = (*field_parser)(x,y,z); + srcfab(i,j,k,n) = field_parser(x,y,z); }); } @@ -376,22 +376,28 @@ WarpX::ShiftGalileanBoundary () Real time_shift = (cur_time - time_of_last_gal_shift); #if (AMREX_SPACEDIM == 3) - amrex::Array galilean_shift = { v_galilean[0]* time_shift, v_galilean[1]*time_shift, v_galilean[2]*time_shift }; + m_galilean_shift = { + m_v_galilean[0]* time_shift, + m_v_galilean[1]*time_shift, + m_v_galilean[2]*time_shift }; #elif (AMREX_SPACEDIM == 2) - amrex::Array galilean_shift = { v_galilean[0]* time_shift, std::numeric_limits::quiet_NaN(), v_galilean[2]*time_shift }; + m_galilean_shift = { + m_v_galilean[0]* time_shift, + std::numeric_limits::quiet_NaN(), + m_v_galilean[2]*time_shift }; #endif #if (AMREX_SPACEDIM == 3) for (int i=0; i *field_parser=nullptr); + HostDeviceParser<3> const& field_parser={}); static void GotoNextLine (std::istream& is); @@ -144,9 +144,9 @@ public: #endif #ifdef WARPX_USE_PSATD - // If true (overwritten by the user in the input file), the update equation - // for E contains both J and rho (at times n and n+1) on the right hand side - bool update_with_rho = false; + // If true, the update equation for E contains both J and rho (at times n and n+1): + // default is false for standard PSATD and true for Galilean PSATD (set in WarpX.cpp) + bool update_with_rho; #endif // div E cleaning @@ -167,6 +167,7 @@ public: static bool use_filter; static bool use_kspace_filter; static bool use_filter_compensation; + static bool use_damp_fields_in_z_guard; static bool serialize_ics; // Back transformation diagnostic @@ -268,8 +269,8 @@ public: amrex::Vector< std::unique_ptr > nci_godfrey_filter_bxbyez; amrex::Real time_of_last_gal_shift = 0; - amrex::Array v_galilean = {{0}}; - + amrex::Array m_v_galilean = {{0}}; + amrex::Array m_galilean_shift = {{0}}; static int num_mirrors; amrex::Vector mirror_z; @@ -290,7 +291,7 @@ public: /** * \brief - * This function shifts the boundary of the grid by 'v_galilean*dt'. + * This function shifts the boundary of the grid by 'm_v_galilean*dt'. * In doding so, only positions attributes are changed while fields remain unchanged. */ void ShiftGalileanBoundary (); @@ -341,6 +342,16 @@ public: */ IntervalsParser get_load_balance_intervals () const {return load_balance_intervals;} + /** + * \brief Private function for spectral solver + * Applies a damping factor in the guards cells that extend + * beyond the extent of the domain, reducing fluctuations that + * can appear in parallel simulations. This will be called + * when use_damp_fields_in_z_guard is true. + */ + void DampFieldsInGuards (std::array,3>& Efield, + std::array,3>& Bfield); + #ifdef WARPX_DIM_RZ void ApplyInverseVolumeScalingToCurrentDensity(amrex::MultiFab* Jx, amrex::MultiFab* Jy, @@ -477,6 +488,8 @@ public: const amrex::IntVect getngF() const { return guard_cells.ng_alloc_F; } const amrex::IntVect getngExtra() const { return guard_cells.ng_Extra; } const amrex::IntVect getngUpdateAux() const { return guard_cells.ng_UpdateAux; } + const amrex::IntVect get_ng_depos_J() const {return guard_cells.ng_depos_J;} + const amrex::IntVect get_ng_depos_rho() const {return guard_cells.ng_depos_rho;} void ComputeSpaceChargeField (bool const reset_fields); void AddSpaceChargeField (WarpXParticleContainer& pc); @@ -510,8 +523,8 @@ public: */ void InitializeExternalFieldsOnGridUsingParser ( amrex::MultiFab *mfx, amrex::MultiFab *mfy, amrex::MultiFab *mfz, - ParserWrapper<3> *xfield_parser, ParserWrapper<3> *yfield_parser, - ParserWrapper<3> *zfield_parser, const int lev); + HostDeviceParser<3> const& xfield_parser, HostDeviceParser<3> const& yfield_parser, + HostDeviceParser<3> const& zfield_parser, const int lev); /** \brief adds particle and cell contributions in cells to compute heuristic * cost in each box on each level, and records in `costs` @@ -552,6 +565,10 @@ protected: //! Tagging cells for refinement virtual void ErrorEst (int lev, amrex::TagBoxArray& tags, amrex::Real time, int /*ngrow*/) final; + //! Use this function to override the Level 0 grids made by AMReX. + //! This function is called in amrex::AmrCore::InitFromScratch. + virtual void PostProcessBaseGrids (amrex::BoxArray& ba0) const final; + //! Make a new level from scratch using provided BoxArray and //! DistributionMapping. Only used during initialization. Called //! by AmrCoreInitFromScratch. @@ -561,8 +578,8 @@ protected: //! Make a new level using provided BoxArray and //! DistributionMapping and fill with interpolated coarse level //! data. Called by AmrCore::regrid. - virtual void MakeNewLevelFromCoarse (int lev, amrex::Real time, const amrex::BoxArray& ba, - const amrex::DistributionMapping& dm) final + virtual void MakeNewLevelFromCoarse (int /*lev*/, amrex::Real /*time*/, const amrex::BoxArray& /*ba*/, + const amrex::DistributionMapping& /*dm*/) final { amrex::Abort("MakeNewLevelFromCoarse: To be implemented"); } //! Remake an existing level using provided BoxArray and @@ -832,6 +849,9 @@ private: int noy_fft = 16; int noz_fft = 16; + // Domain decomposition on Level 0 + amrex::IntVect numprocs{0}; + #ifdef WARPX_USE_PSATD private: void EvolvePSATD (int numsteps); diff --git a/Source/WarpX.cpp b/Source/WarpX.cpp index 631380a04f8..0792a685cec 100644 --- a/Source/WarpX.cpp +++ b/Source/WarpX.cpp @@ -91,6 +91,7 @@ bool WarpX::galerkin_interpolation = true; bool WarpX::use_filter = false; bool WarpX::use_kspace_filter = false; bool WarpX::use_filter_compensation = false; +bool WarpX::use_damp_fields_in_z_guard = false; bool WarpX::serialize_ics = false; bool WarpX::refine_plasma = false; @@ -98,7 +99,7 @@ bool WarpX::refine_plasma = false; int WarpX::num_mirrors = 0; IntervalsParser WarpX::sort_intervals; -amrex::IntVect WarpX::sort_bin_size(AMREX_D_DECL(4,4,4)); +amrex::IntVect WarpX::sort_bin_size(AMREX_D_DECL(1,1,1)); bool WarpX::do_back_transformed_diagnostics = false; std::string WarpX::lab_data_directory = "lab_frame_data"; @@ -175,9 +176,6 @@ WarpX::WarpX () t_old.resize(nlevs_max, std::numeric_limits::lowest()); dt.resize(nlevs_max, std::numeric_limits::max()); - // Diagnostics - multi_diags = std::unique_ptr (new MultiDiagnostics()); - // Particle Container mypc = std::unique_ptr (new MultiParticleContainer(this)); warpx_do_continuous_injection = mypc->doContinuousInjection(); @@ -192,6 +190,9 @@ WarpX::WarpX () } do_back_transformed_particles = mypc->doBackTransformedDiagnostics(); + // Diagnostics + multi_diags = std::unique_ptr (new MultiDiagnostics()); + /** create object for reduced diagnostics */ reduced_diags = new MultiReducedDiags(); @@ -333,6 +334,22 @@ WarpX::ReadParameters () { ParmParse pp("warpx"); + std::vector numprocs_in; + pp.queryarr("numprocs", numprocs_in); + if (not numprocs_in.empty()) { + AMREX_ALWAYS_ASSERT_WITH_MESSAGE + (numprocs_in.size() == AMREX_SPACEDIM, + "warpx.numprocs, if specified, must have AMREX_SPACEDIM numbers"); + AMREX_ALWAYS_ASSERT_WITH_MESSAGE + (ParallelDescriptor::NProcs() == AMREX_D_TERM(numprocs_in[0], + *numprocs_in[1], + *numprocs_in[2]), + "warpx.numprocs, if specified, its product must be equal to the number of processes"); + for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { + numprocs[idim] = numprocs_in[idim]; + } + } + // set random seed std::string random_seed = "default"; pp.query("random_seed", random_seed); @@ -357,9 +374,9 @@ WarpX::ReadParameters () pp.query("do_subcycling", do_subcycling); pp.query("use_hybrid_QED", use_hybrid_QED); pp.query("safe_guard_cells", safe_guard_cells); - std::string override_sync_int_string = "1"; - pp.query("override_sync_int", override_sync_int_string); - override_sync_intervals = IntervalsParser(override_sync_int_string); + std::vector override_sync_int_string_vec = {"1"}; + pp.queryarr("override_sync_int", override_sync_int_string_vec); + override_sync_intervals = IntervalsParser(override_sync_int_string_vec); AMREX_ALWAYS_ASSERT_WITH_MESSAGE(do_subcycling != 1 || max_level <= 1, "Subcycling method 1 only works for 2 levels."); @@ -476,12 +493,12 @@ WarpX::ReadParameters () pp.query("n_field_gather_buffer", n_field_gather_buffer); pp.query("n_current_deposition_buffer", n_current_deposition_buffer); #ifdef AMREX_USE_GPU - std::string sort_int_string = "4"; + std::vectorsort_int_string_vec = {"4"}; #else - std::string sort_int_string = "-1"; + std::vector sort_int_string_vec = {"-1"}; #endif - pp.query("sort_int", sort_int_string); - sort_intervals = IntervalsParser(sort_int_string); + pp.queryarr("sort_int", sort_int_string_vec); + sort_intervals = IntervalsParser(sort_int_string_vec); Vector vect_sort_bin_size(AMREX_SPACEDIM,1); bool sort_bin_size_is_specified = pp.queryarr("sort_bin_size", vect_sort_bin_size); @@ -556,9 +573,9 @@ WarpX::ReadParameters () fine_tag_hi = RealVect{hi}; } - std::string load_balance_int_string = "0"; - pp.query("load_balance_int", load_balance_int_string); - load_balance_intervals = IntervalsParser(load_balance_int_string); + std::vector load_balance_int_string_vec = {"0"}; + pp.queryarr("load_balance_int", load_balance_int_string_vec); + load_balance_intervals = IntervalsParser(load_balance_int_string_vec); pp.query("load_balance_with_sfc", load_balance_with_sfc); pp.query("load_balance_knapsack_factor", load_balance_knapsack_factor); pp.query("load_balance_efficiency_ratio_threshold", load_balance_efficiency_ratio_threshold); @@ -622,6 +639,7 @@ WarpX::ReadParameters () ParmParse pp("psatd"); pp.query("periodic_single_box_fft", fft_periodic_single_box); pp.query("fftw_plan_measure", fftw_plan_measure); + std::string nox_str; std::string noy_str; std::string noz_str; @@ -654,12 +672,29 @@ WarpX::ReadParameters () } pp.query("current_correction", current_correction); - pp.query("update_with_rho", update_with_rho); - pp.query("v_galilean", v_galilean); + pp.query("v_galilean", m_v_galilean); pp.query("do_time_averaging", fft_do_time_averaging); // Scale the velocity by the speed of light - for (int i=0; i<3; i++) v_galilean[i] *= PhysConst::c; + for (int i=0; i<3; i++) m_v_galilean[i] *= PhysConst::c; + + if (m_v_galilean[0] == 0. && m_v_galilean[1] == 0. && m_v_galilean[2] == 0.) { + update_with_rho = false; // standard PSATD + } + else { + update_with_rho = true; // Galilean PSATD + } + + // Overwrite update_with_rho with value set in input file + pp.query("update_with_rho", update_with_rho); + +# ifdef WARPX_DIM_RZ + if (!Geom(0).isPeriodic(1)) { + use_damp_fields_in_z_guard = true; + } + pp.query("use_damp_fields_in_z_guard", use_damp_fields_in_z_guard); +# endif + } #endif @@ -824,7 +859,7 @@ WarpX::AllocLevelData (int lev, const BoxArray& ba, const DistributionMapping& d NCIGodfreyFilter::m_stencil_width, maxwell_solver_id, maxLevel(), - WarpX::v_galilean, + WarpX::m_v_galilean, safe_guard_cells); if (mypc->nSpeciesDepositOnMainGrid() && n_current_deposition_buffer == 0) { @@ -925,26 +960,26 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm // std::array dx = CellSize(lev); - Bfield_fp[lev][0].reset( new MultiFab(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE+ngextra)); - Bfield_fp[lev][1].reset( new MultiFab(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE+ngextra)); - Bfield_fp[lev][2].reset( new MultiFab(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE+ngextra)); + Bfield_fp[lev][0] = std::make_unique(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE+ngextra); + Bfield_fp[lev][1] = std::make_unique(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE+ngextra); + Bfield_fp[lev][2] = std::make_unique(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE+ngextra); - Efield_fp[lev][0].reset( new MultiFab(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE+ngextra)); - Efield_fp[lev][1].reset( new MultiFab(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE+ngextra)); - Efield_fp[lev][2].reset( new MultiFab(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE+ngextra)); + Efield_fp[lev][0] = std::make_unique(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE+ngextra); + Efield_fp[lev][1] = std::make_unique(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE+ngextra); + Efield_fp[lev][2] = std::make_unique(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE+ngextra); - current_fp[lev][0].reset( new MultiFab(amrex::convert(ba,jx_nodal_flag),dm,ncomps,ngJ)); - current_fp[lev][1].reset( new MultiFab(amrex::convert(ba,jy_nodal_flag),dm,ncomps,ngJ)); - current_fp[lev][2].reset( new MultiFab(amrex::convert(ba,jz_nodal_flag),dm,ncomps,ngJ)); + current_fp[lev][0] = std::make_unique(amrex::convert(ba,jx_nodal_flag),dm,ncomps,ngJ); + current_fp[lev][1] = std::make_unique(amrex::convert(ba,jy_nodal_flag),dm,ncomps,ngJ); + current_fp[lev][2] = std::make_unique(amrex::convert(ba,jz_nodal_flag),dm,ncomps,ngJ); - Bfield_avg_fp[lev][0].reset( new MultiFab(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE)); - Bfield_avg_fp[lev][1].reset( new MultiFab(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE)); - Bfield_avg_fp[lev][2].reset( new MultiFab(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE)); + Bfield_avg_fp[lev][0] = std::make_unique(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE); + Bfield_avg_fp[lev][1] = std::make_unique(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE); + Bfield_avg_fp[lev][2] = std::make_unique(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE); - Efield_avg_fp[lev][0].reset( new MultiFab(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE)); - Efield_avg_fp[lev][1].reset( new MultiFab(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE)); - Efield_avg_fp[lev][2].reset( new MultiFab(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE)); + Efield_avg_fp[lev][0] = std::make_unique(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE); + Efield_avg_fp[lev][1] = std::make_unique(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE); + Efield_avg_fp[lev][2] = std::make_unique(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE); #ifdef PULSAR if (do_dive_cleaning || (plot_rho) ) @@ -957,9 +992,9 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm if (do_subcycling == 1 && lev == 0) { - current_store[lev][0].reset( new MultiFab(amrex::convert(ba,jx_nodal_flag),dm,ncomps,ngJ)); - current_store[lev][1].reset( new MultiFab(amrex::convert(ba,jy_nodal_flag),dm,ncomps,ngJ)); - current_store[lev][2].reset( new MultiFab(amrex::convert(ba,jz_nodal_flag),dm,ncomps,ngJ)); + current_store[lev][0] = std::make_unique(amrex::convert(ba,jx_nodal_flag),dm,ncomps,ngJ); + current_store[lev][1] = std::make_unique(amrex::convert(ba,jy_nodal_flag),dm,ncomps,ngJ); + current_store[lev][2] = std::make_unique(amrex::convert(ba,jz_nodal_flag),dm,ncomps,ngJ); } if (do_dive_cleaning) @@ -979,17 +1014,28 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm # endif // Check whether the option periodic, single box is valid here if (fft_periodic_single_box) { - AMREX_ALWAYS_ASSERT_WITH_MESSAGE( geom[0].isAllPeriodic() && ba.size()==1 && lev==0, - "The option `psatd.periodic_single_box_fft` can only be used for a periodic domain, decomposed in a single box."); +# ifdef WARPX_DIM_RZ + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + geom[0].isPeriodic(1) // domain is periodic in z + && ba.size() == 1 && lev == 0, // domain is decomposed in a single box + "The option `psatd.periodic_single_box_fft` can only be used for a periodic domain, decomposed in a single box"); +# else + AMREX_ALWAYS_ASSERT_WITH_MESSAGE( + geom[0].isAllPeriodic() // domain is periodic in all directions + && ba.size() == 1 && lev == 0, // domain is decomposed in a single box + "The option `psatd.periodic_single_box_fft` can only be used for a periodic domain, decomposed in a single box"); +# endif } // Get the cell-centered box BoxArray realspace_ba = ba; // Copy box realspace_ba.enclosedCells(); // Make it cell-centered // Define spectral solver # ifdef WARPX_DIM_RZ - realspace_ba.grow(1, ngE[1]); // add guard cells only in z - spectral_solver_fp[lev].reset( new SpectralSolverRZ( realspace_ba, dm, - n_rz_azimuthal_modes, noz_fft, do_nodal, dx_vect, dt[lev], lev ) ); + if ( fft_periodic_single_box == false ) { + realspace_ba.grow(1, ngE[1]); // add guard cells only in z + } + spectral_solver_fp[lev] = std::make_unique( realspace_ba, dm, + n_rz_azimuthal_modes, noz_fft, do_nodal, m_v_galilean, dx_vect, dt[lev], lev ); if (use_kspace_filter) { spectral_solver_fp[lev]->InitFilter(filter_npass_each_dir, use_filter_compensation); } @@ -997,14 +1043,14 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm if ( fft_periodic_single_box == false ) { realspace_ba.grow(ngE); // add guard cells } - bool const pml=false; - spectral_solver_fp[lev].reset( new SpectralSolver( realspace_ba, dm, - nox_fft, noy_fft, noz_fft, do_nodal, v_galilean, dx_vect, dt[lev], - pml, fft_periodic_single_box, update_with_rho, fft_do_time_averaging ) ); + bool const pml_flag_false=false; + spectral_solver_fp[lev] = std::make_unique( realspace_ba, dm, + nox_fft, noy_fft, noz_fft, do_nodal, m_v_galilean, dx_vect, dt[lev], + pml_flag_false, fft_periodic_single_box, update_with_rho, fft_do_time_averaging ); # endif #endif - m_fdtd_solver_fp[lev].reset( - new FiniteDifferenceSolver(maxwell_solver_id, dx, do_nodal) ); + m_fdtd_solver_fp[lev] = std::make_unique( + maxwell_solver_id, dx, do_nodal); // // The Aux patch (i.e., the full solution) // @@ -1012,13 +1058,13 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm { // Create aux multifabs on Nodal Box Array BoxArray const nba = amrex::convert(ba,IntVect::TheNodeVector()); - Bfield_aux[lev][0].reset( new MultiFab(nba,dm,ncomps,ngE)); - Bfield_aux[lev][1].reset( new MultiFab(nba,dm,ncomps,ngE)); - Bfield_aux[lev][2].reset( new MultiFab(nba,dm,ncomps,ngE)); + Bfield_aux[lev][0] = std::make_unique(nba,dm,ncomps,ngE); + Bfield_aux[lev][1] = std::make_unique(nba,dm,ncomps,ngE); + Bfield_aux[lev][2] = std::make_unique(nba,dm,ncomps,ngE); - Efield_aux[lev][0].reset( new MultiFab(nba,dm,ncomps,ngE)); - Efield_aux[lev][1].reset( new MultiFab(nba,dm,ncomps,ngE)); - Efield_aux[lev][2].reset( new MultiFab(nba,dm,ncomps,ngE)); + Efield_aux[lev][0] = std::make_unique(nba,dm,ncomps,ngE); + Efield_aux[lev][1] = std::make_unique(nba,dm,ncomps,ngE); + Efield_aux[lev][2] = std::make_unique(nba,dm,ncomps,ngE); } else if (lev == 0) { @@ -1032,22 +1078,22 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm } else { - Bfield_aux[lev][0].reset( new MultiFab(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE)); - Bfield_aux[lev][1].reset( new MultiFab(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE)); - Bfield_aux[lev][2].reset( new MultiFab(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE)); + Bfield_aux[lev][0] = std::make_unique(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE); + Bfield_aux[lev][1] = std::make_unique(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE); + Bfield_aux[lev][2] = std::make_unique(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE); - Efield_aux[lev][0].reset( new MultiFab(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE)); - Efield_aux[lev][1].reset( new MultiFab(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE)); - Efield_aux[lev][2].reset( new MultiFab(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE)); + Efield_aux[lev][0] = std::make_unique(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE); + Efield_aux[lev][1] = std::make_unique(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE); + Efield_aux[lev][2] = std::make_unique(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE); - Bfield_avg_aux[lev][0].reset( new MultiFab(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE)); - Bfield_avg_aux[lev][1].reset( new MultiFab(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE)); - Bfield_avg_aux[lev][2].reset( new MultiFab(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE)); + Bfield_avg_aux[lev][0] = std::make_unique(amrex::convert(ba,Bx_nodal_flag),dm,ncomps,ngE); + Bfield_avg_aux[lev][1] = std::make_unique(amrex::convert(ba,By_nodal_flag),dm,ncomps,ngE); + Bfield_avg_aux[lev][2] = std::make_unique(amrex::convert(ba,Bz_nodal_flag),dm,ncomps,ngE); - Efield_avg_aux[lev][0].reset( new MultiFab(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE)); - Efield_avg_aux[lev][1].reset( new MultiFab(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE)); - Efield_avg_aux[lev][2].reset( new MultiFab(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE)); + Efield_avg_aux[lev][0] = std::make_unique(amrex::convert(ba,Ex_nodal_flag),dm,ncomps,ngE); + Efield_avg_aux[lev][1] = std::make_unique(amrex::convert(ba,Ey_nodal_flag),dm,ncomps,ngE); + Efield_avg_aux[lev][2] = std::make_unique(amrex::convert(ba,Ez_nodal_flag),dm,ncomps,ngE); } @@ -1061,29 +1107,29 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm std::array cdx = CellSize(lev-1); // Create the MultiFabs for B - Bfield_cp[lev][0].reset( new MultiFab(amrex::convert(cba,Bx_nodal_flag),dm,ncomps,ngE)); - Bfield_cp[lev][1].reset( new MultiFab(amrex::convert(cba,By_nodal_flag),dm,ncomps,ngE)); - Bfield_cp[lev][2].reset( new MultiFab(amrex::convert(cba,Bz_nodal_flag),dm,ncomps,ngE)); + Bfield_cp[lev][0] = std::make_unique(amrex::convert(cba,Bx_nodal_flag),dm,ncomps,ngE); + Bfield_cp[lev][1] = std::make_unique(amrex::convert(cba,By_nodal_flag),dm,ncomps,ngE); + Bfield_cp[lev][2] = std::make_unique(amrex::convert(cba,Bz_nodal_flag),dm,ncomps,ngE); // Create the MultiFabs for E - Efield_cp[lev][0].reset( new MultiFab(amrex::convert(cba,Ex_nodal_flag),dm,ncomps,ngE)); - Efield_cp[lev][1].reset( new MultiFab(amrex::convert(cba,Ey_nodal_flag),dm,ncomps,ngE)); - Efield_cp[lev][2].reset( new MultiFab(amrex::convert(cba,Ez_nodal_flag),dm,ncomps,ngE)); + Efield_cp[lev][0] = std::make_unique(amrex::convert(cba,Ex_nodal_flag),dm,ncomps,ngE); + Efield_cp[lev][1] = std::make_unique(amrex::convert(cba,Ey_nodal_flag),dm,ncomps,ngE); + Efield_cp[lev][2] = std::make_unique(amrex::convert(cba,Ez_nodal_flag),dm,ncomps,ngE); // Create the MultiFabs for B_avg - Bfield_avg_cp[lev][0].reset( new MultiFab(amrex::convert(cba,Bx_nodal_flag),dm,ncomps,ngE)); - Bfield_avg_cp[lev][1].reset( new MultiFab(amrex::convert(cba,By_nodal_flag),dm,ncomps,ngE)); - Bfield_avg_cp[lev][2].reset( new MultiFab(amrex::convert(cba,Bz_nodal_flag),dm,ncomps,ngE)); + Bfield_avg_cp[lev][0] = std::make_unique(amrex::convert(cba,Bx_nodal_flag),dm,ncomps,ngE); + Bfield_avg_cp[lev][1] = std::make_unique(amrex::convert(cba,By_nodal_flag),dm,ncomps,ngE); + Bfield_avg_cp[lev][2] = std::make_unique(amrex::convert(cba,Bz_nodal_flag),dm,ncomps,ngE); // Create the MultiFabs for E_avg - Efield_avg_cp[lev][0].reset( new MultiFab(amrex::convert(cba,Ex_nodal_flag),dm,ncomps,ngE)); - Efield_avg_cp[lev][1].reset( new MultiFab(amrex::convert(cba,Ey_nodal_flag),dm,ncomps,ngE)); - Efield_avg_cp[lev][2].reset( new MultiFab(amrex::convert(cba,Ez_nodal_flag),dm,ncomps,ngE)); + Efield_avg_cp[lev][0] = std::make_unique(amrex::convert(cba,Ex_nodal_flag),dm,ncomps,ngE); + Efield_avg_cp[lev][1] = std::make_unique(amrex::convert(cba,Ey_nodal_flag),dm,ncomps,ngE); + Efield_avg_cp[lev][2] = std::make_unique(amrex::convert(cba,Ez_nodal_flag),dm,ncomps,ngE); // Create the MultiFabs for the current - current_cp[lev][0].reset( new MultiFab(amrex::convert(cba,jx_nodal_flag),dm,ncomps,ngJ)); - current_cp[lev][1].reset( new MultiFab(amrex::convert(cba,jy_nodal_flag),dm,ncomps,ngJ)); - current_cp[lev][2].reset( new MultiFab(amrex::convert(cba,jz_nodal_flag),dm,ncomps,ngJ)); + current_cp[lev][0] = std::make_unique(amrex::convert(cba,jx_nodal_flag),dm,ncomps,ngJ); + current_cp[lev][1] = std::make_unique(amrex::convert(cba,jy_nodal_flag),dm,ncomps,ngJ); + current_cp[lev][2] = std::make_unique(amrex::convert(cba,jz_nodal_flag),dm,ncomps,ngJ); #ifdef PULSAR if (do_dive_cleaning || (plot_rho) ) @@ -1109,21 +1155,21 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm RealVect cdx_vect(cdx[0], cdx[2]); # endif // Get the cell-centered box, with guard cells - BoxArray realspace_ba = cba;// Copy box - realspace_ba.enclosedCells(); // Make it cell-centered + BoxArray c_realspace_ba = cba;// Copy box + c_realspace_ba.enclosedCells(); // Make it cell-centered // Define spectral solver # ifdef WARPX_DIM_RZ - realspace_ba.grow(1, ngE[1]); // add guard cells only in z - spectral_solver_cp[lev].reset( new SpectralSolverRZ( realspace_ba, dm, - n_rz_azimuthal_modes, noz_fft, do_nodal, cdx_vect, dt[lev], lev ) ); + c_realspace_ba.grow(1, ngE[1]); // add guard cells only in z + spectral_solver_cp[lev] = std::make_unique( c_realspace_ba, dm, + n_rz_azimuthal_modes, noz_fft, do_nodal, m_v_galilean, cdx_vect, dt[lev], lev ); if (use_kspace_filter) { spectral_solver_cp[lev]->InitFilter(filter_npass_each_dir, use_filter_compensation); } # else - realspace_ba.grow(ngE); // add guard cells - spectral_solver_cp[lev].reset( new SpectralSolver( realspace_ba, dm, - nox_fft, noy_fft, noz_fft, do_nodal, v_galilean, cdx_vect, dt[lev], - pml, fft_periodic_single_box, update_with_rho, fft_do_time_averaging ) ); + c_realspace_ba.grow(ngE); // add guard cells + spectral_solver_cp[lev] = std::make_unique( c_realspace_ba, dm, + nox_fft, noy_fft, noz_fft, do_nodal, m_v_galilean, cdx_vect, dt[lev], + pml_flag_false, fft_periodic_single_box, update_with_rho, fft_do_time_averaging ); # endif #endif m_fdtd_solver_cp[lev].reset( @@ -1142,37 +1188,37 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm if (n_field_gather_buffer > 0 || mypc->nSpeciesGatherFromMainGrid() > 0) { if (aux_is_nodal) { BoxArray const& cnba = amrex::convert(cba,IntVect::TheNodeVector()); - Bfield_cax[lev][0].reset( new MultiFab(cnba,dm,ncomps,ngE)); - Bfield_cax[lev][1].reset( new MultiFab(cnba,dm,ncomps,ngE)); - Bfield_cax[lev][2].reset( new MultiFab(cnba,dm,ncomps,ngE)); - Efield_cax[lev][0].reset( new MultiFab(cnba,dm,ncomps,ngE)); - Efield_cax[lev][1].reset( new MultiFab(cnba,dm,ncomps,ngE)); - Efield_cax[lev][2].reset( new MultiFab(cnba,dm,ncomps,ngE)); + Bfield_cax[lev][0] = std::make_unique(cnba,dm,ncomps,ngE); + Bfield_cax[lev][1] = std::make_unique(cnba,dm,ncomps,ngE); + Bfield_cax[lev][2] = std::make_unique(cnba,dm,ncomps,ngE); + Efield_cax[lev][0] = std::make_unique(cnba,dm,ncomps,ngE); + Efield_cax[lev][1] = std::make_unique(cnba,dm,ncomps,ngE); + Efield_cax[lev][2] = std::make_unique(cnba,dm,ncomps,ngE); } else { // Create the MultiFabs for B - Bfield_cax[lev][0].reset( new MultiFab(amrex::convert(cba,Bx_nodal_flag),dm,ncomps,ngE)); - Bfield_cax[lev][1].reset( new MultiFab(amrex::convert(cba,By_nodal_flag),dm,ncomps,ngE)); - Bfield_cax[lev][2].reset( new MultiFab(amrex::convert(cba,Bz_nodal_flag),dm,ncomps,ngE)); + Bfield_cax[lev][0] = std::make_unique(amrex::convert(cba,Bx_nodal_flag),dm,ncomps,ngE); + Bfield_cax[lev][1] = std::make_unique(amrex::convert(cba,By_nodal_flag),dm,ncomps,ngE); + Bfield_cax[lev][2] = std::make_unique(amrex::convert(cba,Bz_nodal_flag),dm,ncomps,ngE); // Create the MultiFabs for E - Efield_cax[lev][0].reset( new MultiFab(amrex::convert(cba,Ex_nodal_flag),dm,ncomps,ngE)); - Efield_cax[lev][1].reset( new MultiFab(amrex::convert(cba,Ey_nodal_flag),dm,ncomps,ngE)); - Efield_cax[lev][2].reset( new MultiFab(amrex::convert(cba,Ez_nodal_flag),dm,ncomps,ngE)); + Efield_cax[lev][0] = std::make_unique(amrex::convert(cba,Ex_nodal_flag),dm,ncomps,ngE); + Efield_cax[lev][1] = std::make_unique(amrex::convert(cba,Ey_nodal_flag),dm,ncomps,ngE); + Efield_cax[lev][2] = std::make_unique(amrex::convert(cba,Ez_nodal_flag),dm,ncomps,ngE); } - gather_buffer_masks[lev].reset( new iMultiFab(ba, dm, ncomps, 1) ); + gather_buffer_masks[lev] = std::make_unique(ba, dm, ncomps, 1 ); // Gather buffer masks have 1 ghost cell, because of the fact // that particles may move by more than one cell when using subcycling. } if (n_current_deposition_buffer > 0) { - current_buf[lev][0].reset( new MultiFab(amrex::convert(cba,jx_nodal_flag),dm,ncomps,ngJ)); - current_buf[lev][1].reset( new MultiFab(amrex::convert(cba,jy_nodal_flag),dm,ncomps,ngJ)); - current_buf[lev][2].reset( new MultiFab(amrex::convert(cba,jz_nodal_flag),dm,ncomps,ngJ)); + current_buf[lev][0] = std::make_unique(amrex::convert(cba,jx_nodal_flag),dm,ncomps,ngJ); + current_buf[lev][1] = std::make_unique(amrex::convert(cba,jy_nodal_flag),dm,ncomps,ngJ); + current_buf[lev][2] = std::make_unique(amrex::convert(cba,jz_nodal_flag),dm,ncomps,ngJ); if (rho_cp[lev]) { - charge_buf[lev].reset( new MultiFab(amrex::convert(cba,rho_nodal_flag),dm,2*ncomps,ngRho)); + charge_buf[lev] = std::make_unique(amrex::convert(cba,rho_nodal_flag),dm,2*ncomps,ngRho); } - current_buffer_masks[lev].reset( new iMultiFab(ba, dm, ncomps, 1) ); + current_buffer_masks[lev] = std::make_unique(ba, dm, ncomps, 1); // Current buffer masks have 1 ghost cell, because of the fact // that particles may move by more than one cell when using subcycling. } diff --git a/Source/main.cpp b/Source/main.cpp index 68063070a22..0ac1e93581f 100644 --- a/Source/main.cpp +++ b/Source/main.cpp @@ -8,6 +8,7 @@ */ #include "WarpX.H" #include "Initialization/WarpXAMReXInit.H" +#include "Utils/MPIInitHelpers.H" #include "Utils/WarpXUtil.H" #include "Utils/WarpXProfilerWrapper.H" @@ -15,32 +16,22 @@ #include #include +#if defined(AMREX_USE_HIP) && defined(WARPX_USE_PSATD) +#include +#endif int main(int argc, char* argv[]) { using namespace amrex; -#if defined(AMREX_USE_MPI) -# ifdef AMREX_MPI_THREAD_MULTIPLE - int provided = -1; - MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &provided); - AMREX_ALWAYS_ASSERT(provided >= MPI_THREAD_MULTIPLE); -# else -# if defined(_OPENMP) && defined(WARPX_USE_PSATD) - int provided = -1; - MPI_Init_thread(&argc, &argv, MPI_THREAD_FUNNELED, &provided); - AMREX_ALWAYS_ASSERT(provided >= MPI_THREAD_FUNNELED); -# else - MPI_Init(&argc, &argv); -# endif -# endif -#endif + auto mpi_thread_levels = utils::warpx_mpi_init(argc, argv); warpx_amrex_init(argc, argv); - // in Debug mode, we need a larger stack limit than usual bc of the parser. -#if defined(AMREX_USE_CUDA) && defined(AMREX_DEBUG) - AMREX_CUDA_SAFE_CALL(cudaDeviceSetLimit(cudaLimitStackSize, 20*1024)); + utils::warpx_check_mpi_thread_level(mpi_thread_levels); + +#if defined(AMREX_USE_HIP) && defined(WARPX_USE_PSATD) + rocfft_setup(); #endif ConvertLabParamsToBoost(); @@ -62,15 +53,19 @@ int main(int argc, char* argv[]) ParallelDescriptor::ReduceRealMax(end_total, ParallelDescriptor::IOProcessorNumber()); if (warpx.Verbose()) { - amrex::Print() << "Total Time : " << end_total << '\n'; - amrex::Print() << "WarpX Version: " << WarpX::Version() << '\n'; - amrex::Print() << "PICSAR Version: " << WarpX::PicsarVersion() << '\n'; + Print() << "Total Time : " << end_total << '\n'; + Print() << "WarpX Version: " << WarpX::Version() << '\n'; + Print() << "PICSAR Version: " << WarpX::PicsarVersion() << '\n'; } } WARPX_PROFILE_VAR_STOP(pmain); - amrex::Finalize(); +#if defined(AMREX_USE_HIP) && defined(WARPX_USE_PSATD) + rocfft_cleanup(); +#endif + + Finalize(); #if defined(AMREX_USE_MPI) MPI_Finalize(); #endif diff --git a/Tools/BatchScripts/batch_cori.sh b/Tools/BatchScripts/batch_cori.sh index ee2658451ff..a827ad7799b 100644 --- a/Tools/BatchScripts/batch_cori.sh +++ b/Tools/BatchScripts/batch_cori.sh @@ -37,4 +37,7 @@ export WARPX_NTHREADS_PER_NODE=$(( ${WARPX_HYPERTHREAD_LEVEL} * ${CORI_NCORES_PE export OMP_NUM_THREADS=$(( ${WARPX_NTHREADS_PER_NODE} / ${WARPX_NMPI_PER_NODE} )) export WARPX_THREAD_COUNT=$(( ${CORI_NHYPERTHREADS_MAX} / ${WARPX_NMPI_PER_NODE} )) +# for async_io support: (optional) +export MPICH_MAX_THREAD_SAFETY=multiple + srun --cpu_bind=cores -n $(( ${SLURM_JOB_NUM_NODES} * ${WARPX_NMPI_PER_NODE} )) -c ${WARPX_THREAD_COUNT} diff --git a/Tools/BatchScripts/batch_cori_haswell.sh b/Tools/BatchScripts/batch_cori_haswell.sh index a1a21defccd..0f3c66b495e 100644 --- a/Tools/BatchScripts/batch_cori_haswell.sh +++ b/Tools/BatchScripts/batch_cori_haswell.sh @@ -31,6 +31,9 @@ export OMP_PROC_BIND=spread export OMP_PLACES=threads export OMP_NUM_THREADS=16 +# for async_io support: (optional) +export MPICH_MAX_THREAD_SAFETY=multiple + EXE="" srun --cpu_bind=cores -n $(( ${SLURM_JOB_NUM_NODES} * ${WARPX_NMPI_PER_NODE} )) ${EXE} diff --git a/Tools/BatchScripts/batch_lassen.sh b/Tools/BatchScripts/batch_lassen.sh new file mode 100644 index 00000000000..0fd2500c58d --- /dev/null +++ b/Tools/BatchScripts/batch_lassen.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Copyright 2020 Axel Huebl +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL +# +# Refs.: +# https://jsrunvisualizer.olcf.ornl.gov/?s4f0o11n6c7g1r11d1b1l0= +# https://hpc.llnl.gov/training/tutorials/using-lcs-sierra-system#quick16 + +#BSUB -G +#BSUB -W 00:10 +#BSUB -nnodes 2 +#BSUB -alloc_flags smt4 +#BSUB -J WarpX +#BSUB -o WarpXo.%J +#BSUB -e WarpXe.%J + +export OMP_NUM_THREADS=1 +jsrun -r 4 -a 1 -g 1 -c 7 -l GPU-CPU -d packed -b rs -M "-gpu" > output.txt diff --git a/Tools/BatchScripts/batch_quartz.sh b/Tools/BatchScripts/batch_quartz.sh new file mode 100644 index 00000000000..4c1a82ff8e9 --- /dev/null +++ b/Tools/BatchScripts/batch_quartz.sh @@ -0,0 +1,40 @@ +#!/bin/bash -l + +# Just increase this number of you need more nodes. +#SBATCH -N 2 +#SBATCH -t 24:00:00 +#SBATCH -A + +#SBATCH -J WarpX +#SBATCH -q pbatch +#SBATCH --qos=normal +#SBATCH --license=lustre1,lustre2 +#SBATCH --export=ALL +#SBATCH -e error.txt +#SBATCH -o output.txt +# one MPI rank per half-socket (see below) +#SBATCH --tasks-per-node=2 +# request all logical (virtual) cores per half-socket +#SBATCH --cpus-per-task=18 + + +# each Quartz node has 1 socket of Intel Xeon E5-2695 v4 +# each Xeon CPU is divided into 2 bus rings that each have direct L3 access +export WARPX_NMPI_PER_NODE=2 + +# each MPI rank per half-socket has 9 physical cores +# or 18 logical (virtual) cores +# over-subscribing each physical core with 2x +# hyperthreading led to a slight (3.5%) speedup on Cori's Intel Xeon E5-2698 v3, +# so we do the same here +# the settings below make sure threads are close to the +# controlling MPI rank (process) per half socket and +# distribute equally over close-by physical cores and, +# for N>9, also equally over close-by logical cores +export OMP_PROC_BIND=spread +export OMP_PLACES=threads +export OMP_NUM_THREADS=18 + +EXE="" # e.g. ./warpx + +srun --cpu_bind=cores -n $(( ${SLURM_JOB_NUM_NODES} * ${WARPX_NMPI_PER_NODE} )) ${EXE} diff --git a/Tools/PerformanceTests/functions_perftest.py b/Tools/PerformanceTests/functions_perftest.py index cea2b610753..6af4189b6c3 100644 --- a/Tools/PerformanceTests/functions_perftest.py +++ b/Tools/PerformanceTests/functions_perftest.py @@ -191,7 +191,7 @@ def extract_dataframe(filename, n_steps): line_match_looptime = re.search('\nWarpX::Evolve().*', search_area) time_wo_initialization = float(line_match_looptime.group(0).split()[3]) # New, might break something - line_match_WritePlotFile = re.search('\nFlushFormatPlotfile::WriteToFile().*', search_area) + line_match_WritePlotFile = re.search('\nDiagnostics::FilterComputePackFlush().*', search_area) if line_match_WritePlotFile is not None: time_WritePlotFile = float(line_match_WritePlotFile.group(0).split()[3]) else: diff --git a/Tools/PostProcessing/Visualization.ipynb b/Tools/PostProcessing/Visualization.ipynb index 8dd2f344161..dbeb9a734fc 100644 --- a/Tools/PostProcessing/Visualization.ipynb +++ b/Tools/PostProcessing/Visualization.ipynb @@ -45,7 +45,9 @@ "metadata": {}, "outputs": [], "source": [ - "plotfile = './diags/plotfiles/plt00001'\n", + "diag_name = 'diag' # E.g., diagnostics.diags_names = diag\n", + "iteration = 0\n", + "plotfile = './diags/{}{:05d}'.format(diag_name, iteration)\n", "field = 'Ex'\n", "species = 'electron'\n", "ds = yt.load( plotfile ) # Load the plotfile\n", @@ -94,11 +96,11 @@ "# Get particle quantities\n", "ad = ds.all_data()\n", "x = ad[species, 'particle_position_x'].v\n", - "z = ad[species, 'particle_position_y'].v\n", + "z = ad[species, 'particle_position_z'].v\n", "\n", "# Plot image\n", "plt.figure()\n", - "plt.imshow(Bx, extent=extent)\n", + "plt.imshow(Bx[:,Bx.shape[1]//2,:], extent=extent, aspect='auto')\n", "plt.scatter(z,x,s=.1,c='k')" ] }, @@ -132,7 +134,7 @@ "header = './lab_frame_data/Header'\n", "allrd, info = read_raw_data.read_lab_snapshot(snapshot, header) # Read field data\n", "F = allrd[field]\n", - "print( \"Available info: \", *list(info.keys()) )\n", + "print( \"Available info: \", list(info.keys()) )\n", "print(\"Available fields: \", info['field_names'])\n", "nx = info['nx']\n", "nz = info['nz']\n", @@ -145,7 +147,7 @@ "\n", "plt.figure(figsize=(6, 3))\n", "extent = np.array([info['zmin'], info['zmax'], info['xmin'], info['xmax']])\n", - "plt.imshow(F, aspect='auto', extent=extent, cmap='seismic')\n", + "plt.imshow(F[:,F.shape[1]//2,:], aspect='auto', extent=extent, cmap='seismic')\n", "plt.colorbar()\n", "plt.plot(zbo, xbo, 'g.', markersize=1.)\n" ] diff --git a/cmake/WarpXFunctions.cmake b/cmake/WarpXFunctions.cmake index 24ddd2d7d65..ebb7db62ecc 100644 --- a/cmake/WarpXFunctions.cmake +++ b/cmake/WarpXFunctions.cmake @@ -157,7 +157,7 @@ function(set_warpx_binary_name) endif() if(WarpX_OPENPMD) - set_property(TARGET WarpX APPEND_STRING PROPERTY OUTPUT_NAME ".OMP") + set_property(TARGET WarpX APPEND_STRING PROPERTY OUTPUT_NAME ".OPMD") endif() if(WarpX_PSATD) @@ -240,6 +240,10 @@ function(warpx_print_summary) message(" COMPUTE: ${WarpX_COMPUTE}") message(" DIMS: ${WarpX_DIMS}") message(" MPI: ${WarpX_MPI}") + if(MPI) + message(" MPI (thread multiple): ${WarpX_MPI_THREAD_MULTIPLE}") + endif() + message(" Parser depth: ${WarpX_PARSER_DEPTH}") message(" PSATD: ${WarpX_PSATD}") message(" PRECISION: ${WarpX_PRECISION}") message(" OPENPMD: ${WarpX_OPENPMD}") diff --git a/cmake/dependencies/AMReX.cmake b/cmake/dependencies/AMReX.cmake index 21290115466..8b2f8bac243 100644 --- a/cmake/dependencies/AMReX.cmake +++ b/cmake/dependencies/AMReX.cmake @@ -47,6 +47,11 @@ macro(find_amrex) if(WarpX_MPI) set(ENABLE_MPI ON CACHE INTERNAL "") + if(WarpX_MPI_THREAD_MULTIPLE) + set(ENABLE_MPI_THREAD_MULTIPLE ON CACHE INTERNAL "") + else() + set(ENABLE_MPI_THREAD_MULTIPLE OFF CACHE INTERNAL "") + endif() else() set(ENABLE_MPI OFF CACHE INTERNAL "") endif() @@ -65,7 +70,7 @@ macro(find_amrex) set(ENABLE_PARTICLES ON CACHE INTERNAL "") set(ENABLE_TINY_PROFILE ON CACHE BOOL "") - # ENABLE_SENSEI_IN_SITU + # ENABLE_SENSEI # we'll need this for Python bindings #set(ENABLE_PIC ON CACHE INTERNAL "") @@ -120,9 +125,10 @@ macro(find_amrex) mark_as_advanced(ENABLE_LINEAR_SOLVERS) mark_as_advanced(ENABLE_MEM_PROFILE) mark_as_advanced(ENABLE_MPI) + mark_as_advanced(ENABLE_MPI_THREAD_MULTIPLE) mark_as_advanced(ENABLE_OMP) mark_as_advanced(ENABLE_PIC) - mark_as_advanced(ENABLE_SENSEI_INSITU) + mark_as_advanced(ENABLE_SENSEI) mark_as_advanced(ENABLE_TINY_PROFILE) mark_as_advanced(TP_PROFILE) mark_as_advanced(USE_XSDK_DEFAULTS) diff --git a/cmake/dependencies/PICSAR.cmake b/cmake/dependencies/PICSAR.cmake index cac75d997bf..ad6f064fce5 100644 --- a/cmake/dependencies/PICSAR.cmake +++ b/cmake/dependencies/PICSAR.cmake @@ -15,7 +15,7 @@ function(find_picsar) if(NOT fetchedpicsar_POPULATED) FetchContent_Populate(fetchedpicsar) - add_subdirectory(${fetchedpicsar_SOURCE_DIR}/src/multi_physics ${fetchedpicsar_BINARY_DIR}) + add_subdirectory(${fetchedpicsar_SOURCE_DIR}/src/multi_physics/QED ${fetchedpicsar_BINARY_DIR}) endif() # advanced fetch options diff --git a/cmake/dependencies/openPMD.cmake b/cmake/dependencies/openPMD.cmake index 562996d9097..0130c69e86d 100644 --- a/cmake/dependencies/openPMD.cmake +++ b/cmake/dependencies/openPMD.cmake @@ -4,7 +4,7 @@ function(find_openpmd) include(FetchContent) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) - # see https://openpmd-api.readthedocs.io/en/0.11.1-alpha/dev/buildoptions.html + # see https://openpmd-api.readthedocs.io/en/0.12.0-alpha/dev/buildoptions.html set(openPMD_USE_MPI ${WarpX_MPI} CACHE INTERNAL "") set(openPMD_USE_PYTHON OFF CACHE INTERNAL "") set(BUILD_CLI_TOOLS OFF CACHE INTERNAL "") # FIXME @@ -52,7 +52,7 @@ function(find_openpmd) else() set(COMPONENT_WMPI NOMPI) endif() - find_package(openPMD 0.11.1 CONFIG REQUIRED COMPONENTS ${COMPONENT_WMPI}) + find_package(openPMD 0.12.0 CONFIG REQUIRED COMPONENTS ${COMPONENT_WMPI}) message(STATUS "openPMD-api: Found version '${openPMD_VERSION}'") endif() endfunction() diff --git a/run_test.sh b/run_test.sh index 546452f8a22..428f215174b 100755 --- a/run_test.sh +++ b/run_test.sh @@ -45,12 +45,13 @@ ln -s ${tmp_dir} test_dir # Switch to the test directory cd test_dir +echo "cd $PWD" # Clone PICSAR and AMReX git clone --branch development https://github.com/AMReX-Codes/amrex.git # Use QED brach for QED tests if [ "${WARPX_CI_QED}" = "TRUE" ]; then - git clone --branch QED https://github.com/ECP-WarpX/picsar.git + git clone --branch development https://github.com/ECP-WarpX/picsar.git else git clone --branch development https://github.com/ECP-WarpX/picsar.git fi @@ -61,12 +62,14 @@ git clone https://github.com/ECP-WarpX/regression_testing.git # Prepare regression tests mkdir -p rt-WarpX/WarpX-benchmarks cd warpx/Regression +echo "cd $PWD" python prepare_file_travis.py cp travis-tests.ini ../../rt-WarpX cp -r Checksum ../../regression_testing/ # Run tests cd ../../regression_testing/ +echo "cd $PWD" # run only tests specified in variable tests_arg (single test or multiple tests) if [[ ! -z "${tests_arg}" ]]; then python regtest.py ../rt-WarpX/travis-tests.ini --no_update all --source_git_hash=${WARPX_TEST_COMMIT} "${tests_run}"