From 4bca649643e8b4bb0987496d7f9c08c4dd50e709 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 8 Sep 2023 16:37:45 -0500 Subject: [PATCH 01/39] Lassen RHEL8 (#4278) Update the Lassen (LLNL) documentation for their RHEL8 transition and Python support. --- Docs/source/install/hpc/lassen.rst | 192 +++++++++++++----- .../lassen-llnl/install_v100_dependencies.sh | 132 ++++++++++++ Tools/machines/lassen-llnl/install_v100_ml.sh | 61 ++++++ .../{lassen.bsub => lassen_v100.bsub} | 0 .../lassen_v100_warpx.profile.example | 52 +++++ .../lassen-llnl/lassen_warpx.profile.example | 44 ---- .../summit-olcf/summit_warpx.profile.example | 2 +- 7 files changed, 385 insertions(+), 98 deletions(-) create mode 100755 Tools/machines/lassen-llnl/install_v100_dependencies.sh create mode 100755 Tools/machines/lassen-llnl/install_v100_ml.sh rename Tools/machines/lassen-llnl/{lassen.bsub => lassen_v100.bsub} (100%) create mode 100644 Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example delete mode 100644 Tools/machines/lassen-llnl/lassen_warpx.profile.example diff --git a/Docs/source/install/hpc/lassen.rst b/Docs/source/install/hpc/lassen.rst index 1f03a3c117f..785bce3b385 100644 --- a/Docs/source/install/hpc/lassen.rst +++ b/Docs/source/install/hpc/lassen.rst @@ -3,7 +3,7 @@ Lassen (LLNL) ============= -The `Lassen V100 GPU cluster `_ is located at LLNL. +The `Lassen V100 GPU cluster `__ is located at LLNL. Introduction @@ -11,85 +11,168 @@ Introduction If you are new to this system, **please see the following resources**: -* `LLNL user account `_ -* `Lassen user guide `_ -* Batch system: `LSF `_ -* `Production directories `_: +* `LLNL user account `__ (login required) +* `Lassen user guide `__ +* Batch system: `LSF `__ +* `Jupyter service `__ (`documentation `__, login required) +* `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 ------------- +Login +----- + +.. note:: + + Lassen is currently transitioning to RHEL8. + During this transition, first SSH into lassen and then ``ssh eatoss4`` next to work with the updated RHEL8/TOSS4 nodes. + + Approximately September 2023, the new software environment on these nodes will be the new default. -Use the following commands to download the WarpX source code and switch to the correct branch: + +Preparation +----------- + +Use the following commands to download the WarpX source code: .. 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 (``$HOME/lassen_warpx.profile``). +We use system software modules, add environment hints and further dependencies via the file ``$HOME/lassen_v100_warpx.profile``. +Create it now: -.. literalinclude:: ../../../../Tools/machines/lassen-llnl/lassen_warpx.profile.example - :language: bash - :caption: You can copy this file from ``Tools/machines/lassen-llnl/lassen_warpx.profile.example``. +.. code-block:: bash + + cp $HOME/src/warpx/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example $HOME/lassen_v100_warpx.profile + +.. dropdown:: Script Details + :color: light + :icon: info + :animate: fade-in-slide-down + + .. literalinclude:: ../../../../Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example + :language: bash -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: +Edit the 2nd line of this script, which sets the ``export proj=""`` variable. +For example, if you are member of the project ``nsldt``, then run ``vi $HOME/lassen_v100_warpx.profile``. +Enter the edit mode by typing ``i`` and edit line 2 to read: .. code-block:: bash - source $HOME/lassen_warpx.profile + export proj="nsldt" + +Exit the ``vi`` editor with ``Esc`` and then type ``:wq`` (write & quit). + +.. important:: + + Now, and as the first step on future logins to lassen, activate these environment settings: -And since Lassen does not yet provide a module for them, install ADIOS2, BLAS++ and LAPACK++: + .. code-block:: bash + + source $HOME/lassen_v100_warpx.profile + +Finally, since lassen does not yet provide software modules for some of our dependencies, install them once: .. code-block:: bash - # c-blosc (I/O compression) - git clone -b v1.21.1 https://github.com/Blosc/c-blosc.git src/c-blosc - rm -rf src/c-blosc-lassen-build - cmake -S src/c-blosc -B src/c-blosc-lassen-build -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DDEACTIVATE_AVX2=OFF -DCMAKE_INSTALL_PREFIX=$HOME/sw/lassen/c-blosc-1.21.1 - cmake --build src/c-blosc-lassen-build --target install --parallel 16 - - # HDF5 - git clone -b hdf5-1_14_1-2 https://github.com/HDFGroup/hdf5.git src/hdf5 - rm -rf src/hdf5-lassen-build - cmake -S src/hdf5 -B src/hdf5-lassen-build -DBUILD_TESTING=OFF -DHDF5_ENABLE_PARALLEL=ON -DCMAKE_INSTALL_PREFIX=$HOME/sw/lassen/hdf5-1.14.1.2 - cmake --build src/hdf5-lassen-build --target install --parallel 16 - - # ADIOS2 - git clone -b v2.8.3 https://github.com/ornladios/ADIOS2.git src/adios2 - rm -rf src/adios2-lassen-build - cmake -S src/adios2 -B src/adios2-lassen-build -DBUILD_TESTING=OFF -DADIOS2_BUILD_EXAMPLES=OFF -DADIOS2_USE_Blosc=ON -DADIOS2_USE_Fortran=OFF -DADIOS2_USE_Python=OFF -DADIOS2_USE_SST=OFF -DADIOS2_USE_ZeroMQ=OFF -DCMAKE_INSTALL_PREFIX=$HOME/sw/lassen/adios2-2.8.3 - cmake --build src/adios2-lassen-build --target install -j 16 - - # BLAS++ (for PSATD+RZ) - git clone https://github.com/icl-utk-edu/blaspp.git src/blaspp - rm -rf src/blaspp-lassen-build - cmake -S src/blaspp -B src/blaspp-lassen-build -Duse_openmp=ON -Dgpu_backend=cuda -Duse_cmake_find_blas=ON -DBLA_VENDOR=IBMESSL -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=$HOME/sw/lassen/blaspp-master - cmake --build src/blaspp-lassen-build --target install --parallel 16 - - # LAPACK++ (for PSATD+RZ) - git clone https://github.com/icl-utk-edu/lapackpp.git src/lapackpp - rm -rf src/lapackpp-lassen-build - CXXFLAGS="-DLAPACK_FORTRAN_ADD_" cmake -S src/lapackpp -B src/lapackpp-lassen-build -Duse_cmake_find_lapack=ON -DBLA_VENDOR=IBMESSL -DCMAKE_CXX_STANDARD=17 -Dbuild_tests=OFF -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON -DCMAKE_INSTALL_PREFIX=$HOME/sw/lassen/lapackpp-master -DLAPACK_LIBRARIES=/usr/lib64/liblapack.so - cmake --build src/lapackpp-lassen-build --target install --parallel 16 - -Then, ``cd`` into the directory ``$HOME/src/warpx`` and use the following commands to compile: + bash $HOME/src/warpx/Tools/machines/lassen-llnl/install_v100_dependencies.sh + source $HOME/sw/lassen/gpu/venvs/warpx-lassen/bin/activate + +.. dropdown:: Script Details + :color: light + :icon: info + :animate: fade-in-slide-down + + .. literalinclude:: ../../../../Tools/machines/lassen-llnl/install_v100_dependencies.sh + :language: bash + +.. dropdown:: AI/ML Dependencies (Optional) + :animate: fade-in-slide-down + + If you plan to run AI/ML workflows depending on pyTorch, run the next step as well. + This will take a while and should be skipped if not needed. + + .. code-block:: bash + + runNode bash $HOME/src/warpx/Tools/machines/lassen-llnl/install_v100_ml.sh + + .. dropdown:: Script Details + :color: light + :icon: info + :animate: fade-in-slide-down + + .. literalinclude:: ../../../../Tools/machines/lassen-llnl/install_v100_ml.sh + :language: bash + + For `optimas dependencies `__ (incl. scikit-learn), plan another hour of build time: + + .. code-block:: bash + + python3 -m pip install -r $HOME/src/warpx/Tools/optimas/requirements.txt + + +.. _building-lassen-compilation: + +Compilation +----------- + +Use the following :ref:`cmake commands ` to compile the application executable: .. code-block:: bash cd $HOME/src/warpx - rm -rf build_lassen - cmake -S . -B build_lassen -DWarpX_COMPUTE=CUDA -DWarpX_DIMS="1;2;RZ;3" -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON - cmake --build build_lassen -j 10 -The other :ref:`general compile-time options ` apply as usual. + cmake -S . -B build_lassen -DWarpX_COMPUTE=CUDA -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON -DWarpX_DIMS="1;2;RZ;3" + cmake --build build_lassen -j 8 + +The WarpX application executables are now in ``$HOME/src/warpx/build_lassen/bin/``. +Additionally, the following commands will install WarpX as a Python module: + +.. code-block:: bash + + rm -rf build_lassen_py + + cmake -S . -B build_lassen_py -DWarpX_COMPUTE=CUDA -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON -DWarpX_APP=OFF -DWarpX_PYTHON=ON -DWarpX_DIMS="1;2;RZ;3" + cmake --build build_lassen_py -j 8 --target pip_install + +Now, you can :ref:`submit lassen compute jobs ` for WarpX :ref:`Python (PICMI) scripts ` (:ref:`example scripts `). +Or, you can use the WarpX executables to submit lassen jobs (:ref:`example inputs `). +For executables, you can reference their location in your :ref:`job script ` or copy them to a location in ``$PROJWORK/$proj/``. + -**That's it!** -WarpX executables for 1D, 2D, RZ and 3D are now in ``build_lassen/bin/`` and :ref:`can be run ` with the respective :ref:`example inputs files `. -Most people execute the binary directly or copy it out to a location in ``/p/gpfs1/$(whoami)``. +.. _building-lassen-update: + +Update WarpX & Dependencies +--------------------------- + +If you already installed WarpX in the past and want to update it, start by getting the latest source code: + +.. code-block:: bash + + cd $HOME/src/warpx + + # read the output of this command - does it look ok? + git status + + # get the latest WarpX source code + git fetch + git pull + + # read the output of these commands - do they look ok? + git status + git log # press q to exit + +And, if needed, + +- :ref:`update the lassen_v100_warpx.profile file `, +- log out and into the system, activate the now updated environment profile as usual, +- :ref:`execute the dependency install scripts `. + +As a last step, clean the build directory ``rm -rf $HOME/src/warpx/build_lassen`` and rebuild WarpX. .. _running-cpp-lassen: @@ -146,3 +229,6 @@ Known System Issues .. code-block:: bash export OMPI_MCA_coll_ibm_skip_allgatherv=true + +As part of the same `CORAL acquisition program `__, Lassen is very similar to the design of Summit (OLCF). +Thus, when encountering new issues it is worth checking also the :ref:`known Summit issues and work-arounds `. diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh new file mode 100755 index 00000000000..8c6699c19e7 --- /dev/null +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -0,0 +1,132 @@ +#!/bin/bash +# +# Copyright 2023 The WarpX Community +# +# This file is part of WarpX. +# +# Author: Axel Huebl +# License: BSD-3-Clause-LBNL + +# Exit on first error encountered ############################################# +# +set -eu -o pipefail + + +# Check: ###################################################################### +# +# Was perlmutter_gpu_warpx.profile sourced and configured correctly? +if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in your lassen_v100_warpx.profile file! Please edit its line 2 to continue!"; exit 1; fi + + +# Remove old dependencies ##################################################### +# +SW_DIR="${HOME}/sw/lassen/gpu/" +# better, but needs a request: /usr/WS2/${USER}/ +rm -rf ${SW_DIR} +mkdir -p ${SW_DIR} + +# remove common user mistakes in python, located in .local instead of a venv +python3 -m pip uninstall -qq -y pywarpx +python3 -m pip uninstall -qq -y warpx +python3 -m pip uninstall -qqq -y mpi4py 2>/dev/null || true + + +# General extra dependencies ################################################## +# + +# tmpfs build directory: avoids issues often seen with $HOME and is faster +build_dir=$(mktemp -d) + +# c-blosc (I/O compression) +if [ -d $HOME/src/c-blosc ] +then + cd $HOME/src/c-blosc + git fetch --prune + git checkout v1.21.1 + cd - +else + git clone -b v1.21.1 https://github.com/Blosc/c-blosc.git $HOME/src/c-blosc +fi +cmake -S $HOME/src/c-blosc -B ${build_dir}/c-blosc-lassen-build -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DDEACTIVATE_AVX2=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/c-blosc-1.21.1 +cmake --build ${build_dir}/c-blosc-lassen-build --target install --parallel 10 + +# HDF5 +if [ -d $HOME/src/hdf5 ] +then + cd $HOME/src/hdf5 + git fetch --prune + git checkout hdf5-1_14_1-2 + cd - +else + git clone -b hdf5-1_14_1-2 https://github.com/HDFGroup/hdf5.git $HOME/src/hdf5 +fi +cmake -S $HOME/src/hdf5 -B ${build_dir}/hdf5-lassen-build -DBUILD_TESTING=OFF -DHDF5_ENABLE_PARALLEL=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/hdf5-1.14.1.2 +cmake --build ${build_dir}/hdf5-lassen-build --target install --parallel 10 + +# ADIOS2 +if [ -d $HOME/src/adios2 ] +then + cd $HOME/src/adios2 + git fetch --prune + git checkout v2.8.3 + cd - +else + git clone -b v2.8.3 https://github.com/ornladios/ADIOS2.git $HOME/src/adios2 +fi +cmake -S $HOME/src/adios2 -B ${build_dir}/adios2-lassen-build -DBUILD_TESTING=OFF -DADIOS2_BUILD_EXAMPLES=OFF -DADIOS2_USE_Blosc=ON -DADIOS2_USE_Fortran=OFF -DADIOS2_USE_Python=OFF -DADIOS2_USE_SST=OFF -DADIOS2_USE_ZeroMQ=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/adios2-2.8.3 +cmake --build ${build_dir}/adios2-lassen-build --target install -j 10 + +# BLAS++ (for PSATD+RZ) +if [ -d $HOME/src/blaspp ] +then + cd $HOME/src/blaspp + git fetch --prune + git checkout master + git pull + cd - +else + git clone https://github.com/icl-utk-edu/blaspp.git $HOME/src/blaspp +fi +cmake -S $HOME/src/blaspp -B ${build_dir}/blaspp-lassen-build -Duse_openmp=ON -Dgpu_backend=cuda -Duse_cmake_find_blas=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=${SW_DIR}/blaspp-master +cmake --build ${build_dir}/blaspp-lassen-build --target install --parallel 10 + +# LAPACK++ (for PSATD+RZ) +if [ -d $HOME/src/lapackpp ] +then + cd $HOME/src/lapackpp + git fetch --prune + git checkout master + git pull + cd - +else + git clone https://github.com/icl-utk-edu/lapackpp.git $HOME/src/lapackpp +fi +CXXFLAGS="-DLAPACK_FORTRAN_ADD_" cmake -S ${HOME}/src/lapackpp -B ${build_dir}/lapackpp-lassen-build -Duse_cmake_find_lapack=ON -DCMAKE_CXX_STANDARD=17 -Dbuild_tests=OFF -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/lapackpp-master -DLAPACK_LIBRARIES=/usr/lib64/liblapack.so +cmake --build ${build_dir}/lapackpp-lassen-build --target install --parallel 10 + +# remove build temporary directory +rm -rf ${build_dir} + + +# Python ###################################################################### +# +python3 -m pip install --upgrade --user virtualenv +rm -rf ${SW_DIR}/venvs/warpx-lassen +python3 -m venv ${SW_DIR}/venvs/warpx-lassen +source ${SW_DIR}/venvs/warpx-lassen/bin/activate +python3 -m pip install --upgrade pip +python3 -m pip cache purge +python3 -m pip install --upgrade wheel +python3 -m pip install --upgrade cython +python3 -m pip install --upgrade numpy +python3 -m pip install --upgrade pandas +python3 -m pip install --upgrade -Ccompile-args="-j10" scipy +python3 -m pip install --upgrade mpi4py --no-cache-dir --no-build-isolation --no-binary mpi4py +python3 -m pip install --upgrade openpmd-api +python3 -m pip install --upgrade matplotlib==3.2.2 # does not try to build freetype itself +python3 -m pip install --upgrade yt + +# install or update WarpX dependencies such as picmistandard +python3 -m pip install --upgrade -r $HOME/src/warpx/requirements.txt + +# for ML dependencies, see install_v100_ml.sh diff --git a/Tools/machines/lassen-llnl/install_v100_ml.sh b/Tools/machines/lassen-llnl/install_v100_ml.sh new file mode 100755 index 00000000000..47f1bf5a2eb --- /dev/null +++ b/Tools/machines/lassen-llnl/install_v100_ml.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# Copyright 2023 The WarpX Community +# +# This file is part of WarpX. +# +# Author: Axel Huebl +# License: BSD-3-Clause-LBNL + +# Exit on first error encountered ############################################# +# +set -eu -o pipefail + + +# Check: ###################################################################### +# +# Was perlmutter_gpu_warpx.profile sourced and configured correctly? +if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in your lassen_v100_warpx.profile file! Please edit its line 2 to continue!"; exit 1; fi + + +# Remove old dependencies ##################################################### +# +# remove common user mistakes in python, located in .local instead of a venv +python3 -m pip uninstall -qqq -y torch 2>/dev/null || true + + +# Python ML ################################################################### +# +# for basic python dependencies, see install_v100_dependencies.sh + +# optional: for libEnsemble - WIP: issues with nlopt +# python3 -m pip install -r $HOME/src/warpx/Tools/LibEnsemble/requirements.txt + +# optional: for pytorch +if [ -d ${HOME}/src/pytorch ] +then + cd ${HOME}/src/pytorch + git fetch + git checkout . + git checkout v2.0.1 + cd - +else + git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git /ccs/proj/${proj}/${USER}/src/pytorch +fi +cd ${HOME}/src/pytorch +rm -rf build +python3 -m pip install -r requirements.txt +# patch to avoid compile issues +# https://github.com/pytorch/pytorch/issues/97497#issuecomment-1499069641 +# https://github.com/pytorch/pytorch/pull/98511 +wget -q -O - https://github.com/pytorch/pytorch/pull/98511.patch | git apply +USE_CUDA=1 BLAS=OpenBLAS MAX_JOBS=64 ATEN_AVX512_256=OFF BUILD_TEST=0 python3 setup.py develop +# (optional) If using torch.compile with inductor/triton, install the matching version of triton +#make triton +rm -rf build +cd - + +# optional: optimas dependencies (based on libEnsemble & ax->botorch->gpytorch->pytorch) +# commented because scikit-learn et al. compile > 2 hrs +# please run manually on a login node if needed +#python3 -m pip install -r $HOME/src/warpx/Tools/optimas/requirements.txt diff --git a/Tools/machines/lassen-llnl/lassen.bsub b/Tools/machines/lassen-llnl/lassen_v100.bsub similarity index 100% rename from Tools/machines/lassen-llnl/lassen.bsub rename to Tools/machines/lassen-llnl/lassen_v100.bsub diff --git a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example new file mode 100644 index 00000000000..d5f2a633bd3 --- /dev/null +++ b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example @@ -0,0 +1,52 @@ +# please set your project account +#export proj="" # edit this and comment in + +# required dependencies +module load cmake/3.23.1 +module load clang/12.0.1-gcc-8.3.1 +module load cuda/12.0.0 + +# optional: for QED lookup table generation support +module load boost/1.70.0 + +# optional: for openPMD support +export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/c-blosc-1.21.1:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/hdf5-1.14.1.2:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/adios2-2.8.3:$CMAKE_PREFIX_PATH +export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/c-blosc-1.21.1/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/hdf5-1.14.1.2/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/adios2-2.8.3/lib64:$LD_LIBRARY_PATH + +# optional: for PSATD in RZ geometry support +export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/blaspp-master:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/lapackpp-master:$CMAKE_PREFIX_PATH +export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/blaspp-master/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/lapackpp-master/lib64:$LD_LIBRARY_PATH + +# optional: for Python bindings +module load python/3.8.2 + +if [ -d "$HOME/sw/lassen/gpu/venvs/warpx-lassen" ] +then + source $HOME/sw/lassen/gpu/venvs/warpx-lassen/bin/activate +fi + +# optional: an alias to request an interactive node for two hours +alias getNode="bsub -G $proj -W 2:00 -nnodes 1 -Is /bin/bash" +# an alias to run a command on a batch node for up to 30min +# usage: runNode +alias runNode="bsub -q debug -P $proj -W 2:00 -nnodes 1 -I" + +# fix system defaults: do not escape $ with a \ on tab completion +shopt -s direxpand + +# optimize CUDA compilation for V100 +export AMREX_CUDA_ARCH=7.0 +export CUDAARCHS=70 + +# compiler environment hints +export CC=$(which clang) +export CXX=$(which clang++) +export FC=$(which gfortran) +export CUDACXX=$(which nvcc) +export CUDAHOSTCXX=$(which clang++) diff --git a/Tools/machines/lassen-llnl/lassen_warpx.profile.example b/Tools/machines/lassen-llnl/lassen_warpx.profile.example deleted file mode 100644 index fb605b53ec2..00000000000 --- a/Tools/machines/lassen-llnl/lassen_warpx.profile.example +++ /dev/null @@ -1,44 +0,0 @@ -# please set your project account -#export proj="" # edit this and comment in - -# required dependencies -module load cmake/3.23.1 -module load clang/12.0.1-gcc-8.3.1 -module load cuda/12.0.0 - -# optional: for QED lookup table generation support -module load boost/1.70.0 - -# optional: for openPMD support -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/c-blosc-1.21.1:$CMAKE_PREFIX_PATH -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/hdf5-1.14.1.2:$CMAKE_PREFIX_PATH -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/adios2-2.8.3:$CMAKE_PREFIX_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/c-blosc-1.21.1/lib64:$LD_LIBRARY_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/hdf5-1.14.1.2/lib64:$LD_LIBRARY_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/adios2-2.8.3/lib64:$LD_LIBRARY_PATH - -# optional: for PSATD in RZ geometry support -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/blaspp-master:$CMAKE_PREFIX_PATH -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/lapackpp-master:$CMAKE_PREFIX_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/blaspp-master/lib64:$LD_LIBRARY_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/lapackpp-master/lib64:$LD_LIBRARY_PATH - -# 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 - -# optimize CUDA compilation for V100 -export AMREX_CUDA_ARCH=7.0 -export CUDAARCHS=70 - -# compiler environment hints -export CC=$(which clang) -export CXX=$(which clang++) -export FC=$(which gfortran) -export CUDACXX=$(which nvcc) -export CUDAHOSTCXX=$(which clang++) diff --git a/Tools/machines/summit-olcf/summit_warpx.profile.example b/Tools/machines/summit-olcf/summit_warpx.profile.example index c060102d6e8..5d88bb9aeed 100644 --- a/Tools/machines/summit-olcf/summit_warpx.profile.example +++ b/Tools/machines/summit-olcf/summit_warpx.profile.example @@ -63,7 +63,7 @@ fi # for paralle execution, start on the batch node: jsrun alias getNode="bsub -q debug -P $proj -W 2:00 -nnodes 1 -Is /bin/bash" # an alias to run a command on a batch node for up to 30min -# usage: nrun +# usage: runNode alias runNode="bsub -q debug -P $proj -W 2:00 -nnodes 1 -I" # fix system defaults: do not escape $ with a \ on tab completion From 14db70afd12a995d16aee001e18b2efd07acf688 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 8 Sep 2023 14:51:41 -0700 Subject: [PATCH 02/39] Lassen (LLNL): Numpy==1.22 See https://github.com/numpy/numpy/issues/24673 --- Tools/machines/lassen-llnl/install_v100_dependencies.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh index 8c6699c19e7..88a5d482908 100755 --- a/Tools/machines/lassen-llnl/install_v100_dependencies.sh +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -118,7 +118,8 @@ python3 -m pip install --upgrade pip python3 -m pip cache purge python3 -m pip install --upgrade wheel python3 -m pip install --upgrade cython -python3 -m pip install --upgrade numpy +# see https://github.com/numpy/numpy/issues/24673 +python3 -m pip install --upgrade numpy==1.22 python3 -m pip install --upgrade pandas python3 -m pip install --upgrade -Ccompile-args="-j10" scipy python3 -m pip install --upgrade mpi4py --no-cache-dir --no-build-isolation --no-binary mpi4py From 0a1e6a0ad1800670ceb291751a6e330c525f8eaa Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 8 Sep 2023 15:05:51 -0700 Subject: [PATCH 03/39] Lassen (LLNL): New SW Directory On workspace --- .../lassen-llnl/install_v100_dependencies.sh | 3 +-- Tools/machines/lassen-llnl/install_v100_ml.sh | 2 +- .../lassen_v100_warpx.profile.example | 25 ++++++++++--------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh index 88a5d482908..fcf0bb14afd 100755 --- a/Tools/machines/lassen-llnl/install_v100_dependencies.sh +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -20,8 +20,7 @@ if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in yo # Remove old dependencies ##################################################### # -SW_DIR="${HOME}/sw/lassen/gpu/" -# better, but needs a request: /usr/WS2/${USER}/ +SW_DIR="/usr/workspace/${USER}/lassen/gpu" rm -rf ${SW_DIR} mkdir -p ${SW_DIR} diff --git a/Tools/machines/lassen-llnl/install_v100_ml.sh b/Tools/machines/lassen-llnl/install_v100_ml.sh index 47f1bf5a2eb..e736f83ba20 100755 --- a/Tools/machines/lassen-llnl/install_v100_ml.sh +++ b/Tools/machines/lassen-llnl/install_v100_ml.sh @@ -40,7 +40,7 @@ then git checkout v2.0.1 cd - else - git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git /ccs/proj/${proj}/${USER}/src/pytorch + git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git ${HOME}/src/pytorch fi cd ${HOME}/src/pytorch rm -rf build diff --git a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example index d5f2a633bd3..09808a03ebb 100644 --- a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example +++ b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example @@ -10,25 +10,26 @@ module load cuda/12.0.0 module load boost/1.70.0 # optional: for openPMD support -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/c-blosc-1.21.1:$CMAKE_PREFIX_PATH -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/hdf5-1.14.1.2:$CMAKE_PREFIX_PATH -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/adios2-2.8.3:$CMAKE_PREFIX_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/c-blosc-1.21.1/lib64:$LD_LIBRARY_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/hdf5-1.14.1.2/lib64:$LD_LIBRARY_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/adios2-2.8.3/lib64:$LD_LIBRARY_PATH +SW_DIR="/usr/workspace/${USER}/lassen/gpu" +export CMAKE_PREFIX_PATH=${SW_DIR}/c-blosc-1.21.1:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=${SW_DIR}/hdf5-1.14.1.2:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=${SW_DIR}/adios2-2.8.3:$CMAKE_PREFIX_PATH +export LD_LIBRARY_PATH=${SW_DIR}/c-blosc-1.21.1/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=${SW_DIR}/hdf5-1.14.1.2/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=${SW_DIR}/adios2-2.8.3/lib64:$LD_LIBRARY_PATH # optional: for PSATD in RZ geometry support -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/blaspp-master:$CMAKE_PREFIX_PATH -export CMAKE_PREFIX_PATH=$HOME/sw/lassen/gpu/lapackpp-master:$CMAKE_PREFIX_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/blaspp-master/lib64:$LD_LIBRARY_PATH -export LD_LIBRARY_PATH=$HOME/sw/lassen/gpu/lapackpp-master/lib64:$LD_LIBRARY_PATH +export CMAKE_PREFIX_PATH=${SW_DIR}/blaspp-master:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=${SW_DIR}/lapackpp-master:$CMAKE_PREFIX_PATH +export LD_LIBRARY_PATH=${SW_DIR}/blaspp-master/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=${SW_DIR}/lapackpp-master/lib64:$LD_LIBRARY_PATH # optional: for Python bindings module load python/3.8.2 -if [ -d "$HOME/sw/lassen/gpu/venvs/warpx-lassen" ] +if [ -d "${SW_DIR}/venvs/warpx-lassen" ] then - source $HOME/sw/lassen/gpu/venvs/warpx-lassen/bin/activate + source ${SW_DIR}/venvs/warpx-lassen/bin/activate fi # optional: an alias to request an interactive node for two hours From 2eb664a66d9b220c402be90c234ef7397f074c07 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 8 Sep 2023 18:22:40 -0500 Subject: [PATCH 04/39] Quartz (LLNL): New Modules, Clang (#4281) Module update on Quartz requires changes. Switching to Clang 14 as the compiler. --- Docs/source/install/hpc/quartz.rst | 8 +++---- .../quartz-llnl/quartz_warpx.profile.example | 23 ++++++++----------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/Docs/source/install/hpc/quartz.rst b/Docs/source/install/hpc/quartz.rst index 8902ec2cf45..95d4d5539c0 100644 --- a/Docs/source/install/hpc/quartz.rst +++ b/Docs/source/install/hpc/quartz.rst @@ -46,15 +46,15 @@ Then, ``cd`` into the directory ``$HOME/src/warpx`` and use the following comman .. code-block:: bash cd $HOME/src/warpx - rm -rf build + rm -rf build_quartz - cmake -S . -B build -DWarpX_DIMS="1;2;3" -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON - cmake --build build -j 6 + cmake -S . -B build_quartz -DWarpX_DIMS="1;2;3" -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON + cmake --build build_quartz -j 6 The other :ref:`general compile-time options ` apply as usual. **That's it!** -A 3D WarpX executable is now in ``build/bin/`` and :ref:`can be run ` with a :ref:`3D example inputs file `. +A 3D WarpX executable is now in ``build_quartz/bin/`` and :ref:`can be run ` with a :ref:`3D example inputs file `. Most people execute the binary directly or copy it out to a location in ``/p/lustre1/$(whoami)``. diff --git a/Tools/machines/quartz-llnl/quartz_warpx.profile.example b/Tools/machines/quartz-llnl/quartz_warpx.profile.example index 370e4a601ac..b54a4458658 100644 --- a/Tools/machines/quartz-llnl/quartz_warpx.profile.example +++ b/Tools/machines/quartz-llnl/quartz_warpx.profile.example @@ -2,25 +2,25 @@ #export proj= # required dependencies -module load cmake/3.20.2 -module load intel/2021.4 -module load mvapich2/2.3 +module load cmake/3.23.1 +module load clang/14.0.6-magic +module load mvapich2/2.3.7 # optional: for PSATD support -module load fftw/3.3.8 +module load fftw/3.3.10 # optional: for QED lookup table generation support -module load boost/1.73.0 +module load boost/1.80.0 # optional: for openPMD support # TODO ADIOS2 -module load hdf5-parallel/1.10.2 +module load hdf5-parallel/1.14.0 # optional: for PSATD in RZ geometry support # TODO: blaspp lapackpp # optional: for Python bindings -module load python/3.8.2 +module load python/3.9.12 # 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" @@ -29,9 +29,6 @@ alias getNode="srun --time=0:30:00 --nodes=1 --ntasks-per-node=2 --cpus-per-task 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}" +export CC=$(which clang) +export CXX=$(which clang++) +export FC=$(which gfortran) From b973b3e03ddb84bac7ca2ef66a455fb693d08bc1 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 8 Sep 2023 16:49:03 -0700 Subject: [PATCH 05/39] Lassen (LLNL): Matplotlib fix Failed on freetype (no module). --- Tools/machines/lassen-llnl/install_v100_dependencies.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh index fcf0bb14afd..a37f619cbd7 100755 --- a/Tools/machines/lassen-llnl/install_v100_dependencies.sh +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -123,7 +123,7 @@ python3 -m pip install --upgrade pandas python3 -m pip install --upgrade -Ccompile-args="-j10" scipy python3 -m pip install --upgrade mpi4py --no-cache-dir --no-build-isolation --no-binary mpi4py python3 -m pip install --upgrade openpmd-api -python3 -m pip install --upgrade matplotlib==3.2.2 # does not try to build freetype itself +MPLLOCALFREETYPE=1 python3 -m pip install --upgrade matplotlib==3.2.2 # does not try to build freetype itself python3 -m pip install --upgrade yt # install or update WarpX dependencies such as picmistandard From 68e77074afc9ba8f95585d06cb07bc11d37203ff Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 8 Sep 2023 18:18:04 -0700 Subject: [PATCH 06/39] Lassen (LLNL): yt fix Avoid an implicit upgrade of matplotlib. --- .../machines/lassen-llnl/install_v100_dependencies.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh index a37f619cbd7..ed3f618f431 100755 --- a/Tools/machines/lassen-llnl/install_v100_dependencies.sh +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -103,9 +103,6 @@ fi CXXFLAGS="-DLAPACK_FORTRAN_ADD_" cmake -S ${HOME}/src/lapackpp -B ${build_dir}/lapackpp-lassen-build -Duse_cmake_find_lapack=ON -DCMAKE_CXX_STANDARD=17 -Dbuild_tests=OFF -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/lapackpp-master -DLAPACK_LIBRARIES=/usr/lib64/liblapack.so cmake --build ${build_dir}/lapackpp-lassen-build --target install --parallel 10 -# remove build temporary directory -rm -rf ${build_dir} - # Python ###################################################################### # @@ -124,9 +121,14 @@ python3 -m pip install --upgrade -Ccompile-args="-j10" scipy python3 -m pip install --upgrade mpi4py --no-cache-dir --no-build-isolation --no-binary mpi4py python3 -m pip install --upgrade openpmd-api MPLLOCALFREETYPE=1 python3 -m pip install --upgrade matplotlib==3.2.2 # does not try to build freetype itself -python3 -m pip install --upgrade yt +echo "matplotlib==3.2.2" > ${build_dir}/constraints.txt +python3 -m pip install --upgrade -c ${build_dir}/constraints.txt yt # install or update WarpX dependencies such as picmistandard python3 -m pip install --upgrade -r $HOME/src/warpx/requirements.txt # for ML dependencies, see install_v100_ml.sh + + +# remove build temporary directory +rm -rf ${build_dir} From 36b2f9efd6916fa33b2c70e85eae86ee550b1d87 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 8 Sep 2023 21:20:54 -0700 Subject: [PATCH 07/39] Lassen (LLNL): pyTorch w/ GNU Work-around for https://github.com/pytorch/pytorch/issues/108934 --- Tools/machines/lassen-llnl/install_v100_ml.sh | 16 +++++++++------- .../lassen_v100_warpx.profile.example | 2 +- Tools/machines/summit-olcf/install_gpu_ml.sh | 1 + 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Tools/machines/lassen-llnl/install_v100_ml.sh b/Tools/machines/lassen-llnl/install_v100_ml.sh index e736f83ba20..c28f8d21166 100755 --- a/Tools/machines/lassen-llnl/install_v100_ml.sh +++ b/Tools/machines/lassen-llnl/install_v100_ml.sh @@ -37,19 +37,21 @@ then cd ${HOME}/src/pytorch git fetch git checkout . - git checkout v2.0.1 + git checkout v2.1.0-rc3 + git submodule update --init --recursive cd - else - git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git ${HOME}/src/pytorch + git clone -b v2.1.0-rc3 --recurse-submodules https://github.com/pytorch/pytorch.git ${HOME}/src/pytorch fi cd ${HOME}/src/pytorch rm -rf build + +# see https://github.com/pytorch/pytorch/issues/108931 +# https://github.com/pytorch/pytorch/pull/108932 +wget -q -O - https://github.com/pytorch/pytorch/pull/108932.patch | git apply + python3 -m pip install -r requirements.txt -# patch to avoid compile issues -# https://github.com/pytorch/pytorch/issues/97497#issuecomment-1499069641 -# https://github.com/pytorch/pytorch/pull/98511 -wget -q -O - https://github.com/pytorch/pytorch/pull/98511.patch | git apply -USE_CUDA=1 BLAS=OpenBLAS MAX_JOBS=64 ATEN_AVX512_256=OFF BUILD_TEST=0 python3 setup.py develop +CXX=g++ CC=gcc USE_CUDA=1 BLAS=OpenBLAS MAX_JOBS=64 ATEN_AVX512_256=OFF BUILD_TEST=0 python3 setup.py develop # (optional) If using torch.compile with inductor/triton, install the matching version of triton #make triton rm -rf build diff --git a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example index 09808a03ebb..6bbc6a6a57b 100644 --- a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example +++ b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example @@ -50,4 +50,4 @@ export CC=$(which clang) export CXX=$(which clang++) export FC=$(which gfortran) export CUDACXX=$(which nvcc) -export CUDAHOSTCXX=$(which clang++) +export CUDAHOSTCXX=${CXX} diff --git a/Tools/machines/summit-olcf/install_gpu_ml.sh b/Tools/machines/summit-olcf/install_gpu_ml.sh index ff4eaf1555c..25a8eb27abd 100755 --- a/Tools/machines/summit-olcf/install_gpu_ml.sh +++ b/Tools/machines/summit-olcf/install_gpu_ml.sh @@ -60,6 +60,7 @@ then git fetch git checkout . git checkout v2.0.1 + git submodule update --init --recursive cd - else git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git /ccs/proj/${proj}/${USER}/src/pytorch From 5df916b13e14f588e3561f4d2cb8c461599f01eb Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Sun, 10 Sep 2023 19:13:02 -0700 Subject: [PATCH 08/39] Lassen (LLNL): GNU 11.2.1 (#4283) Switch from Clang 12 to GCC 11 on Lassen (LLNL), due to issues seen in PyTorch and a risk of general vectorization issues in Clang/LLVM for PPC64le in LLVM at this point. --- .../lassen-llnl/install_v100_dependencies.sh | 3 +-- Tools/machines/lassen-llnl/install_v100_ml.sh | 15 +++++++++------ .../lassen-llnl/lassen_v100_warpx.profile.example | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh index ed3f618f431..80913c892b6 100755 --- a/Tools/machines/lassen-llnl/install_v100_dependencies.sh +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -114,8 +114,7 @@ python3 -m pip install --upgrade pip python3 -m pip cache purge python3 -m pip install --upgrade wheel python3 -m pip install --upgrade cython -# see https://github.com/numpy/numpy/issues/24673 -python3 -m pip install --upgrade numpy==1.22 +python3 -m pip install --upgrade numpy python3 -m pip install --upgrade pandas python3 -m pip install --upgrade -Ccompile-args="-j10" scipy python3 -m pip install --upgrade mpi4py --no-cache-dir --no-build-isolation --no-binary mpi4py diff --git a/Tools/machines/lassen-llnl/install_v100_ml.sh b/Tools/machines/lassen-llnl/install_v100_ml.sh index c28f8d21166..d3f5cb05241 100755 --- a/Tools/machines/lassen-llnl/install_v100_ml.sh +++ b/Tools/machines/lassen-llnl/install_v100_ml.sh @@ -37,21 +37,24 @@ then cd ${HOME}/src/pytorch git fetch git checkout . - git checkout v2.1.0-rc3 + git checkout v2.0.1 git submodule update --init --recursive cd - else - git clone -b v2.1.0-rc3 --recurse-submodules https://github.com/pytorch/pytorch.git ${HOME}/src/pytorch + git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git ${HOME}/src/pytorch fi cd ${HOME}/src/pytorch rm -rf build -# see https://github.com/pytorch/pytorch/issues/108931 -# https://github.com/pytorch/pytorch/pull/108932 -wget -q -O - https://github.com/pytorch/pytorch/pull/108932.patch | git apply +# see https://github.com/pytorch/pytorch/issues/97497#issuecomment-1499069641 +# https://github.com/pytorch/pytorch/pull/98511 +wget -q -O - https://github.com/pytorch/pytorch/pull/98511.patch | git apply python3 -m pip install -r requirements.txt -CXX=g++ CC=gcc USE_CUDA=1 BLAS=OpenBLAS MAX_JOBS=64 ATEN_AVX512_256=OFF BUILD_TEST=0 python3 setup.py develop + +# see https://github.com/pytorch/pytorch/issues/108984#issuecomment-1712938737 +LDFLAGS="-L${CUDA_HOME}/nvidia/targets/ppc64le-linux/lib/" \ +USE_CUDA=1 BLAS=OpenBLAS MAX_JOBS=64 ATEN_AVX512_256=OFF BUILD_TEST=0 python3 setup.py develop # (optional) If using torch.compile with inductor/triton, install the matching version of triton #make triton rm -rf build diff --git a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example index 6bbc6a6a57b..dd938e85c11 100644 --- a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example +++ b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example @@ -3,7 +3,7 @@ # required dependencies module load cmake/3.23.1 -module load clang/12.0.1-gcc-8.3.1 +module load gcc/11.2.1 module load cuda/12.0.0 # optional: for QED lookup table generation support @@ -46,8 +46,8 @@ export AMREX_CUDA_ARCH=7.0 export CUDAARCHS=70 # compiler environment hints -export CC=$(which clang) -export CXX=$(which clang++) +export CC=$(which gcc) +export CXX=$(which g++) export FC=$(which gfortran) export CUDACXX=$(which nvcc) export CUDAHOSTCXX=${CXX} From 5c00f09d85a9da10624175995cae7ea0bf9e6add Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Sun, 10 Sep 2023 21:26:38 -0700 Subject: [PATCH 09/39] Doc: Lassen (LLNL) Cleanup --- Docs/source/install/hpc/lassen.rst | 6 ++++-- Tools/machines/lassen-llnl/install_v100_dependencies.sh | 2 +- Tools/machines/lassen-llnl/install_v100_ml.sh | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Docs/source/install/hpc/lassen.rst b/Docs/source/install/hpc/lassen.rst index 785bce3b385..7b5630e0272 100644 --- a/Docs/source/install/hpc/lassen.rst +++ b/Docs/source/install/hpc/lassen.rst @@ -29,9 +29,11 @@ Login Lassen is currently transitioning to RHEL8. During this transition, first SSH into lassen and then ``ssh eatoss4`` next to work with the updated RHEL8/TOSS4 nodes. - Approximately September 2023, the new software environment on these nodes will be the new default. + Approximately October 2023, the new software environment on these nodes will be the new default. +.. _building-lassen-preparation: + Preparation ----------- @@ -79,7 +81,7 @@ Finally, since lassen does not yet provide software modules for some of our depe .. code-block:: bash bash $HOME/src/warpx/Tools/machines/lassen-llnl/install_v100_dependencies.sh - source $HOME/sw/lassen/gpu/venvs/warpx-lassen/bin/activate + source /usr/workspace/${USER}/lassen/gpu/venvs/warpx-lassen/bin/activate .. dropdown:: Script Details :color: light diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh index 80913c892b6..21650f09ee0 100755 --- a/Tools/machines/lassen-llnl/install_v100_dependencies.sh +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -14,7 +14,7 @@ set -eu -o pipefail # Check: ###################################################################### # -# Was perlmutter_gpu_warpx.profile sourced and configured correctly? +# Was lassen_v100_warpx.profile sourced and configured correctly? if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in your lassen_v100_warpx.profile file! Please edit its line 2 to continue!"; exit 1; fi diff --git a/Tools/machines/lassen-llnl/install_v100_ml.sh b/Tools/machines/lassen-llnl/install_v100_ml.sh index d3f5cb05241..2ff90adb521 100755 --- a/Tools/machines/lassen-llnl/install_v100_ml.sh +++ b/Tools/machines/lassen-llnl/install_v100_ml.sh @@ -14,7 +14,7 @@ set -eu -o pipefail # Check: ###################################################################### # -# Was perlmutter_gpu_warpx.profile sourced and configured correctly? +# Was lassen_v100_warpx.profile sourced and configured correctly? if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in your lassen_v100_warpx.profile file! Please edit its line 2 to continue!"; exit 1; fi From 2e4d6fdd87615486a7c0da4081b92b2de32af2b5 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Sun, 10 Sep 2023 23:19:14 -0700 Subject: [PATCH 10/39] Quartz (LLNL): PICMI Support (#4284) Document Python (PICMI) and ML support for Quartz (LLNL). --- Docs/source/install/hpc/quartz.rst | 118 ++++++++++++++--- .../quartz-llnl/install_dependencies.sh | 121 ++++++++++++++++++ .../quartz-llnl/quartz_warpx.profile.example | 25 +++- 3 files changed, 245 insertions(+), 19 deletions(-) create mode 100755 Tools/machines/quartz-llnl/install_dependencies.sh diff --git a/Docs/source/install/hpc/quartz.rst b/Docs/source/install/hpc/quartz.rst index 95d4d5539c0..de7c5ace848 100644 --- a/Docs/source/install/hpc/quartz.rst +++ b/Docs/source/install/hpc/quartz.rst @@ -11,52 +11,138 @@ Introduction If you are new to this system, **please see the following resources**: -* `LLNL user account `_ +* `LLNL user account `__ (login required) * `Quartz user guide `_ * Batch system: `Slurm `_ +* `Jupyter service `__ (`documentation `__, login required) * `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 ------------- +.. _building-quartz-preparation: + +Preparation +----------- -Use the following commands to download the WarpX source code and switch to the correct branch: +Use the following commands to download the WarpX source code: .. 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 (``$HOME/quartz_warpx.profile``). +We use system software modules, add environment hints and further dependencies via the file ``$HOME/quartz_warpx.profile``. +Create it now: -.. literalinclude:: ../../../../Tools/machines/quartz-llnl/quartz_warpx.profile.example - :language: bash - :caption: You can copy this file from ``Tools/machines/quartz-llnl/quartz_warpx.profile.example``. +.. code-block:: bash + + cp $HOME/src/warpx/Tools/machines/quartz-llnl/quartz/quartz_warpx.profile.example $HOME/quartz_warpx.profile + +.. dropdown:: Script Details + :color: light + :icon: info + :animate: fade-in-slide-down + + .. literalinclude:: ../../../../Tools/machines/quartz-llnl/quartz/quartz_warpx.profile.example + :language: bash + +Edit the 2nd line of this script, which sets the ``export proj=""`` variable. +For example, if you are member of the project ``tps``, then run ``vi $HOME/quartz_warpx.profile``. +Enter the edit mode by typing ``i`` and edit line 2 to read: + +.. code-block:: bash -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: + export proj="tps" + +Exit the ``vi`` editor with ``Esc`` and then type ``:wq`` (write & quit). + +.. important:: + + Now, and as the first step on future logins to Quartz, activate these environment settings: + + .. code-block:: bash + + source $HOME/quartz_warpx.profile + +Finally, since Quartz does not yet provide software modules for some of our dependencies, install them once: .. code-block:: bash - source $HOME/quartz_warpx.profile + bash $HOME/src/warpx/Tools/machines/quartz-llnl/install_dependencies.sh + source /usr/workspace/${USER}/quartz/venvs/warpx-quartz/bin/activate + +.. dropdown:: Script Details + :color: light + :icon: info + :animate: fade-in-slide-down + + .. literalinclude:: ../../../../Tools/machines/quartz-llnl/install_dependencies.sh + :language: bash + + +.. _building-quartz-compilation: -Then, ``cd`` into the directory ``$HOME/src/warpx`` and use the following commands to compile: +Compilation +----------- + +Use the following :ref:`cmake commands ` to compile the application executable: .. code-block:: bash cd $HOME/src/warpx rm -rf build_quartz - cmake -S . -B build_quartz -DWarpX_DIMS="1;2;3" -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON + cmake -S . -B build_quartz -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON -DWarpX_DIMS="1;2;RZ;3" cmake --build build_quartz -j 6 -The other :ref:`general compile-time options ` apply as usual. +The WarpX application executables are now in ``$HOME/src/warpx/build_quartz/bin/``. +Additionally, the following commands will install WarpX as a Python module: + +.. code-block:: bash + + rm -rf build_quartz_py + + cmake -S . -B build_quartz_py -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON -DWarpX_APP=OFF -DWarpX_PYTHON=ON -DWarpX_DIMS="1;2;RZ;3" + cmake --build build_quartz_py -j 6 --target pip_install + +Now, you can :ref:`submit Quartz compute jobs ` for WarpX :ref:`Python (PICMI) scripts ` (:ref:`example scripts `). +Or, you can use the WarpX executables to submit Quartz jobs (:ref:`example inputs `). +For executables, you can reference their location in your :ref:`job script ` or copy them to a location in ``$PROJWORK/$proj/``. + + +.. _building-quartz-update: + +Update WarpX & Dependencies +--------------------------- + +If you already installed WarpX in the past and want to update it, start by getting the latest source code: + +.. code-block:: bash + + cd $HOME/src/warpx + + # read the output of this command - does it look ok? + git status + + # get the latest WarpX source code + git fetch + git pull + + # read the output of these commands - do they look ok? + git status + git log # press q to exit + +And, if needed, + +- :ref:`update the quartz_warpx.profile file `, +- log out and into the system, activate the now updated environment profile as usual, +- :ref:`execute the dependency install scripts `. + +As a last step, clean the build directory ``rm -rf $HOME/src/warpx/build_quartz`` and rebuild WarpX. -**That's it!** -A 3D WarpX executable is now in ``build_quartz/bin/`` and :ref:`can be run ` with a :ref:`3D example inputs file `. -Most people execute the binary directly or copy it out to a location in ``/p/lustre1/$(whoami)``. +.. _running-cpp-quartz: Running ------- diff --git a/Tools/machines/quartz-llnl/install_dependencies.sh b/Tools/machines/quartz-llnl/install_dependencies.sh new file mode 100755 index 00000000000..c162ac5d390 --- /dev/null +++ b/Tools/machines/quartz-llnl/install_dependencies.sh @@ -0,0 +1,121 @@ +#!/bin/bash +# +# Copyright 2023 The WarpX Community +# +# This file is part of WarpX. +# +# Author: Axel Huebl +# License: BSD-3-Clause-LBNL + +# Exit on first error encountered ############################################# +# +set -eu -o pipefail + + +# Check: ###################################################################### +# +# Was quartz_warpx.profile sourced and configured correctly? +if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in your quartz_warpx.profile file! Please edit its line 2 to continue!"; exit 1; fi + + +# Remove old dependencies ##################################################### +# +SW_DIR="/usr/workspace/${USER}/quartz" +rm -rf ${SW_DIR} +mkdir -p ${SW_DIR} + +# remove common user mistakes in python, located in .local instead of a venv +python3 -m pip uninstall -qq -y pywarpx +python3 -m pip uninstall -qq -y warpx +python3 -m pip uninstall -qqq -y mpi4py 2>/dev/null || true + + +# General extra dependencies ################################################## +# + +# tmpfs build directory: avoids issues often seen with ${HOME} and is faster +build_dir=$(mktemp -d) + +# c-blosc (I/O compression) +if [ -d ${HOME}/src/c-blosc ] +then + cd ${HOME}/src/c-blosc + git fetch --prune + git checkout v1.21.1 + cd - +else + git clone -b v1.21.1 https://github.com/Blosc/c-blosc.git ${HOME}/src/c-blosc +fi +cmake -S ${HOME}/src/c-blosc -B ${build_dir}/c-blosc-quartz-build -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DDEACTIVATE_AVX2=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/c-blosc-1.21.1 +cmake --build ${build_dir}/c-blosc-quartz-build --target install --parallel 6 + +# ADIOS2 +if [ -d ${HOME}/src/adios2 ] +then + cd ${HOME}/src/adios2 + git fetch --prune + git checkout v2.8.3 + cd - +else + git clone -b v2.8.3 https://github.com/ornladios/ADIOS2.git ${HOME}/src/adios2 +fi +cmake -S ${HOME}/src/adios2 -B ${build_dir}/adios2-quartz-build -DBUILD_TESTING=OFF -DADIOS2_BUILD_EXAMPLES=OFF -DADIOS2_USE_Blosc=ON -DADIOS2_USE_Fortran=OFF -DADIOS2_USE_Python=OFF -DADIOS2_USE_SST=OFF -DADIOS2_USE_ZeroMQ=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/adios2-2.8.3 +cmake --build ${build_dir}/adios2-quartz-build --target install -j 6 + +# BLAS++ (for PSATD+RZ) +if [ -d ${HOME}/src/blaspp ] +then + cd ${HOME}/src/blaspp + git fetch --prune + git checkout master + git pull + cd - +else + git clone https://github.com/icl-utk-edu/blaspp.git ${HOME}/src/blaspp +fi +cmake -S ${HOME}/src/blaspp -B ${build_dir}/blaspp-quartz-build -Duse_openmp=ON -Duse_cmake_find_blas=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=${SW_DIR}/blaspp-master +cmake --build ${build_dir}/blaspp-quartz-build --target install --parallel 6 + +# LAPACK++ (for PSATD+RZ) +if [ -d ${HOME}/src/lapackpp ] +then + cd ${HOME}/src/lapackpp + git fetch --prune + git checkout master + git pull + cd - +else + git clone https://github.com/icl-utk-edu/lapackpp.git ${HOME}/src/lapackpp +fi +CXXFLAGS="-DLAPACK_FORTRAN_ADD_" cmake -S ${HOME}/src/lapackpp -B ${build_dir}/lapackpp-quartz-build -Duse_cmake_find_lapack=ON -DCMAKE_CXX_STANDARD=17 -Dbuild_tests=OFF -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/lapackpp-master +cmake --build ${build_dir}/lapackpp-quartz-build --target install --parallel 6 + + +# Python ###################################################################### +# +python3 -m pip install --upgrade --user virtualenv +rm -rf ${SW_DIR}/venvs/warpx-quartz +python3 -m venv ${SW_DIR}/venvs/warpx-quartz +source ${SW_DIR}/venvs/warpx-quartz/bin/activate +python3 -m pip install --upgrade pip +python3 -m pip cache purge +python3 -m pip install --upgrade wheel +python3 -m pip install --upgrade cython +python3 -m pip install --upgrade numpy +python3 -m pip install --upgrade pandas +python3 -m pip install --upgrade scipy +python3 -m pip install --upgrade mpi4py --no-cache-dir --no-build-isolation --no-binary mpi4py +python3 -m pip install --upgrade openpmd-api +python3 -m pip install --upgrade matplotlib +python3 -m pip install --upgrade yt + +# install or update WarpX dependencies such as picmistandard +python3 -m pip install --upgrade -r ${HOME}/src/warpx/requirements.txt + +# ML dependencies +python3 -m pip install --upgrade torch + + +# remove build temporary directory ############################################ +# +rm -rf ${build_dir} diff --git a/Tools/machines/quartz-llnl/quartz_warpx.profile.example b/Tools/machines/quartz-llnl/quartz_warpx.profile.example index b54a4458658..810005bafb7 100644 --- a/Tools/machines/quartz-llnl/quartz_warpx.profile.example +++ b/Tools/machines/quartz-llnl/quartz_warpx.profile.example @@ -1,5 +1,5 @@ # please set your project account -#export proj= +#export proj="" # edit this and comment in # required dependencies module load cmake/3.23.1 @@ -13,21 +13,40 @@ module load fftw/3.3.10 module load boost/1.80.0 # optional: for openPMD support -# TODO ADIOS2 module load hdf5-parallel/1.14.0 +SW_DIR="/usr/workspace/${USER}/quartz" +export CMAKE_PREFIX_PATH=${SW_DIR}/c-blosc-1.21.1:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=${SW_DIR}/adios2-2.8.3:$CMAKE_PREFIX_PATH + # optional: for PSATD in RZ geometry support -# TODO: blaspp lapackpp +export CMAKE_PREFIX_PATH=${SW_DIR}/blaspp-master:$CMAKE_PREFIX_PATH +export CMAKE_PREFIX_PATH=${SW_DIR}/lapackpp-master:$CMAKE_PREFIX_PATH +export LD_LIBRARY_PATH=${SW_DIR}/blaspp-master/lib64:$LD_LIBRARY_PATH +export LD_LIBRARY_PATH=${SW_DIR}/lapackpp-master/lib64:$LD_LIBRARY_PATH # optional: for Python bindings module load python/3.9.12 +if [ -d "${SW_DIR}/venvs/warpx-quartz" ] +then + source ${SW_DIR}/venvs/warpx-quartz/bin/activate +fi + # 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" +# an alias to run a command on a batch node for up to 30min +# usage: runNode +alias runNode="srun --time=0:30:00 --nodes=1 --ntasks-per-node=2 --cpus-per-task=18 -p pdebug" # fix system defaults: do not escape $ with a \ on tab completion shopt -s direxpand +# optimize CPU microarchitecture for Intel Xeon E5-2695 v4 +# note: the cc/CC/ftn wrappers below add those +export CXXFLAGS="-march=broadwell" +export CFLAGS="-march=broadwell" + # compiler environment hints export CC=$(which clang) export CXX=$(which clang++) From ac52a762c297712411fec4877b9770e2bf4d4343 Mon Sep 17 00:00:00 2001 From: Arianna Formenti Date: Mon, 11 Sep 2023 10:18:25 -0700 Subject: [PATCH 11/39] add collider-relevant reduced diags (#4024) * added collider reduced diags * added test * fixed xy_ave and xy_std * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed usage of IntVect and removed print * Set up automated CI test To-do: - Fix bug (erroneous arithmetic operation) - Remove unused parameters from input file * Fix warning: set mass only if WARPX_QED * Use amrex::ParticleReal for mass * Update Make.package to fix GNU Make build * reduced_data.value() only once * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Merge `development` into `coll_rel_red_diags` * updated 3d beam test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated 3d test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * `m_write_header` instead of `m_IsNotRestart` * added angles * updated 3d beam test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added 3 particle test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added warning * updated diags names * updated analysis multiple particles * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * added positrons to test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update CI test * Reuse input file parser from Tools * Apply suggestions from code review * Apply suggestions from code review * changed order of diags and added docs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply WarpX style conventions * Fix CodeQL alerts * assert in RZ and removed fine ref levs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix compilation in 2D * Fix compilation in RZ * Update Docs/source/usage/parameters.rst Co-authored-by: Axel Huebl * Update Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp Co-authored-by: Axel Huebl * Update Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp Co-authored-by: Luca Fedeli * Update Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp Co-authored-by: Luca Fedeli * Update Source/Diagnostics/ReducedDiags/ColliderRelevant.H Co-authored-by: Axel Huebl * Update Source/Diagnostics/ReducedDiags/ColliderRelevant.H Co-authored-by: Axel Huebl * minimized number of kernel launches and parallel ops * updated constructor of ReduceDiags * fixed based on comments * updated docs: if QED not enable * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed ReducedDiags constructor * two passes instead of one for robusteness * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed bugs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix 1D build (typo `wtot` instead of `w_tot`) * refixed compile in RZ * check io process in chi_ave computation * updated test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Temporary fix: remove IOProcessor * Update Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp Co-authored-by: Axel Huebl * Apply suggestions from code review * Remove `Debug` compilation mode from CI test * CI test analysis: check all particles have same charge * Use `m_beam_name` instead of `species_names` --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Edoardo Zoni Co-authored-by: Edoardo Zoni <59625522+EZoni@users.noreply.github.com> Co-authored-by: Axel Huebl Co-authored-by: Luca Fedeli --- Docs/source/usage/parameters.rst | 51 ++ .../analysis_multiple_particles.py | 150 +++++ .../inputs_3d_multiple_particles | 130 +++++ .../benchmarks_json/collider_diagnostics.json | 36 ++ Regression/WarpX-tests.ini | 16 + .../Diagnostics/ReducedDiags/CMakeLists.txt | 1 + .../ReducedDiags/ColliderRelevant.H | 61 ++ .../ReducedDiags/ColliderRelevant.cpp | 548 ++++++++++++++++++ Source/Diagnostics/ReducedDiags/Make.package | 1 + .../ReducedDiags/MultiReducedDiags.cpp | 2 + .../Diagnostics/ReducedDiags/ReducedDiags.cpp | 3 +- Tools/Algorithms/stencil.py | 2 + Tools/Parser/__init__.py | 0 .../input_file_parser.py | 0 14 files changed, 999 insertions(+), 2 deletions(-) create mode 100755 Examples/Tests/collider_relevant_diags/analysis_multiple_particles.py create mode 100644 Examples/Tests/collider_relevant_diags/inputs_3d_multiple_particles create mode 100644 Regression/Checksum/benchmarks_json/collider_diagnostics.json create mode 100644 Source/Diagnostics/ReducedDiags/ColliderRelevant.H create mode 100644 Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp create mode 100644 Tools/Parser/__init__.py rename Tools/{Algorithms => Parser}/input_file_parser.py (100%) diff --git a/Docs/source/usage/parameters.rst b/Docs/source/usage/parameters.rst index 4514e6f3fb0..27d7682899d 100644 --- a/Docs/source/usage/parameters.rst +++ b/Docs/source/usage/parameters.rst @@ -2988,6 +2988,57 @@ Reduced Diagnostics 1 or 0, it is possible to compute the charge on only some part of the embedded boundary. + * ``ColliderRelevant`` + This diagnostics computes properties of two colliding beams that are relevant for particle colliders. + Two species must be specified. Photon species are not supported yet. + It is assumed that the two species propagate and collide along the ``z`` direction. + The output columns (for 3D-XYZ) are the following, where the minimum, average and maximum + are done over the whole species: + + [0]: simulation step (iteration). + + [1]: time (s). + + [2]: time derivative of the luminosity (:math:`m^{-2}s^{-1}`) defined as: + + .. math:: + + \frac{dL}{dt} = 2 c \iiint n_1(x,y,z) n_2(x,y,z) dx dy dz + + where :math:`n_1`, :math:`n_2` are the number densities of the two colliding species. + + [3], [4], [5]: If, QED is enabled, the minimum, average and maximum values of the quantum parameter :math:`\chi` of species 1: + :math:`\chi_{min}`, + :math:`\langle \chi \rangle`, + :math:`\chi_{max}`. + If QED is not enabled, these numbers are not computed. + + [6], [7]: The average and standard deviation of the values of the transverse coordinate :math:`x` (m) of species 1: + :math:`\langle x \rangle`, + :math:`\sqrt{\langle x- \langle x \rangle \rangle^2}`. + + [8], [9]: The average and standard deviation of the values of the transverse coordinate :math:`y` (m) of species 1: + :math:`\langle y \rangle`, + :math:`\sqrt{\langle y- \langle y \rangle \rangle^2}`. + + [10], [11], [12], [13]: The minimum, average, maximum and standard deviation of the angle :math:`\theta_x = \angle (u_x, u_z)` (rad) of species 1: + :math:`{\theta_x}_{min}`, + :math:`\langle \theta_x \rangle`, + :math:`{\theta_x}_{max}`, + :math:`\sqrt{\langle \theta_x- \langle \theta_x \rangle \rangle^2}`. + + [14], [15], [16], [17]: The minimum, average, maximum and standard deviation of the angle :math:`\theta_y = \angle (u_y, u_z)` (rad) of species 1: + :math:`{\theta_y}_{min}`, + :math:`\langle \theta_y \rangle`, + :math:`{\theta_y}_{max}`, + :math:`\sqrt{\langle \theta_y- \langle \theta_y \rangle \rangle^2}`. + + [18], ..., [32]: Analogous quantities for species 2. + + For 2D-XZ, :math:`y`-related quantities are not outputted. + For 1D-Z, :math:`x`-related and :math:`y`-related quantities are not outputted. + RZ geometry is not supported yet. + * ``.intervals`` (`string`) Using the `Intervals Parser`_ syntax, this string defines the timesteps at which reduced diagnostics are written to file. diff --git a/Examples/Tests/collider_relevant_diags/analysis_multiple_particles.py b/Examples/Tests/collider_relevant_diags/analysis_multiple_particles.py new file mode 100755 index 00000000000..b23bb69d52c --- /dev/null +++ b/Examples/Tests/collider_relevant_diags/analysis_multiple_particles.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +import os +import sys + +import numpy as np +import openpmd_api as io +import pandas as pd +from scipy.constants import c, e, hbar, m_e + +sys.path.append('../../../../warpx/Regression/Checksum/') +import checksumAPI + +sys.path.append('../../../../warpx/Tools/Parser/') +from input_file_parser import parse_input_file + +E_crit = m_e**2*c**3/(e*hbar) +B_crit = m_e**2*c**2/(e*hbar) + +def chi(ux, uy, uz, Ex, Ey, Ez, Bx, By, Bz): + gamma = np.sqrt(1.+ux**2+uy**2+uz**2) + vx = ux / gamma * c + vy = uy / gamma * c + vz = uz / gamma * c + tmp1x = Ex + vy*Bz - vz*By + tmp1y = Ey - vx*Bz + vz*Bx + tmp1z = Ez + vx*By - vy*Bx + tmp2 = (Ex*vx + Ey*vy + Ez*vz)/c + chi = gamma/E_crit*np.sqrt(tmp1x**2+tmp1y**2+tmp1z**2 - tmp2**2) + return chi + +def dL_dt(): + series = io.Series("diags/diag2/openpmd_%T.h5",io.Access.read_only) + iterations = np.asarray(series.iterations) + lumi = [] + for n,ts in enumerate(iterations): + it = series.iterations[ts] + rho1 = it.meshes["rho_beam_e"] + dV = np.prod(rho1.grid_spacing) + rho1 = it.meshes["rho_beam_e"][io.Mesh_Record_Component.SCALAR].load_chunk() + rho2 = it.meshes["rho_beam_p"][io.Mesh_Record_Component.SCALAR].load_chunk() + beam_e_charge = it.particles["beam_e"]["charge"][io.Mesh_Record_Component.SCALAR].load_chunk() + beam_p_charge = it.particles["beam_p"]["charge"][io.Mesh_Record_Component.SCALAR].load_chunk() + q1 = beam_e_charge[0] + if not np.all(beam_e_charge == q1): + sys.exit('beam_e particles do not have the same charge') + q2 = beam_p_charge[0] + if not np.all(beam_p_charge == q2): + sys.exit('beam_p particles do not have the same charge') + series.flush() + n1 = rho1/q1 + n2 = rho2/q2 + l = 2*np.sum(n1*n2)*dV*c + lumi.append(l) + return lumi + +input_dict = parse_input_file('inputs_3d_multiple_particles') +Ex, Ey, Ez = [float(w) for w in input_dict['particles.E_external_particle']] +Bx, By, Bz = [float(w) for w in input_dict['particles.B_external_particle']] + +CollDiagFname='diags/reducedfiles/ColliderRelevant_beam_e_beam_p.txt' +df = pd.read_csv(CollDiagFname, sep=" ", header=0) + +for species in ['beam_p', 'beam_e']: + + ux1, ux2, ux3 = [float(w) for w in input_dict[f'{species}.multiple_particles_ux']] + uy1, uy2, uy3 = [float(w) for w in input_dict[f'{species}.multiple_particles_uy']] + uz1, uz2, uz3 = [float(w) for w in input_dict[f'{species}.multiple_particles_uz']] + + x = np.array([float(w) for w in input_dict[f'{species}.multiple_particles_pos_x']]) + y = np.array([float(w) for w in input_dict[f'{species}.multiple_particles_pos_y']]) + + w = np.array([float(w) for w in input_dict[f'{species}.multiple_particles_weight']]) + + CHI_ANALYTICAL = np.array([chi(ux1, uy1, uz1, Ex, Ey, Ez, Bx, By, Bz), + chi(ux2, uy2, uz2, Ex, Ey, Ez, Bx, By, Bz), + chi(ux3, uy3, uz3, Ex, Ey, Ez, Bx, By, Bz)]) + THETAX = np.array([np.arctan2(ux1, uz1), np.arctan2(ux2, uz2), np.arctan2(ux3, uz3)]) + THETAY = np.array([np.arctan2(uy1, uz1), np.arctan2(uy2, uz2), np.arctan2(uy3, uz3)]) + + # CHI MAX + fname=f'diags/reducedfiles/ParticleExtrema_{species}.txt' + chimax_pe = np.loadtxt(fname)[:,19] + chimax_cr = df[[col for col in df.columns if f'chi_max_{species}' in col]].to_numpy() + assert np.allclose(np.max(CHI_ANALYTICAL), chimax_cr, rtol=1e-8) + assert np.allclose(chimax_pe, chimax_cr, rtol=1e-8) + + # CHI MIN + fname=f'diags/reducedfiles/ParticleExtrema_{species}.txt' + chimin_pe = np.loadtxt(fname)[:,18] + chimin_cr = df[[col for col in df.columns if f'chi_min_{species}' in col]].to_numpy() + assert np.allclose(np.min(CHI_ANALYTICAL), chimin_cr, rtol=1e-8) + assert np.allclose(chimin_pe, chimin_cr, rtol=1e-8) + + # CHI AVERAGE + chiave_cr = df[[col for col in df.columns if f'chi_ave_{species}' in col]].to_numpy() + assert np.allclose(np.average(CHI_ANALYTICAL, weights=w), chiave_cr, rtol=1e-8) + + # X AVE STD + x_ave_cr = df[[col for col in df.columns if f']x_ave_{species}' in col]].to_numpy() + x_std_cr = df[[col for col in df.columns if f']x_std_{species}' in col]].to_numpy() + x_ave = np.average(x, weights=w) + x_std = np.sqrt(np.average((x-x_ave)**2, weights=w)) + assert np.allclose(x_ave, x_ave_cr, rtol=1e-8) + assert np.allclose(x_std, x_std_cr, rtol=1e-8) + + # Y AVE STD + y_ave_cr = df[[col for col in df.columns if f']y_ave_{species}' in col]].to_numpy() + y_std_cr = df[[col for col in df.columns if f']y_std_{species}' in col]].to_numpy() + y_ave = np.average(y, weights=w) + y_std = np.sqrt(np.average((y-y_ave)**2, weights=w)) + assert np.allclose(y_ave, y_ave_cr, rtol=1e-8) + assert np.allclose(y_std, y_std_cr, rtol=1e-8) + + # THETA X MIN AVE MAX STD + thetax_min_cr = df[[col for col in df.columns if f'theta_x_min_{species}' in col]].to_numpy() + thetax_ave_cr = df[[col for col in df.columns if f'theta_x_ave_{species}' in col]].to_numpy() + thetax_max_cr = df[[col for col in df.columns if f'theta_x_max_{species}' in col]].to_numpy() + thetax_std_cr = df[[col for col in df.columns if f'theta_x_std_{species}' in col]].to_numpy() + thetax_min = np.min(THETAX) + thetax_ave = np.average(THETAX, weights=w) + thetax_max = np.max(THETAX) + thetax_std = np.sqrt(np.average((THETAX-thetax_ave)**2, weights=w)) + assert np.allclose(thetax_min, thetax_min_cr, rtol=1e-8) + assert np.allclose(thetax_ave, thetax_ave_cr, rtol=1e-8) + assert np.allclose(thetax_max, thetax_max_cr, rtol=1e-8) + assert np.allclose(thetax_std, thetax_std_cr, rtol=1e-8) + + # THETA Y MIN AVE MAX STD + thetay_min_cr = df[[col for col in df.columns if f'theta_y_min_{species}' in col]].to_numpy() + thetay_ave_cr = df[[col for col in df.columns if f'theta_y_ave_{species}' in col]].to_numpy() + thetay_max_cr = df[[col for col in df.columns if f'theta_y_max_{species}' in col]].to_numpy() + thetay_std_cr = df[[col for col in df.columns if f'theta_y_std_{species}' in col]].to_numpy() + thetay_min = np.min(THETAY) + thetay_ave = np.average(THETAY, weights=w) + thetay_max = np.max(THETAY) + thetay_std = np.sqrt(np.average((THETAY-thetay_ave)**2, weights=w)) + assert np.allclose(thetay_min, thetay_min_cr, rtol=1e-8) + assert np.allclose(thetay_ave, thetay_ave_cr, rtol=1e-8) + assert np.allclose(thetay_max, thetay_max_cr, rtol=1e-8) + assert np.allclose(thetay_std, thetay_std_cr, rtol=1e-8) + + # dL/dt + dL_dt_cr = df[[col for col in df.columns if 'dL_dt' in col]].to_numpy() + assert np.allclose(dL_dt_cr, dL_dt(), rtol=1e-8) + +# Checksum analysis +plotfile = sys.argv[1] +test_name = os.path.split(os.getcwd())[1] +checksumAPI.evaluate_checksum(test_name, plotfile) diff --git a/Examples/Tests/collider_relevant_diags/inputs_3d_multiple_particles b/Examples/Tests/collider_relevant_diags/inputs_3d_multiple_particles new file mode 100644 index 00000000000..1efc68c33b0 --- /dev/null +++ b/Examples/Tests/collider_relevant_diags/inputs_3d_multiple_particles @@ -0,0 +1,130 @@ +################################# +########## MY CONSTANTS ######### +################################# +my_constants.nx = 8 +my_constants.ny = 8 +my_constants.nz = 8 + +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 1 +amr.n_cell = nx ny nz +amr.max_grid_size = 4 +amr.blocking_factor = 4 +amr.max_level = 0 +geometry.dims = 3 +geometry.prob_lo = 0 0 0 +geometry.prob_hi = 8 8 8 +particles.do_tiling = 0 +warpx.use_filter = 0 + +################################# +######## BOUNDARY CONDITION ##### +################################# +boundary.field_lo = periodic periodic periodic +boundary.field_hi = periodic periodic periodic +boundary.particle_lo = periodic periodic periodic +boundary.particle_hi = periodic periodic periodic + +################################# +############ NUMERICS ########### +################################# +algo.maxwell_solver = ckc +warpx.cfl = 0.99 +algo.particle_shape = 1 + +################################# +############ FIELDS ############# +################################# +particles.E_ext_particle_init_style = constant +particles.B_ext_particle_init_style = constant +particles.E_external_particle = 10000. 0. 0. +particles.B_external_particle = 0. 5000. 0. + +################################# +########### PARTICLES ########### +################################# +particles.species_names = pho beam_p beam_e +particles.photon_species = pho + +beam_e.species_type = electron +beam_e.injection_style = MultipleParticles +beam_e.multiple_particles_pos_x = 4.5 3.5 0.5 +beam_e.multiple_particles_pos_y = 4.5 2.5 1.5 +beam_e.multiple_particles_pos_z = 4.5 1.5 1.5 +beam_e.multiple_particles_ux = 0.3 0.2 0.1 +beam_e.multiple_particles_uy = 0.4 -0.3 -0.1 +beam_e.multiple_particles_uz = 0.3 0.1 -10. +beam_e.multiple_particles_weight = 1. 2 3 +beam_e.initialize_self_fields = 0 +beam_e.self_fields_required_precision = 5e-10 +beam_e.do_qed_quantum_sync = 1 +beam_e.qed_quantum_sync_phot_product_species = pho +beam_e.do_not_push = 1 +beam_e.do_not_deposit = 1 + +beam_p.species_type = positron +beam_p.injection_style = MultipleParticles +beam_p.multiple_particles_pos_x = 4.5 3.5 0.5 +beam_p.multiple_particles_pos_y = 4.5 2.5 1.5 +beam_p.multiple_particles_pos_z = 4.5 1.5 1.5 +beam_p.multiple_particles_ux = 0.3 0.2 0.1 +beam_p.multiple_particles_uy = 0.4 -0.3 -0.1 +beam_p.multiple_particles_uz = 0.3 0.1 -10. +beam_p.multiple_particles_weight = 1. 2 3 +beam_p.initialize_self_fields = 0 +beam_p.self_fields_required_precision = 5e-10 +beam_p.do_qed_quantum_sync = 1 +beam_p.qed_quantum_sync_phot_product_species = pho +beam_p.do_not_push = 1 +beam_p.do_not_deposit = 1 + +pho.species_type = photon +pho.injection_style = none + +################################# +############# QED ############### +################################# +qed_qs.photon_creation_energy_threshold = 0. +qed_qs.lookup_table_mode = builtin +qed_qs.chi_min = 1.e-3 +warpx.do_qed_schwinger = 0 + +################################# +######### DIAGNOSTICS ########### +################################# +# FULL +diagnostics.diags_names = diag1 diag2 + +diag1.intervals = 1 +diag1.diag_type = Full +diag1.write_species = 1 +diag1.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho_beam_e rho_beam_p rho +diag1.species = pho beam_e beam_p +diag1.format = plotfile +#diag1.dump_last_timestep = 1 + +diag2.intervals = 1 +diag2.diag_type = Full +diag2.write_species = 1 +diag2.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho_beam_e rho_beam_p rho +diag2.species = pho beam_e beam_p +diag2.format = openpmd +diag2.openpmd_backend = h5 +#diag2.dump_last_timestep = 1 + +# REDUCED +warpx.reduced_diags_names = ParticleExtrema_beam_e ParticleExtrema_beam_p ColliderRelevant_beam_e_beam_p + +ColliderRelevant_beam_e_beam_p.type = ColliderRelevant +ColliderRelevant_beam_e_beam_p.intervals = 1 +ColliderRelevant_beam_e_beam_p.species =beam_e beam_p + +ParticleExtrema_beam_e.type = ParticleExtrema +ParticleExtrema_beam_e.intervals = 1 +ParticleExtrema_beam_e.species = beam_e + +ParticleExtrema_beam_p.type = ParticleExtrema +ParticleExtrema_beam_p.intervals = 1 +ParticleExtrema_beam_p.species = beam_p diff --git a/Regression/Checksum/benchmarks_json/collider_diagnostics.json b/Regression/Checksum/benchmarks_json/collider_diagnostics.json new file mode 100644 index 00000000000..06553a7ec19 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/collider_diagnostics.json @@ -0,0 +1,36 @@ +{ + "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, + "rho": 0.0, + "rho_beam_e": 9.613059803999999e-19, + "rho_beam_p": 9.613059803999999e-19 + }, + "beam_e": { + "particle_momentum_x": 1.638554718442694e-22, + "particle_momentum_y": 2.184739624590259e-22, + "particle_momentum_z": 2.8401615119673365e-21, + "particle_opticalDepthQSR": 9.767741201839083e+00, + "particle_position_x": 8.5, + "particle_position_y": 8.5, + "particle_position_z": 7.5, + "particle_weight": 6.0 + }, + "beam_p": { + "particle_momentum_x": 1.638554718442694e-22, + "particle_momentum_y": 2.184739624590259e-22, + "particle_momentum_z": 2.8401615119673365e-21, + "particle_opticalDepthQSR": 8.773870620305825e+00, + "particle_position_x": 8.5, + "particle_position_y": 8.5, + "particle_position_z": 7.5, + "particle_weight": 6.0 + } +} diff --git a/Regression/WarpX-tests.ini b/Regression/WarpX-tests.ini index 65a1e65e723..ab390fcb01b 100644 --- a/Regression/WarpX-tests.ini +++ b/Regression/WarpX-tests.ini @@ -209,6 +209,22 @@ compileTest = 0 doVis = 0 analysisRoutine = Examples/Tests/btd_rz/analysis_BTD_laser_antenna.py +[collider_diagnostics] +buildDir = . +inputFile = Examples/Tests/collider_relevant_diags/inputs_3d_multiple_particles +runtime_params = warpx.abort_on_warning_threshold=high +dim = 3 +addToCompileString = +cmakeSetupOpts = -DWarpX_DIMS=3 +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +analysisRoutine = Examples/Tests/collider_relevant_diags/analysis_multiple_particles.py + [collisionISO] buildDir = . inputFile = Examples/Tests/collision/inputs_3d_isotropization diff --git a/Source/Diagnostics/ReducedDiags/CMakeLists.txt b/Source/Diagnostics/ReducedDiags/CMakeLists.txt index b2a31267087..e63764bc24f 100644 --- a/Source/Diagnostics/ReducedDiags/CMakeLists.txt +++ b/Source/Diagnostics/ReducedDiags/CMakeLists.txt @@ -3,6 +3,7 @@ foreach(D IN LISTS WarpX_DIMS) target_sources(lib_${SD} PRIVATE BeamRelevant.cpp + ColliderRelevant.cpp FieldEnergy.cpp FieldProbe.cpp FieldProbeParticleContainer.cpp diff --git a/Source/Diagnostics/ReducedDiags/ColliderRelevant.H b/Source/Diagnostics/ReducedDiags/ColliderRelevant.H new file mode 100644 index 00000000000..1998c56812d --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ColliderRelevant.H @@ -0,0 +1,61 @@ +/* Copyright 2023 The WarpX Community + * + * This file is part of WarpX. + * + * Authors: Arianna Formenti + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_DIAGNOSTICS_REDUCEDDIAGS_COLLIDERRELEVANT_H_ +#define WARPX_DIAGNOSTICS_REDUCEDDIAGS_COLLIDERRELEVANT_H_ + +#include "ReducedDiags.H" + +#include +#include +#include + +/** + * This class contains diagnostics that are relevant to colliders. + */ +class ColliderRelevant : public ReducedDiags +{ +public: + + /** + * constructor + * @param[in] rd_name reduced diags names + */ + ColliderRelevant(std::string rd_name); + + /// name of the two colliding species + std::vector m_beam_name; + + /** + * \brief This function computes collider-relevant diagnostics. + * @param[in] step current time step + * + * [0]step, [1]time, [2]dL/dt, + * for first species: + * [3]chi_min, [4]chi_ave, [5] chi_max, + * [6]x_ave, [7]x_std, + * [8]y_ave, [9]y_std, + * [10]thetax_min, [11]thetax_ave, [12]thetax_max, [13]thetax_std, + * [14]thetay_min, [15]thetay_ave, [16]thetay_max, [17]thetay_std + * same for second species follows. + */ + void ComputeDiags(int step) override final; + +private: + /// auxiliary structure to store headers and indices of the reduced diagnostics + struct aux_header_index + { + std::string header; + int idx; + }; + + /// map to store header texts and indices of the reduced diagnostics + std::map m_headers_indices; +}; + +#endif // WARPX_DIAGNOSTICS_REDUCEDDIAGS_COLLIDERRELEVANT_H_ diff --git a/Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp b/Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp new file mode 100644 index 00000000000..1e2c16ac737 --- /dev/null +++ b/Source/Diagnostics/ReducedDiags/ColliderRelevant.cpp @@ -0,0 +1,548 @@ +/* Copyright 2023 The WarpX Community + * + * This file is part of WarpX. + * + * Authors: Arianna Formenti, Yinjian Zhao + * License: BSD-3-Clause-LBNL + */ +#include "ColliderRelevant.H" + +#include "Diagnostics/ReducedDiags/ReducedDiags.H" +#if (defined WARPX_QED) +# include "Particles/ElementaryProcess/QEDInternals/QedChiFunctions.H" +#endif +#include "Particles/Gather/FieldGather.H" +#include "Particles/Gather/GetExternalFields.H" +#include "Particles/MultiParticleContainer.H" +#include "Particles/Pusher/GetAndSetPosition.H" +#include "Particles/SpeciesPhysicalProperties.H" +#include "Particles/WarpXParticleContainer.H" +#include "Utils/WarpXConst.H" +#include "Utils/TextMsg.H" +#include "WarpX.H" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace amrex; + +ColliderRelevant::ColliderRelevant (std::string rd_name) +: ReducedDiags{std::move(rd_name)} +{ + // read colliding species names - must be 2 + amrex::ParmParse pp_rd_name(m_rd_name); + pp_rd_name.getarr("species", m_beam_name); + + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + m_beam_name.size() == 2u, + "Collider-relevant diagnostics must involve exactly two species"); + + // RZ coordinate is not supported +#if (defined WARPX_DIM_RZ) + WARPX_ABORT_WITH_MESSAGE( + "Collider-relevant diagnostics do not work in RZ geometry."); +#endif + + ablastr::warn_manager::WMRecordWarning( + "Diagnostics", + "The collider-relevant reduced diagnostic is meant for \ + colliding species propagating along the z direction.", + ablastr::warn_manager::WarnPriority::low); + + ablastr::warn_manager::WMRecordWarning( + "Diagnostics", + "The collider-relevant reduced diagnostic only considers the \ + coarsest level of refinement for the calculations involving chi.", + ablastr::warn_manager::WarnPriority::low); + + // get WarpX class object + auto& warpx = WarpX::GetInstance(); + + // get MultiParticleContainer class object + const MultiParticleContainer& mypc = warpx.GetPartContainer(); + + // loop over species + for (int i_s = 0; i_s < 2; ++i_s) + { + // get WarpXParticleContainer class object + const WarpXParticleContainer& myspc = mypc.GetParticleContainerFromName(m_beam_name[i_s]); + + // get charge + amrex::ParticleReal const q = myspc.getCharge(); + + // photon number density is not available yet + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + q!=amrex::Real(0.0), + "Collider-relevant diagnostic does not work for neutral species yet"); + } + + // function to fill a vector with diags names and create corresponding entry in header + std::vector all_diag_names; + auto add_diag = [&,c=0]( + const std::string& name, const std::string& header) mutable { + m_headers_indices[name] = aux_header_index{header, c++}; + all_diag_names.push_back(name); + }; + +#if (defined WARPX_DIM_3D) + add_diag("dL_dt", "dL_dt(m^-2*s^-1)"); +#elif (defined WARPX_DIM_XZ) + add_diag("dL_dt", "dL_dt(m^-1*s^-1)"); +#else + add_diag("dL_dt", "dL_dt(s^-1)"); +#endif + + // loop over species + for (int i_s = 0; i_s < 2; ++i_s) + { + // get WarpXParticleContainer class object + const WarpXParticleContainer& myspc = mypc.GetParticleContainerFromName(m_beam_name[i_s]); + + if (myspc.DoQED()){ + add_diag("chimin_"+m_beam_name[i_s], "chi_min_"+m_beam_name[i_s]+"()"); + add_diag("chiave_"+m_beam_name[i_s], "chi_ave_"+m_beam_name[i_s]+"()"); + add_diag("chimax_"+m_beam_name[i_s], "chi_max_"+m_beam_name[i_s]+"()"); + } +#if (defined WARPX_DIM_3D) + add_diag("x_ave_"+m_beam_name[i_s], "x_ave_"+m_beam_name[i_s]+"(m)"); + add_diag("x_std_"+m_beam_name[i_s], "x_std_"+m_beam_name[i_s]+"(m)"); + add_diag("y_ave_"+m_beam_name[i_s], "y_ave_"+m_beam_name[i_s]+"(m)"); + add_diag("y_std_"+m_beam_name[i_s], "y_std_"+m_beam_name[i_s]+"(m)"); + add_diag("thetax_min_"+m_beam_name[i_s], "theta_x_min_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetax_ave_"+m_beam_name[i_s], "theta_x_ave_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetax_max_"+m_beam_name[i_s], "theta_x_max_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetax_std_"+m_beam_name[i_s], "theta_x_std_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetay_min_"+m_beam_name[i_s], "theta_y_min_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetay_ave_"+m_beam_name[i_s], "theta_y_ave_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetay_max_"+m_beam_name[i_s], "theta_y_max_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetay_std_"+m_beam_name[i_s], "theta_y_std_"+m_beam_name[i_s]+"(rad)"); + +#elif (defined WARPX_DIM_XZ) + add_diag("x_ave_"+m_beam_name[i_s], "x_ave_"+m_beam_name[i_s]+"(m)"); + add_diag("x_std_"+m_beam_name[i_s], "x_std_"+m_beam_name[i_s]+"(m)"); + add_diag("thetax_min_"+m_beam_name[i_s], "theta_x_min_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetax_ave_"+m_beam_name[i_s], "theta_x_ave_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetax_max_"+m_beam_name[i_s], "theta_x_max_"+m_beam_name[i_s]+"(rad)"); + add_diag("thetax_std_"+m_beam_name[i_s], "theta_x_std_"+m_beam_name[i_s]+"(rad)"); +#endif + m_data.resize(all_diag_names.size()); + } + + if (amrex::ParallelDescriptor::IOProcessor()) + { + if ( m_write_header ) + { + // open file + std::ofstream ofs; + ofs.open(m_path + m_rd_name + "." + m_extension, + std::ofstream::out | std::ofstream::app); + // write header row + int off = 0; + ofs << "#"; + ofs << "[" << off++ << "]step()"; + ofs << m_sep; + ofs << "[" << off++ << "]time(s)"; + for (const auto& name : all_diag_names){ + const auto& el = m_headers_indices[name]; + ofs << m_sep << "[" << el.idx + off << "]" << el.header; + } + ofs << std::endl; + // close file + ofs.close(); + } + } +} + +void ColliderRelevant::ComputeDiags (int step) +{ +#if defined(WARPX_DIM_RZ) + amrex::ignore_unused(step); +#else + + // Judge if the diags should be done + if (!m_intervals.contains(step+1)) { return; } + + // get MultiParticleContainer class object + const MultiParticleContainer& mypc = WarpX::GetInstance().GetPartContainer(); + + // get a reference to WarpX instance + auto& warpx = WarpX::GetInstance(); + + // get cell volume + amrex::Geometry const & geom = warpx.Geom(0); + amrex::Real dV = AMREX_D_TERM(geom.CellSize(0), *geom.CellSize(1), *geom.CellSize(2)); + + const auto get_idx = [&](const std::string& name){ + return m_headers_indices.at(name).idx; + }; + + std::array, 2> num_dens; + + // loop over species + for (int i_s = 0; i_s < 2; ++i_s) + { + // get WarpXParticleContainer class object + WarpXParticleContainer& myspc = mypc.GetParticleContainerFromName(m_beam_name[i_s]); + // get charge + amrex::ParticleReal const q = myspc.getCharge(); + + using PType = typename WarpXParticleContainer::SuperParticleType; + + num_dens[i_s] = myspc.GetChargeDensity(0); + num_dens[i_s]->mult(1./q); + +#if defined(WARPX_DIM_1D_Z) + // w_tot + amrex::Real w_tot = ReduceSum( myspc, + [=] AMREX_GPU_HOST_DEVICE (const PType& p) + { + return p.rdata(PIdx::w); + }); + amrex::ParallelDescriptor::ReduceRealSum(w_tot); +#elif defined(WARPX_DIM_XZ) + // w_tot + // x_ave, + // thetax_min, thetax_ave, thetax_max + amrex::ReduceOps reduce_ops; + auto r = amrex::ParticleReduce>( + myspc, + [=] AMREX_GPU_DEVICE(const PType& p) noexcept -> amrex::GpuTuple + { + const amrex::Real w = p.rdata(PIdx::w); + const amrex::Real x = p.pos(0); + const amrex::Real ux = p.rdata(PIdx::ux); + const amrex::Real uz = p.rdata(PIdx::uz); + const amrex::Real thetax = std::atan2(ux, uz); + return {w, w*x, thetax, w*thetax, thetax}; + }, + reduce_ops); + + amrex::Real w_tot = amrex::get<0>(r); + amrex::Real x_ave = amrex::get<1>(r); + amrex::Real thetax_min = amrex::get<2>(r); + amrex::Real thetax_ave = amrex::get<3>(r); + amrex::Real thetax_max = amrex::get<4>(r); + + amrex::ParallelDescriptor::ReduceRealSum({w_tot, x_ave, thetax_ave}); + amrex::ParallelDescriptor::ReduceRealMin({thetax_min}); + amrex::ParallelDescriptor::ReduceRealMax({thetax_max}); + + // x_std, thetax_std + amrex::Real x_std = 0.0_rt; + amrex::Real thetax_std = 0.0_rt; + + if (w_tot > 0.0_rt) + { + x_ave = x_ave / w_tot; + thetax_ave = thetax_ave / w_tot; + + amrex::ReduceOps reduce_ops_std; + auto r_std = amrex::ParticleReduce>( + myspc, + [=] AMREX_GPU_DEVICE(const PType& p) noexcept -> amrex::GpuTuple + { + const amrex::Real w = p.rdata(PIdx::w); + const amrex::Real x = p.pos(0); + const amrex::Real ux = p.rdata(PIdx::ux); + const amrex::Real uz = p.rdata(PIdx::uz); + const amrex::Real thetax = std::atan2(ux, uz); + const amrex::Real tmp1 = (x - x_ave)*(x - x_ave)*w; + const amrex::Real tmp2 = (thetax - thetax_ave)*(thetax - thetax_ave)*w; + return {tmp1, tmp2}; + }, + reduce_ops_std); + + x_std = amrex::get<0>(r_std); + thetax_std = amrex::get<1>(r_std); + + amrex::ParallelDescriptor::ReduceRealSum({x_std, thetax_std}); + + x_std = std::sqrt(x_std / w_tot); + thetax_std = std::sqrt(thetax_std / w_tot); + } + + m_data[get_idx("x_ave_"+m_beam_name[i_s])] = x_ave; + m_data[get_idx("x_std_"+m_beam_name[i_s])] = x_std; + m_data[get_idx("thetax_min_"+m_beam_name[i_s])] = thetax_min; + m_data[get_idx("thetax_ave_"+m_beam_name[i_s])] = thetax_ave; + m_data[get_idx("thetax_max_"+m_beam_name[i_s])] = thetax_max; + m_data[get_idx("thetax_std_"+m_beam_name[i_s])] = thetax_std; +#elif defined(WARPX_DIM_3D) + // w_tot + // x_ave, y_ave, + // thetax_min, thetax_ave, thetax_max + // thetay_min, thetay_ave, thetay_max + amrex::ReduceOps reduce_ops; + auto r = amrex::ParticleReduce>( + myspc, + [=] AMREX_GPU_DEVICE(const PType& p) noexcept -> amrex::GpuTuple + { + const amrex::Real w = p.rdata(PIdx::w); + const amrex::Real x = p.pos(0); + const amrex::Real y = p.pos(1); + const amrex::Real ux = p.rdata(PIdx::ux); + const amrex::Real uy = p.rdata(PIdx::uy); + const amrex::Real uz = p.rdata(PIdx::uz); + const amrex::Real thetax = std::atan2(ux, uz); + const amrex::Real thetay = std::atan2(uy, uz); + return {w, w*x, w*y, + thetax, w*thetax, thetax, + thetay, w*thetay, thetay}; + }, + reduce_ops); + + amrex::Real w_tot = amrex::get<0>(r); + amrex::Real x_ave = amrex::get<1>(r); + amrex::Real y_ave = amrex::get<2>(r); + amrex::Real thetax_min = amrex::get<3>(r); + amrex::Real thetax_ave = amrex::get<4>(r); + amrex::Real thetax_max = amrex::get<5>(r); + amrex::Real thetay_min = amrex::get<6>(r); + amrex::Real thetay_ave = amrex::get<7>(r); + amrex::Real thetay_max = amrex::get<8>(r); + + amrex::ParallelDescriptor::ReduceRealSum({w_tot, x_ave, y_ave, thetax_ave, thetay_ave}); + amrex::ParallelDescriptor::ReduceRealMin({thetax_min, thetay_min}); + amrex::ParallelDescriptor::ReduceRealMax({thetax_max, thetay_max}); + + // x_std, y_std, thetax_std, thetay_std + amrex::Real x_std = 0.0_rt; + amrex::Real y_std = 0.0_rt; + amrex::Real thetax_std = 0.0_rt; + amrex::Real thetay_std = 0.0_rt; + + if (w_tot > 0.0_rt) + { + x_ave = x_ave / w_tot; + y_ave = y_ave / w_tot; + thetax_ave = thetax_ave / w_tot; + thetay_ave = thetay_ave / w_tot; + + amrex::ReduceOps reduce_ops_std; + auto r_std = amrex::ParticleReduce>( + myspc, + [=] AMREX_GPU_DEVICE(const PType& p) noexcept -> amrex::GpuTuple + { + const amrex::Real w = p.rdata(PIdx::w); + const amrex::Real x = p.pos(0); + const amrex::Real ux = p.rdata(PIdx::ux); + const amrex::Real y = p.pos(1); + const amrex::Real uy = p.rdata(PIdx::uy); + const amrex::Real uz = p.rdata(PIdx::uz); + const amrex::Real thetax = std::atan2(ux, uz); + const amrex::Real thetay = std::atan2(uy, uz); + const amrex::Real tmp1 = (x - x_ave)*(x - x_ave)*w; + const amrex::Real tmp2 = (y - y_ave)*(y - y_ave)*w; + const amrex::Real tmp3 = (thetax - thetax_ave)*(thetax - thetax_ave)*w; + const amrex::Real tmp4 = (thetay - thetay_ave)*(thetay - thetay_ave)*w; + return {tmp1, tmp2, tmp3, tmp4}; + }, + reduce_ops_std); + + x_std = amrex::get<0>(r_std); + y_std = amrex::get<1>(r_std); + thetax_std = amrex::get<2>(r_std); + thetay_std = amrex::get<3>(r_std); + + amrex::ParallelDescriptor::ReduceRealSum({x_std, y_std, thetax_std, thetay_std}); + + x_std = std::sqrt(x_std / w_tot); + y_std = std::sqrt(y_std / w_tot); + thetax_std = std::sqrt(thetax_std / w_tot); + thetay_std = std::sqrt(thetay_std / w_tot); + } + + m_data[get_idx("x_ave_"+m_beam_name[i_s])] = x_ave; + m_data[get_idx("x_std_"+m_beam_name[i_s])] = x_std; + m_data[get_idx("y_ave_"+m_beam_name[i_s])] = y_ave; + m_data[get_idx("y_std_"+m_beam_name[i_s])] = y_std; + m_data[get_idx("thetax_min_"+m_beam_name[i_s])] = thetax_min; + m_data[get_idx("thetax_ave_"+m_beam_name[i_s])] = thetax_ave; + m_data[get_idx("thetax_max_"+m_beam_name[i_s])] = thetax_max; + m_data[get_idx("thetax_std_"+m_beam_name[i_s])] = thetax_std; + m_data[get_idx("thetay_min_"+m_beam_name[i_s])] = thetay_min; + m_data[get_idx("thetay_ave_"+m_beam_name[i_s])] = thetay_ave; + m_data[get_idx("thetay_max_"+m_beam_name[i_s])] = thetay_max; + m_data[get_idx("thetay_std_"+m_beam_name[i_s])] = thetay_std; +#endif + +#if (defined WARPX_QED) + // get mass + amrex::ParticleReal m = myspc.getMass(); + const bool is_photon = myspc.AmIA(); + if (is_photon) { + m = PhysConst::m_e; + } + + // compute chimin, chiave and chimax + amrex::Real chimin_f = 0.0_rt; + amrex::Real chimax_f = 0.0_rt; + amrex::Real chiave_f = 0.0_rt; + + if (myspc.DoQED()) + { + // define variables in preparation for field gatheeduce_data.value()ring + const int n_rz_azimuthal_modes = WarpX::n_rz_azimuthal_modes; + const int nox = WarpX::nox; + const bool galerkin_interpolation = WarpX::galerkin_interpolation; + const amrex::IntVect ngEB = warpx.getngEB(); + + // TODO loop over refinement levels: for (int lev = 0; lev <= level_number; ++lev) + const int lev = 0; + + // define variables in preparation for field gathering + const std::array& dx = WarpX::CellSize(std::max(lev, 0)); + const amrex::GpuArray dx_arr = {dx[0], dx[1], dx[2]}; + const amrex::MultiFab & Ex = warpx.getEfield(lev,0); + const amrex::MultiFab & Ey = warpx.getEfield(lev,1); + const amrex::MultiFab & Ez = warpx.getEfield(lev,2); + const amrex::MultiFab & Bx = warpx.getBfield(lev,0); + const amrex::MultiFab & By = warpx.getBfield(lev,1); + const amrex::MultiFab & Bz = warpx.getBfield(lev,2); + + // declare reduce_op + ReduceOps reduce_op; + ReduceData reduce_data(reduce_op); + using ReduceTuple = typename decltype(reduce_data)::Type; + + // Loop over boxes + for (WarpXParIter pti(myspc, lev); pti.isValid(); ++pti) + { + const auto GetPosition = GetParticlePosition(pti); + // get particle arrays + amrex::ParticleReal* const AMREX_RESTRICT ux = pti.GetAttribs()[PIdx::ux].dataPtr(); + amrex::ParticleReal* const AMREX_RESTRICT uy = pti.GetAttribs()[PIdx::uy].dataPtr(); + amrex::ParticleReal* const AMREX_RESTRICT uz = pti.GetAttribs()[PIdx::uz].dataPtr(); + amrex::ParticleReal* const AMREX_RESTRICT w = pti.GetAttribs()[PIdx::w].dataPtr(); + // declare external fields + const int offset = 0; + const auto getExternalEB = GetExternalEBField(pti, offset); + // define variables in preparation for field gathering + amrex::Box box = pti.tilebox(); + box.grow(ngEB); + const amrex::Dim3 lo = amrex::lbound(box); + const std::array& xyzmin = WarpX::LowerCorner(box, lev, 0._rt); + const amrex::GpuArray xyzmin_arr = {xyzmin[0], xyzmin[1], xyzmin[2]}; + const amrex::Array4 & ex_arr = Ex[pti].array(); + const amrex::Array4 & ey_arr = Ey[pti].array(); + const amrex::Array4 & ez_arr = Ez[pti].array(); + const amrex::Array4 & bx_arr = Bx[pti].array(); + const amrex::Array4 & by_arr = By[pti].array(); + const amrex::Array4 & bz_arr = Bz[pti].array(); + const amrex::IndexType ex_type = Ex[pti].box().ixType(); + const amrex::IndexType ey_type = Ey[pti].box().ixType(); + const amrex::IndexType ez_type = Ez[pti].box().ixType(); + const amrex::IndexType bx_type = Bx[pti].box().ixType(); + const amrex::IndexType by_type = By[pti].box().ixType(); + const amrex::IndexType bz_type = Bz[pti].box().ixType(); + + // evaluate reduce_op + reduce_op.eval(pti.numParticles(), reduce_data, + [=] AMREX_GPU_DEVICE (int i) -> ReduceTuple + { + // get external fields + amrex::ParticleReal xp, yp, zp; + GetPosition(i, xp, yp, zp); + amrex::ParticleReal ex = 0._rt, ey = 0._rt, ez = 0._rt; + amrex::ParticleReal bx = 0._rt, by = 0._rt, bz = 0._rt; + getExternalEB(i, ex, ey, ez, bx, by, bz); + + // gather E and B + doGatherShapeN(xp, yp, zp, + ex, ey, ez, bx, by, bz, + ex_arr, ey_arr, ez_arr, bx_arr, by_arr, bz_arr, + ex_type, ey_type, ez_type, + bx_type, by_type, bz_type, + dx_arr, xyzmin_arr, lo, + n_rz_azimuthal_modes, nox, galerkin_interpolation); + // compute chi + amrex::Real chi = 0.0_rt; + if (is_photon) { + chi = QedUtils::chi_photon(ux[i]*m, uy[i]*m, uz[i]*m, + ex, ey, ez, bx, by, bz); + } else { + chi = QedUtils::chi_ele_pos(ux[i]*m, uy[i]*m, uz[i]*m, + ex, ey, ez, bx, by, bz); + } + return {chi, chi, chi*w[i]}; + }); + } + auto val = reduce_data.value(); + chimin_f = get<0>(val); + chimax_f = get<1>(val); + chiave_f = get<2>(val); + amrex::ParallelDescriptor::ReduceRealMin(chimin_f); + amrex::ParallelDescriptor::ReduceRealMax(chimax_f); + amrex::ParallelDescriptor::ReduceRealSum(chiave_f); + + m_data[get_idx("chimin_"+m_beam_name[i_s])] = chimin_f; + m_data[get_idx("chiave_"+m_beam_name[i_s])] = chiave_f/w_tot; + m_data[get_idx("chimax_"+m_beam_name[i_s])] = chimax_f; + } +#endif + } // end loop over species + + // make density MultiFabs from nodal to cell centered + amrex::BoxArray ba = warpx.boxArray(0); + amrex::DistributionMapping dmap = warpx.DistributionMap(0); + constexpr int ncomp = 1; + constexpr int ngrow = 0; + amrex::MultiFab mf_dst1(ba.convert(amrex::IntVect::TheCellVector()), dmap, ncomp, ngrow); + amrex::MultiFab mf_dst2(ba.convert(amrex::IntVect::TheCellVector()), dmap, ncomp, ngrow); + ablastr::coarsen::sample::Coarsen(mf_dst1, *num_dens[0], 0, 0, ncomp, ngrow); + ablastr::coarsen::sample::Coarsen(mf_dst2, *num_dens[1], 0, 0, ncomp, ngrow); + + // compute luminosity + amrex::Real const n1_dot_n2 = amrex::MultiFab::Dot(mf_dst1, 0, mf_dst2, 0, 1, 0); + amrex::Real const lumi = 2. * PhysConst::c * n1_dot_n2 * dV; + m_data[get_idx("dL_dt")] = lumi; +#endif // not RZ +} diff --git a/Source/Diagnostics/ReducedDiags/Make.package b/Source/Diagnostics/ReducedDiags/Make.package index f265e302d3a..a1f08a24da3 100644 --- a/Source/Diagnostics/ReducedDiags/Make.package +++ b/Source/Diagnostics/ReducedDiags/Make.package @@ -7,6 +7,7 @@ CEXE_sources += FieldProbe.cpp CEXE_sources += FieldProbeParticleContainer.cpp CEXE_sources += FieldMomentum.cpp CEXE_sources += BeamRelevant.cpp +CEXE_sources += ColliderRelevant.cpp CEXE_sources += LoadBalanceCosts.cpp CEXE_sources += LoadBalanceEfficiency.cpp CEXE_sources += ParticleHistogram.cpp diff --git a/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp index d895c4caa23..73e63aeb4d3 100644 --- a/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp +++ b/Source/Diagnostics/ReducedDiags/MultiReducedDiags.cpp @@ -8,6 +8,7 @@ #include "BeamRelevant.H" #include "ChargeOnEB.H" +#include "ColliderRelevant.H" #include "FieldEnergy.H" #include "FieldMaximum.H" #include "FieldProbe.H" @@ -59,6 +60,7 @@ MultiReducedDiags::MultiReducedDiags () {"FieldReduction", [](CS s){return std::make_unique(s);}}, {"RhoMaximum", [](CS s){return std::make_unique(s);}}, {"BeamRelevant", [](CS s){return std::make_unique(s);}}, + {"ColliderRelevant", [](CS s){return std::make_unique(s);}}, {"LoadBalanceCosts", [](CS s){return std::make_unique(s);}}, {"LoadBalanceEfficiency", [](CS s){return std::make_unique(s);}}, {"ParticleHistogram", [](CS s){return std::make_unique(s);}}, diff --git a/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp b/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp index 77377c7d5ed..758b48b7262 100644 --- a/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp +++ b/Source/Diagnostics/ReducedDiags/ReducedDiags.cpp @@ -24,9 +24,8 @@ using namespace amrex; // constructor ReducedDiags::ReducedDiags (std::string rd_name) + : m_rd_name(std::move(rd_name)) { - m_rd_name = rd_name; - BackwardCompatibility(); const ParmParse pp_rd_name(m_rd_name); diff --git a/Tools/Algorithms/stencil.py b/Tools/Algorithms/stencil.py index cb24a62148b..63bb7f11c2e 100644 --- a/Tools/Algorithms/stencil.py +++ b/Tools/Algorithms/stencil.py @@ -13,7 +13,9 @@ import argparse import os +import sys +sys.path.append('../Parser/') from input_file_parser import parse_input_file import matplotlib.pyplot as plt import numpy as np diff --git a/Tools/Parser/__init__.py b/Tools/Parser/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/Tools/Algorithms/input_file_parser.py b/Tools/Parser/input_file_parser.py similarity index 100% rename from Tools/Algorithms/input_file_parser.py rename to Tools/Parser/input_file_parser.py From 7b4cb7321d8379844597a0ddb560cebf079d23f2 Mon Sep 17 00:00:00 2001 From: David Grote Date: Mon, 11 Sep 2023 18:04:47 -0700 Subject: [PATCH 12/39] PICMI: Add `warpx_intervals` option to BTD (#4288) * Add warpx_intervals option to BTD * Add warpx_intervals to LabFrameParticleDiagnostics --- Python/pywarpx/picmi.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/Python/pywarpx/picmi.py b/Python/pywarpx/picmi.py index dbdebf452d1..ce20724950d 100644 --- a/Python/pywarpx/picmi.py +++ b/Python/pywarpx/picmi.py @@ -2249,6 +2249,10 @@ class LabFrameFieldDiagnostic(picmistandard.PICMI_LabFrameFieldDiagnostic, warpx_file_prefix: string, optional Passed to .file_prefix + warpx_intervals: integer or string + Selects the snapshots to be made, instead of using "num_snapshots" which + makes all snapshots. "num_snapshots" is ignored. + warpx_file_min_digits: integer, optional Passed to .file_min_digits @@ -2268,6 +2272,7 @@ def init(self, kw): self.format = kw.pop('warpx_format', None) self.openpmd_backend = kw.pop('warpx_openpmd_backend', None) self.file_prefix = kw.pop('warpx_file_prefix', None) + self.intervals = kw.pop('warpx_intervals', None) self.file_min_digits = kw.pop('warpx_file_min_digits', None) self.buffer_size = kw.pop('warpx_buffer_size', None) self.lower_bound = kw.pop('warpx_lower_bound', None) @@ -2286,10 +2291,15 @@ def initialize_inputs(self): self.diagnostic.diag_hi = self.upper_bound self.diagnostic.do_back_transformed_fields = 1 - self.diagnostic.num_snapshots_lab = self.num_snapshots self.diagnostic.dt_snapshots_lab = self.dt_snapshots self.diagnostic.buffer_size = self.buffer_size + # intervals and num_snapshots_lab cannot both be set + if self.intervals is not None: + self.diagnostic.intervals = self.intervals + else: + self.diagnostic.num_snapshots_lab = self.num_snapshots + self.diagnostic.do_back_transformed_particles = self.write_species # --- Use a set to ensure that fields don't get repeated. @@ -2352,6 +2362,10 @@ class LabFrameParticleDiagnostic(picmistandard.PICMI_LabFrameParticleDiagnostic, warpx_file_prefix: string, optional Passed to .file_prefix + warpx_intervals: integer or string + Selects the snapshots to be made, instead of using "num_snapshots" which + makes all snapshots. "num_snapshots" is ignored. + warpx_file_min_digits: integer, optional Passed to .file_min_digits @@ -2365,6 +2379,7 @@ def init(self, kw): self.format = kw.pop('warpx_format', None) self.openpmd_backend = kw.pop('warpx_openpmd_backend', None) self.file_prefix = kw.pop('warpx_file_prefix', None) + self.intervals = kw.pop('warpx_intervals', None) self.file_min_digits = kw.pop('warpx_file_min_digits', None) self.buffer_size = kw.pop('warpx_buffer_size', None) self.write_fields = kw.pop('warpx_write_fields', None) @@ -2379,10 +2394,15 @@ def initialize_inputs(self): self.diagnostic.file_min_digits = self.file_min_digits self.diagnostic.do_back_transformed_particles = 1 - self.diagnostic.num_snapshots_lab = self.num_snapshots self.diagnostic.dt_snapshots_lab = self.dt_snapshots self.diagnostic.buffer_size = self.buffer_size + # intervals and num_snapshots_lab cannot both be set + if self.intervals is not None: + self.diagnostic.intervals = self.intervals + else: + self.diagnostic.num_snapshots_lab = self.num_snapshots + self.diagnostic.do_back_transformed_fields = self.write_fields self.set_write_dir() From 12a695d9fa518ad23a4f0375c0ccbafcabb80473 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Tue, 12 Sep 2023 11:34:26 -0700 Subject: [PATCH 13/39] AMReX/PICSAR: Weekly Update (#4293) * AMReX: Weekly Update * PICSAR: Use Tag --- .github/workflows/cuda.yml | 2 +- Regression/WarpX-GPU-tests.ini | 2 +- Regression/WarpX-tests.ini | 2 +- cmake/dependencies/AMReX.cmake | 2 +- cmake/dependencies/PICSAR.cmake | 2 +- run_test.sh | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cuda.yml b/.github/workflows/cuda.yml index 17b8c259f0a..88037297d35 100644 --- a/.github/workflows/cuda.yml +++ b/.github/workflows/cuda.yml @@ -111,7 +111,7 @@ jobs: which nvcc || echo "nvcc not in PATH!" git clone https://github.com/AMReX-Codes/amrex.git ../amrex - cd ../amrex && git checkout --detach 23.09 && cd - + cd ../amrex && git checkout --detach b98bdae9fb67e5d9aafc488de92c53001bd323ec && cd - make COMP=gcc QED=FALSE USE_MPI=TRUE USE_GPU=TRUE USE_OMP=FALSE USE_PSATD=TRUE USE_CCACHE=TRUE -j 2 build_nvhpc21-11-nvcc: diff --git a/Regression/WarpX-GPU-tests.ini b/Regression/WarpX-GPU-tests.ini index c40dac874df..0e033f186b3 100644 --- a/Regression/WarpX-GPU-tests.ini +++ b/Regression/WarpX-GPU-tests.ini @@ -60,7 +60,7 @@ emailBody = Check https://ccse.lbl.gov/pub/GpuRegressionTesting/WarpX/ for more [AMReX] dir = /home/regtester/git/amrex/ -branch = 23.09 +branch = b98bdae9fb67e5d9aafc488de92c53001bd323ec [source] dir = /home/regtester/git/WarpX diff --git a/Regression/WarpX-tests.ini b/Regression/WarpX-tests.ini index ab390fcb01b..48364535760 100644 --- a/Regression/WarpX-tests.ini +++ b/Regression/WarpX-tests.ini @@ -59,7 +59,7 @@ emailBody = Check https://ccse.lbl.gov/pub/RegressionTesting/WarpX/ for more det [AMReX] dir = /home/regtester/AMReX_RegTesting/amrex/ -branch = 23.09 +branch = b98bdae9fb67e5d9aafc488de92c53001bd323ec [source] dir = /home/regtester/AMReX_RegTesting/warpx diff --git a/cmake/dependencies/AMReX.cmake b/cmake/dependencies/AMReX.cmake index 1e2697a4e50..8d873655fe0 100644 --- a/cmake/dependencies/AMReX.cmake +++ b/cmake/dependencies/AMReX.cmake @@ -257,7 +257,7 @@ set(WarpX_amrex_src "" set(WarpX_amrex_repo "https://github.com/AMReX-Codes/amrex.git" CACHE STRING "Repository URI to pull and build AMReX from if(WarpX_amrex_internal)") -set(WarpX_amrex_branch "23.09" +set(WarpX_amrex_branch "b98bdae9fb67e5d9aafc488de92c53001bd323ec" CACHE STRING "Repository branch for WarpX_amrex_repo if(WarpX_amrex_internal)") diff --git a/cmake/dependencies/PICSAR.cmake b/cmake/dependencies/PICSAR.cmake index d04269b8876..8400dc06a4b 100644 --- a/cmake/dependencies/PICSAR.cmake +++ b/cmake/dependencies/PICSAR.cmake @@ -106,7 +106,7 @@ if(WarpX_QED) set(WarpX_picsar_repo "https://github.com/ECP-WarpX/picsar.git" CACHE STRING "Repository URI to pull and build PICSAR from if(WarpX_picsar_internal)") - set(WarpX_picsar_branch "aa54e985398c1d575abc7e6737cdbc660a13765f" + set(WarpX_picsar_branch "23.09" CACHE STRING "Repository branch for WarpX_picsar_repo if(WarpX_picsar_internal)") diff --git a/run_test.sh b/run_test.sh index faf4bdd85c4..37b5d370667 100755 --- a/run_test.sh +++ b/run_test.sh @@ -71,7 +71,7 @@ python3 -m pip install --upgrade -r warpx/Regression/requirements.txt # Clone AMReX and warpx-data git clone https://github.com/AMReX-Codes/amrex.git -cd amrex && git checkout --detach 23.09 && cd - +cd amrex && git checkout --detach b98bdae9fb67e5d9aafc488de92c53001bd323ec && cd - # warpx-data contains various required data sets git clone --depth 1 https://github.com/ECP-WarpX/warpx-data.git # openPMD-example-datasets contains various required data sets From 51f908bf15e737ca4ac0cc2871de2d2de77e43a7 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Tue, 12 Sep 2023 16:14:17 -0700 Subject: [PATCH 14/39] Doc: cupy on Perlmutter (NERSC) (#4289) Document & automate how to install `cupy` on Perlmutter. --- Tools/machines/perlmutter-nersc/install_gpu_dependencies.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/machines/perlmutter-nersc/install_gpu_dependencies.sh b/Tools/machines/perlmutter-nersc/install_gpu_dependencies.sh index a60c3ba7594..7bcc7053974 100755 --- a/Tools/machines/perlmutter-nersc/install_gpu_dependencies.sh +++ b/Tools/machines/perlmutter-nersc/install_gpu_dependencies.sh @@ -127,6 +127,7 @@ python3 -m pip install --upgrade matplotlib python3 -m pip install --upgrade yt # install or update WarpX dependencies such as picmistandard python3 -m pip install --upgrade -r $HOME/src/warpx/requirements.txt +python3 -m pip install cupy-cuda11x # CUDA 11.7 compatible wheel # optional: for libEnsemble python3 -m pip install -r $HOME/src/warpx/Tools/LibEnsemble/requirements.txt # optional: for optimas (based on libEnsemble & ax->botorch->gpytorch->pytorch) From 15dacea8160503a0bf297063545b2b834a7f4b77 Mon Sep 17 00:00:00 2001 From: Marco Garten Date: Tue, 12 Sep 2023 19:03:00 -0700 Subject: [PATCH 15/39] Revert PR4266 - lower and uppers bounds (#4294) The lower and upper bounds are already present within PICMI. --- Python/pywarpx/picmi.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Python/pywarpx/picmi.py b/Python/pywarpx/picmi.py index ce20724950d..056026fbb8f 100644 --- a/Python/pywarpx/picmi.py +++ b/Python/pywarpx/picmi.py @@ -1963,14 +1963,6 @@ class FieldDiagnostic(picmistandard.PICMI_FieldDiagnostic, WarpXDiagnosticBase): warpx_dump_rz_modes: bool, optional Flag whether to dump the data for all RZ modes - - warpx_lower_bound: vector of floats, optional - Lower corner of output fields - that is passed to .lower_bound - - warpx_upper_bound: vector of floats, optional - Upper corner of output fields - that is passed to .upper_bound """ def init(self, kw): @@ -1984,8 +1976,6 @@ def init(self, kw): self.file_prefix = kw.pop('warpx_file_prefix', None) self.file_min_digits = kw.pop('warpx_file_min_digits', None) self.dump_rz_modes = kw.pop('warpx_dump_rz_modes', None) - self.lower_bound = kw.pop('warpx_lower_bound', None) - self.upper_bound = kw.pop('warpx_upper_bound', None) def initialize_inputs(self): From 5f40a2b5304d2a01c3c5676faca8dc5b9714ed4f Mon Sep 17 00:00:00 2001 From: Remi Lehe Date: Wed, 13 Sep 2023 09:12:58 -0700 Subject: [PATCH 16/39] Add flag to set GPU-aware MPI from PICMI (#4298) --- Python/pywarpx/picmi.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Python/pywarpx/picmi.py b/Python/pywarpx/picmi.py index 056026fbb8f..ee4422daca6 100644 --- a/Python/pywarpx/picmi.py +++ b/Python/pywarpx/picmi.py @@ -1680,6 +1680,9 @@ class Simulation(picmistandard.PICMI_Simulation): warpx_amrex_the_arena_init_size: long int, optional The amount of memory in bytes to allocate in the Arena. + warpx_amrex_use_gpu_aware_mpi: bool, optional + Whether to use GPU-aware MPI communications + warpx_zmax_plasma_to_compute_max_step: float, optional Sets the simulation run time based on the maximum z value @@ -1739,6 +1742,7 @@ def init(self, kw): self.amr_restart = kw.pop('warpx_amr_restart', None) self.amrex_the_arena_is_managed = kw.pop('warpx_amrex_the_arena_is_managed', None) self.amrex_the_arena_init_size = kw.pop('warpx_amrex_the_arena_init_size', None) + self.amrex_use_gpu_aware_mpi = kw.pop('warpx_amrex_use_gpu_aware_mpi', None) self.zmax_plasma_to_compute_max_step = kw.pop('warpx_zmax_plasma_to_compute_max_step', None) self.compute_max_step_from_btd = kw.pop('warpx_compute_max_step_from_btd', None) @@ -1869,6 +1873,9 @@ def initialize_inputs(self): if self.amrex_the_arena_init_size is not None: pywarpx.amrex.the_arena_init_size = self.amrex_the_arena_init_size + if self.amrex_use_gpu_aware_mpi is not None: + pywarpx.amrex.use_gpu_aware_mpi = self.amrex_use_gpu_aware_mpi + def initialize_warpx(self, mpi_comm=None): if self.warpx_initialized: return From 6c4436e1f9ec0a2fcd40cdf2692beea14074e459 Mon Sep 17 00:00:00 2001 From: Luca Fedeli Date: Thu, 14 Sep 2023 08:38:12 +0900 Subject: [PATCH 17/39] Clang tidy CI test: add misc-definitions-in-headers check (#4253) * Clang-tidy: add misc-definitions-in-headers check * address issues found with clang-tidy * remove std::cout added for debug purposes * add back newline --- .clang-tidy | 4 +- .../SpectralHankelTransform/BesselRoots.H | 119 +------------- .../SpectralHankelTransform/BesselRoots.cpp | 153 ++++++++++++++++++ .../SpectralHankelTransform/CMakeLists.txt | 1 + .../SpectralHankelTransform/Make.package | 1 + Source/Initialization/InjectorMomentum.H | 1 + Source/Particles/Sorting/CMakeLists.txt | 1 + Source/Particles/Sorting/Make.package | 2 + Source/Particles/Sorting/SortingUtils.H | 13 +- Source/Particles/Sorting/SortingUtils.cpp | 22 +++ 10 files changed, 190 insertions(+), 127 deletions(-) create mode 100644 Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.cpp create mode 100644 Source/Particles/Sorting/SortingUtils.cpp diff --git a/.clang-tidy b/.clang-tidy index 2d495081c8d..5a3deadf304 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -18,6 +18,7 @@ Checks: '-*, google-build-namespaces, google-global-names-in-headers, misc-const-correctness, + misc-definitions-in-headers, misc-misleading-bidirectional, misc-misleading-identifier, misc-misplaced-const, @@ -25,7 +26,6 @@ Checks: '-*, misc-unused-alias-decls, misc-unused-parameters, misc-unused-using-decls, - -misc-definitions-in-headers, modernize-avoid-bind, modernize-concat-nested-namespaces, modernize-deprecated-headers, @@ -82,5 +82,7 @@ Checks: '-*, CheckOptions: - key: modernize-pass-by-value.ValuesOnly value: 'true' +- key: misc-definitions-in-headers.HeaderFileExtensions + value: "H," HeaderFilterRegex: 'Source[a-z_A-Z0-9\/]+\.H$' diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H index 64ffc80ac37..2285ab05679 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.H @@ -32,14 +32,11 @@ ! (www.jpmoreau.fr) ! ------------------------------------------------------------------------ */ -#include "Utils/WarpXConst.H" +#ifndef WARPX_BESSEL_ROOTS_H_ +#define WARPX_BESSEL_ROOTS_H_ #include - -#include - - -void SecantRootFinder(int n, int nitmx, amrex::Real tol, amrex::Real *zeroj, int *ier); +#include /*---------------------------------------------------------------------- * calculate the first nk zeroes of bessel function j(n, x) @@ -56,112 +53,6 @@ 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, amrex::Vector& roots, amrex::Vector &ier) { - using namespace amrex::literals; - - amrex::Real zeroj; - int ierror, ik, k; - - const amrex::Real tol = 1e-14_rt; - const amrex::Real nitmx = 10; - - const amrex::Real c1 = 1.8557571_rt; - const amrex::Real c2 = 1.033150_rt; - const amrex::Real c3 = 0.00397_rt; - const amrex::Real c4 = 0.0908_rt; - const amrex::Real c5 = 0.043_rt; - - const amrex::Real t0 = 4.0_rt*n*n; - const amrex::Real t1 = t0 - 1.0_rt; - const amrex::Real t3 = 4.0_rt*t1*(7.0_rt*t0 - 31.0_rt); - const amrex::Real t5 = 32.0_rt*t1*((83.0_rt*t0 - 982.0_rt)*t0 + 3779.0_rt); - const amrex::Real t7 = 64.0_rt*t1*(((6949.0_rt*t0 - 153855.0_rt)*t0 + 1585743.0_rt)*t0 - 6277237.0_rt); - - roots.resize(nk); - ier.resize(nk); - - // first zero - if (n == 0) { - zeroj = c1 + c2 - c3 - c4 + c5; - SecantRootFinder(n, nitmx, tol, &zeroj, &ierror); - ier[0] = ierror; - roots[0] = zeroj; - ik = 1; - } - else { - // Include the trivial root - ier[0] = 0; - roots[0] = 0.; - const amrex::Real f1 = std::pow(n, (1.0_rt/3.0_rt)); - const amrex::Real f2 = f1*f1*n; - const amrex::Real f3 = f1*n*n; - zeroj = n + c1*f1 + (c2/f1) - (c3/n) - (c4/f2) + (c5/f3); - SecantRootFinder(n, nitmx, tol, &zeroj, &ierror); - ier[1] = ierror; - roots[1] = zeroj; - ik = 2; - } - - // other zeroes - // k counts the nontrivial roots - // ik counts all roots - k = 2; - while (ik < nk) { - - // mac mahon's series for k >> n - const amrex::Real b0 = (k + 0.5_rt*n - 0.25_rt)*MathConst::pi; - const amrex::Real b1 = 8.0_rt*b0; - const amrex::Real b2 = b1*b1; - const amrex::Real b3 = 3.0_rt*b1*b2; - const amrex::Real b5 = 5.0_rt*b3*b2; - const amrex::Real b7 = 7.0_rt*b5*b2; - - zeroj = b0 - (t1/b1) - (t3/b3) - (t5/b5) - (t7/b7); - - const amrex::Real errj = std::abs(jn(n, zeroj)); - - // improve solution using procedure SecantRootFinder - if (errj > tol) SecantRootFinder(n, nitmx, tol, &zeroj, &ierror); - - roots[ik] = zeroj; - ier[ik] = ierror; - - k++; - ik++; - } -} - -void SecantRootFinder(int n, int nitmx, amrex::Real tol, amrex::Real *zeroj, int *ier) { - using namespace amrex::literals; - - amrex::Real p0, p1, q0, q1, dp, p; - amrex::Real c[2]; - - c[0] = 0.95_rt; - c[1] = 0.999_rt; - *ier = 0; - - p = *zeroj; - for (int ntry=0 ; ntry <= 1 ; ntry++) { - p0 = c[ntry]*(*zeroj); +void GetBesselRoots(int n, int nk, amrex::Vector& roots, amrex::Vector &ier); - p1 = *zeroj; - q0 = jn(n, p0); - q1 = jn(n, p1); - for (int it=1; it <= nitmx; it++) { - if (q1 == q0) break; - p = p1 - q1*(p1 - p0)/(q1 - q0); - dp = p - p1; - if (it > 1 && std::abs(dp) < tol) { - *zeroj = p; - return; - } - p0 = p1; - q0 = q1; - p1 = p; - q1 = jn(n, p1); - } - } - *ier = 3; - *zeroj = p; -} +#endif // WARPX_BESSEL_ROOTS_H_ diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.cpp b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.cpp new file mode 100644 index 00000000000..210a4baffc7 --- /dev/null +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/BesselRoots.cpp @@ -0,0 +1,153 @@ +/* Copyright 2019 David Grote + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +/* ------------------------------------------------------------------------- +! program to calculate the first zeroes (root abscissas) of the first +! kind bessel function of integer order n using the subroutine rootj. +! -------------------------------------------------------------------------- +! sample run: +! +! (calculate the first 10 zeroes of 1st kind bessel function of order 2). +! +! zeroes of bessel function of order: 2 +! +! number of calculated zeroes: 10 +! +! table of root abcissas (5 items per line) +! 5.135622 8.417244 11.619841 14.795952 17.959819 + 21.116997 24.270112 27.420574 30.569204 33.716520 +! +! table of error codes (5 items per line) +! 0 0 0 0 0 +! 0 0 0 0 0 +! +! -------------------------------------------------------------------------- +! reference: from numath library by tuan dang trong in fortran 77 +! [bibli 18]. +! +! c++ release 1.0 by j-p moreau, paris +! (www.jpmoreau.fr) +! ------------------------------------------------------------------------ */ + +#include "BesselRoots.H" + +#include "Utils/WarpXConst.H" + +#include + +namespace{ + + void SecantRootFinder(int n, int nitmx, amrex::Real tol, amrex::Real *zeroj, int *ier) { + using namespace amrex::literals; + + amrex::Real p0, p1, q0, q1, dp, p; + amrex::Real c[2]; + + c[0] = 0.95_rt; + c[1] = 0.999_rt; + *ier = 0; + + p = *zeroj; + for (int ntry=0 ; ntry <= 1 ; ntry++) { + p0 = c[ntry]*(*zeroj); + + p1 = *zeroj; + q0 = jn(n, p0); + q1 = jn(n, p1); + for (int it=1; it <= nitmx; it++) { + if (q1 == q0) break; + p = p1 - q1*(p1 - p0)/(q1 - q0); + dp = p - p1; + if (it > 1 && std::abs(dp) < tol) { + *zeroj = p; + return; + } + p0 = p1; + q0 = q1; + p1 = p; + q1 = jn(n, p1); + } + } + *ier = 3; + *zeroj = p; + } + +} + +void GetBesselRoots(int n, int nk, amrex::Vector& roots, amrex::Vector &ier) { + using namespace amrex::literals; + + amrex::Real zeroj; + int ierror, ik, k; + + const amrex::Real tol = 1e-14_rt; + const amrex::Real nitmx = 10; + + const amrex::Real c1 = 1.8557571_rt; + const amrex::Real c2 = 1.033150_rt; + const amrex::Real c3 = 0.00397_rt; + const amrex::Real c4 = 0.0908_rt; + const amrex::Real c5 = 0.043_rt; + + const amrex::Real t0 = 4.0_rt*n*n; + const amrex::Real t1 = t0 - 1.0_rt; + const amrex::Real t3 = 4.0_rt*t1*(7.0_rt*t0 - 31.0_rt); + const amrex::Real t5 = 32.0_rt*t1*((83.0_rt*t0 - 982.0_rt)*t0 + 3779.0_rt); + const amrex::Real t7 = 64.0_rt*t1*(((6949.0_rt*t0 - 153855.0_rt)*t0 + 1585743.0_rt)*t0 - 6277237.0_rt); + + roots.resize(nk); + ier.resize(nk); + + // first zero + if (n == 0) { + zeroj = c1 + c2 - c3 - c4 + c5; + ::SecantRootFinder(n, nitmx, tol, &zeroj, &ierror); + ier[0] = ierror; + roots[0] = zeroj; + ik = 1; + } + else { + // Include the trivial root + ier[0] = 0; + roots[0] = 0.; + const amrex::Real f1 = std::pow(n, (1.0_rt/3.0_rt)); + const amrex::Real f2 = f1*f1*n; + const amrex::Real f3 = f1*n*n; + zeroj = n + c1*f1 + (c2/f1) - (c3/n) - (c4/f2) + (c5/f3); + ::SecantRootFinder(n, nitmx, tol, &zeroj, &ierror); + ier[1] = ierror; + roots[1] = zeroj; + ik = 2; + } + + // other zeroes + // k counts the nontrivial roots + // ik counts all roots + k = 2; + while (ik < nk) { + + // mac mahon's series for k >> n + const amrex::Real b0 = (k + 0.5_rt*n - 0.25_rt)*MathConst::pi; + const amrex::Real b1 = 8.0_rt*b0; + const amrex::Real b2 = b1*b1; + const amrex::Real b3 = 3.0_rt*b1*b2; + const amrex::Real b5 = 5.0_rt*b3*b2; + const amrex::Real b7 = 7.0_rt*b5*b2; + + zeroj = b0 - (t1/b1) - (t3/b3) - (t5/b5) - (t7/b7); + + const amrex::Real errj = std::abs(jn(n, zeroj)); + + // improve solution using procedure SecantRootFinder + if (errj > tol) ::SecantRootFinder(n, nitmx, tol, &zeroj, &ierror); + + roots[ik] = zeroj; + ier[ik] = ierror; + + k++; + ik++; + } +} diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/CMakeLists.txt b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/CMakeLists.txt index a3d2b60bcc4..70c9740367f 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/CMakeLists.txt +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/CMakeLists.txt @@ -1,5 +1,6 @@ target_sources(lib_rz PRIVATE + BesselRoots.cpp SpectralHankelTransformer.cpp HankelTransform.cpp ) diff --git a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/Make.package b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/Make.package index 8bb1d7ef7b4..37ca9a93186 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/Make.package +++ b/Source/FieldSolver/SpectralSolver/SpectralHankelTransform/Make.package @@ -1,3 +1,4 @@ +CEXE_sources += BesselRoots.cpp CEXE_sources += SpectralHankelTransformer.cpp CEXE_sources += HankelTransform.cpp diff --git a/Source/Initialization/InjectorMomentum.H b/Source/Initialization/InjectorMomentum.H index 87b81381ce9..b0afafa730b 100644 --- a/Source/Initialization/InjectorMomentum.H +++ b/Source/Initialization/InjectorMomentum.H @@ -94,6 +94,7 @@ namespace { * @param u_th Momentum spread * @param engine Object used to generate random numbers */ + AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE amrex::Real generateGaussianFluxDist( amrex::Real u_m, amrex::Real u_th, amrex::RandomEngine const& engine ) { diff --git a/Source/Particles/Sorting/CMakeLists.txt b/Source/Particles/Sorting/CMakeLists.txt index d3c378e43b8..60a156f4649 100644 --- a/Source/Particles/Sorting/CMakeLists.txt +++ b/Source/Particles/Sorting/CMakeLists.txt @@ -3,5 +3,6 @@ foreach(D IN LISTS WarpX_DIMS) target_sources(lib_${SD} PRIVATE Partition.cpp + SortingUtils.cpp ) endforeach() diff --git a/Source/Particles/Sorting/Make.package b/Source/Particles/Sorting/Make.package index 16efc02b89e..e6ad1604dc7 100644 --- a/Source/Particles/Sorting/Make.package +++ b/Source/Particles/Sorting/Make.package @@ -1,2 +1,4 @@ CEXE_sources += Partition.cpp +CEXE_sources += SortingUtils.cpp + VPATH_LOCATIONS += $(WARPX_HOME)/Source/Particles/Sorting diff --git a/Source/Particles/Sorting/SortingUtils.H b/Source/Particles/Sorting/SortingUtils.H index 0bec92d6efc..dd53ad6f610 100644 --- a/Source/Particles/Sorting/SortingUtils.H +++ b/Source/Particles/Sorting/SortingUtils.H @@ -19,18 +19,7 @@ * * \param[inout] v Vector of integers, to be filled by this routine */ -void fillWithConsecutiveIntegers( amrex::Gpu::DeviceVector& v ) -{ -#ifdef AMREX_USE_GPU - // On GPU: Use amrex - auto data = v.data(); - auto N = v.size(); - AMREX_FOR_1D( N, i, {data[i] = i;}); -#else - // On CPU: Use std library - std::iota( v.begin(), v.end(), 0L ); -#endif -} +void fillWithConsecutiveIntegers( amrex::Gpu::DeviceVector& v ); /** \brief Find the indices that would reorder the elements of `predicate` * so that the elements with non-zero value precede the other elements diff --git a/Source/Particles/Sorting/SortingUtils.cpp b/Source/Particles/Sorting/SortingUtils.cpp new file mode 100644 index 00000000000..699119e8e18 --- /dev/null +++ b/Source/Particles/Sorting/SortingUtils.cpp @@ -0,0 +1,22 @@ +/* Copyright 2019-2020 Andrew Myers, Maxence Thevenet, Remi Lehe + * Weiqun Zhang + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#include "SortingUtils.H" + +void fillWithConsecutiveIntegers( amrex::Gpu::DeviceVector& v ) +{ +#ifdef AMREX_USE_GPU + // On GPU: Use amrex + auto data = v.data(); + auto N = v.size(); + AMREX_FOR_1D( N, i, {data[i] = i;}); +#else + // On CPU: Use std library + std::iota( v.begin(), v.end(), 0L ); +#endif +} From ec82914c6753cb3fbb9e20f8c66fc59ae115c080 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 15 Sep 2023 08:18:38 -0700 Subject: [PATCH 18/39] Conda: `make` (#4292) Useful on very fresh Ubuntu/WSL/Docker environments where not even `built-essential` was installed. Also available on all operating systems now, so no downside to list: https://anaconda.org/conda-forge/make/ --- Docs/source/install/dependencies.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Docs/source/install/dependencies.rst b/Docs/source/install/dependencies.rst index a198712cc01..6a8b2093fa3 100644 --- a/Docs/source/install/dependencies.rst +++ b/Docs/source/install/dependencies.rst @@ -75,7 +75,7 @@ Conda (Linux/macOS/Windows) .. code-block:: bash - conda create -n warpx-cpu-mpich-dev -c conda-forge blaspp boost ccache cmake compilers git lapackpp "openpmd-api=*=mpi_mpich*" python numpy pandas scipy yt "fftw=*=mpi_mpich*" pkg-config matplotlib mamba mpich mpi4py ninja pip virtualenv + conda create -n warpx-cpu-mpich-dev -c conda-forge blaspp boost ccache cmake compilers git lapackpp "openpmd-api=*=mpi_mpich*" python make numpy pandas scipy yt "fftw=*=mpi_mpich*" pkg-config matplotlib mamba mpich mpi4py ninja pip virtualenv conda activate warpx-cpu-mpich-dev # compile WarpX with -DWarpX_MPI=ON @@ -85,7 +85,7 @@ Conda (Linux/macOS/Windows) .. code-block:: bash - conda create -n warpx-cpu-dev -c conda-forge blaspp boost ccache cmake compilers git lapackpp openpmd-api python numpy pandas scipy yt fftw pkg-config matplotlib mamba ninja pip virtualenv + conda create -n warpx-cpu-dev -c conda-forge blaspp boost ccache cmake compilers git lapackpp openpmd-api python make numpy pandas scipy yt fftw pkg-config matplotlib mamba ninja pip virtualenv conda activate warpx-cpu-dev # compile WarpX with -DWarpX_MPI=OFF From 366dfe5b8e4bb9173c2dc66095648305483bc417 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Fri, 15 Sep 2023 08:19:33 -0700 Subject: [PATCH 19/39] Doc: Conda CUDA Development (#4290) Document how to install the CUDA Toolkit with Conda for development. --- Docs/source/install/dependencies.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Docs/source/install/dependencies.rst b/Docs/source/install/dependencies.rst index 6a8b2093fa3..7bd6c7dc8d5 100644 --- a/Docs/source/install/dependencies.rst +++ b/Docs/source/install/dependencies.rst @@ -107,6 +107,14 @@ For OpenMP support, you will further need: conda install -c conda-forge llvm-openmp +For Nvidia CUDA GPU support, you will need to have `a recent CUDA driver installed `__ or you can lower the CUDA version of `the Nvidia cuda package `__ and `conda-forge to match your drivers `__ and then add these packages: + +.. code-block:: bash + + conda install -c nvidia -c conda-forge cuda cupy + +More info for `CUDA-enabled ML packages `__. + Spack (Linux/macOS) ------------------- From a0a3db1638ac94896924dc2a75b7e7e07072f9c8 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 18 Sep 2023 10:22:38 -0700 Subject: [PATCH 20/39] AMReX: Weekly Update (#4309) --- .github/workflows/cuda.yml | 2 +- Regression/WarpX-GPU-tests.ini | 2 +- Regression/WarpX-tests.ini | 2 +- cmake/dependencies/AMReX.cmake | 2 +- run_test.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cuda.yml b/.github/workflows/cuda.yml index 88037297d35..0f3adb8593a 100644 --- a/.github/workflows/cuda.yml +++ b/.github/workflows/cuda.yml @@ -111,7 +111,7 @@ jobs: which nvcc || echo "nvcc not in PATH!" git clone https://github.com/AMReX-Codes/amrex.git ../amrex - cd ../amrex && git checkout --detach b98bdae9fb67e5d9aafc488de92c53001bd323ec && cd - + cd ../amrex && git checkout --detach 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 && cd - make COMP=gcc QED=FALSE USE_MPI=TRUE USE_GPU=TRUE USE_OMP=FALSE USE_PSATD=TRUE USE_CCACHE=TRUE -j 2 build_nvhpc21-11-nvcc: diff --git a/Regression/WarpX-GPU-tests.ini b/Regression/WarpX-GPU-tests.ini index 0e033f186b3..c20dc5ed93c 100644 --- a/Regression/WarpX-GPU-tests.ini +++ b/Regression/WarpX-GPU-tests.ini @@ -60,7 +60,7 @@ emailBody = Check https://ccse.lbl.gov/pub/GpuRegressionTesting/WarpX/ for more [AMReX] dir = /home/regtester/git/amrex/ -branch = b98bdae9fb67e5d9aafc488de92c53001bd323ec +branch = 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 [source] dir = /home/regtester/git/WarpX diff --git a/Regression/WarpX-tests.ini b/Regression/WarpX-tests.ini index 48364535760..56139d3da17 100644 --- a/Regression/WarpX-tests.ini +++ b/Regression/WarpX-tests.ini @@ -59,7 +59,7 @@ emailBody = Check https://ccse.lbl.gov/pub/RegressionTesting/WarpX/ for more det [AMReX] dir = /home/regtester/AMReX_RegTesting/amrex/ -branch = b98bdae9fb67e5d9aafc488de92c53001bd323ec +branch = 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 [source] dir = /home/regtester/AMReX_RegTesting/warpx diff --git a/cmake/dependencies/AMReX.cmake b/cmake/dependencies/AMReX.cmake index 8d873655fe0..fb80973c96b 100644 --- a/cmake/dependencies/AMReX.cmake +++ b/cmake/dependencies/AMReX.cmake @@ -257,7 +257,7 @@ set(WarpX_amrex_src "" set(WarpX_amrex_repo "https://github.com/AMReX-Codes/amrex.git" CACHE STRING "Repository URI to pull and build AMReX from if(WarpX_amrex_internal)") -set(WarpX_amrex_branch "b98bdae9fb67e5d9aafc488de92c53001bd323ec" +set(WarpX_amrex_branch "48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4" CACHE STRING "Repository branch for WarpX_amrex_repo if(WarpX_amrex_internal)") diff --git a/run_test.sh b/run_test.sh index 37b5d370667..e24095ee8cf 100755 --- a/run_test.sh +++ b/run_test.sh @@ -71,7 +71,7 @@ python3 -m pip install --upgrade -r warpx/Regression/requirements.txt # Clone AMReX and warpx-data git clone https://github.com/AMReX-Codes/amrex.git -cd amrex && git checkout --detach b98bdae9fb67e5d9aafc488de92c53001bd323ec && cd - +cd amrex && git checkout --detach 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 && cd - # warpx-data contains various required data sets git clone --depth 1 https://github.com/ECP-WarpX/warpx-data.git # openPMD-example-datasets contains various required data sets From 23a896d3c990eb4b7e86ebd8cb1fad723287e5c6 Mon Sep 17 00:00:00 2001 From: Remi Lehe Date: Wed, 20 Sep 2023 00:25:55 -0700 Subject: [PATCH 21/39] Prevent NaNs in Coulomb collision module (#4304) * Prevent NaNs in collisions between particles * Avoid another division by 0 t# Please enter the commit message for your changes. Lines starting --- .../Coulomb/UpdateMomentumPerezElastic.H | 343 +++++++++--------- 1 file changed, 176 insertions(+), 167 deletions(-) diff --git a/Source/Particles/Collision/BinaryCollision/Coulomb/UpdateMomentumPerezElastic.H b/Source/Particles/Collision/BinaryCollision/Coulomb/UpdateMomentumPerezElastic.H index 7c2432f6e1b..3101047e211 100644 --- a/Source/Particles/Collision/BinaryCollision/Coulomb/UpdateMomentumPerezElastic.H +++ b/Source/Particles/Collision/BinaryCollision/Coulomb/UpdateMomentumPerezElastic.H @@ -102,184 +102,193 @@ void UpdateMomentumPerezElastic ( T_PR const g1s = ( T_PR(1.0) - vcDv1*inv_c2 )*gc*g1; T_PR const g2s = ( T_PR(1.0) - vcDv2*inv_c2 )*gc*g2; - // Compute the Coulomb log lnLmd - T_PR lnLmd; - if ( L > T_PR(0.0) ) { lnLmd = L; } - else - { - // Compute b0 according to eq (22) from Perez et al., Phys.Plasmas.19.083104 (2012) - // Note: there is a typo in the equation, the last square is incorrect! - // See the SMILEI documentation: https://smileipic.github.io/Smilei/Understand/collisions.html - // and https://github.com/ECP-WarpX/WarpX/files/3799803/main.pdf from GitHub #429 - T_PR const b0 = amrex::Math::abs(q1*q2) * inv_c2 / - (T_PR(4.0)*MathConst::pi*PhysConst::ep0) * gc/mass_g * - ( m1*g1s*m2*g2s/(p1sm*p1sm*inv_c2) + T_PR(1.0) ); - - // Compute the minimal impact parameter - constexpr T_PR hbar_pi = static_cast(PhysConst::hbar*MathConst::pi); - const T_PR bmin = amrex::max(hbar_pi/p1sm, b0); - - // Compute the Coulomb log lnLmd - lnLmd = amrex::max( T_PR(2.0), - T_PR(0.5)*std::log(T_PR(1.0)+lmdD*lmdD/(bmin*bmin)) ); - } - // Compute s - const auto tts = m1*g1s*m2*g2s/(inv_c2*p1sm*p1sm) + T_PR(1.0); - const auto tts2 = tts*tts; - T_PR s = n1*n2/n12 * dt*lnLmd*q1*q1*q2*q2 / - ( T_PR(4.0) * MathConst::pi * PhysConst::ep0 * PhysConst::ep0 * - m1*g1*m2*g2/(inv_c2*inv_c2) ) * gc*p1sm/mass_g * tts2; - - // Compute s' - const auto cbrt_n1 = std::cbrt(n1); - const auto cbrt_n2 = std::cbrt(n2); - const auto coeff = static_cast( - std::pow(4.0*MathConst::pi/3.0,1.0/3.0)); - T_PR const vrel = mass_g*p1sm/(m1*g1s*m2*g2s*gc); - T_PR const sp = coeff * n1*n2/n12 * dt * vrel * (m1+m2) / - amrex::max( m1*cbrt_n1*cbrt_n1, - m2*cbrt_n2*cbrt_n2); - - // Determine s - s = amrex::min(s,sp); - - // Get random numbers - T_PR r = amrex::Random(engine); - - // Compute scattering angle - T_PR cosXs; - T_PR sinXs; - if ( s <= T_PR(0.1) ) - { - while ( true ) + T_PR s = 0; + if (p1sm > std::numeric_limits::min()) { + + // s is non-zero (i.e. particles scatter) only if the relative + // motion between particles is not negligible (p1sm non-zero) + + // Compute the Coulomb log lnLmd first + T_PR lnLmd; + if ( L > T_PR(0.0) ) { lnLmd = L; } + else { - cosXs = T_PR(1.0) + s * std::log(r); - // Avoid the bug when r is too small such that cosXs < -1 - if ( cosXs >= T_PR(-1.0) ) { break; } - r = amrex::Random(engine); + // Compute b0 according to eq (22) from Perez et al., Phys.Plasmas.19.083104 (2012) + // Note: there is a typo in the equation, the last square is incorrect! + // See the SMILEI documentation: https://smileipic.github.io/Smilei/Understand/collisions.html + // and https://github.com/ECP-WarpX/WarpX/files/3799803/main.pdf from GitHub #429 + T_PR const b0 = amrex::Math::abs(q1*q2) * inv_c2 / + (T_PR(4.0)*MathConst::pi*PhysConst::ep0) * gc/mass_g * + ( m1*g1s*m2*g2s/(p1sm*p1sm*inv_c2) + T_PR(1.0) ); + + // Compute the minimal impact parameter + constexpr T_PR hbar_pi = static_cast(PhysConst::hbar*MathConst::pi); + const T_PR bmin = amrex::max(hbar_pi/p1sm, b0); + + // Compute the Coulomb log lnLmd + lnLmd = amrex::max( T_PR(2.0), + T_PR(0.5)*std::log(T_PR(1.0)+lmdD*lmdD/(bmin*bmin)) ); } - } - else if ( s > T_PR(0.1) && s <= T_PR(3.0) ) - { - T_PR const Ainv = static_cast( - 0.0056958 + 0.9560202*s - 0.508139*s*s + - 0.47913906*s*s*s - 0.12788975*s*s*s*s + 0.02389567*s*s*s*s*s); - cosXs = Ainv * std::log( std::exp(T_PR(-1.0)/Ainv) + - T_PR(2.0) * r * std::sinh(T_PR(1.0)/Ainv) ); - } - else if ( s > T_PR(3.0) && s <= T_PR(6.0) ) - { - T_PR const A = T_PR(3.0) * std::exp(-s); - cosXs = T_PR(1.0)/A * std::log( std::exp(-A) + - T_PR(2.0) * r * std::sinh(A) ); - } - else - { - cosXs = T_PR(2.0) * r - T_PR(1.0); - } - sinXs = std::sqrt(T_PR(1.0) - cosXs*cosXs); - - // Get random azimuthal angle - T_PR const phis = amrex::Random(engine) * T_PR(2.0) * MathConst::pi; - T_PR const cosphis = std::cos(phis); - T_PR const sinphis = std::sin(phis); - - // Compute post-collision momenta pfs in COM - T_PR p1fsx; - T_PR p1fsy; - T_PR p1fsz; - // p1sp is the p1s perpendicular - T_PR p1sp = std::sqrt( p1sx*p1sx + p1sy*p1sy ); - // Make sure p1sp is not almost zero - if ( p1sp > std::numeric_limits::min() ) - { - p1fsx = ( p1sx*p1sz/p1sp ) * sinXs*cosphis + - ( p1sy*p1sm/p1sp ) * sinXs*sinphis + - ( p1sx ) * cosXs; - p1fsy = ( p1sy*p1sz/p1sp ) * sinXs*cosphis + - (-p1sx*p1sm/p1sp ) * sinXs*sinphis + - ( p1sy ) * cosXs; - p1fsz = (-p1sp ) * sinXs*cosphis + - ( T_PR(0.0) ) * sinXs*sinphis + - ( p1sz ) * cosXs; - // Note a negative sign is different from - // Eq. (12) in Perez's paper, - // but they are the same due to the random nature of phis. - } - else - { - // If the previous p1sp is almost zero - // x->y y->z z->x - // This set is equivalent to the one in Nanbu's paper - p1sp = std::sqrt( p1sy*p1sy + p1sz*p1sz ); - p1fsy = ( p1sy*p1sx/p1sp ) * sinXs*cosphis + - ( p1sz*p1sm/p1sp ) * sinXs*sinphis + - ( p1sy ) * cosXs; - p1fsz = ( p1sz*p1sx/p1sp ) * sinXs*cosphis + - (-p1sy*p1sm/p1sp ) * sinXs*sinphis + - ( p1sz ) * cosXs; - p1fsx = (-p1sp ) * sinXs*cosphis + - ( T_PR(0.0) ) * sinXs*sinphis + - ( p1sx ) * cosXs; - } - T_PR const p2fsx = -p1fsx; - T_PR const p2fsy = -p1fsy; - T_PR const p2fsz = -p1fsz; + // Compute s + const auto tts = m1*g1s*m2*g2s/(inv_c2*p1sm*p1sm) + T_PR(1.0); + const auto tts2 = tts*tts; + s = n1*n2/n12 * dt*lnLmd*q1*q1*q2*q2 / + ( T_PR(4.0) * MathConst::pi * PhysConst::ep0 * PhysConst::ep0 * + m1*g1*m2*g2/(inv_c2*inv_c2) ) * gc*p1sm/mass_g * tts2; - // Transform from COM to lab frame - T_PR p1fx; T_PR p2fx; - T_PR p1fy; T_PR p2fy; - T_PR p1fz; T_PR p2fz; - if ( vcms > std::numeric_limits::min() ) - { - T_PR const vcDp1fs = vcx*p1fsx + vcy*p1fsy + vcz*p1fsz; - T_PR const vcDp2fs = vcx*p2fsx + vcy*p2fsy + vcz*p2fsz; - /* factor = (gc-1.0)/vcms; Rewrite to avoid subtraction losing precision when gc is close to 1 */ - T_PR const factor = gc*gc*inv_c2/(gc+T_PR(1.0)); - T_PR const factor1 = factor*vcDp1fs + m1*g1s*gc; - T_PR const factor2 = factor*vcDp2fs + m2*g2s*gc; - p1fx = p1fsx + vcx * factor1; - p1fy = p1fsy + vcy * factor1; - p1fz = p1fsz + vcz * factor1; - p2fx = p2fsx + vcx * factor2; - p2fy = p2fsy + vcy * factor2; - p2fz = p2fsz + vcz * factor2; - } - else // If vcms = 0, don't do Lorentz-transform. - { - p1fx = p1fsx; - p1fy = p1fsy; - p1fz = p1fsz; - p2fx = p2fsx; - p2fy = p2fsy; - p2fz = p2fsz; - } + // Compute s' + const auto cbrt_n1 = std::cbrt(n1); + const auto cbrt_n2 = std::cbrt(n2); + const auto coeff = static_cast( + std::pow(4.0*MathConst::pi/3.0,1.0/3.0)); + T_PR const vrel = mass_g*p1sm/(m1*g1s*m2*g2s*gc); + T_PR const sp = coeff * n1*n2/n12 * dt * vrel * (m1+m2) / + amrex::max( m1*cbrt_n1*cbrt_n1, + m2*cbrt_n2*cbrt_n2); - // Rejection method - r = amrex::Random(engine); - if ( w2 > r*amrex::max(w1, w2) ) - { - u1x = p1fx / m1; - u1y = p1fy / m1; - u1z = p1fz / m1; -#ifndef AMREX_USE_DPCPP - AMREX_ASSERT(!std::isnan(u1x+u1y+u1z+u2x+u2y+u2z)); - AMREX_ASSERT(!std::isinf(u1x+u1y+u1z+u2x+u2y+u2z)); -#endif + // Determine s + s = amrex::min(s,sp); } - r = amrex::Random(engine); - if ( w1 > r*amrex::max(w1, w2) ) - { - u2x = p2fx / m2; - u2y = p2fy / m2; - u2z = p2fz / m2; + + // Only modify momenta if is s is non-zero + if (s > std::numeric_limits::min()) { + + // Get random numbers + T_PR r = amrex::Random(engine); + + // Compute scattering angle + T_PR cosXs; + T_PR sinXs; + if ( s <= T_PR(0.1) ) + { + while ( true ) + { + cosXs = T_PR(1.0) + s * std::log(r); + // Avoid the bug when r is too small such that cosXs < -1 + if ( cosXs >= T_PR(-1.0) ) { break; } + r = amrex::Random(engine); + } + } + else if ( s > T_PR(0.1) && s <= T_PR(3.0) ) + { + T_PR const Ainv = static_cast( + 0.0056958 + 0.9560202*s - 0.508139*s*s + + 0.47913906*s*s*s - 0.12788975*s*s*s*s + 0.02389567*s*s*s*s*s); + cosXs = Ainv * std::log( std::exp(T_PR(-1.0)/Ainv) + + T_PR(2.0) * r * std::sinh(T_PR(1.0)/Ainv) ); + } + else if ( s > T_PR(3.0) && s <= T_PR(6.0) ) + { + T_PR const A = T_PR(3.0) * std::exp(-s); + cosXs = T_PR(1.0)/A * std::log( std::exp(-A) + + T_PR(2.0) * r * std::sinh(A) ); + } + else + { + cosXs = T_PR(2.0) * r - T_PR(1.0); + } + sinXs = std::sqrt(T_PR(1.0) - cosXs*cosXs); + + // Get random azimuthal angle + T_PR const phis = amrex::Random(engine) * T_PR(2.0) * MathConst::pi; + T_PR const cosphis = std::cos(phis); + T_PR const sinphis = std::sin(phis); + + // Compute post-collision momenta pfs in COM + T_PR p1fsx; + T_PR p1fsy; + T_PR p1fsz; + // p1sp is the p1s perpendicular + T_PR p1sp = std::sqrt( p1sx*p1sx + p1sy*p1sy ); + // Make sure p1sp is not almost zero + if ( p1sp > std::numeric_limits::min() ) + { + p1fsx = ( p1sx*p1sz/p1sp ) * sinXs*cosphis + + ( p1sy*p1sm/p1sp ) * sinXs*sinphis + + ( p1sx ) * cosXs; + p1fsy = ( p1sy*p1sz/p1sp ) * sinXs*cosphis + + (-p1sx*p1sm/p1sp ) * sinXs*sinphis + + ( p1sy ) * cosXs; + p1fsz = (-p1sp ) * sinXs*cosphis + + ( T_PR(0.0) ) * sinXs*sinphis + + ( p1sz ) * cosXs; + // Note a negative sign is different from + // Eq. (12) in Perez's paper, + // but they are the same due to the random nature of phis. + } + else + { + // If the previous p1sp is almost zero + // x->y y->z z->x + // This set is equivalent to the one in Nanbu's paper + p1sp = std::sqrt( p1sy*p1sy + p1sz*p1sz ); + p1fsy = ( p1sy*p1sx/p1sp ) * sinXs*cosphis + + ( p1sz*p1sm/p1sp ) * sinXs*sinphis + + ( p1sy ) * cosXs; + p1fsz = ( p1sz*p1sx/p1sp ) * sinXs*cosphis + + (-p1sy*p1sm/p1sp ) * sinXs*sinphis + + ( p1sz ) * cosXs; + p1fsx = (-p1sp ) * sinXs*cosphis + + ( T_PR(0.0) ) * sinXs*sinphis + + ( p1sx ) * cosXs; + } + + T_PR const p2fsx = -p1fsx; + T_PR const p2fsy = -p1fsy; + T_PR const p2fsz = -p1fsz; + + // Transform from COM to lab frame + T_PR p1fx; T_PR p2fx; + T_PR p1fy; T_PR p2fy; + T_PR p1fz; T_PR p2fz; + if ( vcms > std::numeric_limits::min() ) + { + T_PR const vcDp1fs = vcx*p1fsx + vcy*p1fsy + vcz*p1fsz; + T_PR const vcDp2fs = vcx*p2fsx + vcy*p2fsy + vcz*p2fsz; + /* factor = (gc-1.0)/vcms; Rewrite to avoid subtraction losing precision when gc is close to 1 */ + T_PR const factor = gc*gc*inv_c2/(gc+T_PR(1.0)); + T_PR const factor1 = factor*vcDp1fs + m1*g1s*gc; + T_PR const factor2 = factor*vcDp2fs + m2*g2s*gc; + p1fx = p1fsx + vcx * factor1; + p1fy = p1fsy + vcy * factor1; + p1fz = p1fsz + vcz * factor1; + p2fx = p2fsx + vcx * factor2; + p2fy = p2fsy + vcy * factor2; + p2fz = p2fsz + vcz * factor2; + } + else // If vcms = 0, don't do Lorentz-transform. + { + p1fx = p1fsx; + p1fy = p1fsy; + p1fz = p1fsz; + p2fx = p2fsx; + p2fy = p2fsy; + p2fz = p2fsz; + } + + // Rejection method + r = amrex::Random(engine); + if ( w2 > r*amrex::max(w1, w2) ) + { + u1x = p1fx / m1; + u1y = p1fy / m1; + u1z = p1fz / m1; + } + r = amrex::Random(engine); + if ( w1 > r*amrex::max(w1, w2) ) + { + u2x = p2fx / m2; + u2y = p2fy / m2; + u2z = p2fz / m2; + } #ifndef AMREX_USE_DPCPP AMREX_ASSERT(!std::isnan(u1x+u1y+u1z+u2x+u2y+u2z)); AMREX_ASSERT(!std::isinf(u1x+u1y+u1z+u2x+u2y+u2z)); #endif - } + + } // if s > std::numeric_limits::min() } From 40e9c17fbac25e3836e69f376cb6a2a845621729 Mon Sep 17 00:00:00 2001 From: Luca Fedeli Date: Fri, 22 Sep 2023 01:14:11 +0900 Subject: [PATCH 22/39] Fix check of PML + Silver-Mueller compatibility (#4297) * fix check of PML + Silver Mueller compatibility * complete check * refactoring * fix logic bug --- Source/WarpX.cpp | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/Source/WarpX.cpp b/Source/WarpX.cpp index 508790ef20f..6857ad85bcf 100644 --- a/Source/WarpX.cpp +++ b/Source/WarpX.cpp @@ -871,26 +871,30 @@ WarpX::ReadParameters () quantum_xi_c2 = static_cast(quantum_xi * PhysConst::c * PhysConst::c); } - for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) { - WARPX_ALWAYS_ASSERT_WITH_MESSAGE( - !( - ( WarpX::field_boundary_lo[idim] == FieldBoundaryType::PML && - WarpX::field_boundary_lo[idim] == FieldBoundaryType::Absorbing_SilverMueller ) || - ( WarpX::field_boundary_hi[idim] == FieldBoundaryType::PML && - WarpX::field_boundary_hi[idim] == FieldBoundaryType::Absorbing_SilverMueller ) - ), - "PML and Silver-Mueller boundary conditions cannot be activated at the same time."); + const auto at_least_one_boundary_is_pml = + (std::any_of(WarpX::field_boundary_lo.begin(), WarpX::field_boundary_lo.end(), + [](const auto& cc){return cc == FieldBoundaryType::PML;}) + || + std::any_of(WarpX::field_boundary_hi.begin(), WarpX::field_boundary_hi.end(), + [](const auto& cc){return cc == FieldBoundaryType::PML;}) + ); + const auto at_least_one_boundary_is_silver_mueller = + (std::any_of(WarpX::field_boundary_lo.begin(), WarpX::field_boundary_lo.end(), + [](const auto& cc){return cc == FieldBoundaryType::Absorbing_SilverMueller;}) + || + std::any_of(WarpX::field_boundary_hi.begin(), WarpX::field_boundary_hi.end(), + [](const auto& cc){return cc == FieldBoundaryType::Absorbing_SilverMueller;}) + ); - if (WarpX::field_boundary_lo[idim] == FieldBoundaryType::Absorbing_SilverMueller || - WarpX::field_boundary_hi[idim] == FieldBoundaryType::Absorbing_SilverMueller) - { - // SilverMueller is implemented for Yee - WARPX_ALWAYS_ASSERT_WITH_MESSAGE( - electromagnetic_solver_id == ElectromagneticSolverAlgo::Yee, - "The Silver-Mueller boundary condition can only be used with the Yee solver."); - } - } + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + !(at_least_one_boundary_is_pml && at_least_one_boundary_is_silver_mueller), + "PML and Silver-Mueller boundary conditions cannot be activated at the same time."); + + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + (!at_least_one_boundary_is_silver_mueller) || + (electromagnetic_solver_id == ElectromagneticSolverAlgo::Yee), + "The Silver-Mueller boundary condition can only be used with the Yee solver."); utils::parser::queryWithParser(pp_warpx, "pml_ncell", pml_ncell); utils::parser::queryWithParser(pp_warpx, "pml_delta", pml_delta); From ef3fa0518975a5049fb2a5cf314d7b7c5b0832e7 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 21 Sep 2023 21:37:29 +0200 Subject: [PATCH 23/39] Lassen (LLNL): Sources & Builds Off-Home (#4311) Moving the source directories and build instructions for WarpX itself from `$HOME` to the per-user NFS directory `/usr/workspace/${USER}/lassen/src`, which has a larger default quota. --- Docs/source/install/hpc/lassen.rst | 18 ++++---- .../lassen-llnl/install_v100_dependencies.sh | 44 ++++++++++--------- Tools/machines/lassen-llnl/install_v100_ml.sh | 15 ++++--- .../lassen_v100_warpx.profile.example | 1 + 4 files changed, 42 insertions(+), 36 deletions(-) diff --git a/Docs/source/install/hpc/lassen.rst b/Docs/source/install/hpc/lassen.rst index 7b5630e0272..5726254652a 100644 --- a/Docs/source/install/hpc/lassen.rst +++ b/Docs/source/install/hpc/lassen.rst @@ -41,14 +41,14 @@ Use the following commands to download the WarpX source code: .. code-block:: bash - git clone https://github.com/ECP-WarpX/WarpX.git $HOME/src/warpx + git clone https://github.com/ECP-WarpX/WarpX.git /usr/workspace/${USER}/lassen/src/warpx We use system software modules, add environment hints and further dependencies via the file ``$HOME/lassen_v100_warpx.profile``. Create it now: .. code-block:: bash - cp $HOME/src/warpx/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example $HOME/lassen_v100_warpx.profile + cp /usr/workspace/${USER}/lassen/src/warpx/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example $HOME/lassen_v100_warpx.profile .. dropdown:: Script Details :color: light @@ -80,7 +80,7 @@ Finally, since lassen does not yet provide software modules for some of our depe .. code-block:: bash - bash $HOME/src/warpx/Tools/machines/lassen-llnl/install_v100_dependencies.sh + bash /usr/workspace/${USER}/lassen/src/warpx/Tools/machines/lassen-llnl/install_v100_dependencies.sh source /usr/workspace/${USER}/lassen/gpu/venvs/warpx-lassen/bin/activate .. dropdown:: Script Details @@ -99,7 +99,7 @@ Finally, since lassen does not yet provide software modules for some of our depe .. code-block:: bash - runNode bash $HOME/src/warpx/Tools/machines/lassen-llnl/install_v100_ml.sh + runNode bash /usr/workspace/${USER}/lassen/src/warpx/Tools/machines/lassen-llnl/install_v100_ml.sh .. dropdown:: Script Details :color: light @@ -113,7 +113,7 @@ Finally, since lassen does not yet provide software modules for some of our depe .. code-block:: bash - python3 -m pip install -r $HOME/src/warpx/Tools/optimas/requirements.txt + python3 -m pip install -r /usr/workspace/${USER}/lassen/src/warpx/Tools/optimas/requirements.txt .. _building-lassen-compilation: @@ -125,13 +125,13 @@ Use the following :ref:`cmake commands ` to compile the applicat .. code-block:: bash - cd $HOME/src/warpx + cd /usr/workspace/${USER}/lassen/src/warpx rm -rf build_lassen cmake -S . -B build_lassen -DWarpX_COMPUTE=CUDA -DWarpX_PSATD=ON -DWarpX_QED_TABLE_GEN=ON -DWarpX_DIMS="1;2;RZ;3" cmake --build build_lassen -j 8 -The WarpX application executables are now in ``$HOME/src/warpx/build_lassen/bin/``. +The WarpX application executables are now in ``/usr/workspace/${USER}/lassen/src/warpx/build_lassen/bin/``. Additionally, the following commands will install WarpX as a Python module: .. code-block:: bash @@ -155,7 +155,7 @@ If you already installed WarpX in the past and want to update it, start by getti .. code-block:: bash - cd $HOME/src/warpx + cd /usr/workspace/${USER}/lassen/src/warpx # read the output of this command - does it look ok? git status @@ -174,7 +174,7 @@ And, if needed, - log out and into the system, activate the now updated environment profile as usual, - :ref:`execute the dependency install scripts `. -As a last step, clean the build directory ``rm -rf $HOME/src/warpx/build_lassen`` and rebuild WarpX. +As a last step, clean the build directory ``rm -rf /usr/workspace/${USER}/lassen/src/warpx/build_lassen`` and rebuild WarpX. .. _running-cpp-lassen: diff --git a/Tools/machines/lassen-llnl/install_v100_dependencies.sh b/Tools/machines/lassen-llnl/install_v100_dependencies.sh index 21650f09ee0..223b41d1967 100755 --- a/Tools/machines/lassen-llnl/install_v100_dependencies.sh +++ b/Tools/machines/lassen-llnl/install_v100_dependencies.sh @@ -20,9 +20,11 @@ if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in yo # Remove old dependencies ##################################################### # +SRC_DIR="/usr/workspace/${USER}/lassen/src" SW_DIR="/usr/workspace/${USER}/lassen/gpu" rm -rf ${SW_DIR} mkdir -p ${SW_DIR} +mkdir -p ${SRC_DIR} # remove common user mistakes in python, located in .local instead of a venv python3 -m pip uninstall -qq -y pywarpx @@ -37,70 +39,70 @@ python3 -m pip uninstall -qqq -y mpi4py 2>/dev/null || true build_dir=$(mktemp -d) # c-blosc (I/O compression) -if [ -d $HOME/src/c-blosc ] +if [ -d ${SRC_DIR}/c-blosc ] then - cd $HOME/src/c-blosc + cd ${SRC_DIR}/c-blosc git fetch --prune git checkout v1.21.1 cd - else - git clone -b v1.21.1 https://github.com/Blosc/c-blosc.git $HOME/src/c-blosc + git clone -b v1.21.1 https://github.com/Blosc/c-blosc.git ${SRC_DIR}/c-blosc fi -cmake -S $HOME/src/c-blosc -B ${build_dir}/c-blosc-lassen-build -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DDEACTIVATE_AVX2=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/c-blosc-1.21.1 +cmake -S ${SRC_DIR}/c-blosc -B ${build_dir}/c-blosc-lassen-build -DBUILD_TESTS=OFF -DBUILD_BENCHMARKS=OFF -DDEACTIVATE_AVX2=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/c-blosc-1.21.1 cmake --build ${build_dir}/c-blosc-lassen-build --target install --parallel 10 # HDF5 -if [ -d $HOME/src/hdf5 ] +if [ -d ${SRC_DIR}/hdf5 ] then - cd $HOME/src/hdf5 + cd ${SRC_DIR}/hdf5 git fetch --prune git checkout hdf5-1_14_1-2 cd - else - git clone -b hdf5-1_14_1-2 https://github.com/HDFGroup/hdf5.git $HOME/src/hdf5 + git clone -b hdf5-1_14_1-2 https://github.com/HDFGroup/hdf5.git ${SRC_DIR}/hdf5 fi -cmake -S $HOME/src/hdf5 -B ${build_dir}/hdf5-lassen-build -DBUILD_TESTING=OFF -DHDF5_ENABLE_PARALLEL=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/hdf5-1.14.1.2 +cmake -S ${SRC_DIR}/hdf5 -B ${build_dir}/hdf5-lassen-build -DBUILD_TESTING=OFF -DHDF5_ENABLE_PARALLEL=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/hdf5-1.14.1.2 cmake --build ${build_dir}/hdf5-lassen-build --target install --parallel 10 # ADIOS2 -if [ -d $HOME/src/adios2 ] +if [ -d ${SRC_DIR}/adios2 ] then - cd $HOME/src/adios2 + cd ${SRC_DIR}/adios2 git fetch --prune git checkout v2.8.3 cd - else - git clone -b v2.8.3 https://github.com/ornladios/ADIOS2.git $HOME/src/adios2 + git clone -b v2.8.3 https://github.com/ornladios/ADIOS2.git ${SRC_DIR}/adios2 fi -cmake -S $HOME/src/adios2 -B ${build_dir}/adios2-lassen-build -DBUILD_TESTING=OFF -DADIOS2_BUILD_EXAMPLES=OFF -DADIOS2_USE_Blosc=ON -DADIOS2_USE_Fortran=OFF -DADIOS2_USE_Python=OFF -DADIOS2_USE_SST=OFF -DADIOS2_USE_ZeroMQ=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/adios2-2.8.3 +cmake -S ${SRC_DIR}/adios2 -B ${build_dir}/adios2-lassen-build -DBUILD_TESTING=OFF -DADIOS2_BUILD_EXAMPLES=OFF -DADIOS2_USE_Blosc=ON -DADIOS2_USE_Fortran=OFF -DADIOS2_USE_Python=OFF -DADIOS2_USE_SST=OFF -DADIOS2_USE_ZeroMQ=OFF -DCMAKE_INSTALL_PREFIX=${SW_DIR}/adios2-2.8.3 cmake --build ${build_dir}/adios2-lassen-build --target install -j 10 # BLAS++ (for PSATD+RZ) -if [ -d $HOME/src/blaspp ] +if [ -d ${SRC_DIR}/blaspp ] then - cd $HOME/src/blaspp + cd ${SRC_DIR}/blaspp git fetch --prune git checkout master git pull cd - else - git clone https://github.com/icl-utk-edu/blaspp.git $HOME/src/blaspp + git clone https://github.com/icl-utk-edu/blaspp.git ${SRC_DIR}/blaspp fi -cmake -S $HOME/src/blaspp -B ${build_dir}/blaspp-lassen-build -Duse_openmp=ON -Dgpu_backend=cuda -Duse_cmake_find_blas=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=${SW_DIR}/blaspp-master +cmake -S ${SRC_DIR}/blaspp -B ${build_dir}/blaspp-lassen-build -Duse_openmp=ON -Dgpu_backend=cuda -Duse_cmake_find_blas=ON -DCMAKE_CXX_STANDARD=17 -DCMAKE_INSTALL_PREFIX=${SW_DIR}/blaspp-master cmake --build ${build_dir}/blaspp-lassen-build --target install --parallel 10 # LAPACK++ (for PSATD+RZ) -if [ -d $HOME/src/lapackpp ] +if [ -d ${SRC_DIR}/lapackpp ] then - cd $HOME/src/lapackpp + cd ${SRC_DIR}/lapackpp git fetch --prune git checkout master git pull cd - else - git clone https://github.com/icl-utk-edu/lapackpp.git $HOME/src/lapackpp + git clone https://github.com/icl-utk-edu/lapackpp.git ${SRC_DIR}/lapackpp fi -CXXFLAGS="-DLAPACK_FORTRAN_ADD_" cmake -S ${HOME}/src/lapackpp -B ${build_dir}/lapackpp-lassen-build -Duse_cmake_find_lapack=ON -DCMAKE_CXX_STANDARD=17 -Dbuild_tests=OFF -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/lapackpp-master -DLAPACK_LIBRARIES=/usr/lib64/liblapack.so +CXXFLAGS="-DLAPACK_FORTRAN_ADD_" cmake -S ${SRC_DIR}/lapackpp -B ${build_dir}/lapackpp-lassen-build -Duse_cmake_find_lapack=ON -DCMAKE_CXX_STANDARD=17 -Dbuild_tests=OFF -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON -DCMAKE_INSTALL_PREFIX=${SW_DIR}/lapackpp-master -DLAPACK_LIBRARIES=/usr/lib64/liblapack.so cmake --build ${build_dir}/lapackpp-lassen-build --target install --parallel 10 @@ -124,7 +126,7 @@ echo "matplotlib==3.2.2" > ${build_dir}/constraints.txt python3 -m pip install --upgrade -c ${build_dir}/constraints.txt yt # install or update WarpX dependencies such as picmistandard -python3 -m pip install --upgrade -r $HOME/src/warpx/requirements.txt +python3 -m pip install --upgrade -r ${SRC_DIR}/warpx/requirements.txt # for ML dependencies, see install_v100_ml.sh diff --git a/Tools/machines/lassen-llnl/install_v100_ml.sh b/Tools/machines/lassen-llnl/install_v100_ml.sh index 2ff90adb521..6e00be035d6 100755 --- a/Tools/machines/lassen-llnl/install_v100_ml.sh +++ b/Tools/machines/lassen-llnl/install_v100_ml.sh @@ -20,6 +20,9 @@ if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in yo # Remove old dependencies ##################################################### # +SRC_DIR="/usr/workspace/${USER}/lassen/src" +mkdir -p ${SRC_DIR} + # remove common user mistakes in python, located in .local instead of a venv python3 -m pip uninstall -qqq -y torch 2>/dev/null || true @@ -29,21 +32,21 @@ python3 -m pip uninstall -qqq -y torch 2>/dev/null || true # for basic python dependencies, see install_v100_dependencies.sh # optional: for libEnsemble - WIP: issues with nlopt -# python3 -m pip install -r $HOME/src/warpx/Tools/LibEnsemble/requirements.txt +# python3 -m pip install -r ${SRC_DIR}/warpx/Tools/LibEnsemble/requirements.txt # optional: for pytorch -if [ -d ${HOME}/src/pytorch ] +if [ -d ${SRC_DIR}/pytorch ] then - cd ${HOME}/src/pytorch + cd ${SRC_DIR}/pytorch git fetch git checkout . git checkout v2.0.1 git submodule update --init --recursive cd - else - git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git ${HOME}/src/pytorch + git clone -b v2.0.1 --recurse-submodules https://github.com/pytorch/pytorch.git ${SRC_DIR}/pytorch fi -cd ${HOME}/src/pytorch +cd ${SRC_DIR}/pytorch rm -rf build # see https://github.com/pytorch/pytorch/issues/97497#issuecomment-1499069641 @@ -63,4 +66,4 @@ cd - # optional: optimas dependencies (based on libEnsemble & ax->botorch->gpytorch->pytorch) # commented because scikit-learn et al. compile > 2 hrs # please run manually on a login node if needed -#python3 -m pip install -r $HOME/src/warpx/Tools/optimas/requirements.txt +#python3 -m pip install -r ${SRC_DIR}/warpx/Tools/optimas/requirements.txt diff --git a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example index dd938e85c11..652af2a2822 100644 --- a/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example +++ b/Tools/machines/lassen-llnl/lassen_v100_warpx.profile.example @@ -10,6 +10,7 @@ module load cuda/12.0.0 module load boost/1.70.0 # optional: for openPMD support +SRC_DIR="/usr/workspace/${USER}/lassen/src" SW_DIR="/usr/workspace/${USER}/lassen/gpu" export CMAKE_PREFIX_PATH=${SW_DIR}/c-blosc-1.21.1:$CMAKE_PREFIX_PATH export CMAKE_PREFIX_PATH=${SW_DIR}/hdf5-1.14.1.2:$CMAKE_PREFIX_PATH From 9956295e22978fb10ce9bcfae334f5ecc815a4a5 Mon Sep 17 00:00:00 2001 From: Remi Lehe Date: Thu, 21 Sep 2023 18:08:44 -0700 Subject: [PATCH 24/39] Implement Galilean PML (#733) * Implement Galilean PML * Activate the PML * Add automated test * Fix compilation error * Fix more compilation errors * Fix more compilation error * Fix more compilation bugs * Fix more compilation bugs * Clean up code, debug CI * Fix CI * Use both collocated and staggered k vectors * Reset CI benchmark * Update test * Add Doxygen, clean up methods signature * Improve abort messages * Implement correct analytical equations * Remove class PsatdAlgorithmGalileanPml * Add Doxygen strings for class `PsatdAlgorithmPml` * Update clang tidy --------- Co-authored-by: Edoardo Zoni --- Examples/Tests/pml/analysis_pml_psatd.py | 29 +- .../benchmarks_json/pml_x_galilean.json | 12 + Regression/WarpX-tests.ini | 16 + Source/BoundaryConditions/PML.cpp | 8 +- .../SpectralAlgorithms/PsatdAlgorithmPml.H | 66 +++- .../SpectralAlgorithms/PsatdAlgorithmPml.cpp | 362 ++++++++++-------- .../SpectralSolver/SpectralSolver.cpp | 4 +- 7 files changed, 312 insertions(+), 185 deletions(-) create mode 100644 Regression/Checksum/benchmarks_json/pml_x_galilean.json diff --git a/Examples/Tests/pml/analysis_pml_psatd.py b/Examples/Tests/pml/analysis_pml_psatd.py index ca3d7cc3012..50d0b2ac1c1 100755 --- a/Examples/Tests/pml/analysis_pml_psatd.py +++ b/Examples/Tests/pml/analysis_pml_psatd.py @@ -8,6 +8,7 @@ # License: BSD-3-Clause-LBNL import os +import re import sys import numpy as np @@ -19,13 +20,18 @@ filename = sys.argv[1] -############################ -### INITIAL LASER ENERGY ### -############################ -energy_start = 7.282940107273505e-08 # electromagnetic energy at iteration 50 +galilean = True if re.search("galilean", filename) else False + +# Initial laser energy (at iteration 50) +if galilean: + filename_init = 'pml_x_galilean_plt000050' + energy_start = 4.439376199524034e-08 +else: + filename_init = 'pml_x_psatd_plt000050' + energy_start = 7.282940107273505e-08 # Check consistency of field energy diagnostics with initial energy above -ds = yt.load('pml_x_psatd_plt000050') +ds = yt.load(filename_init) all_data_level_0 = ds.covering_grid(level=0, left_edge=ds.domain_left_edge, dims=ds.domain_dimensions) Bx = all_data_level_0['boxlib', 'Bx'].v.squeeze() By = all_data_level_0['boxlib', 'By'].v.squeeze() @@ -43,10 +49,8 @@ print("relative error = " + str(error)) assert (error < tolerance) -########################## -### FINAL LASER ENERGY ### -########################## -ds = yt.load( filename ) +# Final laser energy +ds = yt.load(filename) all_data_level_0 = ds.covering_grid(level=0,left_edge=ds.domain_left_edge, dims=ds.domain_dimensions) Bx = all_data_level_0['boxlib', 'Bx'].v.squeeze() By = all_data_level_0['boxlib', 'By'].v.squeeze() @@ -58,8 +62,8 @@ energyB = np.sum(0.5 / scc.mu_0 * (Bx**2 + By**2 + Bz**2)) energy_end = energyE + energyB -reflectivity = energy_end / energy_start -reflectivity_max = 1e-06 +reflectivity = energy_end / energy_start_diags +reflectivity_max = 1e-6 print("reflectivity = " + str(reflectivity)) print("reflectivity_max = " + str(reflectivity_max)) @@ -70,7 +74,8 @@ sys.path.insert(0, '../../../../warpx/Examples/') from analysis_default_restart import check_restart -check_restart(filename) +if not galilean: + check_restart(filename) test_name = os.path.split(os.getcwd())[1] checksumAPI.evaluate_checksum(test_name, filename) diff --git a/Regression/Checksum/benchmarks_json/pml_x_galilean.json b/Regression/Checksum/benchmarks_json/pml_x_galilean.json new file mode 100644 index 00000000000..b32eb7a6518 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/pml_x_galilean.json @@ -0,0 +1,12 @@ +{ + "lev=0": { + "Bx": 9.111505955244123e-09, + "By": 1.743610723263872e-08, + "Bz": 8.692076437162089e-09, + "Ex": 4.8377358061392215, + "Ey": 3.8096703194246215, + "Ez": 4.747325170136437, + "divE": 480170.26408400893, + "rho": 1.7297829145620754e-06 + } +} \ No newline at end of file diff --git a/Regression/WarpX-tests.ini b/Regression/WarpX-tests.ini index 56139d3da17..a3a04f609b2 100644 --- a/Regression/WarpX-tests.ini +++ b/Regression/WarpX-tests.ini @@ -2754,6 +2754,22 @@ compileTest = 0 doVis = 0 analysisRoutine = Examples/Tests/pml/analysis_pml_ckc.py +[pml_x_galilean] +buildDir = . +inputFile = Examples/Tests/pml/inputs_2d +runtime_params = algo.maxwell_solver=psatd psatd.update_with_rho=1 warpx.do_dynamic_scheduling=0 diag1.fields_to_plot=Ex Ey Ez Bx By Bz rho divE warpx.cfl=0.7071067811865475 warpx.do_pml_dive_cleaning=1 warpx.do_pml_divb_cleaning=1 psatd.current_correction=0 warpx.abort_on_warning_threshold=medium psatd.v_galilean=0. 0. 0.99 warpx.grid_type=collocated +dim = 2 +addToCompileString = USE_PSATD=TRUE +cmakeSetupOpts = -DWarpX_DIMS=2 -DWarpX_PSATD=ON +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +analysisRoutine = Examples/Tests/pml/analysis_pml_psatd.py + [pml_x_psatd] buildDir = . inputFile = Examples/Tests/pml/inputs_2d diff --git a/Source/BoundaryConditions/PML.cpp b/Source/BoundaryConditions/PML.cpp index d34a2f73222..5fc3e2dd3dc 100644 --- a/Source/BoundaryConditions/PML.cpp +++ b/Source/BoundaryConditions/PML.cpp @@ -738,11 +738,11 @@ PML::PML (const int lev, const BoxArray& grid_ba, const DistributionMapping& gri const RealVect dx{AMREX_D_DECL(geom->CellSize(0), geom->CellSize(1), geom->CellSize(2))}; // Get the cell-centered box, with guard cells BoxArray realspace_ba = ba; // Copy box - amrex::Vector const v_galilean_zero = {0., 0., 0.}; + amrex::Vector const v_galilean = WarpX::GetInstance().m_v_galilean; amrex::Vector const v_comoving_zero = {0., 0., 0.}; realspace_ba.enclosedCells().grow(nge); // cell-centered + guard cells spectral_solver_fp = std::make_unique(lev, realspace_ba, dm, - nox_fft, noy_fft, noz_fft, grid_type, v_galilean_zero, + nox_fft, noy_fft, noz_fft, grid_type, v_galilean, v_comoving_zero, dx, dt, in_pml, periodic_single_box, update_with_rho, fft_do_time_averaging, psatd_solution_type, J_in_time, rho_in_time, m_dive_cleaning, m_divb_cleaning); #endif @@ -846,11 +846,11 @@ PML::PML (const int lev, const BoxArray& grid_ba, const DistributionMapping& gri const RealVect cdx{AMREX_D_DECL(cgeom->CellSize(0), cgeom->CellSize(1), cgeom->CellSize(2))}; // Get the cell-centered box, with guard cells BoxArray realspace_cba = cba; // Copy box - amrex::Vector const v_galilean_zero = {0., 0., 0.}; + amrex::Vector const v_galilean = WarpX::GetInstance().m_v_galilean; amrex::Vector const v_comoving_zero = {0., 0., 0.}; realspace_cba.enclosedCells().grow(nge); // cell-centered + guard cells spectral_solver_cp = std::make_unique(lev, realspace_cba, cdm, - nox_fft, noy_fft, noz_fft, grid_type, v_galilean_zero, + nox_fft, noy_fft, noz_fft, grid_type, v_galilean, v_comoving_zero, cdx, dt, in_pml, periodic_single_box, update_with_rho, fft_do_time_averaging, psatd_solution_type, J_in_time, rho_in_time, m_dive_cleaning, m_divb_cleaning); #endif diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.H b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.H index 7f50d1cd8ea..cef6331888e 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.H +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.H @@ -21,27 +21,59 @@ #if WARPX_USE_PSATD -/* \brief Class that updates the field in spectral space +/* + * \brief Class that updates the field in spectral space * and stores the coefficients of the corresponding update equation. */ class PsatdAlgorithmPml : public SpectralBaseAlgorithm { public: - PsatdAlgorithmPml(const SpectralKSpace& spectral_kspace, - const amrex::DistributionMapping& dm, - const SpectralFieldIndex& spectral_index, - int norder_x, int norder_y, - int norder_z, short grid_type, - amrex::Real dt, - bool dive_cleaning, - bool divb_cleaning); - void InitializeSpectralCoefficients( + /** + * \brief Constructor of the class PsatdAlgorithmPml + * + * \param[in] spectral_kspace Spectral space + * \param[in] dm Distribution mapping + * \param[in] spectral_index Object containing indices to access data in spectral space + * \param[in] norder_x Order of the spectral solver along x + * \param[in] norder_y Order of the spectral solver along y + * \param[in] norder_z Order of the spectral solver along z + * \param[in] grid_type Type of grid (collocated or not) + * \param[in] v_galilean Galilean velocity + * \param[in] dt Time step of the simulation + * \param[in] dive_cleaning Whether to use divergence correction for E (F term) + * \param[in] divb_cleaning Whether to use divergence correction for B (G term) + */ + PsatdAlgorithmPml( const SpectralKSpace& spectral_kspace, const amrex::DistributionMapping& dm, - amrex::Real dt); + const SpectralFieldIndex& spectral_index, + int norder_x, + int norder_y, + int norder_z, + short grid_type, + const amrex::Vector& v_galilean, + amrex::Real dt, + bool dive_cleaning, + bool divb_cleaning); + + /** + * \brief Initializes the coefficients used in \c pushSpectralFields + * to update the E and B fields + * + * \param[in] spectral_kspace Spectral space + * \param[in] dm Distribution mapping + */ + void InitializeSpectralCoefficients( + const SpectralKSpace& spectral_kspace, + const amrex::DistributionMapping& dm); - // Redefine functions from base class + /** + * \brief Updates the E and B fields in spectral space, + * according to the relevant PSATD equations + * + * \param[in,out] f All fields in spectral space + */ virtual void pushSpectralFields(SpectralFieldData& f) const override final; /** @@ -67,10 +99,20 @@ class PsatdAlgorithmPml : public SpectralBaseAlgorithm virtual void VayDeposition (SpectralFieldData& field_data) override final; private: + SpectralRealCoefficients C_coef, S_ck_coef, inv_k2_coef; + SpectralComplexCoefficients T2_coef; + // Centered modified finite-order k vectors + KVectorComponent modified_kx_vec_centered; +#if defined(WARPX_DIM_3D) + KVectorComponent modified_ky_vec_centered; +#endif + KVectorComponent modified_kz_vec_centered; + amrex::Vector m_v_galilean; amrex::Real m_dt; bool m_dive_cleaning; bool m_divb_cleaning; + bool m_is_galilean; }; #endif // WARPX_USE_PSATD diff --git a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.cpp b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.cpp index 1a4039915e0..be91d920cba 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralAlgorithms/PsatdAlgorithmPml.cpp @@ -9,6 +9,7 @@ #include "FieldSolver/SpectralSolver/SpectralFieldData.H" #include "FieldSolver/SpectralSolver/SpectralKSpace.H" #include "Utils/TextMsg.H" +#include "Utils/WarpXAlgorithmSelection.H" #include "Utils/WarpXConst.H" #include "Utils/WarpX_Complex.H" @@ -29,59 +30,85 @@ using namespace amrex; -/* \brief Initialize coefficients for the update equation */ -PsatdAlgorithmPml::PsatdAlgorithmPml(const SpectralKSpace& spectral_kspace, - const DistributionMapping& dm, - const SpectralFieldIndex& spectral_index, - const int norder_x, const int norder_y, - const int norder_z, const short grid_type, - const Real dt, - const bool dive_cleaning, const bool divb_cleaning) - // Initialize members of base class - : SpectralBaseAlgorithm(spectral_kspace, dm, spectral_index, norder_x, norder_y, norder_z, grid_type), - m_dt(dt), - m_dive_cleaning(dive_cleaning), - m_divb_cleaning(divb_cleaning) +PsatdAlgorithmPml::PsatdAlgorithmPml( + const SpectralKSpace& spectral_kspace, + const DistributionMapping& dm, + const SpectralFieldIndex& spectral_index, + int norder_x, + int norder_y, + int norder_z, + short grid_type, + const amrex::Vector& v_galilean, + Real dt, + bool dive_cleaning, + bool divb_cleaning) : + SpectralBaseAlgorithm(spectral_kspace, dm, spectral_index, norder_x, norder_y, norder_z, grid_type), + // Initialize the centered finite-order modified k vectors: + // these are computed always with the assumption of centered grids + // (argument grid_type=GridType::Collocated), for both collocated and staggered grids + modified_kx_vec_centered(spectral_kspace.getModifiedKComponent(dm, 0, norder_x, GridType::Collocated)), +#if defined(WARPX_DIM_3D) + modified_ky_vec_centered(spectral_kspace.getModifiedKComponent(dm, 1, norder_y, GridType::Collocated)), + modified_kz_vec_centered(spectral_kspace.getModifiedKComponent(dm, 2, norder_z, GridType::Collocated)), +#else + modified_kz_vec_centered(spectral_kspace.getModifiedKComponent(dm, 1, norder_z, GridType::Collocated)), +#endif + m_v_galilean(v_galilean), + m_dt(dt), + m_dive_cleaning(dive_cleaning), + m_divb_cleaning(divb_cleaning) { const BoxArray& ba = spectral_kspace.spectralspace_ba; - // Allocate the arrays of coefficients - C_coef = SpectralRealCoefficients(ba, dm, 1, 0); - S_ck_coef = SpectralRealCoefficients(ba, dm, 1, 0); + m_is_galilean = (v_galilean[0] != 0.) || (v_galilean[1] != 0.) || (v_galilean[2] != 0.); + + // Allocate arrays of coefficients + C_coef = SpectralRealCoefficients(ba, dm, 1, 0); + S_ck_coef = SpectralRealCoefficients(ba, dm, 1, 0); inv_k2_coef = SpectralRealCoefficients(ba, dm, 1, 0); - InitializeSpectralCoefficients(spectral_kspace, dm, dt); -} + // Allocate this coefficient only with Galilean PSATD + if (m_is_galilean) + { + T2_coef = SpectralComplexCoefficients(ba, dm, 1, 0); + } -/* Advance the E and B field in spectral space (stored in `f`) - * over one time step */ -void -PsatdAlgorithmPml::pushSpectralFields(SpectralFieldData& f) const { + InitializeSpectralCoefficients(spectral_kspace, dm); +} +void PsatdAlgorithmPml::pushSpectralFields(SpectralFieldData& f) const +{ const bool dive_cleaning = m_dive_cleaning; const bool divb_cleaning = m_divb_cleaning; + const bool is_galilean = m_is_galilean; const SpectralFieldIndex& Idx = m_spectral_index; // Loop over boxes - for (MFIter mfi(f.fields); mfi.isValid(); ++mfi){ - - const Box& bx = f.fields[mfi].box(); + for (amrex::MFIter mfi(f.fields); mfi.isValid(); ++mfi) + { + const amrex::Box& bx = f.fields[mfi].box(); // Extract arrays for the fields to be updated - const Array4 fields = f.fields[mfi].array(); + const amrex::Array4 fields = f.fields[mfi].array(); // Extract arrays for the coefficients - const Array4 C_arr = C_coef[mfi].array(); - const Array4 S_ck_arr = S_ck_coef[mfi].array(); - const Array4 inv_k2_arr = inv_k2_coef[mfi].array(); + const amrex::Array4 C_arr = C_coef[mfi].array(); + const amrex::Array4 S_ck_arr = S_ck_coef[mfi].array(); + const amrex::Array4 inv_k2_arr = inv_k2_coef[mfi].array(); + + amrex::Array4 T2_arr; + if (is_galilean) + { + T2_arr = T2_coef[mfi].array(); + } // Extract pointers for the k vectors - const Real* modified_kx_arr = modified_kx_vec[mfi].dataPtr(); + const amrex::Real* modified_kx_arr = modified_kx_vec[mfi].dataPtr(); #if defined(WARPX_DIM_3D) - const Real* modified_ky_arr = modified_ky_vec[mfi].dataPtr(); + const amrex::Real* modified_ky_arr = modified_ky_vec[mfi].dataPtr(); #endif - const Real* modified_kz_arr = modified_kz_vec[mfi].dataPtr(); + const amrex::Real* modified_kz_arr = modified_kz_vec[mfi].dataPtr(); const amrex::Real dt = m_dt; @@ -154,28 +181,29 @@ PsatdAlgorithmPml::pushSpectralFields(SpectralFieldData& f) const { } // k vector values, and coefficients - const Real kx = modified_kx_arr[i]; + const amrex::Real kx = modified_kx_arr[i]; #if defined(WARPX_DIM_3D) - const Real ky = modified_ky_arr[j]; - const Real kz = modified_kz_arr[k]; + const amrex::Real ky = modified_ky_arr[j]; + const amrex::Real kz = modified_kz_arr[k]; #else - constexpr Real ky = 0._rt; - const Real kz = modified_kz_arr[j]; + constexpr amrex::Real ky = 0._rt; + const amrex::Real kz = modified_kz_arr[j]; #endif - constexpr Real c2 = PhysConst::c * PhysConst::c; + constexpr amrex::Real c2 = PhysConst::c*PhysConst::c; const Complex I = Complex{0._rt, 1._rt}; - const Real kx2 = kx*kx; - const Real ky2 = ky*ky; - const Real kz2 = kz*kz; - const Real k_norm = std::sqrt(kx2 + ky2 + kz2); - - if (k_norm != 0._rt) { + const amrex::Real kx2 = kx*kx; + const amrex::Real ky2 = ky*ky; + const amrex::Real kz2 = kz*kz; + const amrex::Real knorm = std::sqrt(kx2 + ky2 + kz2); + if (knorm != 0._rt) + { const amrex::Real C = C_arr(i,j,k); const amrex::Real S_ck = S_ck_arr(i,j,k); const amrex::Real inv_k2 = inv_k2_arr(i,j,k); + const Complex T2 = (is_galilean) ? T2_arr(i,j,k) : 1.0_rt; const amrex::Real C1 = (kx2 * C + ky2 + kz2) * inv_k2; const amrex::Real C2 = (kx2 + ky2 * C + kz2) * inv_k2; @@ -218,42 +246,42 @@ PsatdAlgorithmPml::pushSpectralFields(SpectralFieldData& f) const { const Complex C22_c2 = C22 / c2; // Update E - fields(i,j,k,Idx.Exy) = C2 * Exy + C5 * Exz + C9 * Ey - + C10 * Bx + C11 * By + C19 * Bz; + fields(i,j,k,Idx.Exy) = T2 * (C2 * Exy + C5 * Exz + C9 * Ey + + C10 * Bx + C11 * By + C19 * Bz); - fields(i,j,k,Idx.Exz) = C6 * Exy + C3 * Exz + C8 * Ez - - C10 * Bx - C22 * By - C12 * Bz; + fields(i,j,k,Idx.Exz) = T2 * (C6 * Exy + C3 * Exz + C8 * Ez + - C10 * Bx - C22 * By - C12 * Bz); - fields(i,j,k,Idx.Eyz) = C3 * Eyz + C6 * Eyx + C7 * Ez - + C21 * Bx + C10 * By + C13 * Bz; + fields(i,j,k,Idx.Eyz) = T2 * (C3 * Eyz + C6 * Eyx + C7 * Ez + + C21 * Bx + C10 * By + C13 * Bz); - fields(i,j,k,Idx.Eyx) = C9 * Ex + C4 * Eyz + C1 * Eyx - - C14 * Bx - C10 * By - C18 * Bz; + fields(i,j,k,Idx.Eyx) = T2 * (C9 * Ex + C4 * Eyz + C1 * Eyx + - C14 * Bx - C10 * By - C18 * Bz); - fields(i,j,k,Idx.Ezx) = C8 * Ex + C1 * Ezx + C4 * Ezy - + C15 * Bx + C17 * By + C10 * Bz; + fields(i,j,k,Idx.Ezx) = T2 * (C8 * Ex + C1 * Ezx + C4 * Ezy + + C15 * Bx + C17 * By + C10 * Bz); - fields(i,j,k,Idx.Ezy) = C7 * Ey + C5 * Ezx + C2 * Ezy - - C20 * Bx - C16 * By - C10 * Bz; + fields(i,j,k,Idx.Ezy) = T2 * (C7 * Ey + C5 * Ezx + C2 * Ezy + - C20 * Bx - C16 * By - C10 * Bz); // Update B - fields(i,j,k,Idx.Bxy) = C2 * Bxy + C5 * Bxz + C9 * By - - C10_c2 * Ex - C11_c2 * Ey - C19_c2 * Ez; + fields(i,j,k,Idx.Bxy) = T2 * (C2 * Bxy + C5 * Bxz + C9 * By + - C10_c2 * Ex - C11_c2 * Ey - C19_c2 * Ez); - fields(i,j,k,Idx.Bxz) = C6 * Bxy + C3 * Bxz + C8 * Bz - + C10_c2 * Ex + C22_c2 * Ey + C12_c2 * Ez; + fields(i,j,k,Idx.Bxz) = T2 * (C6 * Bxy + C3 * Bxz + C8 * Bz + + C10_c2 * Ex + C22_c2 * Ey + C12_c2 * Ez); - fields(i,j,k,Idx.Byz) = C3 * Byz + C6 * Byx + C7 * Bz - - C21_c2 * Ex - C10_c2 * Ey - C13_c2 * Ez; + fields(i,j,k,Idx.Byz) = T2 * (C3 * Byz + C6 * Byx + C7 * Bz + - C21_c2 * Ex - C10_c2 * Ey - C13_c2 * Ez); - fields(i,j,k,Idx.Byx) = C9 * Bx + C4 * Byz + C1 * Byx - + C14_c2 * Ex + C10_c2 * Ey + C18_c2 * Ez; + fields(i,j,k,Idx.Byx) = T2 * (C9 * Bx + C4 * Byz + C1 * Byx + + C14_c2 * Ex + C10_c2 * Ey + C18_c2 * Ez); - fields(i,j,k,Idx.Bzx) = C8 * Bx + C1 * Bzx + C4 * Bzy - - C15_c2 * Ex - C17_c2 * Ey - C10_c2 * Ez; + fields(i,j,k,Idx.Bzx) = T2 * (C8 * Bx + C1 * Bzx + C4 * Bzy + - C15_c2 * Ex - C17_c2 * Ey - C10_c2 * Ez); - fields(i,j,k,Idx.Bzy) = C7 * By + C5 * Bzx + C2 * Bzy - + C20_c2 * Ex + C16_c2 * Ey + C10_c2 * Ez; + fields(i,j,k,Idx.Bzy) = T2 * (C7 * By + C5 * Bzx + C2 * Bzy + + C20_c2 * Ex + C16_c2 * Ey + C10_c2 * Ez); } else if (dive_cleaning && divb_cleaning) { @@ -266,80 +294,80 @@ PsatdAlgorithmPml::pushSpectralFields(SpectralFieldData& f) const { const Complex C25_c2 = C25 / c2; // Update E - fields(i,j,k,Idx.Exx) = C1 * Exx + C4 * Exy + C4 * Exz - - C9 * Ey - C8 * Ez + C23 * F; + fields(i,j,k,Idx.Exx) = T2 * (C1 * Exx + C4 * Exy + C4 * Exz + - C9 * Ey - C8 * Ez + C23 * F); - fields(i,j,k,Idx.Exy) = C5 * Exx + C2 * Exy + C5 * Exz - + C9 * Ey + C24 * Bz - C7 * G; + fields(i,j,k,Idx.Exy) = T2 * (C5 * Exx + C2 * Exy + C5 * Exz + + C9 * Ey + C24 * Bz - C7 * G); - fields(i,j,k,Idx.Exz) = C6 * Exx + C6 * Exy + C3 * Exz - + C8 * Ez - C25 * By + C7 * G; + fields(i,j,k,Idx.Exz) = T2 * (C6 * Exx + C6 * Exy + C3 * Exz + + C8 * Ez - C25 * By + C7 * G); - fields(i,j,k,Idx.Eyx) = C9 * Ex + C1 * Eyx + C4 * Eyy - + C4 * Eyz - C23 * Bz + C8 * G; + fields(i,j,k,Idx.Eyx) = T2 * (C9 * Ex + C1 * Eyx + C4 * Eyy + + C4 * Eyz - C23 * Bz + C8 * G); - fields(i,j,k,Idx.Eyy) = - C9 * Ex + C5 * Eyx + C2 * Eyy - + C5 * Eyz - C7 * Ez + C24 * F; + fields(i,j,k,Idx.Eyy) = T2 * (- C9 * Ex + C5 * Eyx + C2 * Eyy + + C5 * Eyz - C7 * Ez + C24 * F); - fields(i,j,k,Idx.Eyz) = C6 * Eyx + C6 * Eyy + C3 * Eyz - + C7 * Ez + C25 * Bx - C8 * G; + fields(i,j,k,Idx.Eyz) = T2 * (C6 * Eyx + C6 * Eyy + C3 * Eyz + + C7 * Ez + C25 * Bx - C8 * G); - fields(i,j,k,Idx.Ezx) = C8 * Ex + C1 * Ezx + C4 * Ezy - + C4 * Ezz + C23 * By - C9 * G; + fields(i,j,k,Idx.Ezx) = T2 * (C8 * Ex + C1 * Ezx + C4 * Ezy + + C4 * Ezz + C23 * By - C9 * G); - fields(i,j,k,Idx.Ezy) = C7 * Ey + C5 * Ezx + C2 * Ezy - + C5 * Ezz - C24 * Bx + C9 * G; + fields(i,j,k,Idx.Ezy) = T2 * (C7 * Ey + C5 * Ezx + C2 * Ezy + + C5 * Ezz - C24 * Bx + C9 * G); - fields(i,j,k,Idx.Ezz) = - C8 * Ex - C7 * Ey + C6 * Ezx - + C6 * Ezy + C3 * Ezz + C25 * F; + fields(i,j,k,Idx.Ezz) = T2 * (- C8 * Ex - C7 * Ey + C6 * Ezx + + C6 * Ezy + C3 * Ezz + C25 * F); // Update B - fields(i,j,k,Idx.Bxx) = C1 * Bxx + C4 * Bxy + C4 * Bxz - - C9 * By - C8 * Bz + C23_c2 * G; + fields(i,j,k,Idx.Bxx) = T2 * (C1 * Bxx + C4 * Bxy + C4 * Bxz + - C9 * By - C8 * Bz + C23_c2 * G); - fields(i,j,k,Idx.Bxy) = - C24_c2 * Ez + C5 * Bxx + C2 * Bxy - + C5 * Bxz + C9 * By + C7 * F; + fields(i,j,k,Idx.Bxy) = T2 * (- C24_c2 * Ez + C5 * Bxx + C2 * Bxy + + C5 * Bxz + C9 * By + C7 * F); - fields(i,j,k,Idx.Bxz) = C25_c2 * Ey + C6 * Bxx + C6 * Bxy - + C3 * Bxz + C8 * Bz - C7 * F; + fields(i,j,k,Idx.Bxz) = T2 * (C25_c2 * Ey + C6 * Bxx + C6 * Bxy + + C3 * Bxz + C8 * Bz - C7 * F); - fields(i,j,k,Idx.Byx) = C23_c2 * Ez + C9 * Bx + C1 * Byx - + C4 * Byy + C4 * Byz - C8 * F; + fields(i,j,k,Idx.Byx) = T2 * (C23_c2 * Ez + C9 * Bx + C1 * Byx + + C4 * Byy + C4 * Byz - C8 * F); - fields(i,j,k,Idx.Byy) = - C9 * Bx + C5 * Byx + C2 * Byy - + C5 * Byz - C7 * Bz + C24_c2 * G; + fields(i,j,k,Idx.Byy) = T2 * (- C9 * Bx + C5 * Byx + C2 * Byy + + C5 * Byz - C7 * Bz + C24_c2 * G); - fields(i,j,k,Idx.Byz) = - C25_c2 * Ex + C6 * Byx + C6 * Byy - + C3 * Byz + C7 * Bz + C8 * F; + fields(i,j,k,Idx.Byz) = T2 * (- C25_c2 * Ex + C6 * Byx + C6 * Byy + + C3 * Byz + C7 * Bz + C8 * F); - fields(i,j,k,Idx.Bzx) = - C23_c2 * Ey + C8 * Bx + C1 * Bzx - + C4 * Bzy + C4 * Bzz + C9 * F; + fields(i,j,k,Idx.Bzx) = T2 * (- C23_c2 * Ey + C8 * Bx + C1 * Bzx + + C4 * Bzy + C4 * Bzz + C9 * F); - fields(i,j,k,Idx.Bzy) = C24_c2 * Ex + C7 * By + C5 * Bzx - + C2 * Bzy + C5 * Bzz - C9 * F; + fields(i,j,k,Idx.Bzy) = T2 * (C24_c2 * Ex + C7 * By + C5 * Bzx + + C2 * Bzy + C5 * Bzz - C9 * F); - fields(i,j,k,Idx.Bzz) = - C8 * Bx - C7 * By + C6 * Bzx - + C6 * Bzy + C3 * Bzz + C25_c2 * G; + fields(i,j,k,Idx.Bzz) = T2 * (- C8 * Bx - C7 * By + C6 * Bzx + + C6 * Bzy + C3 * Bzz + C25_c2 * G); // Update F - fields(i,j,k,Idx.Fx) = C23_c2 * Ex + C8 * By - C9 * Bz - + C1 * Fx + C4 * Fy + C4 * Fz; + fields(i,j,k,Idx.Fx) = T2 * (C23_c2 * Ex + C8 * By - C9 * Bz + + C1 * Fx + C4 * Fy + C4 * Fz); - fields(i,j,k,Idx.Fy) = C24_c2 * Ey - C7 * Bx + C9 * Bz - + C5 * Fx + C2 * Fy + C5 * Fz; + fields(i,j,k,Idx.Fy) = T2 * (C24_c2 * Ey - C7 * Bx + C9 * Bz + + C5 * Fx + C2 * Fy + C5 * Fz); - fields(i,j,k,Idx.Fz) = C25_c2 * Ez + C7 * Bx - C8 * By - + C6 * Fx + C6 * Fy + C3 * Fz; + fields(i,j,k,Idx.Fz) = T2 * (C25_c2 * Ez + C7 * Bx - C8 * By + + C6 * Fx + C6 * Fy + C3 * Fz); // Update G - fields(i,j,k,Idx.Gx) = - C8 * Ey + C9 * Ez + C23 * Bx - + C1 * Gx + C4 * Gy + C4 * Gz; + fields(i,j,k,Idx.Gx) = T2 * (- C8 * Ey + C9 * Ez + C23 * Bx + + C1 * Gx + C4 * Gy + C4 * Gz); - fields(i,j,k,Idx.Gy) = C7 * Ex - C9 * Ez + C24 * By - + C5 * Gx + C2 * Gy + C5 * Gz; + fields(i,j,k,Idx.Gy) = T2 * (C7 * Ex - C9 * Ez + C24 * By + + C5 * Gx + C2 * Gy + C5 * Gz); - fields(i,j,k,Idx.Gz) = - C7 * Ex + C8 * Ey + C25 * Bz - + C6 * Gx + C6 * Gy + C3 * Gz; + fields(i,j,k,Idx.Gz) = T2 * (- C7 * Ex + C8 * Ey + C25 * Bz + + C6 * Gx + C6 * Gy + C3 * Gz); } } }); @@ -348,71 +376,95 @@ PsatdAlgorithmPml::pushSpectralFields(SpectralFieldData& f) const { void PsatdAlgorithmPml::InitializeSpectralCoefficients ( const SpectralKSpace& spectral_kspace, - const amrex::DistributionMapping& dm, - const amrex::Real dt) + const amrex::DistributionMapping& dm) { - const BoxArray& ba = spectral_kspace.spectralspace_ba; + const amrex::Real dt = m_dt; + const bool is_galilean = m_is_galilean; - // 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) { + const amrex::BoxArray& ba = spectral_kspace.spectralspace_ba; - const Box& bx = ba[mfi]; + // Loop over boxes and allocate the corresponding coefficients + // for each box owned by the local MPI process + for (amrex::MFIter mfi(ba, dm); mfi.isValid(); ++mfi) + { + const amrex::Box& bx = ba[mfi]; // Extract pointers for the k vectors - const Real* modified_kx = modified_kx_vec[mfi].dataPtr(); + const amrex::Real* kx = modified_kx_vec[mfi].dataPtr(); + const amrex::Real* kx_c = modified_kx_vec_centered[mfi].dataPtr(); #if defined(WARPX_DIM_3D) - const Real* modified_ky = modified_ky_vec[mfi].dataPtr(); + const amrex::Real* ky = modified_ky_vec[mfi].dataPtr(); + const amrex::Real* ky_c = modified_ky_vec_centered[mfi].dataPtr(); #endif - const Real* modified_kz = modified_kz_vec[mfi].dataPtr(); + const amrex::Real* kz = modified_kz_vec[mfi].dataPtr(); + const amrex::Real* kz_c = modified_kz_vec_centered[mfi].dataPtr(); // Extract arrays for the coefficients - const Array4 C = C_coef[mfi].array(); - const Array4 S_ck = S_ck_coef[mfi].array(); - const Array4 inv_k2 = inv_k2_coef[mfi].array(); + const amrex::Array4 C = C_coef[mfi].array(); + const amrex::Array4 S_ck = S_ck_coef[mfi].array(); + const amrex::Array4 inv_k2 = inv_k2_coef[mfi].array(); - // Loop over indices within one box - ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + amrex::Array4 T2; + if (is_galilean) { - const Real kx = modified_kx[i]; + T2 = T2_coef[mfi].array(); + } + + // Extract Galilean velocity + amrex::Real vg_x = m_v_galilean[0]; #if defined(WARPX_DIM_3D) - const Real ky = modified_ky[j]; - const Real kz = modified_kz[k]; -#else - constexpr Real ky = 0._rt; - const Real kz = modified_kz[j]; + amrex::Real vg_y = m_v_galilean[1]; #endif + amrex::Real vg_z = m_v_galilean[2]; + // Loop over indices within one box + ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { // Calculate norm of vector - const Real k_norm = std::sqrt(kx*kx + ky*ky + kz*kz); - const Real k2 = k_norm * k_norm; + const amrex::Real knorm = std::sqrt( + amrex::Math::powi<2>(kx[i]) + +#if (AMREX_SPACEDIM==3) + amrex::Math::powi<2>(ky[j]) + amrex::Math::powi<2>(kz[k])); +#else + amrex::Math::powi<2>(kz[j])); +#endif + // Calculate the dot product of the k vector with the Galilean velocity. + // This has to be computed always with the centered (collocated) finite-order + // modified k vectors, to work correctly for both collocated and staggered grids. + // w_c = 0 always with standard PSATD (zero Galilean velocity). + const amrex::Real w_c = kx_c[i]*vg_x + +#if (AMREX_SPACEDIM==3) + ky_c[j]*vg_y + kz_c[k]*vg_z; +#else + kz_c[j]*vg_z; +#endif + constexpr amrex::Real c = PhysConst::c; + constexpr Complex I{0._rt,1._rt}; - // Calculate coefficients - constexpr Real c = PhysConst::c; + // Coefficients for knorm = 0 do not need to be set + if (knorm != 0._rt) + { + C(i,j,k) = std::cos(c*knorm*dt); + S_ck(i,j,k) = std::sin(c*knorm*dt) / (c*knorm); + inv_k2(i,j,k) = 1._rt / (knorm*knorm); - // Coefficients for k_norm = 0 do not need to be set - if (k_norm != 0._rt) { - C(i,j,k) = std::cos(c * k_norm * dt); - S_ck(i,j,k) = std::sin(c * k_norm * dt) / (c * k_norm); - inv_k2(i,j,k) = 1._rt / k2; + if (is_galilean) + { + T2(i,j,k) = amrex::exp(I*w_c*dt); + } } }); } } -void -PsatdAlgorithmPml::CurrentCorrection (SpectralFieldData& /*field_data*/) +void PsatdAlgorithmPml::CurrentCorrection (SpectralFieldData& /*field_data*/) { - WARPX_ABORT_WITH_MESSAGE( - "Current correction not implemented for PML PSATD"); + WARPX_ABORT_WITH_MESSAGE("Current correction not implemented for PML PSATD"); } -void -PsatdAlgorithmPml::VayDeposition (SpectralFieldData& /*field_data*/) +void PsatdAlgorithmPml::VayDeposition (SpectralFieldData& /*field_data*/) { - WARPX_ABORT_WITH_MESSAGE( - "Vay deposition not implemented for PML PSATD"); + WARPX_ABORT_WITH_MESSAGE("Vay deposition not implemented for PML PSATD"); } #endif // WARPX_USE_PSATD diff --git a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp index 362af06a354..80fb2c39545 100644 --- a/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp +++ b/Source/FieldSolver/SpectralSolver/SpectralSolver.cpp @@ -52,11 +52,11 @@ SpectralSolver::SpectralSolver( // - Select the algorithm depending on the input parameters // Initialize the corresponding coefficients over k space - if (pml) // PSATD equations in the PML region + if (pml) // PSATD or Galilean PSATD equations in the PML region { algorithm = std::make_unique( k_space, dm, m_spectral_index, norder_x, norder_y, norder_z, grid_type, - dt, dive_cleaning, divb_cleaning); + v_galilean, dt, dive_cleaning, divb_cleaning); } else // PSATD equations in the regular domain { From db87b8685f89a011f81b323f12d2391554aaa68d Mon Sep 17 00:00:00 2001 From: Luca Fedeli Date: Sat, 23 Sep 2023 00:27:50 +0900 Subject: [PATCH 25/39] Clang tidy CI test: add almost all misc-* checks (#4268) --- .clang-tidy | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 5a3deadf304..42f4fc36065 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -17,15 +17,9 @@ Checks: '-*, google-build-explicit-make-pair, google-build-namespaces, google-global-names-in-headers, - misc-const-correctness, - misc-definitions-in-headers, - misc-misleading-bidirectional, - misc-misleading-identifier, - misc-misplaced-const, - misc-uniqueptr-reset-release, - misc-unused-alias-decls, - misc-unused-parameters, - misc-unused-using-decls, + misc-*, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, modernize-avoid-bind, modernize-concat-nested-namespaces, modernize-deprecated-headers, From a471915fdd1df3154b316650dfeff54f4f0a1525 Mon Sep 17 00:00:00 2001 From: David Grote Date: Sat, 23 Sep 2023 10:43:51 -0700 Subject: [PATCH 26/39] Use cupy when available with the particle container wrapper (#4203) * Use cupy when available * cupy helper & copy-to-host helper * cupy helper & copy-to-host helper * Fix get_species_charge_sum (revert) * Import Updates * Pass copy_to_host through the particle gather routines * Add documentation for the Python particle gather routines * Fix fetch of particle theta, passing copy_to_host, and use numpy when copy to host * Avoid cupy for copy_to_host - explicit copy in AMReX possible - this way, we can support AoS data copies * Add cupy struct reference --------- Co-authored-by: Axel Huebl --- Python/pywarpx/LoadThirdParty.py | 35 +++ Python/pywarpx/__init__.py | 1 + Python/pywarpx/particle_containers.py | 363 ++++++++++++++++++++------ 3 files changed, 323 insertions(+), 76 deletions(-) create mode 100644 Python/pywarpx/LoadThirdParty.py diff --git a/Python/pywarpx/LoadThirdParty.py b/Python/pywarpx/LoadThirdParty.py new file mode 100644 index 00000000000..ea62d558eeb --- /dev/null +++ b/Python/pywarpx/LoadThirdParty.py @@ -0,0 +1,35 @@ +# This file is part of WarpX. +# +# Authors: Axel Huebl +# License: BSD-3-Clause-LBNL + +from ._libwarpx import libwarpx + + +def load_cupy(): + """ + This is a helper for + https://docs.cupy.dev/en/stable/user_guide/basic.html + """ + amr = libwarpx.amr + status = None + + if amr.Config.have_gpu: + try: + import cupy as cp + xp = cp + # Note: found and will use cupy + except ImportError: + status = "Warning: GPU found but cupy not available! Trying managed memory in numpy..." + import numpy as np + xp = np + if amr.Config.gpu_backend == "SYCL": + status = "Warning: SYCL GPU backend not yet implemented for Python" + import numpy as np + xp = np + + else: + import numpy as np + xp = np + # Note: found and will use numpy + return xp, status diff --git a/Python/pywarpx/__init__.py b/Python/pywarpx/__init__.py index 8b57f60fb79..f89926399ca 100644 --- a/Python/pywarpx/__init__.py +++ b/Python/pywarpx/__init__.py @@ -34,6 +34,7 @@ from .HybridPICModel import hybridpicmodel from .Interpolation import interpolation from .Lasers import lasers +from .LoadThirdParty import load_cupy from .PSATD import psatd from .Particles import newspecies, particles from .WarpX import warpx diff --git a/Python/pywarpx/particle_containers.py b/Python/pywarpx/particle_containers.py index aed05121668..c74549bcdb7 100644 --- a/Python/pywarpx/particle_containers.py +++ b/Python/pywarpx/particle_containers.py @@ -2,12 +2,13 @@ # # This file is part of WarpX. # -# Authors: David Grote, Roelof Groenewald +# Authors: David Grote, Roelof Groenewald, Axel Huebl # # License: BSD-3-Clause-LBNL import numpy as np +from .LoadThirdParty import load_cupy from ._libwarpx import libwarpx @@ -183,40 +184,63 @@ def add_real_comp(self, pid_name, comm=True): ''' self.particle_container.add_real_comp(pid_name, comm) - def get_particle_structs(self, level): + def get_particle_structs(self, level, copy_to_host=False): ''' - This returns a list of numpy arrays containing the particle struct data + This returns a list of numpy or cupy arrays containing the particle struct data on each tile for this process. The particle data is represented as a structured - numpy array and contains the particle 'x', 'y', 'z', and 'idcpu'. + array and contains the particle 'x', 'y', 'z', and 'idcpu'. - The data for the numpy arrays are not copied, but share the underlying - memory buffer with WarpX. The numpy arrays are fully writeable. + Unless copy_to_host is specified, the data for the structs are + not copied, but share the underlying memory buffer with WarpX. The + arrays are fully writeable. + + Note that cupy does not support structs: + https://github.com/cupy/cupy/issues/2031 + and will return arrays of binary blobs for the AoS (DP: "|V24"). If copied + to host via copy_to_host, we correct for the right numpy AoS type. Parameters ---------- level : int - The refinement level to reference + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. Returns ------- - List of numpy arrays + List of arrays The requested particle struct data ''' particle_data = [] for pti in libwarpx.libwarpx_so.WarpXParIter(self.particle_container, level): - aos_arr = np.array(pti.aos(), copy=False) - particle_data.append(aos_arr) + if copy_to_host: + particle_data.append(pti.aos().to_numpy(copy=True)) + else: + if libwarpx.amr.Config.have_gpu: + libwarpx.amr.Print( + "get_particle_structs: cupy does not yet support structs. " + "https://github.com/cupy/cupy/issues/2031" + "Did you mean copy_to_host=True?" + ) + xp, cupy_status = load_cupy() + if cupy_status is not None: + libwarpx.amr.Print(cupy_status) + aos_arr = xp.array(pti.aos(), copy=False) # void blobs for cupy + particle_data.append(aos_arr) return particle_data - def get_particle_arrays(self, comp_name, level): + def get_particle_arrays(self, comp_name, level, copy_to_host=False): ''' - This returns a list of numpy arrays containing the particle array data + This returns a list of numpy or cupy arrays containing the particle array data on each tile for this process. - The data for the numpy arrays are not copied, but share the underlying - memory buffer with WarpX. The numpy arrays are fully writeable. + Unless copy_to_host is specified, the data for the arrays are not + copied, but share the underlying memory buffer with WarpX. The + arrays are fully writeable. Parameters ---------- @@ -224,13 +248,17 @@ def get_particle_arrays(self, comp_name, level): comp_name : str The component of the array data that will be returned - level : int - The refinement level to reference + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. Returns ------- - List of numpy arrays + List of arrays The requested particle array data ''' comp_idx = self.particle_container.get_comp_index(comp_name) @@ -238,101 +266,226 @@ def get_particle_arrays(self, comp_name, level): data_array = [] for pti in libwarpx.libwarpx_so.WarpXParIter(self.particle_container, level): soa = pti.soa() - data_array.append(np.array(soa.GetRealData(comp_idx), copy=False)) + idx = soa.GetRealData(comp_idx) + if copy_to_host: + data_array.append(idx.to_numpy(copy=True)) + else: + xp, cupy_status = load_cupy() + if cupy_status is not None: + libwarpx.amr.Print(cupy_status) + + data_array.append(xp.array(idx, copy=False)) + return data_array - def get_particle_id(self, level=0): + def get_particle_id(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle 'id' + Return a list of numpy or cupy arrays containing the particle 'id' numbers on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle ids ''' - structs = self.get_particle_structs(level) + structs = self.get_particle_structs(level, copy_to_host) return [libwarpx.amr.unpack_ids(struct['cpuid']) for struct in structs] - def get_particle_cpu(self, level=0): + def get_particle_cpu(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle 'cpu' + Return a list of numpy or cupy arrays containing the particle 'cpu' numbers on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle cpus ''' - structs = self.get_particle_structs(level) + structs = self.get_particle_structs(level, copy_to_host) return [libwarpx.amr.unpack_cpus(struct['cpuid']) for struct in structs] - def get_particle_x(self, level=0): + def get_particle_x(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle 'x' + Return a list of numpy or cupy arrays containing the particle 'x' positions on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle x position ''' - structs = self.get_particle_structs(level) + if copy_to_host: + # Use the numpy version of cosine + xp = np + else: + xp, cupy_status = load_cupy() + + structs = self.get_particle_structs(level, copy_to_host) if libwarpx.geometry_dim == '3d' or libwarpx.geometry_dim == '2d': return [struct['x'] for struct in structs] elif libwarpx.geometry_dim == 'rz': - return [struct['x']*np.cos(theta) for struct, theta in zip(structs, self.get_particle_theta())] + theta = self.get_particle_theta(level, copy_to_host) + return [struct['x']*xp.cos(theta) for struct, theta in zip(structs, theta)] elif libwarpx.geometry_dim == '1d': raise Exception('get_particle_x: There is no x coordinate with 1D Cartesian') xp = property(get_particle_x) - def get_particle_y(self, level=0): + def get_particle_y(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle 'y' + Return a list of numpy or cupy arrays containing the particle 'y' positions on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle y position ''' - structs = self.get_particle_structs(level) + if copy_to_host: + # Use the numpy version of sine + xp = np + else: + xp, cupy_status = load_cupy() + + structs = self.get_particle_structs(level, copy_to_host) if libwarpx.geometry_dim == '3d': return [struct['y'] for struct in structs] elif libwarpx.geometry_dim == 'rz': - return [struct['x']*np.sin(theta) for struct, theta in zip(structs, self.get_particle_theta())] + theta = self.get_particle_theta(level, copy_to_host) + return [struct['x']*xp.sin(theta) for struct, theta in zip(structs, theta)] elif libwarpx.geometry_dim == '1d' or libwarpx.geometry_dim == '2d': raise Exception('get_particle_y: There is no y coordinate with 1D or 2D Cartesian') yp = property(get_particle_y) - def get_particle_r(self, level=0): + def get_particle_r(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle 'r' + Return a list of numpy or cupy arrays containing the particle 'r' positions on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle r position ''' - structs = self.get_particle_structs(level) + xp, cupy_status = load_cupy() + + structs = self.get_particle_structs(level, copy_to_host) if libwarpx.geometry_dim == 'rz': return [struct['x'] for struct in structs] elif libwarpx.geometry_dim == '3d': - return [np.sqrt(struct['x']**2 + struct['y']**2) for struct in structs] + return [xp.sqrt(struct['x']**2 + struct['y']**2) for struct in structs] elif libwarpx.geometry_dim == '2d' or libwarpx.geometry_dim == '1d': raise Exception('get_particle_r: There is no r coordinate with 1D or 2D Cartesian') rp = property(get_particle_r) - def get_particle_theta(self, level=0): + def get_particle_theta(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle + Return a list of numpy or cupy arrays containing the particle theta on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle theta position ''' + xp, cupy_status = load_cupy() + if libwarpx.geometry_dim == 'rz': - return self.get_particle_arrays('theta', level) + return self.get_particle_arrays('theta', level, copy_to_host) elif libwarpx.geometry_dim == '3d': - structs = self.get_particle_structs(level) - return [np.arctan2(struct['y'], struct['x']) for struct in structs] + structs = self.get_particle_structs(level, copy_to_host) + return [xp.arctan2(struct['y'], struct['x']) for struct in structs] elif libwarpx.geometry_dim == '2d' or libwarpx.geometry_dim == '1d': raise Exception('get_particle_theta: There is no theta coordinate with 1D or 2D Cartesian') thetap = property(get_particle_theta) - def get_particle_z(self, level=0): + def get_particle_z(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle 'z' + Return a list of numpy or cupy arrays containing the particle 'z' positions on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle z position ''' - structs = self.get_particle_structs(level) + structs = self.get_particle_structs(level, copy_to_host) if libwarpx.geometry_dim == '3d': return [struct['z'] for struct in structs] elif libwarpx.geometry_dim == 'rz' or libwarpx.geometry_dim == '2d': @@ -341,45 +494,101 @@ def get_particle_z(self, level=0): return [struct['x'] for struct in structs] zp = property(get_particle_z) - def get_particle_weight(self, level=0): + def get_particle_weight(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle + Return a list of numpy or cupy arrays containing the particle weight on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle weight ''' - return self.get_particle_arrays('w', level) + return self.get_particle_arrays('w', level, copy_to_host=copy_to_host) wp = property(get_particle_weight) - def get_particle_ux(self, level=0): + def get_particle_ux(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle + Return a list of numpy or cupy arrays containing the particle x momentum on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle x momentum ''' - return self.get_particle_arrays('ux', level) + return self.get_particle_arrays('ux', level, copy_to_host=copy_to_host) uxp = property(get_particle_ux) - def get_particle_uy(self, level=0): + def get_particle_uy(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle + Return a list of numpy or cupy arrays containing the particle y momentum on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle y momentum ''' - return self.get_particle_arrays('uy', level) + return self.get_particle_arrays('uy', level, copy_to_host=copy_to_host) uyp = property(get_particle_uy) - def get_particle_uz(self, level=0): + def get_particle_uz(self, level=0, copy_to_host=False): ''' - - Return a list of numpy arrays containing the particle + Return a list of numpy or cupy arrays containing the particle z momentum on each tile. + Parameters + ---------- + + level : int + The refinement level to reference (default=0) + + copy_to_host : bool + For GPU-enabled runs, one can either return the GPU + arrays (the default) or force a device-to-host copy. + + Returns + ------- + + List of arrays + The requested particle z momentum ''' - return self.get_particle_arrays('uz', level) + return self.get_particle_arrays('uz', level, copy_to_host=copy_to_host) uzp = property(get_particle_uz) def get_species_charge_sum(self, local=False): @@ -457,13 +666,13 @@ def get_particle_boundary_buffer_size(self, species_name, boundary, local=False) def get_particle_boundary_buffer_structs(self, species_name, boundary, level): ''' - This returns a list of numpy arrays containing the particle struct data + This returns a list of numpy or cupy arrays containing the particle struct data for a species that has been scraped by a specific simulation boundary. The - particle data is represented as a structured numpy array and contains the + particle data is represented as a structured array and contains the particle 'x', 'y', 'z', and 'idcpu'. - The data for the numpy arrays are not copied, but share the underlying - memory buffer with WarpX. The numpy arrays are fully writeable. + The data for the arrays are not copied, but share the underlying + memory buffer with WarpX. The arrays are fully writeable. Parameters ---------- @@ -500,11 +709,11 @@ def get_particle_boundary_buffer_structs(self, species_name, boundary, level): def get_particle_boundary_buffer(self, species_name, boundary, comp_name, level): ''' - This returns a list of numpy arrays containing the particle array data + This returns a list of numpy or cupy arrays containing the particle array data for a species that has been scraped by a specific simulation boundary. - The data for the numpy arrays are not copied, but share the underlying - memory buffer with WarpX. The numpy arrays are fully writeable. + The data for the arrays are not copied, but share the underlying + memory buffer with WarpX. The arrays are fully writeable. Parameters ---------- @@ -524,6 +733,8 @@ def get_particle_boundary_buffer(self, species_name, boundary, comp_name, level) level : int Which AMR level to retrieve scraped particle data from. ''' + xp, cupy_status = load_cupy() + part_container = self.particle_buffer.get_particle_container( species_name, self._get_boundary_number(boundary) ) @@ -533,14 +744,14 @@ def get_particle_boundary_buffer(self, species_name, boundary, comp_name, level) comp_idx = part_container.num_int_comps() - 1 for ii, pti in enumerate(libwarpx.libwarpx_so.BoundaryBufferParIter(part_container, level)): soa = pti.soa() - data_array.append(np.array(soa.GetIntData(comp_idx), copy=False)) + data_array.append(xp.array(soa.GetIntData(comp_idx), copy=False)) else: mypc = libwarpx.warpx.multi_particle_container() sim_part_container_wrapper = mypc.get_particle_container_from_name(species_name) comp_idx = sim_part_container_wrapper.get_comp_index(comp_name) for ii, pti in enumerate(libwarpx.libwarpx_so.BoundaryBufferParIter(part_container, level)): soa = pti.soa() - data_array.append(np.array(soa.GetRealData(comp_idx), copy=False)) + data_array.append(xp.array(soa.GetRealData(comp_idx), copy=False)) return data_array From 8dcd35cc8bcf4dc262bca6f033f32417c1fe6b6f Mon Sep 17 00:00:00 2001 From: Revathi Jambunathan <41089244+RevathiJambunathan@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:29:30 -0700 Subject: [PATCH 27/39] fix distribution map initialization for particle buffers in BTD (#4318) --- Source/Diagnostics/BTDiagnostics.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Diagnostics/BTDiagnostics.cpp b/Source/Diagnostics/BTDiagnostics.cpp index b79132e83db..34bd720be96 100644 --- a/Source/Diagnostics/BTDiagnostics.cpp +++ b/Source/Diagnostics/BTDiagnostics.cpp @@ -1450,8 +1450,9 @@ BTDiagnostics::PrepareParticleDataForOutput() { // Check if the zslice is in domain const bool ZSliceInDomain = GetZSliceInDomainFlag (i_buffer, lev); - if (ZSliceInDomain) { - if ( m_totalParticles_in_buffer[i_buffer][i] == 0) { + const bool kindexInSnapshotBox = GetKIndexInSnapshotBoxFlag (i_buffer, lev); + if (kindexInSnapshotBox) { + if ( buffer_empty(i_buffer) ) { if (!m_do_back_transformed_fields || m_varnames_fields.empty()) { if ( m_buffer_flush_counter[i_buffer] == 0) { DefineSnapshotGeometry(i_buffer, lev); From 9b5d93333422c4bf8024416c802554f23044dd64 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 25 Sep 2023 18:37:42 +0200 Subject: [PATCH 28/39] Doc: Perlmutter (NERSC) E4S 23.05 Boost & CCache (#4302) Update the Boost and CCache modules to use the latest E4S (23.05) GCC/11.2.0 stack on Perlmutter. --- .../perlmutter-nersc/perlmutter_cpu_warpx.profile.example | 4 ++-- .../perlmutter-nersc/perlmutter_gpu_warpx.profile.example | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example b/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example index ab5a0c963b8..a722c9a9104 100644 --- a/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example +++ b/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example @@ -11,7 +11,7 @@ module load cmake/3.22.0 module load cray-fftw/3.3.10.3 # optional: for QED support with detailed tables -export BOOST_ROOT=/global/common/software/spackecp/perlmutter/e4s-22.11/83104/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/boost-1.80.0-ute7nbx4wmfrw53q7hyh6wyezub5ljry +export BOOST_ROOT=/global/common/software/spackecp/perlmutter/e4s-23.05/default/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/boost-1.82.0-ow5r5qrgslcwu33grygouajmuluzuzv3 # optional: for openPMD and PSATD+RZ support module load cray-hdf5-parallel/1.12.2.1 @@ -26,7 +26,7 @@ export LD_LIBRARY_PATH=${CFS}/${proj}/${USER}/sw/perlmutter/cpu/blaspp-master/li export LD_LIBRARY_PATH=${CFS}/${proj}/${USER}/sw/perlmutter/cpu/lapackpp-master/lib64:$LD_LIBRARY_PATH # optional: CCache -export PATH=/global/common/software/spackecp/perlmutter/e4s-22.05/78535/spack/opt/spack/cray-sles15-zen3/gcc-11.2.0/ccache-4.5.1-ybl7xefvggn6hov4dsdxxnztji74tolj/bin:$PATH +export PATH=/global/common/software/spackecp/perlmutter/e4s-23.05/default/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/ccache-4.8-eqk2d3bipbpkgwxq7ujlp6mckwal4dwz/bin:$PATH # optional: for Python bindings or libEnsemble module load cray-python/3.9.13.1 diff --git a/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example b/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example index 09d6a61b458..629a2073a9e 100644 --- a/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example +++ b/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example @@ -9,7 +9,7 @@ if [ -z ${proj-} ]; then echo "WARNING: The 'proj' variable is not yet set in yo module load cmake/3.22.0 # optional: for QED support with detailed tables -export BOOST_ROOT=/global/common/software/spackecp/perlmutter/e4s-22.11/83104/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/boost-1.80.0-ute7nbx4wmfrw53q7hyh6wyezub5ljry +export BOOST_ROOT=/global/common/software/spackecp/perlmutter/e4s-23.05/default/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/boost-1.82.0-ow5r5qrgslcwu33grygouajmuluzuzv3 # optional: for openPMD and PSATD+RZ support module load cray-hdf5-parallel/1.12.2.1 @@ -24,7 +24,7 @@ export LD_LIBRARY_PATH=${CFS}/${proj%_g}/${USER}/sw/perlmutter/gpu/blaspp-master export LD_LIBRARY_PATH=${CFS}/${proj%_g}/${USER}/sw/perlmutter/gpu/lapackpp-master/lib64:$LD_LIBRARY_PATH # optional: CCache -export PATH=/global/common/software/spackecp/perlmutter/e4s-22.05/78535/spack/opt/spack/cray-sles15-zen3/gcc-11.2.0/ccache-4.5.1-ybl7xefvggn6hov4dsdxxnztji74tolj/bin:$PATH +export PATH=/global/common/software/spackecp/perlmutter/e4s-23.05/default/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/ccache-4.8-eqk2d3bipbpkgwxq7ujlp6mckwal4dwz/bin:$PATH # optional: for Python bindings or libEnsemble module load cray-python/3.9.13.1 From 751b6d5b4b83102cc1d1543e2b28d410a9ef60d4 Mon Sep 17 00:00:00 2001 From: Roelof Groenewald <40245517+roelof-groenewald@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:42:29 -0700 Subject: [PATCH 29/39] Add `get_charge_density` to python `WarpXParticleContainer` (#4300) * added `get_charge_density` to pybind `WarpXParticleContainer` and exposed useful fields.py functionality directly for a mf * reduce code duplication in `_WarpXMultiFABWrapper`; changed arguments to strings from f-strings * add `mf_name` argument option to `_MultiFABWrapper` instead of creating a child class * add back deleted docstring --- Python/pywarpx/fields.py | 114 ++++++++++-------- .../Particles/WarpXParticleContainer.cpp | 7 ++ 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/Python/pywarpx/fields.py b/Python/pywarpx/fields.py index ab66a464628..fd647287a53 100644 --- a/Python/pywarpx/fields.py +++ b/Python/pywarpx/fields.py @@ -65,9 +65,14 @@ class _MultiFABWrapper(object): Parameters ---------- + mf: MultiFab + The Multifab that is wrapped. + mf_name: string The name of the MultiFab to be accessed, the tag specified when the - MultiFab is allocated + MultiFab is allocated. The Multifab will be accessed anew from WarpX + everytime it is called if this argument is given instead of directly + providing the Multifab. level: int The refinement level @@ -77,7 +82,8 @@ class _MultiFABWrapper(object): Note that when True, the first n-ghost negative indices will refer to the lower ghost cells. """ - def __init__(self, mf_name, level, include_ghosts=False): + def __init__(self, mf=None, mf_name=None, level=0, include_ghosts=False): + self._mf = mf self.mf_name = mf_name self.level = level self.include_ghosts = include_ghosts @@ -99,10 +105,13 @@ def __iter__(self): @property def mf(self): - # Always fetch this anew in case the C++ MultiFab is recreated - warpx = libwarpx.libwarpx_so.get_instance() - # All MultiFab names have the level suffix - return warpx.multifab(f'{self.mf_name}[level={self.level}]') + if self._mf is not None: + return self._mf + else: + # Always fetch this anew in case the C++ MultiFab is recreated + warpx = libwarpx.libwarpx_so.get_instance() + # All MultiFab names have the level suffix + return warpx.multifab(f'{self.mf_name}[level={self.level}]') @property def shape(self): @@ -544,143 +553,144 @@ def max_index(self, *args): def norm0(self, *args): return self.mf.norm0(*args) + def ExWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_aux[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_aux[x]', level=level, include_ghosts=include_ghosts) def EyWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_aux[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_aux[y]', level=level, include_ghosts=include_ghosts) def EzWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_aux[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_aux[z]', level=level, include_ghosts=include_ghosts) def BxWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_aux[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_aux[x]', level=level, include_ghosts=include_ghosts) def ByWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_aux[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_aux[y]', level=level, include_ghosts=include_ghosts) def BzWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_aux[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_aux[z]', level=level, include_ghosts=include_ghosts) def JxWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp[x]', level=level, include_ghosts=include_ghosts) def JyWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp[y]', level=level, include_ghosts=include_ghosts) def JzWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp[z]', level=level, include_ghosts=include_ghosts) def ExFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_fp[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_fp[x]', level=level, include_ghosts=include_ghosts) def EyFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_fp[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_fp[y]', level=level, include_ghosts=include_ghosts) def EzFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_fp[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_fp[z]', level=level, include_ghosts=include_ghosts) def BxFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_fp[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_fp[x]', level=level, include_ghosts=include_ghosts) def ByFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_fp[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_fp[y]', level=level, include_ghosts=include_ghosts) def BzFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_fp[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_fp[z]', level=level, include_ghosts=include_ghosts) def JxFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp[x]', level=level, include_ghosts=include_ghosts) def JyFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp[y]', level=level, include_ghosts=include_ghosts) def JzFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp[z]', level=level, include_ghosts=include_ghosts) def RhoFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'rho_fp', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='rho_fp', level=level, include_ghosts=include_ghosts) def PhiFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'phi_fp', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='phi_fp', level=level, include_ghosts=include_ghosts) def FFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'F_fp', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='F_fp', level=level, include_ghosts=include_ghosts) def GFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'G_fp', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='G_fp', level=level, include_ghosts=include_ghosts) def AxFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'vector_potential_fp_nodal[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='vector_potential_fp_nodal[x]', level=level, include_ghosts=include_ghosts) def AyFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'vector_potential_fp_nodal[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='vector_potential_fp_nodal[y]', level=level, include_ghosts=include_ghosts) def AzFPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'vector_potential_fp_nodal[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='vector_potential_fp_nodal[z]', level=level, include_ghosts=include_ghosts) def ExCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_cp[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_cp[x]', level=level, include_ghosts=include_ghosts) def EyCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_cp[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_cp[y]', level=level, include_ghosts=include_ghosts) def EzCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Efield_cp[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Efield_cp[z]', level=level, include_ghosts=include_ghosts) def BxCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_cp[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_cp[x]', level=level, include_ghosts=include_ghosts) def ByCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_cp[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_cp[y]', level=level, include_ghosts=include_ghosts) def BzCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'Bfield_cp[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='Bfield_cp[z]', level=level, include_ghosts=include_ghosts) def JxCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_cp[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_cp[x]', level=level, include_ghosts=include_ghosts) def JyCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_cp[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_cp[y]', level=level, include_ghosts=include_ghosts) def JzCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_cp[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_cp[z]', level=level, include_ghosts=include_ghosts) def RhoCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'rho_cp', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='rho_cp', level=level, include_ghosts=include_ghosts) def FCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'F_cp', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='F_cp', level=level, include_ghosts=include_ghosts) def GCPWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'G_cp', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='G_cp', level=level, include_ghosts=include_ghosts) def EdgeLengthsxWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'm_edge_lengths[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='m_edge_lengths[x]', level=level, include_ghosts=include_ghosts) def EdgeLengthsyWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'm_edge_lengths[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='m_edge_lengths[y]', level=level, include_ghosts=include_ghosts) def EdgeLengthszWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'm_edge_lengths[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='m_edge_lengths[z]', level=level, include_ghosts=include_ghosts) def FaceAreasxWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'm_face_areas[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='m_face_areas[x]', level=level, include_ghosts=include_ghosts) def FaceAreasyWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'm_face_areas[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='m_face_areas[y]', level=level, include_ghosts=include_ghosts) def FaceAreaszWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'm_face_areas[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='m_face_areas[z]', level=level, include_ghosts=include_ghosts) def JxFPAmpereWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp_ampere[x]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp_ampere[x]', level=level, include_ghosts=include_ghosts) def JyFPAmpereWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp_ampere[y]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp_ampere[y]', level=level, include_ghosts=include_ghosts) def JzFPAmpereWrapper(level=0, include_ghosts=False): - return _MultiFABWrapper(mf_name=f'current_fp_ampere[z]', level=level, include_ghosts=include_ghosts) + return _MultiFABWrapper(mf_name='current_fp_ampere[z]', level=level, include_ghosts=include_ghosts) def ExFPPMLWrapper(level=0, include_ghosts=False): return _MultiFABWrapper(mf_name='pml_E_fp[x]', level=level, include_ghosts=include_ghosts) diff --git a/Source/Python/Particles/WarpXParticleContainer.cpp b/Source/Python/Particles/WarpXParticleContainer.cpp index b5d3b16269c..386225c8968 100644 --- a/Source/Python/Particles/WarpXParticleContainer.cpp +++ b/Source/Python/Particles/WarpXParticleContainer.cpp @@ -115,5 +115,12 @@ void init_WarpXParticleContainer (py::module& m) }, py::arg("rho"), py::arg("lev") ) + .def("get_charge_density", + [](WarpXParticleContainer& pc, int lev, bool local) + { + return pc.GetChargeDensity(lev, local); + }, + py::arg("lev"), py::arg("local") + ) ; } From 02dbfb5f4a24836d6da1ffeb1ce0887e0edd2170 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Mon, 25 Sep 2023 20:53:19 +0200 Subject: [PATCH 30/39] AMReX: Weekly Update (#4322) --- .github/workflows/cuda.yml | 2 +- Regression/WarpX-GPU-tests.ini | 2 +- Regression/WarpX-tests.ini | 2 +- cmake/dependencies/AMReX.cmake | 2 +- run_test.sh | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cuda.yml b/.github/workflows/cuda.yml index 0f3adb8593a..03a3044848f 100644 --- a/.github/workflows/cuda.yml +++ b/.github/workflows/cuda.yml @@ -111,7 +111,7 @@ jobs: which nvcc || echo "nvcc not in PATH!" git clone https://github.com/AMReX-Codes/amrex.git ../amrex - cd ../amrex && git checkout --detach 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 && cd - + cd ../amrex && git checkout --detach 2e99628138df3b5b0ecf50b0c1201d5547f821a0 && cd - make COMP=gcc QED=FALSE USE_MPI=TRUE USE_GPU=TRUE USE_OMP=FALSE USE_PSATD=TRUE USE_CCACHE=TRUE -j 2 build_nvhpc21-11-nvcc: diff --git a/Regression/WarpX-GPU-tests.ini b/Regression/WarpX-GPU-tests.ini index c20dc5ed93c..c6caa6b915c 100644 --- a/Regression/WarpX-GPU-tests.ini +++ b/Regression/WarpX-GPU-tests.ini @@ -60,7 +60,7 @@ emailBody = Check https://ccse.lbl.gov/pub/GpuRegressionTesting/WarpX/ for more [AMReX] dir = /home/regtester/git/amrex/ -branch = 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 +branch = 2e99628138df3b5b0ecf50b0c1201d5547f821a0 [source] dir = /home/regtester/git/WarpX diff --git a/Regression/WarpX-tests.ini b/Regression/WarpX-tests.ini index a3a04f609b2..da9d8398869 100644 --- a/Regression/WarpX-tests.ini +++ b/Regression/WarpX-tests.ini @@ -59,7 +59,7 @@ emailBody = Check https://ccse.lbl.gov/pub/RegressionTesting/WarpX/ for more det [AMReX] dir = /home/regtester/AMReX_RegTesting/amrex/ -branch = 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 +branch = 2e99628138df3b5b0ecf50b0c1201d5547f821a0 [source] dir = /home/regtester/AMReX_RegTesting/warpx diff --git a/cmake/dependencies/AMReX.cmake b/cmake/dependencies/AMReX.cmake index fb80973c96b..3e0f044f020 100644 --- a/cmake/dependencies/AMReX.cmake +++ b/cmake/dependencies/AMReX.cmake @@ -257,7 +257,7 @@ set(WarpX_amrex_src "" set(WarpX_amrex_repo "https://github.com/AMReX-Codes/amrex.git" CACHE STRING "Repository URI to pull and build AMReX from if(WarpX_amrex_internal)") -set(WarpX_amrex_branch "48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4" +set(WarpX_amrex_branch "2e99628138df3b5b0ecf50b0c1201d5547f821a0" CACHE STRING "Repository branch for WarpX_amrex_repo if(WarpX_amrex_internal)") diff --git a/run_test.sh b/run_test.sh index e24095ee8cf..81237fb6971 100755 --- a/run_test.sh +++ b/run_test.sh @@ -71,7 +71,7 @@ python3 -m pip install --upgrade -r warpx/Regression/requirements.txt # Clone AMReX and warpx-data git clone https://github.com/AMReX-Codes/amrex.git -cd amrex && git checkout --detach 48b3ec7cb7ad99823bd85fad83c13c3cfd5ecdd4 && cd - +cd amrex && git checkout --detach 2e99628138df3b5b0ecf50b0c1201d5547f821a0 && cd - # warpx-data contains various required data sets git clone --depth 1 https://github.com/ECP-WarpX/warpx-data.git # openPMD-example-datasets contains various required data sets From a5c1170fde7f01697a4ebdf8b8a2c6776d851abe Mon Sep 17 00:00:00 2001 From: aveksler1 <124003120+aveksler1@users.noreply.github.com> Date: Mon, 25 Sep 2023 16:43:09 -0700 Subject: [PATCH 31/39] Particle fields diagnostic support for PICMI (#4262) * added particle field interface in picmi FieldDiagnostic * added documentation to doc string. * undo line deletion * catch unexpected keywords * simplified logic * fixed bug and removed unnecessary error check * passing in particle field diagnostic information using a class defined in picmi rather than a dictionary * Added ParticleFieldDiagnostic class to be used in FieldDiagnostics * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * error checking on repeat name required if not using a dictionary * fixed docstring, changed var name to be more explicit, removed error check by using default list, bug fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- Python/pywarpx/picmi.py | 62 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/Python/pywarpx/picmi.py b/Python/pywarpx/picmi.py index ee4422daca6..80efe3ea804 100644 --- a/Python/pywarpx/picmi.py +++ b/Python/pywarpx/picmi.py @@ -8,6 +8,7 @@ """Classes following the PICMI standard """ +from dataclasses import dataclass import os import re @@ -1941,6 +1942,35 @@ def set_write_dir(self): self.diagnostic.file_prefix = os.path.join(write_dir, file_prefix) +@dataclass(frozen=True) +class ParticleFieldDiagnostic: + """ + Class holding particle field diagnostic information to be processed in FieldDiagnostic below. + + Parameters + ---------- + name: str + Name of particle field diagnostic. If a component of a vector field, for the openPMD viewer + to treat it as a vector, the coordinate (i.e x, y, z) should be the last character. + + func: parser str + Parser function to be calculated for each particle per cell. Should be of the form + f(x,y,z,ux,uy,uz) + + do_average: (0 or 1) optional, default 1 + Whether the diagnostic is averaged by the sum of particle weights in each cell + + filter: parser str, optional + Parser function returning a boolean for whether to include a particle in the diagnostic. + If not specified, all particles will be included. The function arguments are the same + as the `func` above. + """ + name: str + func: str + do_average: int = 1 + filter: str = None + + class FieldDiagnostic(picmistandard.PICMI_FieldDiagnostic, WarpXDiagnosticBase): """ See `Input Parameters `_ for more information. @@ -1970,6 +2000,15 @@ class FieldDiagnostic(picmistandard.PICMI_FieldDiagnostic, WarpXDiagnosticBase): warpx_dump_rz_modes: bool, optional Flag whether to dump the data for all RZ modes + + warpx_particle_fields_to_plot: list of ParticleFieldDiagnostics + List of ParticleFieldDiagnostic classes to install in the simulation. Error + checking is handled in the class itself. + + warpx_particle_fields_species: list of strings, optional + Species for which to calculate particle_fields_to_plot functions. Fields will + be calculated separately for each specified species. If not passed, default is + all of the available particle species. """ def init(self, kw): @@ -1983,6 +2022,8 @@ def init(self, kw): self.file_prefix = kw.pop('warpx_file_prefix', None) self.file_min_digits = kw.pop('warpx_file_min_digits', None) self.dump_rz_modes = kw.pop('warpx_dump_rz_modes', None) + self.particle_fields_to_plot = kw.pop('warpx_particle_fields_to_plot', []) + self.particle_fields_species = kw.pop('warpx_particle_fields_species', None) def initialize_inputs(self): @@ -2060,6 +2101,27 @@ def initialize_inputs(self): fields_to_plot.sort() self.diagnostic.fields_to_plot = fields_to_plot + particle_fields_to_plot_names = list() + for pfd in self.particle_fields_to_plot: + if pfd.name in particle_fields_to_plot_names: + raise Exception('A particle fields name can not be repeated.') + particle_fields_to_plot_names.append(pfd.name) + self.diagnostic.__setattr__( + f'particle_fields.{pfd.name}(x,y,z,ux,uy,uz)', pfd.func + ) + self.diagnostic.__setattr__( + f'particle_fields.{pfd.name}.do_average', pfd.do_average + ) + self.diagnostic.__setattr__( + f'particle_fields.{pfd.name}.filter(x,y,z,ux,uy,uz)', pfd.filter + ) + + # --- Convert to a sorted list so that the order + # --- is the same on all processors. + particle_fields_to_plot_names.sort() + self.diagnostic.particle_fields_to_plot = particle_fields_to_plot_names + self.diagnostic.particle_fields_species = self.particle_fields_species + self.diagnostic.plot_raw_fields = self.plot_raw_fields self.diagnostic.plot_raw_fields_guards = self.plot_raw_fields_guards self.diagnostic.plot_finepatch = self.plot_finepatch From 8330e27430ba038172f4b3e2fe8a1ab42ef5ea9a Mon Sep 17 00:00:00 2001 From: Marco Garten Date: Tue, 26 Sep 2023 02:05:06 +0200 Subject: [PATCH 32/39] Add particle sorting parameters to pywarpx (#4323) Added parameters: - warpx.sort_intervals - warpx.sort_particles_for_deposition - warpx.sort_idx_type - warpx.sort_bin_size --- Python/pywarpx/picmi.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Python/pywarpx/picmi.py b/Python/pywarpx/picmi.py index 80efe3ea804..9e88da7c66e 100644 --- a/Python/pywarpx/picmi.py +++ b/Python/pywarpx/picmi.py @@ -1708,6 +1708,28 @@ class Simulation(picmistandard.PICMI_Simulation): The domain will be chopped into the exact number of pieces in each dimension as specified by this parameter. https://warpx.readthedocs.io/en/latest/usage/parameters.html#distribution-across-mpi-ranks-and-parallelization https://warpx.readthedocs.io/en/latest/usage/domain_decomposition.html#simple-method + + warpx_sort_intervals: string, optional (defaults: -1 on CPU; 4 on GPU) + Using the Intervals parser syntax, this string defines the timesteps at which particles are sorted. If <=0, do not sort particles. + It is turned on on GPUs for performance reasons (to improve memory locality). + + warpx_sort_particles_for_deposition: bool, optional (default: true for the CUDA backend, otherwise false) + This option controls the type of sorting used if particle sorting is turned on, i.e. if sort_intervals is not <=0. + If `true`, particles will be sorted by cell to optimize deposition with many particles per cell, in the order `x` -> `y` -> `z` -> `ppc`. + If `false`, particles will be sorted by bin, using the sort_bin_size parameter below, in the order `ppc` -> `x` -> `y` -> `z`. + `true` is recommended for best performance on NVIDIA GPUs, especially if there are many particles per cell. + + warpx_sort_idx_type: list of int, optional (default: 0 0 0) + This controls the type of grid used to sort the particles when sort_particles_for_deposition is true. + Possible values are: + idx_type = {0, 0, 0}: Sort particles to a cell centered grid, + idx_type = {1, 1, 1}: Sort particles to a node centered grid, + idx_type = {2, 2, 2}: Compromise between a cell and node centered grid. + In 2D (XZ and RZ), only the first two elements are read. In 1D, only the first element is read. + + warpx_sort_bin_size: list of int, optional (default 1 1 1) + If `sort_intervals` is activated and `sort_particles_for_deposition` is false, particles are sorted in bins of `sort_bin_size` cells. + In 2D, only the first two elements are read. """ # Set the C++ WarpX interface (see _libwarpx.LibWarpX) as an extension to @@ -1746,6 +1768,10 @@ def init(self, kw): self.amrex_use_gpu_aware_mpi = kw.pop('warpx_amrex_use_gpu_aware_mpi', None) self.zmax_plasma_to_compute_max_step = kw.pop('warpx_zmax_plasma_to_compute_max_step', None) self.compute_max_step_from_btd = kw.pop('warpx_compute_max_step_from_btd', None) + self.sort_intervals = kw.pop('warpx_sort_intervals', None) + self.sort_particles_for_deposition = kw.pop('warpx_sort_particles_for_deposition', None) + self.sort_idx_type = kw.pop('warpx_sort_idx_type', None) + self.sort_bin_size = kw.pop('warpx_sort_bin_size', None) self.collisions = kw.pop('warpx_collisions', None) self.embedded_boundary = kw.pop('warpx_embedded_boundary', None) @@ -1774,6 +1800,11 @@ def initialize_inputs(self): pywarpx.warpx.zmax_plasma_to_compute_max_step = self.zmax_plasma_to_compute_max_step pywarpx.warpx.compute_max_step_from_btd = self.compute_max_step_from_btd + pywarpx.warpx.sort_intervals = self.sort_intervals + pywarpx.warpx.sort_particles_for_deposition = self.sort_particles_for_deposition + pywarpx.warpx.sort_idx_type = self.sort_idx_type + pywarpx.warpx.sort_bin_size = self.sort_bin_size + pywarpx.algo.current_deposition = self.current_deposition_algo pywarpx.algo.charge_deposition = self.charge_deposition_algo pywarpx.algo.field_gathering = self.field_gathering_algo From f52205bd770978de7a39ab11e5cffd31f36ccce4 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 28 Sep 2023 01:04:18 +0200 Subject: [PATCH 33/39] Glossary: CEX, SEE (#4325) * Glossary: CEX, SEE Add new terms to our glossary for charge-exchange collisions and secondary electron emission. * plural collisions Co-authored-by: Revathi Jambunathan <41089244+RevathiJambunathan@users.noreply.github.com> --------- Co-authored-by: Revathi Jambunathan <41089244+RevathiJambunathan@users.noreply.github.com> --- Docs/source/glossary.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Docs/source/glossary.rst b/Docs/source/glossary.rst index cb376af481f..131b3a63ae7 100644 --- a/Docs/source/glossary.rst +++ b/Docs/source/glossary.rst @@ -18,6 +18,7 @@ Abbreviations * **BC:** boundary condition (of a simulation) * **BCK:** `Benkler-Chavannes-Kuster `__ method, a stabilization technique for small cells in the electromagnetic solver * **BTD:** backtransformed diagnosics, a method to collect data for analysis from a *boosted frame* simulation +* **CEX:** charge-exchange collisions * **CFL:** the Courant-Friedrichs-Lewy condition, a numerical parameter for the numerical convergence of PDE solvers * **CI:** continuous integration, automated tests that we perform before a proposed code-change is accepted; see PR * **CPU:** `central processing unit `__, we usual mean a socket or generally the host-side of a computer (compared to the accelerator, e.g. GPU) @@ -57,6 +58,7 @@ Abbreviations * **RPA:** radiation-pressure acceleration (of protons/ions), e.g. hole-boring (HB) or light-sail (LS) acceleration * **RZ:** for the coordinate system ``r-z`` in cylindrical geometry; we use "RZ" when we refer to quasi-cylindrical geometry, decomposed in azimuthal modes (see details `here `__) * **SENSEI:** `Scalable in situ analysis and visualization `__, light weight framework for in situ data analysis offering access to multiple visualization and analysis backends +* **SEE:** secondary electron emission * **TNSA:** target-normal sheet acceleration (of protons/ions) Terms From 9bf35a1416f5425da3c98107359f193f62c82c8d Mon Sep 17 00:00:00 2001 From: Grant Johnson <69021085+johnson452@users.noreply.github.com> Date: Thu, 28 Sep 2023 18:44:00 -0700 Subject: [PATCH 34/39] Add fluids in WarpX (#3991) * Added: new fluid-langmuir test * Implemented MultiFluidContainer * Implement WarpXFluidContainter * Fix compilation errors * Call AllocData in WarpX.cpp * Fix allocation of fluid MultiFabs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix example * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix compilation error for Evolve * Cleanup include statements * Fix compilation in 2D * Add fluid initialization * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fill guard cells * Make the code work for any dimension * Fix compilation error * Add call to fluids' evolve/deposition * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added fluid contribution to the current deposition * Fix calls to `FillBoundary` * Fixed calculation of J to properly handle nodal versus CC values * Used different boxes for jx jy jz * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed intial index * Minor Indentation Fixes * Split `DepositCurrent` into 2 separate MFIter loops * Additional comments and cleanup * Prevent double counting of J at the boundaries * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Prepare gather and push * Filled out GatherAndPush function for fluids; currently not working * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed bug with fluid source * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Corrected speed of light factor in the momentum * Added analysis script for 3d langmuir fluid test * Fixed typo * Added docstrings, cleanedup files * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added in MUSCL-Handcock Fluid Advection Evolution, currently nan's due to issue in the x-plane update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleaup WarpXFluidContainer.h * Currected flux jacobian to SI units and added fillboundary commands * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Bug Fix: incorrect velocity being used * Added Fluid Rho Deposition * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add separate .H file for helper functions * Extended the langmuir fluid test case to examine J and rho aswell * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Allocate multi-component arrays * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update Regression/WarpX-tests.ini Co-authored-by: Remi Lehe * Update Source/Fluids/MultiFluidContainer.cpp Co-authored-by: Remi Lehe * Update Source/Fluids/MultiFluidContainer.H Co-authored-by: Remi Lehe * Update Source/Fluids/MultiFluidContainer.H Co-authored-by: Remi Lehe * Update Source/Fluids/MultiFluidContainer_fwd.H Co-authored-by: Remi Lehe * Update Source/Fluids/WarpXFluidContainer.cpp Co-authored-by: Remi Lehe * Update Source/Fluids/WarpXFluidContainer_fwd.H Co-authored-by: Remi Lehe * Simplify calculation in MUSCL-Handcock, Undo removing DTtype inlcude * Changed Q_midpoint staggering to be correctly placed * Updated the checksum test with a new json file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated functions which changed due to pull from main * Updated the fluid test to have the proper J time centering * Rewrote MusclHandcock to avoid MPI comms and temp variable creation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add instrument fluid code with profiling annotations * Reverted to faster Musclhandcock with comms * Eliminate unnessessary communications in MUSCL Handcock scheme * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Some variable name cleanup, optimizing A(muscl)-array calc * Removed unused variables, hopefully fixed Azure pipeline for the Langmuir_fluid_multi * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Bugfixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Bugfixes * Bugfixes for 3D langmuir fluid test on azure * Added 2D (XZ) fluids * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added 1D (z) to fluids * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added RZ to fluids * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Uses correct velocities in the fluxes * Fixed bug for selecting the coordinates with V_calc() in fluids * Formatting * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed json after bugfix * Fixed 1D json from azure pipeline data * Bugfix: Correctly limits domains for RZ fluids(), still a bug in RZ implementation for Jz(r=0, t=0) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Bugfix: fixed small error in fluid-RZ SA/Volume at the outermost cell * Attempted patch for RZ * Minor Cleanup * Bugfix: Fixed volume element selection * Added: RZ centrifugal force term * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Ad-hoc fix to RZ, and print diangostics * Moved ad-hoc fix onto the temp variables, corrected mangitude * Bug fix: RZ volume applied at the wrong place, removed ad-hoc fix * Removed last of the ad-hoc code * Fixed for new plasmainjector * Minor Cleanup * FIX: Corrected CI crash for Langmuir_fluid_Xd tests by passing geom to WarpXFluidContainer * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added RZ checksum, removed commented code * Bugfix for RZ, at the origin, also changed rho output to be called simular to current density * Updated RZ checksum * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Bugfix/improvement: RZ test now correctly outputs and tests rho, J in addition to the electric field * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Appleclang compiler config, shows warnings when compiling with appleclang * Updated fluid-1d test to also check for rho, Jz * Updated fluid-2d test to also check for rho, Jz * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Updated Checksum with Azure Pipeline data * clang-tidy cleanup * Cleanup * Cleanup Flux-Jacobian Calculation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add moving window code for fluids * Fixed issues introduced by changing InitData() signerature * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Simplified Communications in WarpXFluidContainer * Minor cleanup of unused variables * Minor bug fis in Moving window, Added: non-periodic copy-BC to fluids * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Minor cosmetic changes * Fix compilation issue with single-precision particles * Template Higuera-Cary pusher on the type of variables * Use simple function to convert to nodal box * Simplify injection of plasma * Positivity and monotonicity preserving limiter added * Changed the default average routine to minmod, which is more diffusive but still second order TVD, this significantly reduced numerical oscillations for a0 ~ 15 1D fluid WFA sims * Added alternative options to take averages for MUSCL-Handcock * Added a working fluid-envelope model * Added boosted frame capabilities for the fluid * Minor Cleanup * Minor Cleanup on 1D positivity limiter * Major cleanup for 2D, RZ, and 3D MUSCL positivity limiters * Fixes to the boosted frame simulation * Lorentz Transform the envelope field * Minor fix for envelope lorentz transform * Protection against N = 0 * Minor bug fixes for zero density check * Bugfix: Improper check of N = 0, updated * Bugfix: Fixed Lorentz transforms in the fluids-InitData() * Bugfix: Small values of N were causing gamma to have unphysical values; added a check * Mergewq * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Improvment: calc of gamma, added additional slope-limiting utilities * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Change MUSCL scheme to a primative varibale model * Fixed RZ for primative variable sources * Updated tests to match primative vars * Small Bugfix * Added a 1D WFA physical_app + test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Minor fixes to pass CI * Add include statement for fluid * Use temporary variable gamma_boost in device functions * Add missing .H file for Python compilations * Moved WarpX::beta_boost out of device functions * CI Fixes * More CI fixes for device-host issues * Cleanup of some var names * Minor Cleanup * Removed unessesary autos * Fluid Theory Docs * Checks that there is only the m = 0 azimuthal mode, removed TODOs and declared RZ bound default behavior * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed indentation * Fixed Warning about azimuthal modes * Added documentation for fluid input parameters * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed per Remi's partial review, adds checks for do_not_push/gather * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed bug in matrix(J), updated all fluid tests accordingly * RZ iff for centrigual forces * Changed variable names * Updated the 1d_laser_acc case to include the full laser model in the theory. * Changed names of c_{x,y,z} to dt/d{x,y,z} * Code cleanup, mostly of comments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Large change: Rewrote MUSCLadvection() to simplify readability * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Minor change to positivity limiter function implentation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleanup * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleanup flux and velocity functions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleanup for inputs to ave by definind diff functions, fixed RZ boundary problem at r-max * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleanup of the flux functions * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Removed some unused util functions * Simplified linear slope calc function * Reworded docs to not inlcude contact info * New CI test for boosted WFA with fluids * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added current and charge deposition for Hybrid-Ohm and electrostatic solvers * Fixed docs, removed extra .get() on rho/J * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Declare myfl as private * Add flag do_fluid_species * New function to extract species properties * Update injection_style use * Remove PlasmaInjector in WarpXFluidContainer * Remove number of particle per cell from inputs scripts * Add missing files * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix GPU error * Update call to extract particle charge and mass * Update tests * Clean up code * Avoid life-time issues with parsers * Avoid additional life-time issues * Added string that we don't support mesh refinement * Fixed Clangtidy issue, added warning that fluids only work with one mesh refinement level * Minor cleanup * Fixed build issue --------- Co-authored-by: Remi Lehe Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Edoardo Zoni --- CMakeLists.txt | 2 + Docs/source/index.rst | 1 + Docs/source/theory/Fluid_Loop.png | Bin 0 -> 300102 bytes Docs/source/theory/cold_fluid_model.rst | 135 ++ Docs/source/usage/parameters.rst | 45 + .../laser_acceleration/analysis_1d_fluids.py | 174 +++ .../analysis_1d_fluids_boosted.py | 173 +++ .../laser_acceleration/inputs_1d_fluids | 72 + .../inputs_1d_fluids_boosted | 79 + Examples/Tests/langmuir_fluids/analysis_1d.py | 137 ++ Examples/Tests/langmuir_fluids/analysis_2d.py | 159 ++ Examples/Tests/langmuir_fluids/analysis_3d.py | 179 +++ Examples/Tests/langmuir_fluids/analysis_rz.py | 174 +++ Examples/Tests/langmuir_fluids/inputs_1d | 72 + Examples/Tests/langmuir_fluids/inputs_2d | 69 + Examples/Tests/langmuir_fluids/inputs_3d | 74 + Examples/Tests/langmuir_fluids/inputs_rz | 72 + .../benchmarks_json/Langmuir_fluid_1D.json | 7 + .../benchmarks_json/Langmuir_fluid_2D.json | 9 + .../benchmarks_json/Langmuir_fluid_RZ.json | 10 + .../benchmarks_json/Langmuir_fluid_multi.json | 15 + .../LaserAcceleration_1d_fluid.json | 15 + .../LaserAcceleration_1d_fluid_boosted.json | 14 + Regression/WarpX-tests.ini | 108 ++ .../ComputeDiagFunctors/RhoFunctor.cpp | 6 + Source/Evolve/WarpXEvolve.cpp | 8 + Source/FieldSolver/ElectrostaticSolver.cpp | 6 + .../FieldSolver/WarpXPushFieldsHybridPIC.cpp | 9 + Source/Fluids/CMakeLists.txt | 8 + Source/Fluids/Make.package | 4 + Source/Fluids/MultiFluidContainer.H | 78 + Source/Fluids/MultiFluidContainer.cpp | 73 + Source/Fluids/MultiFluidContainer_fwd.H | 12 + Source/Fluids/MusclHancockUtils.H | 484 ++++++ Source/Fluids/WarpXFluidContainer.H | 186 +++ Source/Fluids/WarpXFluidContainer.cpp | 1353 +++++++++++++++++ Source/Fluids/WarpXFluidContainer_fwd.H | 12 + Source/Initialization/PlasmaInjector.H | 7 - Source/Initialization/PlasmaInjector.cpp | 280 +--- Source/Make.WarpX | 1 + Source/Particles/MultiParticleContainer.H | 5 - .../Pusher/UpdateMomentumHigueraCary.H | 52 +- Source/Python/WarpX.cpp | 2 + Source/Utils/CMakeLists.txt | 1 + Source/Utils/Make.package | 1 + Source/Utils/SpeciesUtils.H | 39 + Source/Utils/SpeciesUtils.cpp | 240 +++ Source/Utils/WarpXMovingWindow.cpp | 38 + Source/WarpX.H | 9 + Source/WarpX.cpp | 34 + 50 files changed, 4461 insertions(+), 282 deletions(-) create mode 100644 Docs/source/theory/Fluid_Loop.png create mode 100644 Docs/source/theory/cold_fluid_model.rst create mode 100755 Examples/Physics_applications/laser_acceleration/analysis_1d_fluids.py create mode 100755 Examples/Physics_applications/laser_acceleration/analysis_1d_fluids_boosted.py create mode 100644 Examples/Physics_applications/laser_acceleration/inputs_1d_fluids create mode 100644 Examples/Physics_applications/laser_acceleration/inputs_1d_fluids_boosted create mode 100755 Examples/Tests/langmuir_fluids/analysis_1d.py create mode 100755 Examples/Tests/langmuir_fluids/analysis_2d.py create mode 100755 Examples/Tests/langmuir_fluids/analysis_3d.py create mode 100755 Examples/Tests/langmuir_fluids/analysis_rz.py create mode 100644 Examples/Tests/langmuir_fluids/inputs_1d create mode 100644 Examples/Tests/langmuir_fluids/inputs_2d create mode 100644 Examples/Tests/langmuir_fluids/inputs_3d create mode 100644 Examples/Tests/langmuir_fluids/inputs_rz create mode 100644 Regression/Checksum/benchmarks_json/Langmuir_fluid_1D.json create mode 100644 Regression/Checksum/benchmarks_json/Langmuir_fluid_2D.json create mode 100644 Regression/Checksum/benchmarks_json/Langmuir_fluid_RZ.json create mode 100644 Regression/Checksum/benchmarks_json/Langmuir_fluid_multi.json create mode 100644 Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid.json create mode 100644 Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid_boosted.json create mode 100644 Source/Fluids/CMakeLists.txt create mode 100644 Source/Fluids/Make.package create mode 100644 Source/Fluids/MultiFluidContainer.H create mode 100644 Source/Fluids/MultiFluidContainer.cpp create mode 100644 Source/Fluids/MultiFluidContainer_fwd.H create mode 100644 Source/Fluids/MusclHancockUtils.H create mode 100644 Source/Fluids/WarpXFluidContainer.H create mode 100644 Source/Fluids/WarpXFluidContainer.cpp create mode 100644 Source/Fluids/WarpXFluidContainer_fwd.H create mode 100644 Source/Utils/SpeciesUtils.H create mode 100644 Source/Utils/SpeciesUtils.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2501c9dcaff..f9b1a8bbc2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -372,6 +372,7 @@ if(WarpX_PYTHON) endif() add_subdirectory(Source/ablastr) + if(WarpX_LIB) add_subdirectory(Source/AcceleratorLattice) add_subdirectory(Source/BoundaryConditions) @@ -380,6 +381,7 @@ if(WarpX_LIB) add_subdirectory(Source/Evolve) add_subdirectory(Source/FieldSolver) add_subdirectory(Source/Filter) + add_subdirectory(Source/Fluids) add_subdirectory(Source/Initialization) add_subdirectory(Source/Laser) add_subdirectory(Source/Parallelization) diff --git a/Docs/source/index.rst b/Docs/source/index.rst index 2209b360e26..7fc2a1c5833 100644 --- a/Docs/source/index.rst +++ b/Docs/source/index.rst @@ -116,6 +116,7 @@ Theory theory/input_output theory/collisions theory/kinetic_fluid_hybrid_model + theory/cold_fluid_model Development ----------- diff --git a/Docs/source/theory/Fluid_Loop.png b/Docs/source/theory/Fluid_Loop.png new file mode 100644 index 0000000000000000000000000000000000000000..8d47cd606c5d8ee945d79fea180e6f9ec54a3f48 GIT binary patch literal 300102 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 + +.. raw:: html + +

+ +.. raw:: html + +
+ +Osher, Stanley, and Chi-Wang Shu. "Efficient implementation of essentially non-oscillatory shock-capturing schemes." J. Comput. Phys 77.2 (1988): 439-471. + + +.. raw:: html + +
+ +.. raw:: html + +
+ +Van Leer, Bram. "On the relation between the upwind-differencing schemes of Godunov, Engquist–Osher and Roe." SIAM Journal on Scientific and statistical Computing 5.1 (1984): 1-20. diff --git a/Docs/source/usage/parameters.rst b/Docs/source/usage/parameters.rst index 27d7682899d..3b88e07d447 100644 --- a/Docs/source/usage/parameters.rst +++ b/Docs/source/usage/parameters.rst @@ -1136,6 +1136,20 @@ Particle initialization 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-fluids: + +Cold Relativistic Fluid initialization +-------------------------------------- + +When configuring fluid parameters in WarpX, you can utilize the same parsers as those used for particles. + +* ``fluids.species_names`` (`strings`, separated by spaces) + Defines the names of each fluid species. It is a required input to create and evolve fluid species using the cold relativistic fluid equations. + This input is used similarly to `particle.species_names`, but it is specifically tailored for fluids. + For fluid-specific inputs we use `` as a placeholder. Also see external fields + for how to specify these for fluids as the function names differ. + .. _running-cpp-parameters-laser: Laser initialization @@ -1499,6 +1513,37 @@ Applied to Particles and :math:`E_z = 0`, and :math:`B_x = \mathrm{strength} \cdot y`, :math:`B_y = -\mathrm{strength} \cdot x`, and :math:`B_z = 0`. + +Applied to Cold Relativistic Fluids +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +* ``.E_ext_init_style`` & ``.B_ext_init_style`` (string) optional (default "none") + These parameters determine the type of the external electric and + magnetic fields respectively that are applied directly to the cold relativistic fluids at every timestep. + The field values are specified in the lab frame. + With the default ``none`` style, no field is applied. + Possible values are ``parse_E_ext_function`` or ``parse_B_ext_function``. + + * ``parse_E_ext_function`` or ``parse_B_ext_function``: the field is specified as an analytic + expression that is a function of space (x,y,z) and time (t), relative to the lab frame. + The E-field is specified by the input parameters: + + * ``.Ex_external_function(x,y,z,t)`` + + * ``.Ey_external_function(x,y,z,t)`` + + * ``.Ez_external_function(x,y,z,t)`` + + The B-field is specified by the input parameters: + + * ``.Bx_external_function(x,y,z,t)`` + + * ``.By_external_function(x,y,z,t)`` + + * ``.Bz_external_function(x,y,z,t)`` + + Note that the position is defined in Cartesian coordinates, as a function of (x,y,z), even for RZ. + Accelerator Lattice ^^^^^^^^^^^^^^^^^^^ diff --git a/Examples/Physics_applications/laser_acceleration/analysis_1d_fluids.py b/Examples/Physics_applications/laser_acceleration/analysis_1d_fluids.py new file mode 100755 index 00000000000..a376ed872a9 --- /dev/null +++ b/Examples/Physics_applications/laser_acceleration/analysis_1d_fluids.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2023 Grant Johnson, Remi Lehe +# +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL +# +# This is a script that analyses the simulation results from +# the script `inputs_1d`. This simulates a 1D WFA with Pondermotive Envelope: +# REF: (Equations 20-23) https://journals.aps.org/rmp/pdf/10.1103/RevModPhys.81.1229 +import os +import sys + +import matplotlib + +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import yt + +yt.funcs.mylog.setLevel(50) + +import numpy as np +from scipy.constants import c, e, epsilon_0, m_e + +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +# this will be the name of the plot file +fn = sys.argv[1] + +# Parameters (these parameters must match the parameters in `inputs.multi.rt`) +n0 = 20.e23 +# Plasma frequency +wp = np.sqrt((n0*e**2)/(m_e*epsilon_0)) +kp = wp/c +tau = 15.e-15 +a0 = 2.491668 +e = -e #Electrons +lambda_laser = 0.8e-6 + +zmin = -20e-6; zmax = 100.e-6; Nz = 10240 + +# Compute the theory + +# Call the ode solver +from scipy.integrate import odeint + + +# ODE Function +def odefcn(phi, xi, kp, a0, c, tau, xi_0, lambda_laser): + phi1, phi2 = phi + a_sq = a0**2 * np.exp(-2 * (xi - xi_0)**2 / (c**2 * tau**2))*np.sin(2*np.pi*(xi - xi_0)/lambda_laser)**2 + dphi1_dxi = phi2 + dphi2_dxi = kp**2 * ((1 + a_sq) / (2 * (1 + phi1)**2) - 0.5) + return [dphi1_dxi, dphi2_dxi] + +# Call odeint to solve the ODE +xi_span = [-20e-6, 100e-6] +xi_0 = 0e-6 +phi0 = [0.0, 0.0] +dxi = (zmax-zmin)/Nz +xi = zmin + dxi*( 0.5 + np.arange(Nz) ) +phi = odeint(odefcn, phi0, xi, args=(kp, a0, c, tau, xi_0, lambda_laser)) + +# Change array direction to match the simulations +xi = -xi[::-1] +phi = phi[::-1] +xi_0 = -0e-6 +phi2 = phi[:, 0] +Ez = -phi[:, 1] + +# Compute the derived quantities +a_sq = a0**2 * np.exp(-2 * (xi - xi_0)**2 / (c**2 * tau**2)) *np.sin(2*np.pi*(xi - xi_0)/lambda_laser)**2 +gamma_perp_sq = 1 + a_sq +n = n0 * (gamma_perp_sq + (1 + phi2)**2) / (2 * (1 + phi2)**2) +uz = (gamma_perp_sq - (1 + phi2)**2) / (2 * (1 + phi2)) +gamma = (gamma_perp_sq + (1 + phi2)**2) / (2 * (1 + phi2)) + +# Theory Components [convert to si] +uz *= c +J_th = np.multiply( np.divide(uz,gamma), n ) +J_th *= e +rho_th = e*n +E_th = Ez +E_th *= ((m_e*c*c)/e) +V_th = np.divide(uz,gamma) +V_th /= c +# Remove the ions +rho_th = rho_th - e*n0 + +# Dicate which region to compare solutions over +# (Currently this is the full domain) +min_i = 0 +max_i = 10240 + +# Read the file +ds = yt.load(fn) +t0 = ds.current_time.to_value() +data = ds.covering_grid(level=0, left_edge=ds.domain_left_edge, + dims=ds.domain_dimensions) +# Check the validity of the fields +error_rel = 0 +for field in ['Ez']: + E_sim = data[('mesh',field)].to_ndarray()[:,0,0] + #E_th = get_theoretical_field(field, t0) + max_error = abs(E_sim[min_i:max_i]-E_th[min_i:max_i]).max()/abs(E_th[min_i:max_i]).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the currents +for field in ['Jz']: + J_sim = data[('mesh',field)].to_ndarray()[:,0,0] + #J_th = get_theoretical_J_field(field, t0) + max_error = abs(J_sim[min_i:max_i]-J_th[min_i:max_i]).max()/abs(J_th[min_i:max_i]).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the charge +for field in ['rho']: + rho_sim = data[('boxlib',field)].to_ndarray()[:,0,0] + #rho_th = get_theoretical_rho_field(field, t0) + max_error = abs(rho_sim[min_i:max_i]-rho_th[min_i:max_i]).max()/abs(rho_th[min_i:max_i]).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +V_sim = np.divide(J_sim,rho_sim) +V_sim /= c + +# Create a figure with 2 rows and 2 columns +fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 8)) + +# Titles and labels +titles = ['Ez', 'rho', 'Jz', 'Vz/c'] +xlabel = r'Xi' +ylabel = ['Ez', 'rho', 'Jz', 'Vz/c'] + +# Plotting loop +for i in range(3): + ax = axes[i // 2, i % 2] # Get the current subplot + + # Plot theoretical data + ax.plot(xi, [E_th, rho_th, J_th, V_th][i], label='Theoretical') + + # Plot simulated data + ax.plot(xi, [E_sim, rho_sim, J_sim, V_sim][i], label='Simulated') + + # Set titles and labels + ax.set_title(f'{titles[i]} vs Xi') + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel[i]) + + # Add legend + ax.legend() + +# Adjust subplot layout +plt.tight_layout() + +# Save the figure +plt.savefig('wfa_fluid_nonlinear_1d_analysis.png') + +plt.show() + + +tolerance_rel = 0.20 + +print("error_rel : " + str(error_rel)) +print("tolerance_rel: " + str(tolerance_rel)) + +assert( error_rel < tolerance_rel ) + +test_name = os.path.split(os.getcwd())[1] +checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Physics_applications/laser_acceleration/analysis_1d_fluids_boosted.py b/Examples/Physics_applications/laser_acceleration/analysis_1d_fluids_boosted.py new file mode 100755 index 00000000000..4f4ae2a812a --- /dev/null +++ b/Examples/Physics_applications/laser_acceleration/analysis_1d_fluids_boosted.py @@ -0,0 +1,173 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2023 Grant Johnson, Remi Lehe +# +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL +# +# This is a script that analyses the simulation results from +# the script `inputs_1d`. This simulates a 1D WFA with Pondermotive Envelope: +# REF: (Equations 20-23) https://journals.aps.org/rmp/pdf/10.1103/RevModPhys.81.1229 +import os +import sys + +import matplotlib + +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import yt + +yt.funcs.mylog.setLevel(50) + +import numpy as np +from scipy.constants import c, e, epsilon_0, m_e + +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +# this will be the name of the plot file +fn = sys.argv[1] + +# Parameters (these parameters must match the parameters in `inputs.multi.rt`) +n0 = 20.e23 +# Plasma frequency +wp = np.sqrt((n0*e**2)/(m_e*epsilon_0)) +kp = wp/c +tau = 15.e-15 +a0 = 2.491668 +e = -e #Electrons +lambda_laser = 0.8e-6 + +zmin = -20e-6; zmax = 100.e-6; Nz = 4864 + +# Compute the theory + +# Call the ode solver +from scipy.integrate import odeint + + +# ODE Function +def odefcn(phi, xi, kp, a0, c, tau, xi_0, lambda_laser): + phi1, phi2 = phi + a_sq = a0**2 * np.exp(-2 * (xi - xi_0)**2 / (c**2 * tau**2))*np.sin(2*np.pi*(xi - xi_0)/lambda_laser)**2 + dphi1_dxi = phi2 + dphi2_dxi = kp**2 * ((1 + a_sq) / (2 * (1 + phi1)**2) - 0.5) + return [dphi1_dxi, dphi2_dxi] + +# Call odeint to solve the ODE +xi_span = [-20e-6, 100e-6] +xi_0 = 0e-6 +phi0 = [0.0, 0.0] +dxi = (zmax-zmin)/Nz +xi = zmin + dxi*( 0.5 + np.arange(Nz) ) +phi = odeint(odefcn, phi0, xi, args=(kp, a0, c, tau, xi_0, lambda_laser)) + +# Change array direction to match the simulations +xi = -xi[::-1] +phi = phi[::-1] +xi_0 = -0e-6 +phi2 = phi[:, 0] +Ez = -phi[:, 1] + +# Compute the derived quantities +a_sq = a0**2 * np.exp(-2 * (xi - xi_0)**2 / (c**2 * tau**2)) *np.sin(2*np.pi*(xi - xi_0)/lambda_laser)**2 +gamma_perp_sq = 1 + a_sq +n = n0 * (gamma_perp_sq + (1 + phi2)**2) / (2 * (1 + phi2)**2) +uz = (gamma_perp_sq - (1 + phi2)**2) / (2 * (1 + phi2)) +gamma = (gamma_perp_sq + (1 + phi2)**2) / (2 * (1 + phi2)) + +# Theory Components [convert to si] +uz *= c +J_th = np.multiply( np.divide(uz,gamma), n ) +J_th *= e +rho_th = e*n +E_th = Ez +E_th *= ((m_e*c*c)/e) +V_th = np.divide(uz,gamma) +V_th /= c +# Remove the ions +rho_th = rho_th - e*n0 + +# Dicate which region to compare solutions over (cuttoff 0's from BTD extra) +min_i = 200 +max_i = 4864 + +# Read the file +ds = yt.load(fn) +t0 = ds.current_time.to_value() +data = ds.covering_grid(level=0, left_edge=ds.domain_left_edge, + dims=ds.domain_dimensions) +# Check the validity of the fields +error_rel = 0 +for field in ['Ez']: + E_sim = data[('mesh',field)].to_ndarray()[:,0,0] + #E_th = get_theoretical_field(field, t0) + max_error = abs(E_sim[min_i:max_i]-E_th[min_i:max_i]).max()/abs(E_th[min_i:max_i]).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the currents +for field in ['Jz']: + J_sim = data[('mesh',field)].to_ndarray()[:,0,0] + #J_th = get_theoretical_J_field(field, t0) + max_error = abs(J_sim[min_i:max_i]-J_th[min_i:max_i]).max()/abs(J_th[min_i:max_i]).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the charge +for field in ['rho']: + rho_sim = data[('boxlib',field)].to_ndarray()[:,0,0] + #rho_th = get_theoretical_rho_field(field, t0) + max_error = abs(rho_sim[min_i:max_i]-rho_th[min_i:max_i]).max()/abs(rho_th[min_i:max_i]).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +V_sim = np.divide(J_sim,rho_sim) +V_sim /= c + +# Create a figure with 2 rows and 2 columns +fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 8)) + +# Titles and labels +titles = ['Ez', 'rho', 'Jz', 'Vz/c'] +xlabel = r'Xi' +ylabel = ['Ez', 'rho', 'Jz', 'Vz/c'] + +# Plotting loop +for i in range(3): + ax = axes[i // 2, i % 2] # Get the current subplot + + # Plot theoretical data + ax.plot(xi, [E_th, rho_th, J_th, V_th][i], label='Theoretical') + + # Plot simulated data + ax.plot(xi, [E_sim, rho_sim, J_sim, V_sim][i], label='Simulated') + + # Set titles and labels + ax.set_title(f'{titles[i]} vs Xi') + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel[i]) + + # Add legend + ax.legend() + +# Adjust subplot layout +plt.tight_layout() + +# Save the figure +plt.savefig('wfa_fluid_nonlinear_1d_analysis.png') + +plt.show() + + +tolerance_rel = 0.30 + +print("error_rel : " + str(error_rel)) +print("tolerance_rel: " + str(tolerance_rel)) + +assert( error_rel < tolerance_rel ) + +test_name = os.path.split(os.getcwd())[1] +checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Physics_applications/laser_acceleration/inputs_1d_fluids b/Examples/Physics_applications/laser_acceleration/inputs_1d_fluids new file mode 100644 index 00000000000..73fa6b7283f --- /dev/null +++ b/Examples/Physics_applications/laser_acceleration/inputs_1d_fluids @@ -0,0 +1,72 @@ +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 40000 +amr.n_cell = 10240 +amr.max_grid_size = 512 # maximum size of each AMReX box, used to decompose the domain +amr.blocking_factor = 512 # minimum size of each AMReX box, used to decompose the domain +geometry.dims = 1 +geometry.prob_lo = -120.e-6 # physical domain +geometry.prob_hi = 0.e-6 +amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) + +################################# +####### Boundary condition ###### +################################# +boundary.field_lo = pec +boundary.field_hi = pec + +################################# +############ NUMERICS ########### +################################# +warpx.verbose = 1 +warpx.do_dive_cleaning = 0 +warpx.use_filter = 0 +warpx.cfl = 0.45 #Fluid CFL < 0.5 +warpx.do_moving_window = 1 +warpx.moving_window_dir = z +warpx.moving_window_v = 1.0 # units of speed of light +warpx.do_dynamic_scheduling = 0 +warpx.serialize_initial_conditions = 1 + +################################# +############ PLASMA ############# +################################# +fluids.species_names = electrons ions + +electrons.species_type = electron +electrons.profile = parse_density_function +electrons.density_function(x,y,z) = "1.0e10 + 20.e23*((z*5.e4 + -0.5)*(z>10.e-6)*(z<30.e-6)) + 20.e23*((z>30.e-6))" +electrons.momentum_distribution_type = "at_rest" + +ions.charge = q_e +ions.mass = m_p +ions.profile = parse_density_function +ions.density_function(x,y,z) = "1.0e10 + 20.e23*((z*5.e4 + -0.5)*(z>10.e-6)*(z<30.e-6)) + 20.e23*((z>30.e-6))" +ions.momentum_distribution_type = "at_rest" + +# Order of particle shape factors +algo.particle_shape = 3 + +################################# +############ LASER ############## +################################# +lasers.names = laser1 +laser1.profile = Gaussian +laser1.position = 0. 0. -11.e-6 # This point is on the laser plane +laser1.direction = 0. 0. 1. # The plane normal direction +laser1.polarization = 0. 1. 0. # The main polarization vector +laser1.e_max = 10.e12 # Maximum amplitude of the laser field (in V/m) +laser1.profile_waist = 5.e-6 # The waist of the laser (in m) +laser1.profile_duration = 15.e-15 # The duration of the laser (in s) +laser1.profile_t_peak = 30.e-15 # Time at which the laser reaches its peak (in s) +laser1.profile_focal_distance = 100.e-6 # Focal distance from the antenna (in m) +laser1.wavelength = 0.8e-6 # The wavelength of the laser (in m) + +# Diagnostics +diagnostics.diags_names = diag1 + +# LAB +diag1.intervals = 20000 +diag1.diag_type = Full +diag1.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho diff --git a/Examples/Physics_applications/laser_acceleration/inputs_1d_fluids_boosted b/Examples/Physics_applications/laser_acceleration/inputs_1d_fluids_boosted new file mode 100644 index 00000000000..f9437716f66 --- /dev/null +++ b/Examples/Physics_applications/laser_acceleration/inputs_1d_fluids_boosted @@ -0,0 +1,79 @@ +################################# +####### GENERAL PARAMETERS ###### +################################# +max_step = 10000 +amr.n_cell = 5120 +amr.max_grid_size = 1024 # maximum size of each AMReX box, used to decompose the domain +amr.blocking_factor = 1024 # minimum size of each AMReX box, used to decompose the domain +geometry.dims = 1 +geometry.prob_lo = -120.e-6 # physical domain +geometry.prob_hi = 0.e-6 +amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported) + +################################# +####### Boundary condition ###### +################################# +boundary.field_lo = pec +boundary.field_hi = pec + +################################# +############ NUMERICS ########### +################################# +warpx.verbose = 1 +warpx.do_dive_cleaning = 0 +warpx.use_filter = 0 +warpx.cfl = 0.45 #Fluid CFL < 0.5 +warpx.do_moving_window = 1 +warpx.moving_window_dir = z +warpx.moving_window_v = 1.0 # units of speed of light +warpx.do_dynamic_scheduling = 0 +warpx.serialize_initial_conditions = 1 + +### BOOST ### +warpx.gamma_boost = 1.5 +warpx.boost_direction = z + +################################# +############ PLASMA ############# +################################# +fluids.species_names = electrons ions + +electrons.species_type = electron +electrons.profile = parse_density_function +electrons.density_function(x,y,z) = "1.0e10 + 20.e23*((z*5.e4 + -0.5)*(z>10.e-6)*(z<30.e-6)) + 20.e23*((z>30.e-6))" +electrons.momentum_distribution_type = "at_rest" + +ions.charge = q_e +ions.mass = m_p +ions.profile = parse_density_function +ions.density_function(x,y,z) = "1.0e10 + 20.e23*((z*5.e4 + -0.5)*(z>10.e-6)*(z<30.e-6)) + 20.e23*((z>30.e-6))" +ions.momentum_distribution_type = "at_rest" + +# Order of particle shape factors +algo.particle_shape = 3 + +################################# +############ LASER ############## +################################# +lasers.names = laser1 +laser1.profile = Gaussian +laser1.position = 0. 0. -11.e-6 # This point is on the laser plane +laser1.direction = 0. 0. 1. # The plane normal direction +laser1.polarization = 0. 1. 0. # The main polarization vector +laser1.e_max = 10.e12 # Maximum amplitude of the laser field (in V/m) +laser1.profile_waist = 5.e-6 # The waist of the laser (in m) +laser1.profile_duration = 15.e-15 # The duration of the laser (in s) +laser1.profile_t_peak = 30.e-15 # Time at which the laser reaches its peak (in s) +laser1.profile_focal_distance = 100.e-6 # Focal distance from the antenna (in m) +laser1.wavelength = 0.8e-6 # The wavelength of the laser (in m) + +# Diagnostics +diagnostics.diags_names = diag1 + + +# BOOST +diag1.diag_type = BackTransformed +diag1.do_back_transformed_fields = 1 +diag1.num_snapshots_lab = 2 +diag1.dt_snapshots_lab = 6.e-13 +diag1.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz rho diff --git a/Examples/Tests/langmuir_fluids/analysis_1d.py b/Examples/Tests/langmuir_fluids/analysis_1d.py new file mode 100755 index 00000000000..2d1a8f69d1d --- /dev/null +++ b/Examples/Tests/langmuir_fluids/analysis_1d.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2022 Jean-Luc Vay, Maxence Thevenet, Remi Lehe, Prabhat Kumar, Axel Huebl, Grant Johnson +# +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL +# +# This is a script that analyses the simulation results from +# the script `inputs.multi.rt`. This simulates a 1D periodic plasma wave. +# The electric field in the simulation is given (in theory) by: +# $$ E_z = \epsilon \,\frac{m_e c^2 k_z}{q_e}\sin(k_z z)\sin( \omega_p t)$$ +import os +import sys + +import matplotlib + +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import yt + +yt.funcs.mylog.setLevel(50) + +import numpy as np +from scipy.constants import c, e, epsilon_0, m_e + +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +# this will be the name of the plot file +fn = sys.argv[1] + +# Parameters (these parameters must match the parameters in `inputs.multi.rt`) +epsilon = 0.01 +n = 4.e24 +n_osc_z = 2 +zmin = -20e-6; zmax = 20.e-6; Nz = 128 + +# Wave vector of the wave +kz = 2.*np.pi*n_osc_z/(zmax-zmin) +# Plasma frequency +wp = np.sqrt((n*e**2)/(m_e*epsilon_0)) + +k = {'Ez':kz,'Jz':kz} +cos = {'Ez':(1,1,0), 'Jz':(1,1,0)} +cos_rho = {'rho': (1,1,1)} + +def get_contribution( is_cos, k ): + du = (zmax-zmin)/Nz + u = zmin + du*( 0.5 + np.arange(Nz) ) + if is_cos == 1: + return( np.cos(k*u) ) + else: + return( np.sin(k*u) ) + +def get_theoretical_field( field, t ): + amplitude = epsilon * (m_e*c**2*k[field])/e * np.sin(wp*t) + cos_flag = cos[field] + z_contribution = get_contribution( cos_flag[2], kz ) + + E = amplitude * z_contribution + + return( E ) + +def get_theoretical_J_field( field, t ): + # wpdt/2 accounts for the Yee halfstep offset of the current + dt = t / 80 # SPECIFIC to config parameters! + amplitude = - epsilon_0 * wp * epsilon * (m_e*c**2*k[field])/e * np.cos(wp*t-wp*dt/2) + cos_flag = cos[field] + + z_contribution = get_contribution( cos_flag[2], kz ) + + J = amplitude * z_contribution + + return( J ) + +def get_theoretical_rho_field( field, t ): + amplitude = epsilon_0 * epsilon * (m_e*c**2*(kz*kz))/e * np.sin(wp*t) + cos_flag = cos_rho[field] + z_contribution = get_contribution( cos_flag[2], kz) + + rho = amplitude * z_contribution + + return( rho ) + +# Read the file +ds = yt.load(fn) +t0 = ds.current_time.to_value() +data = ds.covering_grid(level=0, left_edge=ds.domain_left_edge, + dims=ds.domain_dimensions) +# Check the validity of the fields +error_rel = 0 +for field in ['Ez']: + E_sim = data[('mesh',field)].to_ndarray()[:,0,0] + E_th = get_theoretical_field(field, t0) + max_error = abs(E_sim-E_th).max()/abs(E_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the currents +for field in ['Jz']: + J_sim = data[('mesh',field)].to_ndarray()[:,0,0] + J_th = get_theoretical_J_field(field, t0) + max_error = abs(J_sim-J_th).max()/abs(J_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the charge +for field in ['rho']: + rho_sim = data[('boxlib',field)].to_ndarray()[:,0,0] + rho_th = get_theoretical_rho_field(field, t0) + max_error = abs(rho_sim-rho_th).max()/abs(rho_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Plot the last field from the loop (Ez at iteration 80) +plt.subplot2grid( (1,2), (0,0) ) +plt.plot( E_sim ) +#plt.colorbar() +plt.title('Ez, last iteration\n(simulation)') +plt.subplot2grid( (1,2), (0,1) ) +plt.plot( E_th ) +#plt.colorbar() +plt.title('Ez, last iteration\n(theory)') +plt.tight_layout() +plt.savefig('langmuir_fluid_multi_1d_analysis.png') + +tolerance_rel = 0.05 + +print("error_rel : " + str(error_rel)) +print("tolerance_rel: " + str(tolerance_rel)) + +assert( error_rel < tolerance_rel ) + +test_name = os.path.split(os.getcwd())[1] +checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Tests/langmuir_fluids/analysis_2d.py b/Examples/Tests/langmuir_fluids/analysis_2d.py new file mode 100755 index 00000000000..cf5d2fb44de --- /dev/null +++ b/Examples/Tests/langmuir_fluids/analysis_2d.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +# Copyright 2019 Jean-Luc Vay, Maxence Thevenet, Remi Lehe, Grant Johnson +# +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL +# +# This is a script that analyses the simulation results from +# the script `inputs.multi.rt`. This simulates a 3D periodic plasma wave. +# The electric field in the simulation is given (in theory) by: +# $$ E_x = \epsilon \,\frac{m_e c^2 k_x}{q_e}\sin(k_x x)\cos(k_y y)\cos(k_z z)\sin( \omega_p t)$$ +# $$ E_y = \epsilon \,\frac{m_e c^2 k_y}{q_e}\cos(k_x x)\sin(k_y y)\cos(k_z z)\sin( \omega_p t)$$ +# $$ E_z = \epsilon \,\frac{m_e c^2 k_z}{q_e}\cos(k_x x)\cos(k_y y)\sin(k_z z)\sin( \omega_p t)$$ +import os +import sys + +import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable +import yt + +yt.funcs.mylog.setLevel(50) + +import numpy as np +from scipy.constants import c, e, epsilon_0, m_e + +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +# this will be the name of the plot file +fn = sys.argv[1] + +# Parameters (these parameters must match the parameters in `inputs.multi.rt`) +epsilon = 0.01 +n = 4.e24 +n_osc_x = 2 +n_osc_z = 2 +xmin = -20e-6; xmax = 20.e-6; Nx = 128 +zmin = -20e-6; zmax = 20.e-6; Nz = 128 + +# Wave vector of the wave +kx = 2.*np.pi*n_osc_x/(xmax-xmin) +kz = 2.*np.pi*n_osc_z/(zmax-zmin) +# Plasma frequency +wp = np.sqrt((n*e**2)/(m_e*epsilon_0)) + +k = {'Ex':kx, 'Ez':kz, 'Jx':kx, 'Jz':kz} +cos = {'Ex': (0,1,1), 'Ez':(1,1,0),'Jx': (0,1,1), 'Jz':(1,1,0)} +cos_rho = {'rho': (1,1,1)} + +def get_contribution( is_cos, k ): + du = (xmax-xmin)/Nx + u = xmin + du*( 0.5 + np.arange(Nx) ) + if is_cos == 1: + return( np.cos(k*u) ) + else: + return( np.sin(k*u) ) + +def get_theoretical_field( field, t ): + amplitude = epsilon * (m_e*c**2*k[field])/e * np.sin(wp*t) + cos_flag = cos[field] + x_contribution = get_contribution( cos_flag[0], kx ) + z_contribution = get_contribution( cos_flag[2], kz ) + + E = amplitude * x_contribution[:, np.newaxis ] \ + * z_contribution[np.newaxis, :] + + return( E ) + +def get_theoretical_J_field( field, t ): + # wpdt/2 accounts for the Yee halfstep offset of the current + dt = t / 40 # SPECIFIC to config parameters! + amplitude = - epsilon_0 * wp * epsilon * (m_e*c**2*k[field])/e * np.cos(wp*t-wp*dt/2) + cos_flag = cos[field] + x_contribution = get_contribution( cos_flag[0], kx ) + z_contribution = get_contribution( cos_flag[2], kz ) + + J = amplitude * x_contribution[:, np.newaxis] \ + * z_contribution[np.newaxis, :] + + return( J ) + +def get_theoretical_rho_field( field, t ): + amplitude = epsilon_0 * epsilon * (m_e*c**2*(kx*kx+kz*kz))/e * np.sin(wp*t) + cos_flag = cos_rho[field] + x_contribution = get_contribution( cos_flag[0], kx ) + z_contribution = get_contribution( cos_flag[2], kz ) + + rho = amplitude * x_contribution[:, np.newaxis] \ + * z_contribution[ np.newaxis, :] + + return( rho ) + +# Read the file +ds = yt.load(fn) +t0 = ds.current_time.to_value() +data = ds.covering_grid(level = 0, left_edge = ds.domain_left_edge, dims = ds.domain_dimensions) +edge = np.array([(ds.domain_left_edge[1]).item(), (ds.domain_right_edge[1]).item(), \ + (ds.domain_left_edge[0]).item(), (ds.domain_right_edge[0]).item()]) + +# Check the validity of the fields +error_rel = 0 +for field in ['Ex', 'Ez']: + E_sim = data[('mesh',field)].to_ndarray()[:,:,0] + E_th = get_theoretical_field(field, t0) + max_error = abs(E_sim-E_th).max()/abs(E_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the currents +for field in ['Jx', 'Jz']: + J_sim = data[('mesh',field)].to_ndarray()[:,:,0] + J_th = get_theoretical_J_field(field, t0) + max_error = abs(J_sim-J_th).max()/abs(J_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the charge +for field in ['rho']: + rho_sim = data[('boxlib',field)].to_ndarray()[:,:,0] + rho_th = get_theoretical_rho_field(field, t0) + max_error = abs(rho_sim-rho_th).max()/abs(rho_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Plot the last field from the loop (Ez at iteration 40) +fig, (ax1, ax2) = plt.subplots(1, 2, dpi = 100) +# First plot +vmin = E_sim.min() +vmax = E_sim.max() +cax1 = make_axes_locatable(ax1).append_axes('right', size = '5%', pad = '5%') +im1 = ax1.imshow(E_sim, origin = 'lower', extent = edge, vmin = vmin, vmax = vmax) +cb1 = fig.colorbar(im1, cax = cax1) +ax1.set_xlabel(r'$z$') +ax1.set_ylabel(r'$x$') +ax1.set_title(r'$E_z$ (sim)') +# Second plot +vmin = E_th.min() +vmax = E_th.max() +cax2 = make_axes_locatable(ax2).append_axes('right', size = '5%', pad = '5%') +im2 = ax2.imshow(E_th, origin = 'lower', extent = edge, vmin = vmin, vmax = vmax) +cb2 = fig.colorbar(im2, cax = cax2) +ax2.set_xlabel(r'$z$') +ax2.set_ylabel(r'$x$') +ax2.set_title(r'$E_z$ (theory)') +# Save figure +fig.tight_layout() +fig.savefig('Langmuir_fluid_multi_2d_analysis.png', dpi = 200) + +tolerance_rel = 0.05 + +print("error_rel : " + str(error_rel)) +print("tolerance_rel: " + str(tolerance_rel)) + +assert( error_rel < tolerance_rel ) + +test_name = os.path.split(os.getcwd())[1] +checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Tests/langmuir_fluids/analysis_3d.py b/Examples/Tests/langmuir_fluids/analysis_3d.py new file mode 100755 index 00000000000..0211956859e --- /dev/null +++ b/Examples/Tests/langmuir_fluids/analysis_3d.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +# Copyright 2019-2022 Jean-Luc Vay, Maxence Thevenet, Remi Lehe, Axel Huebl, Grant Johnson +# +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL +# +# This is a script that analyses the simulation results from +# the script `inputs.multi.rt`. This simulates a 3D periodic plasma wave. +# The electric field in the simulation is given (in theory) by: +# $$ E_x = \epsilon \,\frac{m_e c^2 k_x}{q_e}\sin(k_x x)\cos(k_y y)\cos(k_z z)\sin( \omega_p t)$$ +# $$ E_y = \epsilon \,\frac{m_e c^2 k_y}{q_e}\cos(k_x x)\sin(k_y y)\cos(k_z z)\sin( \omega_p t)$$ +# $$ E_z = \epsilon \,\frac{m_e c^2 k_z}{q_e}\cos(k_x x)\cos(k_y y)\sin(k_z z)\sin( \omega_p t)$$ +import os +import re +import sys + +import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable +import yt + +yt.funcs.mylog.setLevel(50) + +import numpy as np +from scipy.constants import c, e, epsilon_0, m_e + +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +# this will be the name of the plot file +fn = sys.argv[1] + +# Parameters (these parameters must match the parameters in `inputs.multi.rt`) +epsilon = 0.01 +n = 4.e24 +n_osc_x = 2 +n_osc_y = 2 +n_osc_z = 2 +lo = [-20.e-6, -20.e-6, -20.e-6] +hi = [ 20.e-6, 20.e-6, 20.e-6] +Ncell = [64, 64, 64] + +# Wave vector of the wave +kx = 2.*np.pi*n_osc_x/(hi[0]-lo[0]) +ky = 2.*np.pi*n_osc_y/(hi[1]-lo[1]) +kz = 2.*np.pi*n_osc_z/(hi[2]-lo[2]) +# Plasma frequency +wp = np.sqrt((n*e**2)/(m_e*epsilon_0)) + +k = {'Ex':kx, 'Ey':ky, 'Ez':kz, 'Jx':kx, 'Jy':ky, 'Jz':kz} +cos = {'Ex': (0,1,1), 'Ey':(1,0,1), 'Ez':(1,1,0),'Jx': (0,1,1), 'Jy':(1,0,1), 'Jz':(1,1,0)} +cos_rho = {'rho': (1,1,1)} + +def get_contribution( is_cos, k, idim ): + du = (hi[idim]-lo[idim])/Ncell[idim] + u = lo[idim] + du*( 0.5 + np.arange(Ncell[idim]) ) + if is_cos[idim] == 1: + return( np.cos(k*u) ) + else: + return( np.sin(k*u) ) + +def get_theoretical_field( field, t ): + amplitude = epsilon * (m_e*c**2*k[field])/e * np.sin(wp*t) + cos_flag = cos[field] + x_contribution = get_contribution( cos_flag, kx, 0 ) + y_contribution = get_contribution( cos_flag, ky, 1 ) + z_contribution = get_contribution( cos_flag, kz, 2 ) + + E = amplitude * x_contribution[:, np.newaxis, np.newaxis] \ + * y_contribution[np.newaxis, :, np.newaxis] \ + * z_contribution[np.newaxis, np.newaxis, :] + + return( E ) + +def get_theoretical_J_field( field, t ): + # wpdt/2 accounts for the Yee halfstep offset of the current + dt = t / 40 # SPECIFIC to config parameters! + amplitude = - epsilon_0 * wp * epsilon * (m_e*c**2*k[field])/e * np.cos(wp*t-wp*dt/2) + cos_flag = cos[field] + x_contribution = get_contribution( cos_flag, kx, 0 ) + y_contribution = get_contribution( cos_flag, ky, 1 ) + z_contribution = get_contribution( cos_flag, kz, 2 ) + + J = amplitude * x_contribution[:, np.newaxis, np.newaxis] \ + * y_contribution[np.newaxis, :, np.newaxis] \ + * z_contribution[np.newaxis, np.newaxis, :] + + return( J ) + +def get_theoretical_rho_field( field, t ): + amplitude = epsilon_0 * epsilon * (m_e*c**2*(kx*kx+ky*ky+kz*kz))/e * np.sin(wp*t) + cos_flag = cos_rho[field] + x_contribution = get_contribution( cos_flag, kx, 0 ) + y_contribution = get_contribution( cos_flag, ky, 1 ) + z_contribution = get_contribution( cos_flag, kz, 2 ) + + rho = amplitude * x_contribution[:, np.newaxis, np.newaxis] \ + * y_contribution[np.newaxis, :, np.newaxis] \ + * z_contribution[np.newaxis, np.newaxis, :] + + return( rho ) + +# Read the file +ds = yt.load(fn) + + +t0 = ds.current_time.to_value() +data = ds.covering_grid(level = 0, left_edge = ds.domain_left_edge, dims = ds.domain_dimensions) +edge = np.array([(ds.domain_left_edge[2]).item(), (ds.domain_right_edge[2]).item(), \ + (ds.domain_left_edge[0]).item(), (ds.domain_right_edge[0]).item()]) + +# Check the validity of the fields +error_rel = 0 +for field in ['Ex', 'Ey', 'Ez']: + E_sim = data[('mesh',field)].to_ndarray() + E_th = get_theoretical_field(field, t0) + max_error = abs(E_sim-E_th).max()/abs(E_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + + +# Check the validity of the currents +for field in ['Jx', 'Jy', 'Jz']: + J_sim = data[('mesh',field)].to_ndarray() + J_th = get_theoretical_J_field(field, t0) + max_error = abs(J_sim-J_th).max()/abs(J_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + +# Check the validity of the charge +for field in ['rho']: + rho_sim = data[('boxlib',field)].to_ndarray() + rho_th = get_theoretical_rho_field(field, t0) + max_error = abs(rho_sim-rho_th).max()/abs(rho_th).max() + print('%s: Max error: %.2e' %(field,max_error)) + error_rel = max( error_rel, max_error ) + + +# Plot the last field from the loop (Ez at iteration 40) +fig, (ax1, ax2) = plt.subplots(1, 2, dpi = 100) +# First plot (slice at y=0) +E_plot = E_sim[:,Ncell[1]//2+1,:] +vmin = E_plot.min() +vmax = E_plot.max() +cax1 = make_axes_locatable(ax1).append_axes('right', size = '5%', pad = '5%') +im1 = ax1.imshow(E_plot, origin = 'lower', extent = edge, vmin = vmin, vmax = vmax) +cb1 = fig.colorbar(im1, cax = cax1) +ax1.set_xlabel(r'$z$') +ax1.set_ylabel(r'$x$') +ax1.set_title(r'$E_z$ (sim)') +# Second plot (slice at y=0) +E_plot = E_th[:,Ncell[1]//2+1,:] +vmin = E_plot.min() +vmax = E_plot.max() +cax2 = make_axes_locatable(ax2).append_axes('right', size = '5%', pad = '5%') +im2 = ax2.imshow(E_plot, origin = 'lower', extent = edge, vmin = vmin, vmax = vmax) +cb2 = fig.colorbar(im2, cax = cax2) +ax2.set_xlabel(r'$z$') +ax2.set_ylabel(r'$x$') +ax2.set_title(r'$E_z$ (theory)') +# Save figure +fig.tight_layout() +fig.savefig('Langmuir_fluid_multi_analysis.png', dpi = 200) + +tolerance_rel = 5e-2 + +print("error_rel : " + str(error_rel)) +print("tolerance_rel: " + str(tolerance_rel)) + +assert( error_rel < tolerance_rel ) + +test_name = os.path.split(os.getcwd())[1] + +if re.search( 'single_precision', fn ): + checksumAPI.evaluate_checksum(test_name, fn, rtol=1.e-3) +else: + checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Tests/langmuir_fluids/analysis_rz.py b/Examples/Tests/langmuir_fluids/analysis_rz.py new file mode 100755 index 00000000000..108d054e75a --- /dev/null +++ b/Examples/Tests/langmuir_fluids/analysis_rz.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 + +# Copyright 2019 David Grote, Maxence Thevenet, Grant Johnson, Remi Lehe +# +# This file is part of WarpX. +# +# License: BSD-3-Clause-LBNL +# +# This is a script that analyses the simulation results from +# the script `inputs.multi.rz.rt`. This simulates a RZ periodic plasma wave. +# The electric field in the simulation is given (in theory) by: +# $$ 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) +# Unrelated to the Langmuir waves, we also test the plotfile particle filter function in this +# analysis script. +import os +import re +import sys + +import matplotlib + +matplotlib.use('Agg') +import matplotlib.pyplot as plt +import yt + +yt.funcs.mylog.setLevel(50) + +import numpy as np +from scipy.constants import c, e, epsilon_0, m_e + +sys.path.insert(1, '../../../../warpx/Regression/Checksum/') +import checksumAPI + +# this will be the name of the plot file +fn = sys.argv[1] + +test_name = 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 +w0 = 5.e-6 +n_osc_z = 2 +rmin = 0e-6; rmax = 20.e-6; Nr = 64 +zmin = -20e-6; zmax = 20.e-6; Nz = 128 + +# Wave vector of the wave +k0 = 2.*np.pi*n_osc_z/(zmax-zmin) +# Plasma frequency +wp = np.sqrt((n*e**2)/(m_e*epsilon_0)) +kp = wp/c + +def Er( z, r, epsilon, k0, w0, wp, t) : + """ + Return the radial electric field as an array + of the same length as z and r, in the half-plane theta=0 + """ + Er_array = \ + epsilon * m_e*c**2/e * 2*r/w0**2 * \ + np.exp( -r**2/w0**2 ) * np.sin( k0*z ) * np.sin( wp*t ) + return( Er_array ) + +def Ez( z, r, epsilon, k0, w0, wp, t) : + """ + Return the longitudinal electric field as an array + of the same length as z and r, in the half-plane theta=0 + """ + Ez_array = \ + - epsilon * m_e*c**2/e * k0 * \ + np.exp( -r**2/w0**2 ) * np.cos( k0*z ) * np.sin( wp*t ) + return( Ez_array ) + +def Jr( z, r, epsilon, k0, w0, wp, t) : + """ + Return the radial current density as an array + of the same length as z and r, in the half-plane theta=0 + """ + dt = t / 80 # SPECIFIC to config parameters! + Jr_array = \ + - epsilon_0 * epsilon * m_e*c**2/e * 2*r/w0**2 * \ + np.exp( -r**2/w0**2 ) * np.sin( k0*z ) * np.cos( wp*t -wp*dt/2) * wp #phase_error = wp*dt/2 + return( Jr_array ) + +def Jz( z, r, epsilon, k0, w0, wp, t) : + """ + Return the longitudinal current density as an array + of the same length as z and r, in the half-plane theta=0 + """ + dt = t / 80 # SPECIFIC to config parameters! + Jz_array = \ + epsilon_0 * epsilon * m_e*c**2/e * k0 * \ + np.exp( -r**2/w0**2 ) * np.cos( k0*z ) * np.cos( wp*t -wp*dt/2) * wp #phase_error = wp*dt/2 + return( Jz_array ) + +def rho( z, r, epsilon, k0, w0, wp, t) : + """ + Return the charge density as an array + of the same length as z and r, in the half-plane theta=0 + """ + rho_array = \ + epsilon_0 * epsilon * m_e*c**2/e * np.sin( wp*t ) * np.sin( k0*z ) * np.exp( -r**2/w0**2 ) * \ + ((4.0/(w0**2))*(1 - (r**2)/(w0**2)) + k0**2) + return( rho_array ) + +# Read the file +ds = yt.load(fn) +t0 = ds.current_time.to_value() +data = ds.covering_grid(level=0, left_edge=ds.domain_left_edge, + dims=ds.domain_dimensions) + +# Get cell centered coordinates +dr = (rmax - rmin)/Nr +dz = (zmax - zmin)/Nz +coords = np.indices([Nr, Nz],'d') +rr = rmin + (coords[0] + 0.5)*dr +zz = zmin + (coords[1] + 0.5)*dz + +# Check the validity of the fields +overall_max_error = 0 +Er_sim = data[('boxlib','Er')].to_ndarray()[:,:,0] +Er_th = Er(zz, rr, epsilon, k0, w0, wp, t0) +max_error = abs(Er_sim-Er_th).max()/abs(Er_th).max() +print('Er: Max error: %.2e' %(max_error)) +overall_max_error = max( overall_max_error, max_error ) + +Ez_sim = data[('boxlib','Ez')].to_ndarray()[:,:,0] +Ez_th = Ez(zz, rr, epsilon, k0, w0, wp, t0) +max_error = abs(Ez_sim-Ez_th).max()/abs(Ez_th).max() +print('Ez: Max error: %.2e' %(max_error)) +overall_max_error = max( overall_max_error, max_error ) + +Jr_sim = data[('boxlib','jr')].to_ndarray()[:,:,0] +Jr_th = Jr(zz, rr, epsilon, k0, w0, wp, t0) +max_error = abs(Jr_sim-Jr_th).max()/abs(Jr_th).max() +print('Jr: Max error: %.2e' %(max_error)) +overall_max_error = max( overall_max_error, max_error ) + +Jz_sim = data[('boxlib','jz')].to_ndarray()[:,:,0] +Jz_th = Jz(zz, rr, epsilon, k0, w0, wp, t0) +max_error = abs(Jz_sim-Jz_th).max()/abs(Jz_th).max() +print('Jz: Max error: %.2e' %(max_error)) +overall_max_error = max( overall_max_error, max_error ) + +rho_sim = data[('boxlib','rho')].to_ndarray()[:,:,0] +rho_th = rho(zz, rr, epsilon, k0, w0, wp, t0) +max_error = abs(rho_sim-rho_th).max()/abs(rho_th).max() +print('rho: Max error: %.2e' %(max_error)) +overall_max_error = max( overall_max_error, max_error ) + +# Plot the last field from the loop (Ez at iteration 40) +plt.subplot2grid( (1,2), (0,0) ) +plt.imshow( Ez_sim ) +plt.colorbar() +plt.title('Ez, last iteration\n(simulation)') +plt.subplot2grid( (1,2), (0,1) ) +plt.imshow( Ez_th ) +plt.colorbar() +plt.title('Ez, last iteration\n(theory)') +plt.tight_layout() +plt.savefig(test_name+'_analysis.png') + +error_rel = overall_max_error + +tolerance_rel = 0.08 + +print("error_rel : " + str(error_rel)) +print("tolerance_rel: " + str(tolerance_rel)) + +assert( error_rel < tolerance_rel ) + +checksumAPI.evaluate_checksum(test_name, fn) diff --git a/Examples/Tests/langmuir_fluids/inputs_1d b/Examples/Tests/langmuir_fluids/inputs_1d new file mode 100644 index 00000000000..48fda36c61f --- /dev/null +++ b/Examples/Tests/langmuir_fluids/inputs_1d @@ -0,0 +1,72 @@ +# Maximum number of time steps +max_step = 80 + +# number of grid points +amr.n_cell = 128 + +# Maximum allowable size of each subdomain in the problem domain; +# this is used to decompose the domain for parallel calculations. +amr.max_grid_size = 64 + +# Maximum level in hierarchy (for now must be 0, i.e., one level in total) +amr.max_level = 0 + +# Geometry +geometry.dims = 1 +geometry.prob_lo = -20.e-6 # physical domain +geometry.prob_hi = 20.e-6 + +# Boundary condition +boundary.field_lo = periodic +boundary.field_hi = periodic + +warpx.serialize_initial_conditions = 1 + +# Verbosity +warpx.verbose = 1 + +# Algorithms +warpx.use_filter = 0 + +# CFL +warpx.cfl = 0.8 + +# Parameters for the plasma wave +my_constants.epsilon = 0.01 +my_constants.n0 = 2.e24 # electron and positron densities, #/m^3 +my_constants.wp = sqrt(2.*n0*q_e**2/(epsilon0*m_e)) # plasma frequency +my_constants.kp = wp/clight # plasma wavenumber +my_constants.k = 2.*pi/20.e-6 # perturbation wavenumber +# Note: kp is calculated in SI for a density of 4e24 (i.e. 2e24 electrons + 2e24 positrons) +# k is calculated so as to have 2 periods within the 40e-6 wide box. + +# Particles +fluids.species_names = electrons positrons + +electrons.charge = -q_e +electrons.mass = m_e +electrons.profile = constant +electrons.density = n0 # number of electrons per m^3 +electrons.momentum_distribution_type = parse_momentum_function +electrons.momentum_function_ux(x,y,z) = "epsilon * k/kp * sin(k*x) * cos(k*y) * cos(k*z)" +electrons.momentum_function_uy(x,y,z) = "epsilon * k/kp * cos(k*x) * sin(k*y) * cos(k*z)" +electrons.momentum_function_uz(x,y,z) = "epsilon * k/kp * cos(k*x) * cos(k*y) * sin(k*z)" + +positrons.charge = q_e +positrons.mass = m_e +positrons.profile = constant +positrons.density = n0 # number of positrons per m^3 +positrons.momentum_distribution_type = parse_momentum_function +positrons.momentum_function_ux(x,y,z) = "-epsilon * k/kp * sin(k*x) * cos(k*y) * cos(k*z)" +positrons.momentum_function_uy(x,y,z) = "-epsilon * k/kp * cos(k*x) * sin(k*y) * cos(k*z)" +positrons.momentum_function_uz(x,y,z) = "-epsilon * k/kp * cos(k*x) * cos(k*y) * sin(k*z)" + +# Diagnostics +diagnostics.diags_names = diag1 openpmd +diag1.intervals = 40 +diag1.diag_type = Full +diag1.fields_to_plot = Ez jz rho + +openpmd.intervals = 40 +openpmd.diag_type = Full +openpmd.format = openpmd diff --git a/Examples/Tests/langmuir_fluids/inputs_2d b/Examples/Tests/langmuir_fluids/inputs_2d new file mode 100644 index 00000000000..d75fa0f3393 --- /dev/null +++ b/Examples/Tests/langmuir_fluids/inputs_2d @@ -0,0 +1,69 @@ +# Maximum number of time steps +max_step = 80 + +# number of grid points +amr.n_cell = 128 128 + +# Maximum allowable size of each subdomain in the problem domain; +# this is used to decompose the domain for parallel calculations. +amr.max_grid_size = 64 + +# Maximum level in hierarchy (for now must be 0, i.e., one level in total) +amr.max_level = 0 + +# Geometry +geometry.dims = 2 +geometry.prob_lo = -20.e-6 -20.e-6 # physical domain +geometry.prob_hi = 20.e-6 20.e-6 + +# Boundary condition +boundary.field_lo = periodic periodic +boundary.field_hi = periodic periodic + +warpx.serialize_initial_conditions = 1 + +# Verbosity +warpx.verbose = 1 + +# Algorithms +algo.field_gathering = energy-conserving +warpx.use_filter = 0 + +# CFL +warpx.cfl = 1.0 + +# Parameters for the plasma wave +my_constants.epsilon = 0.01 +my_constants.n0 = 2.e24 # electron and positron densities, #/m^3 +my_constants.wp = sqrt(2.*n0*q_e**2/(epsilon0*m_e)) # plasma frequency +my_constants.kp = wp/clight # plasma wavenumber +my_constants.k = 2.*pi/20.e-6 # perturbation wavenumber +# Note: kp is calculated in SI for a density of 4e24 (i.e. 2e24 electrons + 2e24 positrons) +# k is calculated so as to have 2 periods within the 40e-6 wide box. + +# Fluids +fluids.species_names = electrons positrons + +electrons.charge = -q_e +electrons.mass = m_e +electrons.profile = constant +electrons.density = n0 # number of electrons per m^3 +electrons.momentum_distribution_type = parse_momentum_function +electrons.momentum_function_ux(x,y,z) = "epsilon * k/kp * sin(k*x) * cos(k*y) * cos(k*z)" +electrons.momentum_function_uy(x,y,z) = "epsilon * k/kp * cos(k*x) * sin(k*y) * cos(k*z)" +electrons.momentum_function_uz(x,y,z) = "epsilon * k/kp * cos(k*x) * cos(k*y) * sin(k*z)" + +positrons.charge = q_e +positrons.mass = m_e +positrons.profile = constant +positrons.density = n0 # number of positrons per m^3 +positrons.momentum_distribution_type = parse_momentum_function +positrons.momentum_function_ux(x,y,z) = "-epsilon * k/kp * sin(k*x) * cos(k*y) * cos(k*z)" +positrons.momentum_function_uy(x,y,z) = "-epsilon * k/kp * cos(k*x) * sin(k*y) * cos(k*z)" +positrons.momentum_function_uz(x,y,z) = "-epsilon * k/kp * cos(k*x) * cos(k*y) * sin(k*z)" + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.intervals = 40 +diag1.diag_type = Full +diag1.fields_to_plot = Ex Ez jx jz rho diff --git a/Examples/Tests/langmuir_fluids/inputs_3d b/Examples/Tests/langmuir_fluids/inputs_3d new file mode 100644 index 00000000000..f92ccf0ed60 --- /dev/null +++ b/Examples/Tests/langmuir_fluids/inputs_3d @@ -0,0 +1,74 @@ +# Parameters for the plasma wave +my_constants.max_step = 40 +my_constants.lx = 40.e-6 # length of sides +my_constants.dx = 6.25e-07 # grid cell size +my_constants.nx = lx/dx # number of cells in each dimension +my_constants.epsilon = 0.01 +my_constants.n0 = 2.e24 # electron and positron densities, #/m^3 +my_constants.wp = sqrt(2.*n0*q_e**2/(epsilon0*m_e)) # plasma frequency +my_constants.kp = wp/clight # plasma wavenumber +my_constants.k = 2.*2.*pi/lx # perturbation wavenumber +# Note: kp is calculated in SI for a density of 4e24 (i.e. 2e24 electrons + 2e24 positrons) +# k is calculated so as to have 2 periods within the 40e-6 wide box. + +# Maximum number of time steps +max_step = max_step + +# number of grid points +amr.n_cell = nx nx nx + +# Maximum allowable size of each subdomain in the problem domain; +# this is used to decompose the domain for parallel calculations. +amr.max_grid_size = nx nx nx + +# Maximum level in hierarchy (for now must be 0, i.e., one level in total) +amr.max_level = 0 + +# Geometry +geometry.dims = 3 +geometry.prob_lo = -lx/2. -lx/2. -lx/2. # physical domain +geometry.prob_hi = lx/2. lx/2. lx/2. + +# Boundary condition +boundary.field_lo = periodic periodic periodic +boundary.field_hi = periodic periodic periodic + +warpx.serialize_initial_conditions = 1 + +# Verbosity +warpx.verbose = 1 + +# Algorithms +algo.current_deposition = esirkepov +algo.field_gathering = energy-conserving +warpx.use_filter = 0 + +# CFL +warpx.cfl = 1.0 + +# Fluids +fluids.species_names = electrons positrons + +electrons.charge = -q_e +electrons.mass = m_e +electrons.profile = constant +electrons.density = n0 # number of electrons per m^3 +electrons.momentum_distribution_type = parse_momentum_function +electrons.momentum_function_ux(x,y,z) = "epsilon * k/kp * sin(k*x) * cos(k*y) * cos(k*z)" +electrons.momentum_function_uy(x,y,z) = "epsilon * k/kp * cos(k*x) * sin(k*y) * cos(k*z)" +electrons.momentum_function_uz(x,y,z) = "epsilon * k/kp * cos(k*x) * cos(k*y) * sin(k*z)" + +positrons.charge = q_e +positrons.mass = m_e +positrons.profile = constant +positrons.density = n0 # number of positrons per m^3 +positrons.momentum_distribution_type = parse_momentum_function +positrons.momentum_function_ux(x,y,z) = "-epsilon * k/kp * sin(k*x) * cos(k*y) * cos(k*z)" +positrons.momentum_function_uy(x,y,z) = "-epsilon * k/kp * cos(k*x) * sin(k*y) * cos(k*z)" +positrons.momentum_function_uz(x,y,z) = "-epsilon * k/kp * cos(k*x) * cos(k*y) * sin(k*z)" + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.intervals = max_step +diag1.diag_type = Full +diag1.fields_to_plot = Ex Ey Ez Bx By Bz jx jy jz part_per_cell rho diff --git a/Examples/Tests/langmuir_fluids/inputs_rz b/Examples/Tests/langmuir_fluids/inputs_rz new file mode 100644 index 00000000000..2be427cbce5 --- /dev/null +++ b/Examples/Tests/langmuir_fluids/inputs_rz @@ -0,0 +1,72 @@ +# Parameters for the plasma wave +my_constants.max_step = 80 +my_constants.epsilon = 0.01 +my_constants.n0 = 2.e24 # electron density, #/m^3 +my_constants.wp = sqrt(n0*q_e**2/(epsilon0*m_e)) # plasma frequency +my_constants.kp = wp/clight # plasma wavenumber +my_constants.k0 = 2.*pi/20.e-6 # longitudianl perturbation wavenumber +my_constants.w0 = 5.e-6 # transverse perturbation length +# Note: kp is calculated in SI for a density of 2e24 +# k0 is calculated so as to have 2 periods within the 40e-6 wide box. + +# Maximum number of time steps +max_step = max_step + +# number of grid points +amr.n_cell = 64 128 + +# Maximum allowable size of each subdomain in the problem domain; +# this is used to decompose the domain for parallel calculations. +amr.max_grid_size = 64 + +# Maximum level in hierarchy (for now must be 0, i.e., one level in total) +amr.max_level = 0 + +# Geometry +geometry.dims = RZ +geometry.prob_lo = 0.e-6 -20.e-6 # physical domain +geometry.prob_hi = 20.e-6 20.e-6 +boundary.field_lo = none periodic +boundary.field_hi = none periodic + +warpx.serialize_initial_conditions = 1 + +# Verbosity +warpx.verbose = 1 + +# Algorithms +algo.field_gathering = energy-conserving +algo.current_deposition = esirkepov +warpx.use_filter = 0 + +# CFL +warpx.cfl = 1.0 + +# Having this turned on makes for a more sensitive test +warpx.do_dive_cleaning = 1 + +# Fluids +fluids.species_names = electrons ions + +electrons.charge = -q_e +electrons.mass = m_e +electrons.profile = constant +electrons.density = n0 # number of electrons per m^3 +electrons.momentum_distribution_type = parse_momentum_function +electrons.momentum_function_ux(x,y,z) = "epsilon/kp*2*x/w0**2*exp(-(x**2+y**2)/w0**2)*sin(k0*z)" +electrons.momentum_function_uy(x,y,z) = "epsilon/kp*2*y/w0**2*exp(-(x**2+y**2)/w0**2)*sin(k0*z)" +electrons.momentum_function_uz(x,y,z) = "-epsilon/kp*k0*exp(-(x**2+y**2)/w0**2)*cos(k0*z)" + + +ions.charge = q_e +ions.mass = m_p +ions.profile = constant +ions.density = n0 # number of ions per m^3 +ions.momentum_distribution_type = at_rest + +# Diagnostics +diagnostics.diags_names = diag1 +diag1.intervals = 80 +diag1.diag_type = Full + +diag1.fields_to_plot = jr jz Er Ez Bt rho diff --git a/Regression/Checksum/benchmarks_json/Langmuir_fluid_1D.json b/Regression/Checksum/benchmarks_json/Langmuir_fluid_1D.json new file mode 100644 index 00000000000..454afc73bcc --- /dev/null +++ b/Regression/Checksum/benchmarks_json/Langmuir_fluid_1D.json @@ -0,0 +1,7 @@ +{ + "lev=0": { + "Ez": 123800860242.32724, + "jz": 48549778373280.2, + "rho": 344278.3437340355 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/Langmuir_fluid_2D.json b/Regression/Checksum/benchmarks_json/Langmuir_fluid_2D.json new file mode 100644 index 00000000000..8afc645639d --- /dev/null +++ b/Regression/Checksum/benchmarks_json/Langmuir_fluid_2D.json @@ -0,0 +1,9 @@ +{ + "lev=0": { + "Ex": 3790984950257.2373, + "Ez": 3790984950257.2373, + "jx": 1.0093528745824196e+16, + "jz": 1.0093528745824196e+16, + "rho": 21098347.344494 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/Langmuir_fluid_RZ.json b/Regression/Checksum/benchmarks_json/Langmuir_fluid_RZ.json new file mode 100644 index 00000000000..2613f971f91 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/Langmuir_fluid_RZ.json @@ -0,0 +1,10 @@ +{ + "lev=0": { + "Bt": 3.64571737083816, + "Er": 1300317001574.1577, + "Ez": 1815076954086.807, + "jr": 244937533603400.0, + "jz": 342537403264912.5, + "rho": 10096982.130957149 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/Langmuir_fluid_multi.json b/Regression/Checksum/benchmarks_json/Langmuir_fluid_multi.json new file mode 100644 index 00000000000..1e5d41f6677 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/Langmuir_fluid_multi.json @@ -0,0 +1,15 @@ +{ + "lev=0": { + "Bx": 20.814308179643078, + "By": 20.814308179643234, + "Bz": 20.81430817964295, + "Ex": 82994788022201.23, + "Ey": 82994788022201.23, + "Ez": 82994788022201.23, + "jx": 6.343089944595606e+16, + "jy": 6.3430899445956056e+16, + "jz": 6.3430899445956056e+16, + "part_per_cell": 0.0, + "rho": 694435081.2275198 + } +} \ No newline at end of file diff --git a/Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid.json b/Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid.json new file mode 100644 index 00000000000..2843e49ce22 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid.json @@ -0,0 +1,15 @@ +{ + "lev=0": { + "Bx": 14264658.38987597, + "By": 0.0, + "Bz": 0.0, + "Ex": 0.0, + "Ey": 4276056420659746.5, + "Ez": 762168740318568.1, + "jx": 0.0, + "jy": 7.47674123799233e+16, + "jz": 4.817762115932484e+17, + "rho": 1609691680.1267354 + } +} + diff --git a/Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid_boosted.json b/Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid_boosted.json new file mode 100644 index 00000000000..85919660834 --- /dev/null +++ b/Regression/Checksum/benchmarks_json/LaserAcceleration_1d_fluid_boosted.json @@ -0,0 +1,14 @@ +{ + "lev=0": { + "Bx": 7268241.144300916, + "By": 0.0, + "Bz": 0.0, + "Ex": 0.0, + "Ey": 2146970168314705.0, + "Ez": 370041408451418.1, + "jx": 0.0, + "jy": 3.4818196503065956e+16, + "jz": 2.3196405062669472e+17, + "rho": 775945622.8993922 + } +} \ No newline at end of file diff --git a/Regression/WarpX-tests.ini b/Regression/WarpX-tests.ini index da9d8398869..e4c502d858c 100644 --- a/Regression/WarpX-tests.ini +++ b/Regression/WarpX-tests.ini @@ -1096,6 +1096,78 @@ particleTypes = electrons positrons analysisRoutine = Examples/Tests/langmuir/analysis_3d.py analysisOutputImage = langmuir_multi_analysis.png +[Langmuir_fluid_1D] +buildDir = . +inputFile = Examples/Tests/langmuir_fluids/inputs_1d +runtime_params = warpx.do_dynamic_scheduling=0 +dim = 1 +addToCompileString = +cmakeSetupOpts = -DWarpX_DIMS=1 +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 0 +analysisRoutine = Examples/Tests/langmuir_fluids/analysis_1d.py +analysisOutputImage = langmuir_fluid_multi_1d_analysis.png + +[Langmuir_fluid_RZ] +buildDir = . +inputFile = Examples/Tests/langmuir_fluids/inputs_rz +runtime_params = warpx.do_dynamic_scheduling=0 +dim = 2 +addToCompileString = USE_RZ=TRUE +cmakeSetupOpts = -DWarpX_DIMS=RZ +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 0 +analysisRoutine = Examples/Tests/langmuir_fluids/analysis_rz.py +analysisOutputImage = langmuir_fluid_rz_analysis.png + +[Langmuir_fluid_2D] +buildDir = . +inputFile = Examples/Tests/langmuir_fluids/inputs_2d +runtime_params = warpx.do_dynamic_scheduling=0 +dim = 2 +addToCompileString = +cmakeSetupOpts = -DWarpX_DIMS=2 -DCMAKE_BUILD_TYPE=Debug +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 0 +analysisRoutine = Examples/Tests/langmuir_fluids/analysis_2d.py +analysisOutputImage = langmuir_fluid_multi_2d_analysis.png + +[Langmuir_fluid_multi] +buildDir = . +inputFile = Examples/Tests/langmuir_fluids/inputs_3d +runtime_params = warpx.do_dynamic_scheduling=0 +dim = 3 +addToCompileString = +cmakeSetupOpts = -DWarpX_DIMS=3 +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 0 +analysisRoutine = Examples/Tests/langmuir_fluids/analysis_3d.py +analysisOutputImage = langmuir_fluid_multi_analysis.png + [Langmuir_multi_1d] buildDir = . inputFile = Examples/Tests/langmuir/inputs_1d @@ -1741,6 +1813,42 @@ compareParticles = 1 particleTypes = electrons analysisRoutine = Examples/analysis_default_regression.py +[LaserAcceleration_1d_fluid] +buildDir = . +inputFile = Examples/Physics_applications/laser_acceleration/inputs_1d_fluids +runtime_params = +dim = 1 +addToCompileString = USE_OPENPMD=TRUE QED=FALSE +cmakeSetupOpts = -DWarpX_DIMS=1 -DWarpX_OPENPMD=ON -DWarpX_QED=OFF +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 1 +particleTypes = electrons ions +analysisRoutine = Examples/Physics_applications/laser_acceleration/analysis_1d_fluids.py + +[LaserAcceleration_1d_fluid_boosted] +buildDir = . +inputFile = Examples/Physics_applications/laser_acceleration/inputs_1d_fluids_boosted +runtime_params = +dim = 1 +addToCompileString = USE_OPENPMD=TRUE QED=FALSE +cmakeSetupOpts = -DWarpX_DIMS=1 -DWarpX_OPENPMD=ON -DWarpX_QED=OFF +restartTest = 0 +useMPI = 1 +numprocs = 2 +useOMP = 1 +numthreads = 1 +compileTest = 0 +doVis = 0 +compareParticles = 1 +particleTypes = electrons ions +analysisRoutine = Examples/Physics_applications/laser_acceleration/analysis_1d_fluids_boosted.py + [LaserAccelerationBoost] buildDir = . inputFile = Examples/Physics_applications/laser_acceleration/inputs_2d_boost diff --git a/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp index 73feef4ed00..c36e31c0f93 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/RhoFunctor.cpp @@ -7,6 +7,8 @@ #include "Utils/WarpXAlgorithmSelection.H" #endif #include "Particles/MultiParticleContainer.H" +#include "Fluids/MultiFluidContainer.H" +#include "Fluids/WarpXFluidContainer.H" #include "Particles/WarpXParticleContainer.H" #include "WarpX.H" @@ -41,6 +43,10 @@ RhoFunctor::operator() ( amrex::MultiFab& mf_dst, const int dcomp, const int /*i if (m_species_index == -1) { auto& mypc = warpx.GetPartContainer(); rho = mypc.GetChargeDensity(m_lev, true); + if (warpx.DoFluidSpecies()) { + auto& myfl = warpx.GetFluidContainer(); + myfl.DepositCharge(m_lev, *rho); + } } // Dump rho per species else { diff --git a/Source/Evolve/WarpXEvolve.cpp b/Source/Evolve/WarpXEvolve.cpp index 067cd9a11b6..467a4268d97 100644 --- a/Source/Evolve/WarpXEvolve.cpp +++ b/Source/Evolve/WarpXEvolve.cpp @@ -24,6 +24,8 @@ #endif #include "Parallelization/GuardCellManager.H" #include "Particles/MultiParticleContainer.H" +#include "Fluids/MultiFluidContainer.H" +#include "Fluids/WarpXFluidContainer.H" #include "Particles/ParticleBoundaryBuffer.H" #include "Python/WarpX_py.H" #include "Utils/TextMsg.H" @@ -1031,6 +1033,12 @@ WarpX::PushParticlesandDepose (int lev, amrex::Real cur_time, DtType a_dt_type, // of the filter to avoid incorrect results (moved to `SyncCurrentAndRho()`). // Might this be related to issue #1943? #endif + if (do_fluid_species) { + myfl->Evolve(lev, + *Efield_aux[lev][0],*Efield_aux[lev][1],*Efield_aux[lev][2], + *Bfield_aux[lev][0],*Bfield_aux[lev][1],*Bfield_aux[lev][2], + rho_fp[lev].get(),*current_x, *current_y, *current_z, cur_time, skip_deposition); + } } } diff --git a/Source/FieldSolver/ElectrostaticSolver.cpp b/Source/FieldSolver/ElectrostaticSolver.cpp index a6932f223e2..216e80f7494 100644 --- a/Source/FieldSolver/ElectrostaticSolver.cpp +++ b/Source/FieldSolver/ElectrostaticSolver.cpp @@ -7,6 +7,8 @@ #include "WarpX.H" #include "FieldSolver/ElectrostaticSolver.H" +#include "Fluids/MultiFluidContainer.H" +#include "Fluids/WarpXFluidContainer.H" #include "Parallelization/GuardCellManager.H" #include "Particles/MultiParticleContainer.H" #include "Particles/WarpXParticleContainer.H" @@ -209,6 +211,10 @@ WarpX::AddSpaceChargeFieldLabFrame () // Deposit particle charge density (source of Poisson solver) mypc->DepositCharge(rho_fp, 0.0_rt); + if (do_fluid_species) { + int const lev = 0; + myfl->DepositCharge( lev, *rho_fp[lev] ); + } SyncRho(rho_fp, rho_cp, charge_buf); // Apply filter, perform MPI exchange, interpolate across levels #ifndef WARPX_DIM_RZ diff --git a/Source/FieldSolver/WarpXPushFieldsHybridPIC.cpp b/Source/FieldSolver/WarpXPushFieldsHybridPIC.cpp index b502620641b..36029e7d3eb 100644 --- a/Source/FieldSolver/WarpXPushFieldsHybridPIC.cpp +++ b/Source/FieldSolver/WarpXPushFieldsHybridPIC.cpp @@ -10,6 +10,8 @@ #include "FieldSolver/FiniteDifferenceSolver/HybridPICModel/HybridPICModel.H" #include "Particles/MultiParticleContainer.H" #include "Utils/TextMsg.H" +#include "Fluids/MultiFluidContainer.H" +#include "Fluids/WarpXFluidContainer.H" #include "Utils/WarpXProfilerWrapper.H" #include "WarpX.H" @@ -30,6 +32,13 @@ void WarpX::HybridPICEvolveFields () // Perform current deposition at t_{n+1/2}. mypc->DepositCurrent(current_fp, dt[0], -0.5_rt * dt[0]); + // Deposit cold-relativistic fluid charge and current + if (do_fluid_species) { + int const lev = 0; + myfl->DepositCharge(lev, *rho_fp[lev]); + myfl->DepositCurrent(lev, *current_fp[lev][0], *current_fp[lev][1], *current_fp[lev][2]); + } + // Synchronize J and rho: // filter (if used), exchange guard cells, interpolate across MR levels // and apply boundary conditions diff --git a/Source/Fluids/CMakeLists.txt b/Source/Fluids/CMakeLists.txt new file mode 100644 index 00000000000..a5f28debbbd --- /dev/null +++ b/Source/Fluids/CMakeLists.txt @@ -0,0 +1,8 @@ +foreach(D IN LISTS WarpX_DIMS) + warpx_set_suffix_dims(SD ${D}) + target_sources(lib_${SD} + PRIVATE + MultiFluidContainer.cpp + WarpXFluidContainer.cpp + ) +endforeach() diff --git a/Source/Fluids/Make.package b/Source/Fluids/Make.package new file mode 100644 index 00000000000..96bce415c4a --- /dev/null +++ b/Source/Fluids/Make.package @@ -0,0 +1,4 @@ +CEXE_sources += MultiFluidContainer.cpp +CEXE_sources += WarpXFluidContainer.cpp + +VPATH_LOCATIONS += $(WARPX_HOME)/Source/Fluids diff --git a/Source/Fluids/MultiFluidContainer.H b/Source/Fluids/MultiFluidContainer.H new file mode 100644 index 00000000000..94d95d179e5 --- /dev/null +++ b/Source/Fluids/MultiFluidContainer.H @@ -0,0 +1,78 @@ +/* Copyright 2023 Grant Johnson, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_MultiFluidContainer_H_ +#define WARPX_MultiFluidContainer_H_ +#include "Evolve/WarpXDtType.H" + +#include "WarpXFluidContainer_fwd.H" + +#include +#include + +#include + +/** + * The class MultiFluidContainer holds multiple instances of the + * class WarpXFluidContainer, stored in its member variable "allcontainers". + * The class WarpX typically has a single (pointer to an) instance of + * MultiFluidContainer. + * + * MultiFluidContainer typically has two types of functions: + * - Functions that loop over all instances of WarpXFluidContainer in + * allcontainers and calls the corresponding function (for instance, + * MultiFluidContainer::Evolve loops over all fluid containers and + * calls the corresponding WarpXFluidContainer::Evolve function). + * - Functions that specifically handle multiple species (for instance + * ReadParameters). + */ +class MultiFluidContainer +{ + +public: + + MultiFluidContainer (int nlevs_max); + + ~MultiFluidContainer() {} + + WarpXFluidContainer& + GetFluidContainer (int ispecies) const {return *allcontainers[ispecies];} + +#ifdef WARPX_USE_OPENPMD + std::unique_ptr& GetUniqueContainer(int ispecies) { + return allcontainers[ispecies]; + } +#endif + + void AllocateLevelMFs (int lev, const amrex::BoxArray& ba, const amrex::DistributionMapping& dm); + + void InitData (int lev, amrex::Box init_box, amrex::Real cur_time); + + /// + /// This evolves all the fluids by one PIC time step, including current deposition, the + /// field solve, and pushing the fluids, for all the species in the MultiFluidContainer. + /// + void Evolve (int lev, + const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez, + const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz, + amrex::MultiFab* rho, amrex::MultiFab& jx, amrex::MultiFab& jy, amrex::MultiFab& jz, + amrex::Real cur_time, bool skip_deposition=false); + + int nSpecies() const {return species_names.size();} + + void DepositCharge (int lev, amrex::MultiFab &rho); + void DepositCurrent (int lev, + amrex::MultiFab& jx, amrex::MultiFab& jy, amrex::MultiFab& jz); + +private: + + std::vector species_names; + + // Vector of fluid species + amrex::Vector> allcontainers; + +}; +#endif /*WARPX_MultiFluidContainer_H_*/ diff --git a/Source/Fluids/MultiFluidContainer.cpp b/Source/Fluids/MultiFluidContainer.cpp new file mode 100644 index 00000000000..234cefb4f07 --- /dev/null +++ b/Source/Fluids/MultiFluidContainer.cpp @@ -0,0 +1,73 @@ +/* Copyright 2023 Grant Johnson, Remi Lehe + * + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "MultiFluidContainer.H" +#include "Fluids/WarpXFluidContainer.H" +#include "Utils/Parser/ParserUtils.H" + +#include + +using namespace amrex; + +MultiFluidContainer::MultiFluidContainer (int nlevs_max) +{ + const ParmParse pp_fluids("fluids"); + pp_fluids.queryarr("species_names", species_names); + + const int nspecies = static_cast(species_names.size()); + + allcontainers.resize(nspecies); + for (int i = 0; i < nspecies; ++i) { + allcontainers[i] = std::make_unique(nlevs_max, i, species_names[i]); + } +} + +void +MultiFluidContainer::AllocateLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm) +{ + for (auto& fl : allcontainers) { + fl->AllocateLevelMFs(lev, ba, dm); + } +} + +void +MultiFluidContainer::InitData (int lev, amrex::Box init_box, amrex::Real cur_time) +{ + for (auto& fl : allcontainers) { + fl->InitData(lev, init_box, cur_time); + } +} + + +void +MultiFluidContainer::DepositCharge (int lev, amrex::MultiFab &rho) +{ + for (auto& fl : allcontainers) { + fl->DepositCharge(lev,rho); + } +} + +void +MultiFluidContainer::DepositCurrent (int lev, + amrex::MultiFab& jx, amrex::MultiFab& jy, amrex::MultiFab& jz) +{ + for (auto& fl : allcontainers) { + fl->DepositCurrent(lev,jx,jy,jz); + } +} + +void +MultiFluidContainer::Evolve (int lev, + const MultiFab& Ex, const MultiFab& Ey, const MultiFab& Ez, + const MultiFab& Bx, const MultiFab& By, const MultiFab& Bz, + MultiFab* rho, MultiFab& jx, MultiFab& jy, MultiFab& jz, + amrex::Real cur_time, bool skip_deposition) +{ + for (auto& fl : allcontainers) { + fl->Evolve(lev, Ex, Ey, Ez, Bx, By, Bz, rho, jx, jy, jz, cur_time, skip_deposition); + } +} diff --git a/Source/Fluids/MultiFluidContainer_fwd.H b/Source/Fluids/MultiFluidContainer_fwd.H new file mode 100644 index 00000000000..a1a55c3b450 --- /dev/null +++ b/Source/Fluids/MultiFluidContainer_fwd.H @@ -0,0 +1,12 @@ +/* Copyright 2023 Grant Johnson, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_MultiFluidContainer_fwd_H_ + +class MultiFluidContainer; + +#endif /* WARPX_MultiFluidContainer_fwd_H_ */ diff --git a/Source/Fluids/MusclHancockUtils.H b/Source/Fluids/MusclHancockUtils.H new file mode 100644 index 00000000000..d0c37f9c58f --- /dev/null +++ b/Source/Fluids/MusclHancockUtils.H @@ -0,0 +1,484 @@ +/* Copyright 2023 Grant Johnson, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_MusclHancock_H_ +#define WARPX_MusclHancock_H_ + +#include +#include +#include +#include + + +// Euler push for momentum source (r-direction) +// Note: assumes U normalized by c +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real F_r (amrex::Real r, amrex::Real u_r, amrex::Real u_theta, amrex::Real u_z, amrex::Real dt) +{ + return dt*(-u_theta*u_theta/r)/sqrt(1.0 + u_r*u_r + u_theta*u_theta + u_z*u_z) + u_r; +} + +// Euler push for momentum source (theta-direction) +// Note: assumes U normalized by c +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real F_theta (amrex::Real r, amrex::Real u_r, amrex::Real u_theta, amrex::Real u_z, amrex::Real dt) +{ + return dt*(u_theta*u_r/r)/sqrt(1.0 + u_r*u_r + u_theta*u_theta + u_z*u_z) + u_theta; +} +// Velocity at the half step +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real V_calc (const amrex::Array4& U, int i, int j, int k, int comp, amrex::Real c) +{ + // comp -> x, y, z -> 0, 1, 2, return Vx, Vy, or Vz: + amrex::Real gamma = std::sqrt(1.0 + (U(i,j,k,1)*U(i,j,k,1) + U(i,j,k,2)*U(i,j,k,2) + U(i,j,k,3)*U(i,j,k,3))/(c*c)); + return U(i,j,k,comp+1)/gamma; +} +// mindmod +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real minmod (amrex::Real a, amrex::Real b) +{ + if (a > 0.0 && b > 0.0) + return std::min(a, b); + else if (a < 0.0 && b < 0.0) + return std::max(a, b); + else + return 0.0; +} +// Min of 3 inputs +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real min3 (amrex::Real a, amrex::Real b, amrex::Real c) +{ + return std::min(a, std::min(b, c) ); +} +// Max of 3 inputs +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real max3 (amrex::Real a, amrex::Real b, amrex::Real c) +{ + return std::max(a, std::max(b, c) ); +} +// mindmod +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real minmod3 (amrex::Real a, amrex::Real b , amrex::Real c) +{ + if (a > 0.0 && b > 0.0 && c > 0.0) + return min3(a,b,c); + else if (a < 0.0 && b < 0.0 && c < 0.0) + return max3(a,b,c); + else + return 0.0; +} +//maxmod +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real maxmod (amrex::Real a, amrex::Real b) +{ + if (a > 0.0 && b > 0.0) + return std::max(a, b); + else if (a < 0.0 && b < 0.0) + return std::min(a, b); + else + return 0.0; +} +// Rusanov Flux (density) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real flux_N (const amrex::Array4& Um, const amrex::Array4& Up, +int i, int j, int k, amrex::Real Vm, amrex::Real Vp) +{ + amrex::Real c = std::max( std::abs(Vm) , std::abs(Vp) ); + return 0.5*(Vm*Um(i,j,k,0) + Vp*Up(i,j,k,0)) - (0.5*c)*(Up(i,j,k,0) - Um(i,j,k,0)); +} +// Rusanov Flux (Momentum density x) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real flux_NUx (const amrex::Array4& Um, const amrex::Array4& Up, +int i, int j, int k, amrex::Real Vm, amrex::Real Vp) +{ + amrex::Real c = std::max( std::abs(Vm) , std::abs(Vp) ); + return 0.5*(Vm*Um(i,j,k,0)*Um(i,j,k,1) + Vp*Up(i,j,k,0)*Up(i,j,k,1)) + - (0.5*c)*(Up(i,j,k,0)*Up(i,j,k,1) - Um(i,j,k,0)*Um(i,j,k,1)); +} +// Rusanov Flux (Momentum density y) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real flux_NUy (const amrex::Array4& Um, const amrex::Array4& Up, +int i, int j, int k, amrex::Real Vm, amrex::Real Vp) +{ + amrex::Real c = std::max( std::abs(Vm) , std::abs(Vp) ); + return 0.5*(Vm*Um(i,j,k,0)*Um(i,j,k,2) + Vp*Up(i,j,k,0)*Up(i,j,k,2)) + - (0.5*c)*(Up(i,j,k,0)*Up(i,j,k,2) - Um(i,j,k,0)*Um(i,j,k,2)); +} +// Rusanov Flux (Momentum density z) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real flux_NUz (const amrex::Array4& Um, const amrex::Array4& Up, +int i, int j, int k, amrex::Real Vm, amrex::Real Vp) +{ + amrex::Real c = std::max( std::abs(Vm) , std::abs(Vp) ); + return 0.5*(Vm*Um(i,j,k,0)*Um(i,j,k,3) + Vp*Up(i,j,k,0)*Up(i,j,k,3)) + - (0.5*c)*(Up(i,j,k,0)*Up(i,j,k,3) - Um(i,j,k,0)*Um(i,j,k,3)); +} +// ave_minmod high diffusivity, sigma can be between [1,2] +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real ave_adjustable_diff (amrex::Real a, amrex::Real b) +{ + amrex::Real sigma = 2.0*0.732050807568877; + if (a*b > 0.0) + return minmod3( (a+b)/2.0, sigma*a, sigma*b ); + else + return 0.0; +} +// ave_minmod Low diffusivity +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real ave (amrex::Real a, amrex::Real b) +{ + if (a*b > 0.0) + return minmod3( (a+b)/2.0, 2.0*a, 2.0*b ); + else + return 0.0; +} +// ave_superbee +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real ave_superbee (amrex::Real a, amrex::Real b) +{ + if (a*b > 0.0) + return minmod( maxmod(a,b), minmod(2.0*a,2.0*b)); + else + return 0.0; +} +// stage2 slope limiting +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real ave_stage2 (amrex::Real dQ, amrex::Real a, amrex::Real b, amrex::Real c) +{ + // sigma = sqrt(3) -1 + amrex::Real sigma = 0.732050807568877; + amrex::Real dq_min = 2.0*std::min( b - min3(a,b,c), max3(a,b,c) - b); + return ( std::abs(dQ)/dQ ) * std::min( std::abs(dQ) , sigma*std::abs(dq_min) ); +} +// Returns the offset indices for the "plus" grid +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +void plus_index_offsets (int i, int j, int k, int& ip, int& jp, int& kp, int comp) +{ + // Find the correct offsets +#if defined(WARPX_DIM_3D) + if (comp == 0) { //x + ip = i - 1; jp = j; kp = k; + } else if (comp == 1){ //y + ip = i; jp = j - 1; kp = k; + } else if (comp == 2){ //z + ip = i; jp = j; kp = k - 1; + } + #elif defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + if (comp == 0) { //x + ip = i - 1; jp = j; kp = k; + } else if (comp == 2){ //z + ip = i; jp = j - 1; kp = k; + } +#else + if (comp == 2) { //z + ip = i - 1; jp = j; kp = k; + } +#endif +} +// Compute the zero edges +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +void compute_U_edges (const amrex::Array4& Um, const amrex::Array4& Up, int i, int j, int k, amrex::Box box, +amrex::Real U_tilde0, amrex::Real U_tilde1, amrex::Real U_tilde2, amrex::Real U_tilde3, +amrex::Real dU0x, amrex::Real dU1x, amrex::Real dU2x, amrex::Real dU3x, int comp) +{ + + // comp -> x, y, z -> 0, 1, 2 + int ip, jp, kp; + plus_index_offsets(i, j, k, ip, jp, kp, comp); + + if ( box.contains(i,j,k) ) { + Um(i,j,k,0) = U_tilde0 + dU0x/2.0; + Um(i,j,k,1) = U_tilde1 + dU1x/2.0; + Um(i,j,k,2) = U_tilde2 + dU2x/2.0; + Um(i,j,k,3) = U_tilde3 + dU3x/2.0; + } + + if ( box.contains(ip,jp,kp) ) { + Up(ip,jp,kp,0) = U_tilde0 - dU0x/2.0; + Up(ip,jp,kp,1) = U_tilde1 - dU1x/2.0; + Up(ip,jp,kp,2) = U_tilde2 - dU2x/2.0; + Up(ip,jp,kp,3) = U_tilde3 - dU3x/2.0; + } +} +// Compute the zero edges +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +void set_U_edges_to_zero (const amrex::Array4& Um, +const amrex::Array4& Up, int i, int j, int k, amrex::Box box, int comp) +{ + + // comp -> x, y, z -> 0, 1, 2 + int ip, jp, kp; + plus_index_offsets(i, j, k, ip, jp, kp, comp); + + if ( box.contains(i,j,k) ) { + Um(i,j,k,0) = 0.0; + Um(i,j,k,1) = 0.0; + Um(i,j,k,2) = 0.0; + Um(i,j,k,3) = 0.0; + } + + if ( box.contains(ip,jp,kp) ) { + Up(ip,jp,kp,0) = 0.0; + Up(ip,jp,kp,1) = 0.0; + Up(ip,jp,kp,2) = 0.0; + Up(ip,jp,kp,3) = 0.0; + } +} +// Positivity Limiter +// if Q_minus or Q_plus is zero for the density (i.e. component 0 of Q_minus/Q_plus), set dQ to 0 and recompute Q_minus / Q_plus +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +void positivity_limiter (const amrex::Array4& U_edge_plus, +const amrex::Array4& U_edge_minus, const amrex::Array4& N_arr, +int i, int j, int k, amrex::Box box, amrex::Real Ux, amrex::Real Uy, amrex::Real Uz, +int comp) +{ + + // comp -> x, y, z -> 0, 1, 2 + int ip, jp, kp; + plus_index_offsets(i, j, k, ip, jp, kp, comp); + + // Set the edges to zero. If one edge in a cell is zero, we must self-consistently + // set the slope to zero (hence why we have the three cases, the first is when + // both points exist, and the second two are are edge cases) + if (( box.contains(i,j,k) ) && ( box.contains(ip,jp,kp) )) { + if ((U_edge_minus(i,j,k,0) < 0.0) || (U_edge_plus(ip,jp,kp,0) < 0.0)) { + U_edge_minus(i,j,k,0) = N_arr(i,j,k); + U_edge_minus(i,j,k,1) = Ux; + U_edge_minus(i,j,k,2) = Uy; + U_edge_minus(i,j,k,3) = Uz; + U_edge_plus(ip,jp,kp,0) = N_arr(i,j,k); + U_edge_plus(ip,jp,kp,1) = Ux; + U_edge_plus(ip,jp,kp,2) = Uy; + U_edge_plus(ip,jp,kp,3) = Uz; + } + } else if (( box.contains(i,j,k) ) && ( box.contains(ip,jp,kp) != 1)) { + if (U_edge_minus(i,j,k,0) < 0.0) { + U_edge_minus(i,j,k,0) = N_arr(i,j,k); + U_edge_minus(i,j,k,1) = Ux; + U_edge_minus(i,j,k,2) = Uy; + U_edge_minus(i,j,k,3) = Uz; + } + } else if (( box.contains(i,j,k) != 1 ) && ( box.contains(ip,jp,kp) )) { + if (U_edge_plus(ip,jp,kp,0) < 0.0){ + U_edge_plus(ip,jp,kp,0) = N_arr(i,j,k); + U_edge_plus(ip,jp,kp,1) = Ux; + U_edge_plus(ip,jp,kp,2) = Uy; + U_edge_plus(ip,jp,kp,3) = Uz; + } + } +} + +// Compute the difference in N (down-x) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real DownDx_N (const amrex::Array4& N, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) || defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + return N(i,j,k) - N(i-1,j,k); +#else + amrex::ignore_unused(N, i, j, k); + return 0.0; +#endif +} +// Compute the difference in N (up-x) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real UpDx_N (const amrex::Array4& N, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) || defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + return N(i+1,j,k) - N(i,j,k); +#else + amrex::ignore_unused(N, i, j, k); + return 0.0; +#endif +} +// Compute the difference in N (down-y) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real DownDy_N (const amrex::Array4& N, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) + return N(i,j,k) - N(i,j-1,k); +#else + amrex::ignore_unused(N, i, j, k); + return 0.0; +#endif +} +// Compute the difference in N (up-y) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real UpDy_N (const amrex::Array4& N, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) + return N(i,j+1,k) - N(i,j,k); +#else + amrex::ignore_unused(N, i, j, k); + return 0.0; +#endif +} +// Compute the difference in N (down-z) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real DownDz_N (const amrex::Array4& N, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) + return N(i,j,k) - N(i,j,k-1); +#elif defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + return N(i,j,k) - N(i,j-1,k); +#else + return N(i,j,k) - N(i-1,j,k); +#endif +} +// Compute the difference in N (up-z) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real UpDz_N (const amrex::Array4& N, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) + return N(i,j,k+1) - N(i,j,k); +#elif defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + return N(i,j+1,k) - N(i,j,k); +#else + return N(i+1,j,k) - N(i,j,k); +#endif +} + + +// Compute the difference in U (down-x) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real DownDx_U (const amrex::Array4& N, +const amrex::Array4& NU, amrex::Real& U, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) || defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + // U is zero if N is zero, Check positivity before dividing + amrex::Real U_m = 0; + if (N(i-1,j,k) > 0) U_m = NU(i-1,j,k)/N(i-1,j,k); + return U - U_m; +#else + amrex::ignore_unused(N, NU, U, i, j, k); + return 0.0; +#endif +} +// Compute the difference in U (up-x) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real UpDx_U (const amrex::Array4& N, +const amrex::Array4& NU, amrex::Real& U, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) || defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + // U is zero if N is zero, Check positivity before dividing + amrex::Real U_p = 0; + if (N(i+1,j,k) > 0) U_p = NU(i+1,j,k)/N(i+1,j,k); + return U_p - U; +#else + amrex::ignore_unused(N, NU, U, i, j, k); + return 0.0; +#endif +} + +// Compute the difference in U (down-y) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real DownDy_U (const amrex::Array4& N, +const amrex::Array4& NU, amrex::Real& U, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) + // U is zero if N is zero, Check positivity before dividing + amrex::Real U_m = 0; + if (N(i,j-1,k) > 0) U_m = NU(i,j-1,k)/N(i,j-1,k); + return U - U_m; +#else + amrex::ignore_unused(N, NU, U, i, j, k); + return 0.0; +#endif +} +// Compute the difference in U (up-y) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real UpDy_U (const amrex::Array4& N, +const amrex::Array4& NU, amrex::Real& U, int i, int j, int k) +{ + // Write the correct differences +#if defined(WARPX_DIM_3D) + // U is zero if N is zero, Check positivity before dividing + amrex::Real U_p = 0; + if (N(i,j+1,k) > 0) U_p = NU(i,j+1,k)/N(i,j+1,k); + return U_p - U; +#else + amrex::ignore_unused(N, NU, U, i, j, k); + return 0.0; +#endif +} + +// Compute the difference in U (down-z) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real DownDz_U (const amrex::Array4& N, +const amrex::Array4& NU, amrex::Real& U, int i, int j, int k) +{ + // Write the correct differences + amrex::Real U_m = 0; + + // U is zero if N is zero, Check positivity before dividing +#if defined(WARPX_DIM_3D) + if (N(i,j,k-1) > 0) U_m = NU(i,j,k-1)/N(i,j,k-1); +#elif defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + if (N(i,j-1,k) > 0) U_m = NU(i,j-1,k)/N(i,j-1,k); +#else + if (N(i-1,j,k) > 0) U_m = NU(i-1,j,k)/N(i-1,j,k); +#endif + + // Return the difference + return U - U_m; +} +// Compute the difference in U (up-z) +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real UpDz_U (const amrex::Array4& N, +const amrex::Array4& NU, amrex::Real& U, int i, int j, int k) +{ + // Write the correct differences + amrex::Real U_p = 0; + + // U is zero if N is zero, Check positivity before dividing +#if defined(WARPX_DIM_3D) + if (N(i,j,k+1) > 0) U_p = NU(i,j,k+1)/N(i,j,k+1); +#elif defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + if (N(i,j+1,k) > 0) U_p = NU(i,j+1,k)/N(i,j+1,k); +#else + if (N(i+1,j,k) > 0) U_p = NU(i+1,j,k)/N(i+1,j,k); +#endif + + // Return the difference + return U_p - U; +} + + +// Flux difference calculation +AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE +amrex::Real dF (const amrex::Array4& U_minus, +const amrex::Array4& U_plus,int i,int j,int k,amrex::Real clight, int comp, int dir) +{ + // dir -> x, y, z -> 0, 1, 2 + int ip, jp, kp; + plus_index_offsets(i, j, k, ip, jp, kp, dir); + + amrex::Real V_L_minus = V_calc(U_minus,ip,jp,kp,dir,clight); + amrex::Real V_I_minus = V_calc(U_minus,i,j,k,dir,clight); + amrex::Real V_L_plus = V_calc(U_plus,ip,jp,kp,dir,clight); + amrex::Real V_I_plus = V_calc(U_plus,i,j,k,dir,clight); + + // Flux differences depending on the component to compute + if (comp == 0){ + return flux_N( U_minus, U_plus, i, j, k, V_I_minus, V_I_plus) - flux_N( U_minus, U_plus, ip, jp, kp, V_L_minus, V_L_plus); + } else if (comp == 1){ + return flux_NUx( U_minus, U_plus, i, j, k, V_I_minus, V_I_plus) - flux_NUx( U_minus, U_plus, ip, jp, kp, V_L_minus, V_L_plus); + } else if (comp == 2){ + return flux_NUy( U_minus, U_plus, i, j, k, V_I_minus, V_I_plus) - flux_NUy( U_minus, U_plus, ip, jp, kp, V_L_minus, V_L_plus); + } else { //if (comp == 3) + return flux_NUz( U_minus, U_plus, i, j, k, V_I_minus, V_I_plus) - flux_NUz( U_minus, U_plus, ip, jp, kp, V_L_minus, V_L_plus); + } +} + +#endif /*WARPX_MusclHancock_H_*/ diff --git a/Source/Fluids/WarpXFluidContainer.H b/Source/Fluids/WarpXFluidContainer.H new file mode 100644 index 00000000000..28f37ae393b --- /dev/null +++ b/Source/Fluids/WarpXFluidContainer.H @@ -0,0 +1,186 @@ +/* Copyright 2023 Grant Johnson, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_WarpXFluidContainer_H_ +#define WARPX_WarpXFluidContainer_H_ + +#include "Evolve/WarpXDtType.H" +#include "Initialization/PlasmaInjector.H" +#include "MultiFluidContainer.H" + +#include +#include + +#include + + +/** + * WarpXFluidContainer is the base class from which all concrete + * fluid container classes derive. + * + * WarpXFluidContainer contains the main functions for initialization, + * interaction with the grid (field gather and current deposition), fluid + * source and push, advective update and updates for non-intertial terms. + */ +class WarpXFluidContainer +{ +public: + friend MultiFluidContainer; + + WarpXFluidContainer (int nlevs_max, int ispecies, const std::string& name); + ~WarpXFluidContainer() {} + + void AllocateLevelMFs (int lev, const amrex::BoxArray& ba, const amrex::DistributionMapping& dm); + + void InitData (int lev, amrex::Box init_box, amrex::Real cur_time); + + void ReadParameters (); + + /** + * Evolve updates a single timestep (dt) of the cold relativistic fluid eqautions + */ + void Evolve (int lev, + const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez, + const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz, + amrex::MultiFab* rho, amrex::MultiFab& jx, amrex::MultiFab& jy, amrex::MultiFab& jz, + amrex::Real cur_time, bool skip_deposition=false); + + /** + * AdvectivePush_Muscl takes a single timestep (dt) of the cold relativistic fluid equations + * using a Muscl-Handcock scheme + * + * \brief Advective term, cold-rel. fluids + * + * \param[in] lev refinement level + */ + void AdvectivePush_Muscl (int lev); + + + /** + * Apply (non-periodic) BC on the fluids (needed for spatial derivative), + * and communicate N, NU at boundaries + * + * \brief Apply non-periodic BC to fluids and communicate boundaries + * + * \param[in] lev refinement level + */ + void ApplyBcFluidsAndComms (int lev); + +#if defined(WARPX_DIM_RZ) + /** + * centrifugal_source_rz adds contributions due to curvature acceleration for a + * single timestep using an SSP-RK3 timestep for RZ specifically + * + * \brief Centrifugal source term + * + * \param[in] lev refinement level + */ + void centrifugal_source_rz (int lev); +#endif + + /** + * GatherAndPush introduces the Lorentz term in the cold relativistic fluid + * equations for a single timestep (dt) using Higuera and Cary Push + * + * \brief Lorentz Momentum Source + * + * \param[in] lev refinement level + * \param[in] Ex Yee electric field (x) + * \param[in] Ey Yee electric field (y) + * \param[in] Ez Yee electric field (z) + * \param[in] Bx Yee magnetic field (x) + * \param[in] By Yee magnetic field (y) + * \param[in] Bz Yee magnetic field (z) + * \param[in] cur_time Current time + */ + void GatherAndPush (int lev, + const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez, + const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz, + amrex::Real cur_time); + + /** + * DepositCurrent interpolates the fluid current density comps. onto the Yee grid and + * sums the contributions to the particle current density + * + * \brief Deposit fluid current density. + * + * \param[in] lev refinement level + * \param[in,out] jx current density MultiFab x comp. + * \param[in,out] jy current density MultiFab y comp. + * \param[in,out] jz current density MultiFab z comp. + */ + void DepositCurrent (int lev, + amrex::MultiFab& jx, amrex::MultiFab& jy, amrex::MultiFab& jz); + + /** + * DepositCharge interpolates the fluid charge density onto the Yee grid and + * sums the contributions to the particle charge density + * + * \brief Deposit fluid charge density. + * + * \param[in] lev refinement level + * \param[in,out] rho charge density MultiFab. + */ + void DepositCharge (int lev, amrex::MultiFab &rho, int icomp = 0); + + amrex::Real getCharge () const {return charge;} + amrex::Real getMass () const {return mass;} + +protected: + int species_id; + std::string species_name; + amrex::Real charge; + amrex::Real mass; + + int do_not_push = 0; + int do_not_gather = 0; + int do_not_deposit = 0; + PhysicalSpecies physical_species; + + // Parser for external fields + std::string m_B_ext_s = "none"; + std::string m_E_ext_s = "none"; + + // Parser for B_external on the particle + std::unique_ptr m_Bx_parser; + std::unique_ptr m_By_parser; + std::unique_ptr m_Bz_parser; + amrex::ParserExecutor<4> m_Bxfield_parser; + amrex::ParserExecutor<4> m_Byfield_parser; + amrex::ParserExecutor<4> m_Bzfield_parser; + + // Parser for E_external on the particle + std::unique_ptr m_Ex_parser; + std::unique_ptr m_Ey_parser; + std::unique_ptr m_Ez_parser; + amrex::ParserExecutor<4> m_Exfield_parser; + amrex::ParserExecutor<4> m_Eyfield_parser; + amrex::ParserExecutor<4> m_Ezfield_parser; + + std::unique_ptr h_inj_rho; + InjectorDensity* d_inj_rho = nullptr; + std::unique_ptr density_parser; + + std::unique_ptr h_inj_mom; + InjectorMomentum* d_inj_mom = nullptr; + std::unique_ptr ux_parser; + std::unique_ptr uy_parser; + std::unique_ptr uz_parser; + + // Keep a pointer to TemperatureProperties to ensure the lifetime of the + // contained Parser + std::unique_ptr h_mom_temp; + std::unique_ptr h_mom_vel; + +public: + + // MultiFabs that contain the density (N) and momentum density (NU) of this fluid species, for each refinement level + amrex::Vector< std::unique_ptr > N; + amrex::Vector, 3 > > NU; + +}; + +#endif diff --git a/Source/Fluids/WarpXFluidContainer.cpp b/Source/Fluids/WarpXFluidContainer.cpp new file mode 100644 index 00000000000..758e353208e --- /dev/null +++ b/Source/Fluids/WarpXFluidContainer.cpp @@ -0,0 +1,1353 @@ +/* Copyright 2023 Grant Johnson, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "ablastr/coarsen/sample.H" +#include "Particles/Pusher/UpdateMomentumHigueraCary.H" +#include "Utils/WarpXProfilerWrapper.H" + +#include "MusclHancockUtils.H" +#include "Fluids/WarpXFluidContainer.H" +#include "WarpX.H" +#include +#include "Utils/Parser/ParserUtils.H" +#include "Utils/WarpXUtil.H" +#include "Utils/SpeciesUtils.H" + +using namespace ablastr::utils::communication; +using namespace amrex; + +WarpXFluidContainer::WarpXFluidContainer(int nlevs_max, int ispecies, const std::string &name) +{ + species_id = ispecies; + species_name = name; + + ReadParameters(); + + // Initialize injection objects + const ParmParse pp_species_name(species_name); + SpeciesUtils::parseDensity(species_name, h_inj_rho, density_parser); + SpeciesUtils::parseMomentum(species_name, "none", h_inj_mom, + ux_parser, uy_parser, uz_parser, h_mom_temp, h_mom_vel); + 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(); + + // Resize the list of MultiFabs for the right number of levels + N.resize(nlevs_max); + NU.resize(nlevs_max); +} + +void WarpXFluidContainer::ReadParameters() +{ + + // Extract charge, mass, species type + std::string injection_style = "none"; + SpeciesUtils::extractSpeciesProperties(species_name, injection_style, charge, mass, physical_species); + + const ParmParse pp_species_name(species_name); + pp_species_name.query("do_not_deposit", do_not_deposit); + pp_species_name.query("do_not_gather", do_not_gather); + pp_species_name.query("do_not_push", do_not_push); + + // default values of E_external and B_external + // are used to set the E and B field when "constant" or "parser" + // is not explicitly used in the input + pp_species_name.query("B_ext_init_style", m_B_ext_s); + std::transform(m_B_ext_s.begin(), + m_B_ext_s.end(), + m_B_ext_s.begin(), + ::tolower); + pp_species_name.query("E_ext_init_style", m_E_ext_s); + std::transform(m_E_ext_s.begin(), + m_E_ext_s.end(), + m_E_ext_s.begin(), + ::tolower); + + // Parse external fields: + // if the input string for B_ext_s is + // "parse_b_ext_function" then the mathematical expression + // for the Bx_, By_, Bz_external_function(x,y,z) + // must be provided in the input file. + if (m_B_ext_s == "parse_b_ext_function") { + // store the mathematical expression as string + std::string str_Bx_ext_function; + std::string str_By_ext_function; + std::string str_Bz_ext_function; + utils::parser::Store_parserString( + pp_species_name, "Bx_external_function(x,y,z,t)", + str_Bx_ext_function); + utils::parser::Store_parserString( + pp_species_name, "By_external_function(x,y,z,t)", + str_By_ext_function); + utils::parser::Store_parserString( + pp_species_name, "Bz_external_function(x,y,z,t)", + str_Bz_ext_function); + + // Parser for B_external on the fluid + m_Bx_parser = std::make_unique( + utils::parser::makeParser(str_Bx_ext_function,{"x","y","z","t"})); + m_By_parser = std::make_unique( + utils::parser::makeParser(str_By_ext_function,{"x","y","z","t"})); + m_Bz_parser = std::make_unique( + utils::parser::makeParser(str_Bz_ext_function,{"x","y","z","t"})); + + } + + // if the input string for E_ext_s is + // "parse_e_ext_function" then the mathematical expression + // for the Ex_, Ey_, Ez_external_function(x,y,z) + // must be provided in the input file. + if (m_E_ext_s == "parse_e_ext_function") { + // store the mathematical expression as string + std::string str_Ex_ext_function; + std::string str_Ey_ext_function; + std::string str_Ez_ext_function; + utils::parser::Store_parserString( + pp_species_name, "Ex_external_function(x,y,z,t)", + str_Ex_ext_function); + utils::parser::Store_parserString( + pp_species_name, "Ey_external_function(x,y,z,t)", + str_Ey_ext_function); + utils::parser::Store_parserString( + pp_species_name, "Ez_external_function(x,y,z,t)", + str_Ez_ext_function); + // Parser for E_external on the fluid + m_Ex_parser = std::make_unique( + utils::parser::makeParser(str_Ex_ext_function,{"x","y","z","t"})); + m_Ey_parser = std::make_unique( + utils::parser::makeParser(str_Ey_ext_function,{"x","y","z","t"})); + m_Ez_parser = std::make_unique( + utils::parser::makeParser(str_Ez_ext_function,{"x","y","z","t"})); + } +} + +void WarpXFluidContainer::AllocateLevelMFs(int lev, const BoxArray &ba, const DistributionMapping &dm) +{ + int ncomps = 1; + const amrex::IntVect nguards(AMREX_D_DECL(2, 2, 2)); + + // set human-readable tag for each MultiFab + auto const tag = [lev](std::string tagname) + { + tagname.append("[l=").append(std::to_string(lev)).append("]"); + return tagname; + }; + + WarpX::AllocInitMultiFab(N[lev], amrex::convert(ba, amrex::IntVect::TheNodeVector()), + dm, ncomps, nguards, lev, tag("fluid density"), 0.0_rt); + + WarpX::AllocInitMultiFab(NU[lev][0], amrex::convert(ba, amrex::IntVect::TheNodeVector()), + dm, ncomps, nguards, lev, tag("fluid momentum density [x]"), 0.0_rt); + WarpX::AllocInitMultiFab(NU[lev][1], amrex::convert(ba, amrex::IntVect::TheNodeVector()), + dm, ncomps, nguards, lev, tag("fluid momentum density [y]"), 0.0_rt); + WarpX::AllocInitMultiFab(NU[lev][2], amrex::convert(ba, amrex::IntVect::TheNodeVector()), + dm, ncomps, nguards, lev, tag("fluid momentum density [z]"), 0.0_rt); +} + +void WarpXFluidContainer::InitData(int lev, amrex::Box init_box, amrex::Real cur_time) +{ + WARPX_PROFILE("WarpXFluidContainer::InitData"); + + // Convert initialization box to nodal box + init_box.surroundingNodes(); + + // Create local copies of pointers for GPU kernels + InjectorDensity* inj_rho = d_inj_rho; + InjectorMomentum* inj_mom = d_inj_mom; + + // Extract grid geometry properties + WarpX &warpx = WarpX::GetInstance(); + const amrex::Geometry &geom = warpx.Geom(lev); + const auto dx = geom.CellSizeArray(); + const auto problo = geom.ProbLoArray(); + const amrex::Real clight = PhysConst::c; + const amrex::Real gamma_boost = WarpX::gamma_boost; + const amrex::Real beta_boost = WarpX::beta_boost; + + // Loop through cells and initialize their value +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + + amrex::Box tile_box = mfi.tilebox(N[lev]->ixType().toIntVect()); + amrex::Array4 const &N_arr = N[lev]->array(mfi); + amrex::Array4 const &NUx_arr = NU[lev][0]->array(mfi); + amrex::Array4 const &NUy_arr = NU[lev][1]->array(mfi); + amrex::Array4 const &NUz_arr = NU[lev][2]->array(mfi); + + // Return the intersection of all cells and the ones we wish to update + amrex::Box init_box_intersection = init_box & tile_box; + + amrex::ParallelFor(init_box_intersection, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { +#if defined(WARPX_DIM_3D) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = problo[1] + j * dx[1]; + amrex::Real z = problo[2] + k * dx[2]; +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[1] + j * dx[1]; +#else + amrex::Real x = 0.0_rt; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[0] + i * dx[0]; +#endif + + // Lorentz transform z (from boosted to lab frame) + if (gamma_boost > 1._rt){ + z = gamma_boost*(z + beta_boost*clight*cur_time); + } + + amrex::Real n = inj_rho->getDensity(x, y, z); + amrex::XDim3 u = inj_mom->getBulkMomentum(x, y, z); + + // Give u the right dimensions of m/s + u.x = u.x * clight; + u.y = u.y * clight; + u.z = u.z * clight; + + // Check if n > 0 and if not, don't compute the boost + // Lorentz transform n, u (from lab to boosted frame) + if (n > 0.0){ + if (gamma_boost > 1._rt){ + amrex::Real gamma = sqrt(1.0 + (u.x*u.x + u.y*u.y + u.z*u.z)/(clight*clight)); + amrex::Real n_boosted = gamma_boost*n*( 1.0 - beta_boost*u.z/(gamma*clight) ); + amrex::Real uz_boosted = gamma_boost*(u.z - beta_boost*clight*gamma); + u.z = uz_boosted; + n = n_boosted; + } + } + + // Multiply by clight so u is back in SI units + N_arr(i, j, k) = n; + NUx_arr(i, j, k) = n * u.x; + NUy_arr(i, j, k) = n * u.y; + NUz_arr(i, j, k) = n * u.z; + + } + ); + } +} + + +void WarpXFluidContainer::Evolve( + int lev, + const amrex::MultiFab &Ex, const amrex::MultiFab &Ey, const amrex::MultiFab &Ez, + const amrex::MultiFab &Bx, const amrex::MultiFab &By, const amrex::MultiFab &Bz, + amrex::MultiFab* rho, amrex::MultiFab &jx, amrex::MultiFab &jy, amrex::MultiFab &jz, + amrex::Real cur_time, bool skip_deposition) +{ + + WARPX_PROFILE("WarpXFluidContainer::Evolve"); + + if (rho && ! skip_deposition && ! do_not_deposit) { + // Deposit charge before particle push, in component 0 of MultiFab rho. + DepositCharge(lev, *rho, 0); + } + + // Step the Lorentz Term + if(!do_not_gather){ + GatherAndPush(lev, Ex, Ey, Ez, Bx, By, Bz, cur_time); + } + + // Cylindrical centrifugal term + if(!do_not_push){ +#if defined(WARPX_DIM_RZ) + centrifugal_source_rz(lev); +#endif + + // Apply (non-periodic) BC on the fluids (needed for spatial derivative), + // and communicate N, NU at boundaries + ApplyBcFluidsAndComms(lev); + + // Step the Advective term + AdvectivePush_Muscl(lev); + } + + // Deposit rho to the simulation mesh + // Deposit charge (end of the step) + if (rho && ! skip_deposition && ! do_not_deposit) { + DepositCharge(lev, *rho, 1); + } + + // Deposit J to the simulation mesh + if (!skip_deposition && ! do_not_deposit) { + DepositCurrent(lev, jx, jy, jz); + } +} + +// Momentum source due to curvature +void WarpXFluidContainer::ApplyBcFluidsAndComms (int lev) +{ + WARPX_PROFILE("WarpXFluidContainer::ApplyBcFluidsAndComms"); + + WarpX &warpx = WarpX::GetInstance(); + const amrex::Geometry &geom = warpx.Geom(lev); + const amrex::Periodicity &period = geom.periodicity(); + const Array periodic_directions = geom.isPeriodic(); + amrex::Box domain = geom.Domain(); + // Convert to nodal box + domain.surroundingNodes(); + + // H&C push the momentum +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + + amrex::Box tile_box = mfi.tilebox(N[lev]->ixType().toIntVect()); + + amrex::Array4 N_arr = N[lev]->array(mfi); + amrex::Array4 NUx_arr = NU[lev][0]->array(mfi); + amrex::Array4 NUy_arr = NU[lev][1]->array(mfi); + amrex::Array4 NUz_arr = NU[lev][2]->array(mfi); + + //Grow the tilebox + tile_box.grow(1); + + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + + // If the cell is is first gaurd cell & the dimension is non + // periodic, then copy Q_{i+1} = Q_{i-1}. + // Don't check r-dir in Z: +#if defined(WARPX_DIM_3D) + + // Upper end (index 2) + if ( (periodic_directions[2] != 1) && (k==domain.bigEnd(2)+1) ){ + N_arr(i,j,k) = N_arr(i,j,k-2); + NUx_arr(i,j,k) = NUx_arr(i,j,k-2); + NUy_arr(i,j,k) = NUy_arr(i,j,k-2); + NUz_arr(i,j,k) = NUz_arr(i,j,k-2); + + // Lower end (index 2) + } else if ( (periodic_directions[2] != 1) && (k==domain.smallEnd(2)-1) ) { + N_arr(i,j,k) = N_arr(i,j,k+2); + NUx_arr(i,j,k) = NUx_arr(i,j,k+2); + NUy_arr(i,j,k) = NUy_arr(i,j,k+2); + NUz_arr(i,j,k) = NUz_arr(i,j,k+2); + } + +#elif ( defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) || defined(WARPX_DIM_3D) ) + + // Upper end (index 1) + if ( (periodic_directions[1] != 1) && (j==domain.bigEnd(1)+1) ){ + N_arr(i,j,k) = N_arr(i,j-2,k); + NUx_arr(i,j,k) = NUx_arr(i,j-2,k); + NUy_arr(i,j,k) = NUy_arr(i,j-2,k); + NUz_arr(i,j,k) = NUz_arr(i,j-2,k); + + // Lower end (index 1`) + } else if ( (periodic_directions[1] != 1) && (j==domain.smallEnd(1)-1) ) { + N_arr(i,j,k) = N_arr(i,j+2,k); + NUx_arr(i,j,k) = NUx_arr(i,j+2,k); + NUy_arr(i,j,k) = NUy_arr(i,j+2,k); + NUz_arr(i,j,k) = NUz_arr(i,j+2,k); + + } + +#elif ( defined(WARPX_DIM_1D_Z) || defined(WARPX_DIM_XZ) || defined(WARPX_DIM_3D) ) + + // Upper end (index 0) + if ( (periodic_directions[0] != 1) && (i==domain.bigEnd(0)+1) ){ + N_arr(i,j,k) = N_arr(i-2,j,k); + NUx_arr(i,j,k) = NUx_arr(i-2,j,k); + NUy_arr(i,j,k) = NUy_arr(i-2,j,k); + NUz_arr(i,j,k) = NUz_arr(i-2,j,k); + + // Lower end (index 0) + } else if ( (periodic_directions[0] != 1) && (i==domain.smallEnd(0)-1) ) { + N_arr(i,j,k) = N_arr(i+2,j,k); + NUx_arr(i,j,k) = NUx_arr(i+2,j,k); + NUy_arr(i,j,k) = NUy_arr(i+2,j,k); + NUz_arr(i,j,k) = NUz_arr(i+2,j,k); + } + +#else + +#endif + } + ); + } + + // Fill guard cells + FillBoundary(*N[lev], N[lev]->nGrowVect(), WarpX::do_single_precision_comms, period); + FillBoundary(*NU[lev][0], NU[lev][0]->nGrowVect(), WarpX::do_single_precision_comms, period); + FillBoundary(*NU[lev][1], NU[lev][1]->nGrowVect(), WarpX::do_single_precision_comms, period); + FillBoundary(*NU[lev][2], NU[lev][2]->nGrowVect(), WarpX::do_single_precision_comms, period); +} + +// Muscl Advection Update +void WarpXFluidContainer::AdvectivePush_Muscl (int lev) +{ + WARPX_PROFILE("WarpXFluidContainer::AdvectivePush_Muscl"); + + // Grab the grid spacing + WarpX &warpx = WarpX::GetInstance(); + const Real dt = warpx.getdt(lev); + const amrex::Geometry &geom = warpx.Geom(lev); + const auto dx = geom.CellSizeArray(); + const amrex::Real clight = PhysConst::c; +#if defined(WARPX_DIM_3D) + amrex::Real dt_over_dx = (dt/dx[0]); + amrex::Real dt_over_dy = (dt/dx[1]); + amrex::Real dt_over_dz = (dt/dx[2]); + amrex::Real dt_over_dx_half = 0.5*(dt/dx[0]); + amrex::Real dt_over_dy_half = 0.5*(dt/dx[1]); + amrex::Real dt_over_dz_half = 0.5*(dt/dx[2]); +#elif defined(WARPX_DIM_XZ) + amrex::Real dt_over_dx_half = 0.5*(dt/dx[0]); + amrex::Real dt_over_dz_half = 0.5*(dt/dx[1]); + amrex::Real dt_over_dx = (dt/dx[0]); + amrex::Real dt_over_dz = (dt/dx[1]); +#elif defined(WARPX_DIM_RZ) + const auto problo = geom.ProbLoArray(); + amrex::Real dt_over_dx_half = 0.5*(dt/dx[0]); + amrex::Real dt_over_dz_half = 0.5*(dt/dx[1]); + amrex::Box const& domain = geom.Domain(); +#else + amrex::Real dt_over_dz = (dt/dx[0]); + amrex::Real dt_over_dz_half = 0.5*(dt/dx[0]); +#endif + + amrex::BoxArray ba = N[lev]->boxArray(); + + // Temporary Half-step values +#if defined(WARPX_DIM_3D) + amrex::MultiFab tmp_U_minus_x( amrex::convert(ba, IntVect(0,1,1)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_plus_x( amrex::convert(ba, IntVect(0,1,1)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_minus_y( amrex::convert(ba, IntVect(1,0,1)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_plus_y( amrex::convert(ba, IntVect(1,0,1)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_minus_z( amrex::convert(ba, IntVect(1,1,0)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_plus_z( amrex::convert(ba, IntVect(1,1,0)), N[lev]->DistributionMap(), 4, 1); +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::MultiFab tmp_U_minus_x( amrex::convert(ba, IntVect(0,1)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_plus_x( amrex::convert(ba, IntVect(0,1)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_minus_z( amrex::convert(ba, IntVect(1,0)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_plus_z( amrex::convert(ba, IntVect(1,0)), N[lev]->DistributionMap(), 4, 1); +#else + amrex::MultiFab tmp_U_minus_z( amrex::convert(ba, IntVect(0)), N[lev]->DistributionMap(), 4, 1); + amrex::MultiFab tmp_U_plus_z( amrex::convert(ba, IntVect(0)), N[lev]->DistributionMap(), 4, 1); +#endif + + // Fill edge values of N and U at the half timestep for MUSCL +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + + // Loop over a box with one extra gridpoint in the ghost region to avoid + // an extra MPI communication between the edge value computation loop and + // the flux calculation loop + amrex::Box tile_box = mfi.growntilebox(1); + + // Limit the grown box for RZ at r = 0, r_max +#if defined (WARPX_DIM_RZ) + const int idir = 0; + const int n_cell = -1; + tile_box.growLo(idir, n_cell); + tile_box.growHi(idir, n_cell); +#endif + + amrex::Array4 const &N_arr = N[lev]->array(mfi); + amrex::Array4 const &NUx_arr = NU[lev][0]->array(mfi); + amrex::Array4 const &NUy_arr = NU[lev][1]->array(mfi); + amrex::Array4 const &NUz_arr = NU[lev][2]->array(mfi); + + // Boxes are computed to avoid going out of bounds. + // Grow the entire domain + amrex::Box box = mfi.validbox(); + box.grow(1); +#if defined(WARPX_DIM_3D) + amrex::Box const box_x = amrex::convert( box, tmp_U_minus_x.ixType() ); + amrex::Box const box_y = amrex::convert( box, tmp_U_minus_y.ixType() ); + amrex::Box const box_z = amrex::convert( box, tmp_U_minus_z.ixType() ); +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::Box const box_x = amrex::convert( box, tmp_U_minus_x.ixType() ); + amrex::Box const box_z = amrex::convert( box, tmp_U_minus_z.ixType() ); +#else + amrex::Box const box_z = amrex::convert( box, tmp_U_minus_z.ixType() ); +#endif + + //N and NU are always defined at the nodes, the tmp_Q_* are defined + //inbetween the nodes (i.e. on the staggered Yee grid) and store the + //values of N and U at these points. + //(i.e. the 4 components correspond to N + the 3 components of U) + // Extract the temporary arrays for edge values +#if defined(WARPX_DIM_3D) + amrex::Array4 U_minus_x = tmp_U_minus_x.array(mfi); + amrex::Array4 U_plus_x = tmp_U_plus_x.array(mfi); + amrex::Array4 U_minus_y = tmp_U_minus_y.array(mfi); + amrex::Array4 U_plus_y = tmp_U_plus_y.array(mfi); + amrex::Array4 U_minus_z = tmp_U_minus_z.array(mfi); + amrex::Array4 U_plus_z = tmp_U_plus_z.array(mfi); +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::Array4 U_minus_x = tmp_U_minus_x.array(mfi); + amrex::Array4 U_plus_x = tmp_U_plus_x.array(mfi); + amrex::Array4 U_minus_z = tmp_U_minus_z.array(mfi); + amrex::Array4 U_plus_z = tmp_U_plus_z.array(mfi); +#else + amrex::Array4 U_minus_z = tmp_U_minus_z.array(mfi); + amrex::Array4 U_plus_z = tmp_U_plus_z.array(mfi); +#endif + + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + + // Density positivity check (Makes the algorithm safe from divide by zeros) + if( N_arr(i,j,k) > 0.0){ + + // - Grab local Uz Uy Ux gamma + // Isolate U from NU + amrex::Real Ux = (NUx_arr(i, j, k) / N_arr(i,j,k)); + amrex::Real Uy = (NUy_arr(i, j, k) / N_arr(i,j,k)); + amrex::Real Uz = (NUz_arr(i, j, k) / N_arr(i,j,k)); + + // Compute useful quantities for J + amrex::Real c_sq = clight*clight; + amrex::Real gamma = sqrt(1.0 + (Ux*Ux + Uy*Uy + Uz*Uz)/(c_sq) ); + amrex::Real inv_c2_gamma3 = 1./(c_sq*gamma*gamma*gamma); + + // J represents are 4x4 matrices that show up in the advection + // equations written as a function of U = {N, Ux, Uy, Uz}: + // \partial_t U + Jx \partial_x U + Jy \partial_y U + Jz \partial_z U = 0 +#if defined(WARPX_DIM_3D) || defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + amrex::Real Vx = Ux/gamma; + // Compute the non-zero element of Jx + amrex::Real J00x = Vx; + amrex::Real J01x = N_arr(i,j,k)*(1/gamma)*(1-Vx*Vx/c_sq); + amrex::Real J02x = -N_arr(i,j,k)*Uy*Ux*inv_c2_gamma3; + amrex::Real J03x = -N_arr(i,j,k)*Uz*Ux*inv_c2_gamma3; + amrex::Real J11x = Vx; + amrex::Real J22x = Vx; + amrex::Real J33x = Vx; + + // Compute the cell slopes x + amrex::Real dU0x = ave( DownDx_N(N_arr,i,j,k), UpDx_N(N_arr,i,j,k) ); + amrex::Real dU1x = ave( DownDx_U(N_arr,NUx_arr,Ux,i,j,k), UpDx_U(N_arr,NUx_arr,Ux,i,j,k) ); + amrex::Real dU2x = ave( DownDx_U(N_arr,NUy_arr,Uy,i,j,k), UpDx_U(N_arr,NUy_arr,Uy,i,j,k) ); + amrex::Real dU3x = ave( DownDx_U(N_arr,NUz_arr,Uz,i,j,k), UpDx_U(N_arr,NUz_arr,Uz,i,j,k) ); + +#endif + +#if defined(WARPX_DIM_3D) + amrex::Real Vy = Uy/gamma; + // Compute the non-zero element of Jy + amrex::Real J00y = Vy; + amrex::Real J01y = -N_arr(i,j,k)*Ux*Uy*inv_c2_gamma3; + amrex::Real J02y = N_arr(i,j,k)*(1/gamma)*(1-Vy*Vy/c_sq); + amrex::Real J03y = -N_arr(i,j,k)*Uz*Uy*inv_c2_gamma3; + amrex::Real J11y = Vy; + amrex::Real J22y = Vy; + amrex::Real J33y = Vy; + + // Compute the cell slopes y + amrex::Real dU0y = ave( DownDy_N(N_arr,i,j,k), UpDy_N(N_arr,i,j,k) ); + amrex::Real dU1y = ave( DownDy_U(N_arr,NUx_arr,Ux,i,j,k), UpDy_U(N_arr,NUx_arr,Ux,i,j,k) ); + amrex::Real dU2y = ave( DownDy_U(N_arr,NUy_arr,Uy,i,j,k), UpDy_U(N_arr,NUy_arr,Uy,i,j,k) ); + amrex::Real dU3y = ave( DownDy_U(N_arr,NUz_arr,Uz,i,j,k), UpDy_U(N_arr,NUz_arr,Uz,i,j,k) ); + +#endif + amrex::Real Vz = Uz/gamma; + // Compute the non-zero element of Jz + amrex::Real J00z = Vz; + amrex::Real J01z = -N_arr(i,j,k)*Ux*Uz*inv_c2_gamma3; + amrex::Real J02z = -N_arr(i,j,k)*Uy*Uz*inv_c2_gamma3; + amrex::Real J03z = N_arr(i,j,k)*(1/gamma)*(1-Vz*Vz/c_sq); + amrex::Real J11z = Vz; + amrex::Real J22z = Vz; + amrex::Real J33z = Vz; + + // Compute the cell slopes z + amrex::Real dU0z = ave( DownDz_N(N_arr,i,j,k), UpDz_N(N_arr,i,j,k) ); + amrex::Real dU1z = ave( DownDz_U(N_arr,NUx_arr,Ux,i,j,k), UpDz_U(N_arr,NUx_arr,Ux,i,j,k) ); + amrex::Real dU2z = ave( DownDz_U(N_arr,NUy_arr,Uy,i,j,k), UpDz_U(N_arr,NUy_arr,Uy,i,j,k) ); + amrex::Real dU3z = ave( DownDz_U(N_arr,NUz_arr,Uz,i,j,k), UpDz_U(N_arr,NUz_arr,Uz,i,j,k) ); + + + // Select the specific implementation depending on dimensionality +#if defined(WARPX_DIM_3D) + + // Compute U ([ N, U]) at the halfsteps (U_tidle) using the slopes (dU) + amrex::Real JdU0x = J00x*dU0x + J01x*dU1x + J02x*dU2x + J03x*dU3x; + amrex::Real JdU1x = J11x*dU1x ; + amrex::Real JdU2x = J22x*dU2x ; + amrex::Real JdU3x = J33x*dU3x; + amrex::Real JdU0y = J00y*dU0y + J01y*dU1y + J02y*dU2y + J03y*dU3y; + amrex::Real JdU1y = J11y*dU1y; + amrex::Real JdU2y = J22y*dU2y; + amrex::Real JdU3y = J33y*dU3y; + amrex::Real JdU0z = J00z*dU0z + J01z*dU1z + J02z*dU2z + J03z*dU3z; + amrex::Real JdU1z = J11z*dU1z; + amrex::Real JdU2z = J22z*dU2z; + amrex::Real JdU3z = J33z*dU3z; + amrex::Real U_tilde0 = N_arr(i,j,k) - dt_over_dx_half*JdU0x - dt_over_dy_half*JdU0y - dt_over_dz_half*JdU0z; + amrex::Real U_tilde1 = Ux - dt_over_dx_half*JdU1x - dt_over_dy_half*JdU1y - dt_over_dz_half*JdU1z; + amrex::Real U_tilde2 = Uy - dt_over_dx_half*JdU2x - dt_over_dy_half*JdU2y - dt_over_dz_half*JdU2z; + amrex::Real U_tilde3 = Uz - dt_over_dx_half*JdU3x - dt_over_dy_half*JdU3y - dt_over_dz_half*JdU3z; + + + // Predict U at the cell edges (x) + compute_U_edges(U_minus_x, U_plus_x, i, j, k, box_x, U_tilde0, U_tilde1, U_tilde2, U_tilde3, dU0x, dU1x, dU2x, dU3x,0); + + // Positivity Limiter for density N, if N_edge < 0, + // then set the slope (dU) to to zero in that cell/direction + positivity_limiter (U_plus_x, U_minus_x, N_arr, i, j, k, box_x, Ux, Uy, Uz, 0); + + // Predict U at the cell edges (y) + compute_U_edges(U_minus_y, U_plus_y, i, j, k, box_y, U_tilde0, U_tilde1, U_tilde2, U_tilde3, dU0y, dU1y, dU2y, dU3y,1); + + // Positivity Limiter for density N, if N_edge < 0, + // then set the slope (dU) to to zero in that cell/direction + positivity_limiter (U_plus_y, U_minus_y, N_arr, i, j, k, box_y, Ux, Uy, Uz, 1); + + // Predict U at the cell edges (z) + compute_U_edges(U_minus_z, U_plus_z, i, j, k, box_z, U_tilde0, U_tilde1, U_tilde2, U_tilde3, dU0z, dU1z, dU2z, dU3z,2); + + // Positivity Limiter for density N, if N_edge < 0, + // then set the slope (dU) to to zero in that cell/direction + positivity_limiter (U_plus_z, U_minus_z, N_arr, i, j, k, box_z, Ux, Uy, Uz, 2); + +#elif defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + + // Have no RZ-intertial source for primative vars if in XZ + amrex::Real N_source = 0.0; + +#if defined(WARPX_DIM_RZ) + amrex::Real dr = dx[0]; + amrex::Real r = problo[0] + i * dr; + // Impose "none" boundaries + // Condition: dUx = 0 at r = 0 + if (i == domain.smallEnd(0)) { + // R|_{0+} -> L|_{0-} + // N -> N (N_arr(i-1,j,k) -> N_arr(i+1,j,k)) + // NUr -> -NUr (NUx_arr(i-1,j,k) -> -NUx_arr(i+1,j,k)) + // NUt -> -NUt (NUy_arr(i-1,j,k) -> -NUy_arr(i+1,j,k)) + // NUz -> -NUz (NUz_arr(i-1,j,k) -> NUz_arr(i+1,j,k)) + dU0x = ave( -UpDx_N(N_arr,i,j,k) , UpDx_N(N_arr,i,j,k) ); + // First term in the ave is: U_{x,y} + U_{x,y}_p, + // which can be writen as 2*U_{x,y} + UpDx_U(U_{x,y}) + dU1x = ave( 2.0*Ux + UpDx_U(N_arr,NUx_arr,Ux,i,j,k) , UpDx_U(N_arr,NUx_arr,Ux,i,j,k) ); + dU2x = ave( 2.0*Uy + UpDx_U(N_arr,NUy_arr,Uy,i,j,k) , UpDx_U(N_arr,NUy_arr,Uy,i,j,k) ); + dU3x = ave( -UpDx_U(N_arr,NUz_arr,Uz,i,j,k) , UpDx_U(N_arr,NUz_arr,Uz,i,j,k) ); + } else if (i == domain.bigEnd(0)+1) { + dU0x = ave( DownDx_N(N_arr,i,j,k) , 0.0 ); + dU1x = ave( DownDx_U(N_arr,NUx_arr,Ux,i,j,k) , 0.0 ); + dU2x = ave( DownDx_U(N_arr,NUy_arr,Uy,i,j,k) , 0.0 ); + dU3x = ave( DownDx_U(N_arr,NUz_arr,Uz,i,j,k) , 0.0 ); + } + + // RZ sources: + if (i != domain.smallEnd(0)) { + N_source = N_arr(i,j,k)*Vx/r; + } +#endif + + // Compute U ([ N, U]) at the halfsteps (U_tidle) using the slopes (dU) + amrex::Real JdU0x = J00x*dU0x + J01x*dU1x + J02x*dU2x + J03x*dU3x; + amrex::Real JdU1x = J11x*dU1x; + amrex::Real JdU2x = J22x*dU2x; + amrex::Real JdU3x = J33x*dU3x; + amrex::Real JdU0z = J00z*dU0z + J01z*dU1z + J02z*dU2z + J03z*dU3z; + amrex::Real JdU1z = J11z*dU1z; + amrex::Real JdU2z = J22z*dU2z; + amrex::Real JdU3z = J33z*dU3z; + amrex::Real U_tilde0 = N_arr(i,j,k) - dt_over_dx_half*JdU0x - dt_over_dz_half*JdU0z - (dt/2.0)*N_source; + amrex::Real U_tilde1 = Ux - dt_over_dx_half*JdU1x - dt_over_dz_half*JdU1z; + amrex::Real U_tilde2 = Uy - dt_over_dx_half*JdU2x - dt_over_dz_half*JdU2z; + amrex::Real U_tilde3 = Uz - dt_over_dx_half*JdU3x - dt_over_dz_half*JdU3z; + + // Predict U at the cell edges (x) + compute_U_edges(U_minus_x, U_plus_x, i, j, k, box_x, U_tilde0, U_tilde1, U_tilde2, U_tilde3, dU0x, dU1x, dU2x, dU3x,0); + + // Positivity Limiter for density N, if N_edge < 0, + // then set the slope (dU) to to zero in that cell/direction + positivity_limiter (U_plus_x, U_minus_x, N_arr, i, j, k, box_x, Ux, Uy, Uz, 0); + + // Predict U at the cell edges (z) + compute_U_edges(U_minus_z, U_plus_z, i, j, k, box_z, U_tilde0, U_tilde1, U_tilde2, U_tilde3, dU0z, dU1z, dU2z, dU3z,2); + + // Positivity Limiter for density N, if N_edge < 0, + // then set the slope (dU) to to zero in that cell/direction + positivity_limiter (U_plus_z, U_minus_z, N_arr, i, j, k, box_z, Ux, Uy, Uz, 2); + +#else + + // Compute U ([ N, U]) at the halfsteps (U_tidle) using the slopes (dU) + amrex::Real JdU0z = J00z*dU0z + J01z*dU1z + J02z*dU2z + J03z*dU3z; + amrex::Real JdU1z = J11z*dU1z; + amrex::Real JdU2z = J22z*dU2z; + amrex::Real JdU3z = J33z*dU3z; + amrex::Real U_tilde0 = N_arr(i,j,k) - dt_over_dz_half*JdU0z; + amrex::Real U_tilde1 = Ux - dt_over_dz_half*JdU1z; + amrex::Real U_tilde2 = Uy - dt_over_dz_half*JdU2z; + amrex::Real U_tilde3 = Uz - dt_over_dz_half*JdU3z; + + // Predict U at the cell edges (z) + compute_U_edges(U_minus_z, U_plus_z, i, j, k, box_z, U_tilde0, U_tilde1, U_tilde2, U_tilde3, dU0z, dU1z, dU2z, dU3z,2); + + // Positivity Limiter for density N, if N_edge < 0, + // then set the slope (dU) to to zero in that cell/direction + positivity_limiter (U_plus_z, U_minus_z, N_arr, i, j, k, box_z, Ux, Uy, Uz, 2); + +#endif + // If N<= 0 then set the edge values (U_minus/U_plus) to zero + } else { +#if defined(WARPX_DIM_3D) + set_U_edges_to_zero(U_minus_x, U_plus_x, i, j, k, box_x, 0); + set_U_edges_to_zero(U_minus_y, U_plus_y, i, j, k, box_y, 1); + set_U_edges_to_zero(U_minus_z, U_plus_z, i, j, k, box_z, 2); +#elif defined(WARPX_DIM_RZ) || defined(WARPX_DIM_XZ) + set_U_edges_to_zero(U_minus_x, U_plus_x, i, j, k, box_x, 0); + set_U_edges_to_zero(U_minus_z, U_plus_z, i, j, k, box_z, 2); +#else + set_U_edges_to_zero(U_minus_z, U_plus_z, i, j, k, box_z, 2); +#endif + } + } + ); + } + + // Given the values of `U_minus` and `U_plus`, compute fluxes inbetween nodes, and update N, NU accordingly +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + amrex::Box tile_box = mfi.tilebox(N[lev]->ixType().toIntVect()); + amrex::Array4 N_arr = N[lev]->array(mfi); + amrex::Array4 NUx_arr = NU[lev][0]->array(mfi); + amrex::Array4 NUy_arr = NU[lev][1]->array(mfi); + amrex::Array4 NUz_arr = NU[lev][2]->array(mfi); + +#if defined(WARPX_DIM_3D) + amrex::Array4 const &U_minus_x = tmp_U_minus_x.array(mfi); + amrex::Array4 const &U_plus_x = tmp_U_plus_x.array(mfi); + amrex::Array4 const &U_minus_y = tmp_U_minus_y.array(mfi); + amrex::Array4 const &U_plus_y = tmp_U_plus_y.array(mfi); + amrex::Array4 const &U_minus_z = tmp_U_minus_z.array(mfi); + amrex::Array4 const &U_plus_z = tmp_U_plus_z.array(mfi); +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::Array4 const &U_minus_x = tmp_U_minus_x.array(mfi); + amrex::Array4 const &U_plus_x = tmp_U_plus_x.array(mfi); + amrex::Array4 const &U_minus_z = tmp_U_minus_z.array(mfi); + amrex::Array4 const &U_plus_z = tmp_U_plus_z.array(mfi); +#else + amrex::Array4 const &U_minus_z = tmp_U_minus_z.array(mfi); + amrex::Array4 const &U_plus_z = tmp_U_plus_z.array(mfi); +#endif + + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + + // Select the specific implementation depending on dimensionality +#if defined(WARPX_DIM_3D) + + // Update the conserved variables Q = [N, NU] from tn -> tn + dt + N_arr(i,j,k) = N_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,0,0) + - dt_over_dy*dF(U_minus_y,U_plus_y,i,j,k,clight,0,1) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,0,2); + NUx_arr(i,j,k) = NUx_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,1,0) + - dt_over_dy*dF(U_minus_y,U_plus_y,i,j,k,clight,1,1) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,1,2); + NUy_arr(i,j,k) = NUy_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,2,0) + - dt_over_dy*dF(U_minus_y,U_plus_y,i,j,k,clight,2,1) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,2,2); + NUz_arr(i,j,k) = NUz_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,3,0) + - dt_over_dy*dF(U_minus_y,U_plus_y,i,j,k,clight,3,1) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,3,2); + +#elif defined(WARPX_DIM_XZ) + + // Update the conserved variables Q = [N, NU] from tn -> tn + dt + N_arr(i,j,k) = N_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,0,0) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,0,2); + NUx_arr(i,j,k) = NUx_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,1,0) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,1,2); + NUy_arr(i,j,k) = NUy_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,2,0) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,2,2); + NUz_arr(i,j,k) = NUz_arr(i,j,k) - dt_over_dx*dF(U_minus_x,U_plus_x,i,j,k,clight,3,0) + - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,3,2); + +#elif defined(WARPX_DIM_RZ) + + // Compute the flux areas for RZ + // Cell-centered radius + amrex::Real dr = dx[0]; + amrex::Real dz = dx[1]; + amrex::Real r = problo[0] + i * dr; + amrex::Real Vij = 0.0; + amrex::Real S_Az = 0.0; + + // Volume element and z-facing surfaces + if (i == domain.smallEnd(0)) { + Vij = 2.0*MathConst::pi*(dr/2.0)*(dr/4.0)*dz; + S_Az = 2.0*MathConst::pi*(dr/4.0)*(dr/2.0); + } else if (i == domain.bigEnd(0)+1) { + Vij = 2.0*MathConst::pi*(r - dr/4.0)*(dr/2.0)*dz; + S_Az = 2.0*MathConst::pi*(r - dr/4.0)*(dr/2.0); + } else { + Vij = 2.0*MathConst::pi*r*dr*dz; + S_Az = 2.0*MathConst::pi*(r)*dr; + } + + // Radial Surfaces + amrex::Real S_Ar_plus = 2.0*MathConst::pi*(r + dr/2.0)*dz; + amrex::Real S_Ar_minus = 2.0*MathConst::pi*(r - dr/2.0)*dz; + if (i == domain.smallEnd(0)) + S_Ar_minus = 0.0; + if (i == domain.bigEnd(0)+1) + S_Ar_plus = 2.0*MathConst::pi*(r)*dz; + + // Impose "none" boundaries + // Condition: Vx(r) = 0 at boundaries + amrex::Real Vx_I_minus = V_calc(U_minus_x,i,j,k,0,clight); + amrex::Real Vx_L_plus = V_calc(U_plus_x,i-1,j,k,0,clight); + + // compute the fluxes: + // (note that _plus is shifted due to grid location) + amrex::Real Vx_L_minus = 0.0, Vx_I_plus = 0.0; + amrex::Real F0_minusx = 0.0, F1_minusx = 0.0, F2_minusx = 0.0, F3_minusx = 0.0; + amrex::Real F0_plusx = 0.0, F1_plusx = 0.0, F2_plusx = 0.0, F3_plusx = 0.0; + if (i != domain.smallEnd(0)) { + Vx_L_minus = V_calc(U_minus_x,i-1,j,k,0,clight); + F0_minusx = flux_N( U_minus_x, U_plus_x, i-1, j, k, Vx_L_minus, Vx_L_plus)*S_Ar_minus; + F1_minusx = flux_NUx(U_minus_x, U_plus_x, i-1, j, k, Vx_L_minus, Vx_L_plus)*S_Ar_minus; + F2_minusx = flux_NUy(U_minus_x, U_plus_x, i-1, j, k, Vx_L_minus, Vx_L_plus)*S_Ar_minus; + F3_minusx = flux_NUz(U_minus_x, U_plus_x, i-1, j, k, Vx_L_minus, Vx_L_plus)*S_Ar_minus; + } + if (i < domain.bigEnd(0)) { + Vx_I_plus = V_calc(U_plus_x,i,j,k,0,clight); + F0_plusx = flux_N( U_minus_x, U_plus_x, i , j, k, Vx_I_minus, Vx_I_plus)*S_Ar_plus; + F1_plusx = flux_NUx(U_minus_x, U_plus_x, i , j, k, Vx_I_minus, Vx_I_plus)*S_Ar_plus; + F2_plusx = flux_NUy(U_minus_x, U_plus_x, i , j, k, Vx_I_minus, Vx_I_plus)*S_Ar_plus; + F3_plusx = flux_NUz(U_minus_x, U_plus_x, i , j, k, Vx_I_minus, Vx_I_plus)*S_Ar_plus; + } + + // Update the conserved variables from tn -> tn + dt + N_arr(i,j,k) = N_arr(i,j,k) - (dt/Vij)*(F0_plusx - F0_minusx + dF(U_minus_z,U_plus_z,i,j,k,clight,0,2)*S_Az); + NUx_arr(i,j,k) = NUx_arr(i,j,k) - (dt/Vij)*(F1_plusx - F1_minusx + dF(U_minus_z,U_plus_z,i,j,k,clight,1,2)*S_Az); + NUy_arr(i,j,k) = NUy_arr(i,j,k) - (dt/Vij)*(F2_plusx - F2_minusx + dF(U_minus_z,U_plus_z,i,j,k,clight,2,2)*S_Az); + NUz_arr(i,j,k) = NUz_arr(i,j,k) - (dt/Vij)*(F3_plusx - F3_minusx + dF(U_minus_z,U_plus_z,i,j,k,clight,3,2)*S_Az); + +#else + + // Update the conserved variables Q = [N, NU] from tn -> tn + dt + N_arr(i,j,k) = N_arr(i,j,k) - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,0,2); + NUx_arr(i,j,k) = NUx_arr(i,j,k) - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,1,2); + NUy_arr(i,j,k) = NUy_arr(i,j,k) - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,2,2); + NUz_arr(i,j,k) = NUz_arr(i,j,k) - dt_over_dz*dF(U_minus_z,U_plus_z,i,j,k,clight,3,2); +#endif + } + ); + } +} + + +// Momentum source due to curvature +#if defined(WARPX_DIM_RZ) +void WarpXFluidContainer::centrifugal_source_rz (int lev) +{ + WARPX_PROFILE("WarpXFluidContainer::centrifugal_source_rz"); + + WarpX &warpx = WarpX::GetInstance(); + const Real dt = warpx.getdt(lev); + const amrex::Geometry &geom = warpx.Geom(lev); + const auto dx = geom.CellSizeArray(); + const auto problo = geom.ProbLoArray(); + const amrex::Real clight = PhysConst::c; + amrex::Box const& domain = geom.Domain(); + + // H&C push the momentum +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + + amrex::Box const &tile_box = mfi.tilebox(N[lev]->ixType().toIntVect()); + + amrex::Array4 const &N_arr = N[lev]->array(mfi); + amrex::Array4 NUx_arr = NU[lev][0]->array(mfi); + amrex::Array4 NUy_arr = NU[lev][1]->array(mfi); + amrex::Array4 const &NUz_arr = NU[lev][2]->array(mfi); + + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + + // Verify density is non-zero + if (N_arr(i,j,k)>0.0) { + + // Compute r + amrex::Real r = problo[0] + i * dx[0]; + + // Isolate U from NU + amrex::Real u_r = (NUx_arr(i, j, k) / (N_arr(i,j,k) * clight )); + amrex::Real u_theta = (NUy_arr(i, j, k) / (N_arr(i,j,k) * clight )); + amrex::Real u_z = (NUz_arr(i, j, k) / (N_arr(i,j,k) * clight )); + + // (SSP-RK3) Push the fluid momentum (R and Theta) + // F_r, F_theta are first order euler pushes of our rhs operator + if (i != domain.smallEnd(0)) { + amrex::Real u_r_1 = F_r(r,u_r,u_theta,u_z,dt); + amrex::Real u_theta_1 = F_theta(r,u_r,u_theta,u_z,dt); + amrex::Real u_r_2 = (0.75)*(u_r) + (0.25)*F_r(r,u_r_1,u_theta_1,u_z,dt); + amrex::Real u_theta_2 = (0.75)*(u_theta) + (0.25)*F_theta(r,u_r_1,u_theta_1,u_z,dt); + u_r = (1.0/3.0)*(u_r) + (2.0/3.0)*F_r(r,u_r_2,u_theta_2,u_z,dt); + u_theta = (1.0/3.0)*(u_theta) + (2.0/3.0)*F_theta(r,u_r_2,u_theta_2,u_z,dt); + + // Calculate NU, save NUr, NUtheta + NUx_arr(i,j,k) = N_arr(i,j,k)*u_r*clight; + NUy_arr(i,j,k) = N_arr(i,j,k)*u_theta*clight; + + // BC r = 0, u_theta = 0, and there is no extra source terms + } else { + NUx_arr(i,j,k) = 0.0; + NUy_arr(i,j,k) = 0.0; + } + } + } + ); + } +} +#endif + +// Momentum source from fields +void WarpXFluidContainer::GatherAndPush ( + int lev, + const amrex::MultiFab& Ex, const amrex::MultiFab& Ey, const amrex::MultiFab& Ez, + const amrex::MultiFab& Bx, const amrex::MultiFab& By, const amrex::MultiFab& Bz, + Real t) +{ + WARPX_PROFILE("WarpXFluidContainer::GatherAndPush"); + + WarpX &warpx = WarpX::GetInstance(); + const amrex::Real q = getCharge(); + const amrex::Real m = getMass(); + const Real dt = warpx.getdt(lev); + const amrex::Geometry &geom = warpx.Geom(lev); + const auto dx = geom.CellSizeArray(); + const auto problo = geom.ProbLoArray(); + const amrex::Real gamma_boost = WarpX::gamma_boost; + const amrex::Real beta_boost = WarpX::beta_boost; + //Check whether m_E_ext_s is "none" + bool external_e_fields; // Needs intializing + bool external_b_fields; // Needs intializing + + + // Prepare interpolation of current components to cell center + amrex::GpuArray Nodal_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray Ex_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray Ey_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray Ez_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray Bx_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray By_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray Bz_type = amrex::GpuArray{0, 0, 0}; + for (int i = 0; i < AMREX_SPACEDIM; ++i) + { + Nodal_type[i] = N[lev]->ixType()[i]; + Ex_type[i] = Ex.ixType()[i]; + Ey_type[i] = Ey.ixType()[i]; + Ez_type[i] = Ez.ixType()[i]; + Bx_type[i] = Bx.ixType()[i]; + By_type[i] = By.ixType()[i]; + Bz_type[i] = Bz.ixType()[i]; + } + + // External field parsers + external_e_fields = (m_E_ext_s == "parse_e_ext_function"); + external_b_fields = (m_B_ext_s == "parse_b_ext_function"); + amrex::ParserExecutor<4> Exfield_parser; + amrex::ParserExecutor<4> Eyfield_parser; + amrex::ParserExecutor<4> Ezfield_parser; + amrex::ParserExecutor<4> Bxfield_parser; + amrex::ParserExecutor<4> Byfield_parser; + amrex::ParserExecutor<4> Bzfield_parser; + if (external_e_fields){ + constexpr int num_arguments = 4; //x,y,z,t + Exfield_parser = m_Ex_parser->compile(); + Eyfield_parser = m_Ey_parser->compile(); + Ezfield_parser = m_Ez_parser->compile(); + } + + if (external_b_fields){ + constexpr int num_arguments = 4; //x,y,z,t + Bxfield_parser = m_Bx_parser->compile(); + Byfield_parser = m_By_parser->compile(); + Bzfield_parser = m_Bz_parser->compile(); + } + + + // H&C push the momentum +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + + amrex::Box const &tile_box = mfi.tilebox(N[lev]->ixType().toIntVect()); + + amrex::Array4 const &N_arr = N[lev]->array(mfi); + amrex::Array4 NUx_arr = NU[lev][0]->array(mfi); + amrex::Array4 NUy_arr = NU[lev][1]->array(mfi); + amrex::Array4 NUz_arr = NU[lev][2]->array(mfi); + + amrex::Array4 const& Ex_arr = Ex.array(mfi); + amrex::Array4 const& Ey_arr = Ey.array(mfi); + amrex::Array4 const& Ez_arr = Ez.array(mfi); + amrex::Array4 const& Bx_arr = Bx.array(mfi); + amrex::Array4 const& By_arr = By.array(mfi); + amrex::Array4 const& Bz_arr = Bz.array(mfi); + + // Here, we do not perform any coarsening. + amrex::GpuArray coarsening_ratio = {1, 1, 1}; + + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + + // Only run if density is positive + if (N_arr(i,j,k)>0.0) { + + // Interpolate fields from tmp to Nodal points + amrex::Real Ex_Nodal = ablastr::coarsen::sample::Interp(Ex_arr, + Ex_type, Nodal_type, coarsening_ratio, i, j, k, 0); + amrex::Real Ey_Nodal = ablastr::coarsen::sample::Interp(Ey_arr, + Ey_type, Nodal_type, coarsening_ratio, i, j, k, 0); + amrex::Real Ez_Nodal = ablastr::coarsen::sample::Interp(Ez_arr, + Ez_type, Nodal_type, coarsening_ratio, i, j, k, 0); + amrex::Real Bx_Nodal = ablastr::coarsen::sample::Interp(Bx_arr, + Bx_type, Nodal_type, coarsening_ratio, i, j, k, 0); + amrex::Real By_Nodal = ablastr::coarsen::sample::Interp(By_arr, + By_type, Nodal_type, coarsening_ratio, i, j, k, 0); + amrex::Real Bz_Nodal = ablastr::coarsen::sample::Interp(Bz_arr, + Bz_type, Nodal_type, coarsening_ratio, i, j, k, 0); + + if (gamma_boost > 1._rt) { // Lorentz transform fields due to moving frame + if ( ( external_b_fields ) || ( external_e_fields ) ){ + + // Lorentz transform z (from boosted to lab frame) + amrex::Real Ex_ext_boost, Ey_ext_boost, Ez_ext_boost; + amrex::Real Bx_ext_boost, By_ext_boost, Bz_ext_boost; + amrex::Real Ex_ext_lab, Ey_ext_lab, Ez_ext_lab; + amrex::Real Bx_ext_lab, By_ext_lab, Bz_ext_lab; + + // Grab the location +#if defined(WARPX_DIM_3D) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = problo[1] + j * dx[1]; + amrex::Real z = problo[2] + k * dx[2]; +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[1] + j * dx[1]; +#else + amrex::Real x = 0.0_rt; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[0] + i * dx[0]; +#endif + + // Get the lab frame E and B + // Transform (boosted to lab) + amrex::Real t_lab = gamma_boost*(t + beta_boost*z/PhysConst::c); + amrex::Real z_lab = gamma_boost*(z + beta_boost*PhysConst::c*t); + + // Grab the external fields in the lab frame: + if ( external_e_fields ) { + Ex_ext_lab = Exfield_parser(x, y, z_lab, t_lab); + Ey_ext_lab = Eyfield_parser(x, y, z_lab, t_lab); + Ez_ext_lab = Ezfield_parser(x, y, z_lab, t_lab); + }else{ + Ex_ext_lab = 0.0; + Ey_ext_lab = 0.0; + Ez_ext_lab = 0.0; + } + if ( external_b_fields ) { + Bx_ext_lab = Bxfield_parser(x, y, z_lab, t_lab); + By_ext_lab = Byfield_parser(x, y, z_lab, t_lab); + Bz_ext_lab = Bzfield_parser(x, y, z_lab, t_lab); + }else{ + Bx_ext_lab = 0.0; + By_ext_lab = 0.0; + Bz_ext_lab = 0.0; + } + + // Transform E & B (lab to boosted frame) + // (Require both to for the lorentz transform) + // RHS m_parser + Ez_ext_boost = Ez_ext_lab; + Bz_ext_boost = Bz_ext_lab; + Ex_ext_boost = gamma_boost*(Ex_ext_lab - beta_boost*PhysConst::c*By_ext_lab); + Ey_ext_boost = gamma_boost*(Ey_ext_lab + beta_boost*PhysConst::c*Bx_ext_lab); + Bx_ext_boost = gamma_boost*(Bx_ext_lab + beta_boost*Ey_ext_lab/PhysConst::c); + By_ext_boost = gamma_boost*(By_ext_lab - beta_boost*Ex_ext_lab/PhysConst::c); + + // Then add to Nodal quantities in the boosted frame: + Ex_Nodal += Ex_ext_boost; + Ey_Nodal += Ey_ext_boost; + Ez_Nodal += Ez_ext_boost; + Bx_Nodal += Bx_ext_boost; + By_Nodal += By_ext_boost; + Bz_Nodal += Bz_ext_boost; + } + } else { + + // Added external e fields: + if ( external_e_fields ){ +#if defined(WARPX_DIM_3D) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = problo[1] + j * dx[1]; + amrex::Real z = problo[2] + k * dx[2]; +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[1] + j * dx[1]; +#else + amrex::Real x = 0.0_rt; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[0] + i * dx[0]; +#endif + + Ex_Nodal += Exfield_parser(x, y, z, t); + Ey_Nodal += Eyfield_parser(x, y, z, t); + Ez_Nodal += Ezfield_parser(x, y, z, t); + } + + // Added external b fields: + if ( external_b_fields ){ +#if defined(WARPX_DIM_3D) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = problo[1] + j * dx[1]; + amrex::Real z = problo[2] + k * dx[2]; +#elif defined(WARPX_DIM_XZ) || defined(WARPX_DIM_RZ) + amrex::Real x = problo[0] + i * dx[0]; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[1] + j * dx[1]; +#else + amrex::Real x = 0.0_rt; + amrex::Real y = 0.0_rt; + amrex::Real z = problo[0] + i * dx[0]; +#endif + + Bx_Nodal += Bxfield_parser(x, y, z, t); + By_Nodal += Byfield_parser(x, y, z, t); + Bz_Nodal += Bzfield_parser(x, y, z, t); + } + } + + // Isolate U from NU + amrex::Real tmp_Ux = (NUx_arr(i, j, k) / N_arr(i,j,k)); + amrex::Real tmp_Uy = (NUy_arr(i, j, k) / N_arr(i,j,k)); + amrex::Real tmp_Uz = (NUz_arr(i, j, k) / N_arr(i,j,k)); + + // Enforce RZ boundary conditions +#if defined(WARPX_DIM_RZ) + if ( i == 0 ){ + Ex_Nodal = 0.0; + Ey_Nodal = 0.0; + By_Nodal = 0.0; + Bx_Nodal = 0.0; + } +#endif + + // Push the fluid momentum + UpdateMomentumHigueraCary(tmp_Ux, tmp_Uy, tmp_Uz, + Ex_Nodal, Ey_Nodal, Ez_Nodal, + Bx_Nodal, By_Nodal, Bz_Nodal, q, m, dt ); + + // Calculate NU + NUx_arr(i,j,k) = N_arr(i,j,k)*tmp_Ux; + NUy_arr(i,j,k) = N_arr(i,j,k)*tmp_Uy; + NUz_arr(i,j,k) = N_arr(i,j,k)*tmp_Uz; + } + } + ); + } +} + +void WarpXFluidContainer::DepositCharge (int lev, amrex::MultiFab &rho, int icomp) +{ + WARPX_PROFILE("WarpXFluidContainer::DepositCharge"); + + WarpX &warpx = WarpX::GetInstance(); + const amrex::Geometry &geom = warpx.Geom(lev); + const amrex::Periodicity &period = geom.periodicity(); + const amrex::Real q = getCharge(); + auto const &owner_mask_rho = amrex::OwnerMask(rho, period); + + // Assertion, make sure rho is at the same location as N + AMREX_ALWAYS_ASSERT(rho.ixType().nodeCentered()); + + // Loop over and deposit charge density +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + + amrex::Box const &tile_box = mfi.tilebox(N[lev]->ixType().toIntVect()); + amrex::Array4 const &N_arr = N[lev]->array(mfi); + amrex::Array4 rho_arr = rho.array(mfi); + amrex::Array4 owner_mask_rho_arr = owner_mask_rho->array(mfi); + + // Deposit Rho + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + if ( owner_mask_rho_arr(i,j,k) ) rho_arr(i,j,k,icomp) += q*N_arr(i,j,k); + } + ); + } +} + + +void WarpXFluidContainer::DepositCurrent( + int lev, + amrex::MultiFab &jx, amrex::MultiFab &jy, amrex::MultiFab &jz) +{ + WARPX_PROFILE("WarpXFluidContainer::DepositCurrent"); + + // Temporary nodal currents + amrex::MultiFab tmp_jx_fluid(N[lev]->boxArray(), N[lev]->DistributionMap(), 1, 0); + amrex::MultiFab tmp_jy_fluid(N[lev]->boxArray(), N[lev]->DistributionMap(), 1, 0); + amrex::MultiFab tmp_jz_fluid(N[lev]->boxArray(), N[lev]->DistributionMap(), 1, 0); + + const amrex::Real inv_clight_sq = 1.0_prt / PhysConst::c / PhysConst::c; + const amrex::Real q = getCharge(); + + // Prepare interpolation of current components to cell center + amrex::GpuArray j_nodal_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray jx_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray jy_type = amrex::GpuArray{0, 0, 0}; + amrex::GpuArray jz_type = amrex::GpuArray{0, 0, 0}; + for (int i = 0; i < AMREX_SPACEDIM; ++i) + { + j_nodal_type[i] = tmp_jx_fluid.ixType()[i]; + jx_type[i] = jx.ixType()[i]; + jy_type[i] = jy.ixType()[i]; + jz_type[i] = jz.ixType()[i]; + } + + // We now need to create a mask to fix the double counting. + WarpX &warpx = WarpX::GetInstance(); + const amrex::Geometry &geom = warpx.Geom(lev); + const amrex::Periodicity &period = geom.periodicity(); + auto const &owner_mask_x = amrex::OwnerMask(jx, period); + auto const &owner_mask_y = amrex::OwnerMask(jy, period); + auto const &owner_mask_z = amrex::OwnerMask(jz, period); + + // Calculate j at the nodes +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + amrex::Box const &tile_box = mfi.tilebox(N[lev]->ixType().toIntVect()); + + amrex::Array4 const &N_arr = N[lev]->array(mfi); + amrex::Array4 const &NUx_arr = NU[lev][0]->array(mfi); + amrex::Array4 const &NUy_arr = NU[lev][1]->array(mfi); + amrex::Array4 const &NUz_arr = NU[lev][2]->array(mfi); + + amrex::Array4 tmp_jx_fluid_arr = tmp_jx_fluid.array(mfi); + amrex::Array4 tmp_jy_fluid_arr = tmp_jy_fluid.array(mfi); + amrex::Array4 tmp_jz_fluid_arr = tmp_jz_fluid.array(mfi); + + amrex::ParallelFor(tile_box, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + // Calculate J from fluid quantities + amrex::Real gamma = 1.0, Ux = 0.0, Uy = 0.0, Uz = 0.0; + if (N_arr(i, j, k)>0.0){ + Ux = NUx_arr(i, j, k)/N_arr(i, j, k); + Uy = NUy_arr(i, j, k)/N_arr(i, j, k); + Uz = NUz_arr(i, j, k)/N_arr(i, j, k); + gamma = std::sqrt(1.0 + ( Ux*Ux + Uy*Uy + Uz*Uz) * inv_clight_sq ) ; + } + tmp_jx_fluid_arr(i, j, k) = q * (NUx_arr(i, j, k) / gamma); + tmp_jy_fluid_arr(i, j, k) = q * (NUy_arr(i, j, k) / gamma); + tmp_jz_fluid_arr(i, j, k) = q * (NUz_arr(i, j, k) / gamma); + } + ); + } + + // Interpolate j from the nodes to the simulation mesh (typically Yee mesh) +#ifdef AMREX_USE_OMP +#pragma omp parallel if (amrex::Gpu::notInLaunchRegion()) +#endif + for (MFIter mfi(*N[lev], TilingIfNotGPU()); mfi.isValid(); ++mfi) + { + amrex::Box const &tile_box_x = mfi.tilebox(jx.ixType().toIntVect()); + amrex::Box const &tile_box_y = mfi.tilebox(jy.ixType().toIntVect()); + amrex::Box const &tile_box_z = mfi.tilebox(jz.ixType().toIntVect()); + + amrex::Array4 jx_arr = jx.array(mfi); + amrex::Array4 jy_arr = jy.array(mfi); + amrex::Array4 jz_arr = jz.array(mfi); + + amrex::Array4 tmp_jx_fluid_arr = tmp_jx_fluid.array(mfi); + amrex::Array4 tmp_jy_fluid_arr = tmp_jy_fluid.array(mfi); + amrex::Array4 tmp_jz_fluid_arr = tmp_jz_fluid.array(mfi); + + amrex::Array4 owner_mask_x_arr = owner_mask_x->array(mfi); + amrex::Array4 owner_mask_y_arr = owner_mask_y->array(mfi); + amrex::Array4 owner_mask_z_arr = owner_mask_z->array(mfi); + + // When using the `Interp` function, one needs to specify whether coarsening is desired. + // Here, we do not perform any coarsening. + amrex::GpuArray coarsening_ratio = {1, 1, 1}; + + + // Interpolate fluid current and deposit it + // ( mask double counting ) + amrex::ParallelFor( tile_box_x, tile_box_y, tile_box_z, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + amrex::Real jx_tmp = ablastr::coarsen::sample::Interp(tmp_jx_fluid_arr, + j_nodal_type, jx_type, coarsening_ratio, i, j, k, 0); + if ( owner_mask_x_arr(i,j,k) ) jx_arr(i, j, k) += jx_tmp; + }, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + amrex::Real jy_tmp = ablastr::coarsen::sample::Interp(tmp_jy_fluid_arr, + j_nodal_type, jy_type, coarsening_ratio, i, j, k, 0); + if ( owner_mask_y_arr(i,j,k) ) jy_arr(i, j, k) += jy_tmp; + }, + [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept + { + amrex::Real jz_tmp = ablastr::coarsen::sample::Interp(tmp_jz_fluid_arr, + j_nodal_type, jz_type, coarsening_ratio, i, j, k, 0); + if ( owner_mask_z_arr(i,j,k) ) jz_arr(i, j, k) += jz_tmp; + } + ); + } +} diff --git a/Source/Fluids/WarpXFluidContainer_fwd.H b/Source/Fluids/WarpXFluidContainer_fwd.H new file mode 100644 index 00000000000..77a6bc0c991 --- /dev/null +++ b/Source/Fluids/WarpXFluidContainer_fwd.H @@ -0,0 +1,12 @@ +/* Copyright 2023 Grant Johnson, Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ + +#ifndef WARPX_WarpXFluidContainer_fwd_H_ + +class WarpXFluidContainer; + +#endif /* WARPX_WarpXFluidContainer_fwd_H_ */ diff --git a/Source/Initialization/PlasmaInjector.H b/Source/Initialization/PlasmaInjector.H index 89ea4a00a78..ac64980b952 100644 --- a/Source/Initialization/PlasmaInjector.H +++ b/Source/Initialization/PlasmaInjector.H @@ -120,11 +120,7 @@ public: bool radially_weighted = true; - std::string str_density_function; std::string str_flux_function; - std::string str_momentum_function_ux; - std::string str_momentum_function_uy; - std::string str_momentum_function_uz; amrex::Real xmin, xmax; amrex::Real ymin, ymax; @@ -146,7 +142,6 @@ protected: PhysicalSpecies physical_species = PhysicalSpecies::unspecified; - amrex::Real density; amrex::Real flux; int species_id; @@ -185,9 +180,7 @@ protected: void setupNuniformPerCell (const amrex::ParmParse& pp_species_name); void setupExternalFile (const amrex::ParmParse& pp_species_name); - void parseDensity (const amrex::ParmParse& pp_species_name); void parseFlux (const amrex::ParmParse& pp_species_name); - void parseMomentum (const amrex::ParmParse& pp_species_name, const std::string& style); }; #endif diff --git a/Source/Initialization/PlasmaInjector.cpp b/Source/Initialization/PlasmaInjector.cpp index b880d4a46e7..56d29a8a1bb 100644 --- a/Source/Initialization/PlasmaInjector.cpp +++ b/Source/Initialization/PlasmaInjector.cpp @@ -14,8 +14,8 @@ #include "Initialization/InjectorDensity.H" #include "Initialization/InjectorMomentum.H" #include "Initialization/InjectorPosition.H" -#include "Particles/SpeciesPhysicalProperties.H" #include "Utils/Parser/ParserUtils.H" +#include "Utils/SpeciesUtils.H" #include "Utils/TextMsg.H" #include "Utils/WarpXConst.H" #include "WarpX.H" @@ -45,17 +45,6 @@ using namespace amrex::literals; -namespace { - void StringParseAbortMessage(const std::string& var, - const std::string& name) { - std::stringstream stringstream; - std::string string; - stringstream << var << " string '" << name << "' not recognized."; - string = stringstream.str(); - WARPX_ABORT_WITH_MESSAGE(string); - } -} - PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name, const amrex::Geometry& geom): species_id{ispecies}, species_name{name} @@ -123,60 +112,14 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name, utils::parser::queryWithParser(pp_species_name, "density_min", density_min); utils::parser::queryWithParser(pp_species_name, "density_max", density_max); - std::string physical_species_s; - const bool species_is_specified = pp_species_name.query("species_type", physical_species_s); - if (species_is_specified){ - const auto physical_species_from_string = species::from_string( physical_species_s ); - WARPX_ALWAYS_ASSERT_WITH_MESSAGE(physical_species_from_string, - physical_species_s + " does not exist!"); - physical_species = physical_species_from_string.value(); - charge = species::get_charge( physical_species ); - mass = species::get_mass( physical_species ); - } - - // Parse injection style std::string injection_style = "none"; + // Parse injection style pp_species_name.query("injection_style", injection_style); std::transform(injection_style.begin(), - injection_style.end(), - injection_style.begin(), - ::tolower); - - // parse charge and mass - const bool charge_is_specified = - utils::parser::queryWithParser(pp_species_name, "charge", charge); - const bool mass_is_specified = - utils::parser::queryWithParser(pp_species_name, "mass", mass); - - if ( charge_is_specified && species_is_specified ){ - ablastr::warn_manager::WMRecordWarning("Species", - "Both '" + species_name + ".charge' and " + - species_name + ".species_type' are specified.\n" + - species_name + ".charge' will take precedence.\n"); - - } - WARPX_ALWAYS_ASSERT_WITH_MESSAGE( - charge_is_specified || - species_is_specified || - (injection_style == "external_file"), - "Need to specify at least one of species_type or charge for species '" + - species_name + "'." - ); - - if ( mass_is_specified && species_is_specified ){ - ablastr::warn_manager::WMRecordWarning("Species", - "Both '" + species_name + ".mass' and " + - species_name + ".species_type' are specified.\n" + - species_name + ".mass' will take precedence.\n"); - } - - WARPX_ALWAYS_ASSERT_WITH_MESSAGE( - mass_is_specified || - species_is_specified || - (injection_style == "external_file"), - "Need to specify at least one of species_type or mass for species '" + - species_name + "'." - ); + injection_style.end(), + injection_style.begin(), + ::tolower); + SpeciesUtils::extractSpeciesProperties(species_name, injection_style, charge, mass, physical_species); num_particles_per_cell_each_dim.assign(3, 0); @@ -197,9 +140,27 @@ PlasmaInjector::PlasmaInjector (int ispecies, const std::string& name, } else if (injection_style == "external_file") { setupExternalFile(pp_species_name); } else if (injection_style != "none") { - StringParseAbortMessage("Injection style", injection_style); + SpeciesUtils::StringParseAbortMessage("Injection style", injection_style); } + 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(); } @@ -286,7 +247,8 @@ void PlasmaInjector::setupGaussianBeam (const amrex::ParmParse& pp_species_name) WARPX_ALWAYS_ASSERT_WITH_MESSAGE( valid_symmetries.count(symmetrization_order), "Error: Symmetrization only supported to orders 4 or 8 "); gaussian_beam = true; - parseMomentum(pp_species_name, "gaussian_beam"); + SpeciesUtils::parseMomentum(species_name, "gaussian_beam", h_inj_mom, + ux_parser, uy_parser, uz_parser, h_mom_temp, h_mom_vel); #if defined(WARPX_DIM_XZ) WARPX_ALWAYS_ASSERT_WITH_MESSAGE( y_rms > 0._rt, "Error: Gaussian beam y_rms must be strictly greater than 0 in 2D " @@ -325,9 +287,9 @@ void PlasmaInjector::setupNRandomPerCell (const amrex::ParmParse& pp_species_nam #else d_inj_pos = h_inj_pos.get(); #endif - - parseDensity(pp_species_name); - parseMomentum(pp_species_name, "nrandompercell"); + SpeciesUtils::parseDensity(species_name, h_inj_rho, density_parser); + SpeciesUtils::parseMomentum(species_name, "nrandompercell", h_inj_mom, + ux_parser, uy_parser, uz_parser, h_mom_temp, h_mom_vel); } void PlasmaInjector::setupNFluxPerCell (const amrex::ParmParse& pp_species_name) @@ -404,7 +366,8 @@ void PlasmaInjector::setupNFluxPerCell (const amrex::ParmParse& pp_species_name) #endif parseFlux(pp_species_name); - parseMomentum(pp_species_name, "nfluxpercell"); + SpeciesUtils::parseMomentum(species_name, "nfluxpercell", h_inj_mom, + ux_parser, uy_parser, uz_parser, h_mom_temp, h_mom_vel, flux_normal_axis, flux_direction); } void PlasmaInjector::setupNuniformPerCell (const amrex::ParmParse& pp_species_name) @@ -455,8 +418,9 @@ void PlasmaInjector::setupNuniformPerCell (const amrex::ParmParse& pp_species_na 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]; - parseDensity(pp_species_name); - parseMomentum(pp_species_name, "nuniformpercell"); + SpeciesUtils::parseDensity(species_name, h_inj_rho, density_parser); + SpeciesUtils::parseMomentum(species_name, "nuniformpercell", h_inj_mom, + ux_parser, uy_parser, uz_parser, h_mom_temp, h_mom_vel); } void PlasmaInjector::setupExternalFile (const amrex::ParmParse& pp_species_name) @@ -563,46 +527,6 @@ void PlasmaInjector::setupExternalFile (const amrex::ParmParse& pp_species_name) #endif // WARPX_USE_OPENPMD } -// Depending on injection type at runtime, initialize inj_rho -// so that inj_rho->getDensity calls -// InjectorPosition[Constant or Predefined or etc.].getDensity. -void PlasmaInjector::parseDensity (const amrex::ParmParse& pp_species_name) -{ - // parse density information - std::string rho_prof_s; - pp_species_name.get("profile", rho_prof_s); - std::transform(rho_prof_s.begin(), rho_prof_s.end(), - rho_prof_s.begin(), ::tolower); - if (rho_prof_s == "constant") { - utils::parser::getWithParser(pp_species_name, "density", density); - // Construct InjectorDensity with InjectorDensityConstant. - h_inj_rho.reset(new InjectorDensity((InjectorDensityConstant*)nullptr, density)); - } else if (rho_prof_s == "predefined") { - // Construct InjectorDensity with InjectorDensityPredefined. - h_inj_rho.reset(new InjectorDensity((InjectorDensityPredefined*)nullptr,species_name)); - } else if (rho_prof_s == "parse_density_function") { - utils::parser::Store_parserString( - pp_species_name, "density_function(x,y,z)", str_density_function); - // Construct InjectorDensity with InjectorDensityParser. - density_parser = std::make_unique( - utils::parser::makeParser(str_density_function,{"x","y","z"})); - h_inj_rho.reset(new InjectorDensity((InjectorDensityParser*)nullptr, - density_parser->compile<3>())); - } else { - StringParseAbortMessage("Density profile type", rho_prof_s); - } - - 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 - } -} - // Depending on injection type at runtime, initialize inj_flux // so that inj_flux->getFlux calls // InjectorFlux[Constant or Parser or etc.].getFlux. @@ -626,7 +550,7 @@ void PlasmaInjector::parseFlux (const amrex::ParmParse& pp_species_name) h_inj_flux.reset(new InjectorFlux((InjectorFluxParser*)nullptr, flux_parser->compile<4>())); } else { - StringParseAbortMessage("Flux profile type", flux_prof_s); + SpeciesUtils::StringParseAbortMessage("Flux profile type", flux_prof_s); } if (h_inj_flux) { #ifdef AMREX_USE_GPU @@ -640,138 +564,6 @@ void PlasmaInjector::parseFlux (const amrex::ParmParse& pp_species_name) } -// Depending on injection type at runtime, initialize inj_mom -// so that inj_mom->getMomentum calls -// InjectorMomentum[Constant or Gaussian or etc.].getMomentum. -void PlasmaInjector::parseMomentum (const amrex::ParmParse& pp_species_name, const std::string& style) -{ - using namespace amrex::literals; - - // parse momentum information - std::string mom_dist_s; - pp_species_name.get("momentum_distribution_type", mom_dist_s); - std::transform(mom_dist_s.begin(), - mom_dist_s.end(), - mom_dist_s.begin(), - ::tolower); - if (mom_dist_s == "at_rest") { - constexpr amrex::Real ux = 0._rt; - constexpr amrex::Real uy = 0._rt; - constexpr amrex::Real uz = 0._rt; - // Construct InjectorMomentum with InjectorMomentumConstant. - h_inj_mom.reset(new InjectorMomentum((InjectorMomentumConstant*)nullptr, ux, uy, uz)); - } else if (mom_dist_s == "constant") { - amrex::Real ux = 0._rt; - amrex::Real uy = 0._rt; - amrex::Real uz = 0._rt; - utils::parser::queryWithParser(pp_species_name, "ux", ux); - utils::parser::queryWithParser(pp_species_name, "uy", uy); - utils::parser::queryWithParser(pp_species_name, "uz", uz); - // Construct InjectorMomentum with InjectorMomentumConstant. - h_inj_mom.reset(new InjectorMomentum((InjectorMomentumConstant*)nullptr, ux, uy, uz)); - } else if (mom_dist_s == "gaussian") { - amrex::Real ux_m = 0._rt; - amrex::Real uy_m = 0._rt; - amrex::Real uz_m = 0._rt; - amrex::Real ux_th = 0._rt; - amrex::Real uy_th = 0._rt; - amrex::Real uz_th = 0._rt; - utils::parser::queryWithParser(pp_species_name, "ux_m", ux_m); - utils::parser::queryWithParser(pp_species_name, "uy_m", uy_m); - utils::parser::queryWithParser(pp_species_name, "uz_m", uz_m); - utils::parser::queryWithParser(pp_species_name, "ux_th", ux_th); - utils::parser::queryWithParser(pp_species_name, "uy_th", uy_th); - utils::parser::queryWithParser(pp_species_name, "uz_th", uz_th); - // Construct InjectorMomentum with InjectorMomentumGaussian. - 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 == "gaussianflux") { - WARPX_ALWAYS_ASSERT_WITH_MESSAGE(style == "nfluxpercell", - "Error: gaussianflux can only be used with injection_style = NFluxPerCell"); - amrex::Real ux_m = 0._rt; - amrex::Real uy_m = 0._rt; - amrex::Real uz_m = 0._rt; - amrex::Real ux_th = 0._rt; - amrex::Real uy_th = 0._rt; - amrex::Real uz_th = 0._rt; - utils::parser::queryWithParser(pp_species_name, "ux_m", ux_m); - utils::parser::queryWithParser(pp_species_name, "uy_m", uy_m); - utils::parser::queryWithParser(pp_species_name, "uz_m", uz_m); - utils::parser::queryWithParser(pp_species_name, "ux_th", ux_th); - utils::parser::queryWithParser(pp_species_name, "uy_th", uy_th); - utils::parser::queryWithParser(pp_species_name, "uz_th", uz_th); - // Construct InjectorMomentum with InjectorMomentumGaussianFlux. - h_inj_mom.reset(new InjectorMomentum((InjectorMomentumGaussianFlux*)nullptr, - ux_m, uy_m, uz_m, ux_th, uy_th, uz_th, - flux_normal_axis, flux_direction)); - } else if (mom_dist_s == "uniform") { - amrex::Real ux_min = 0._rt; - amrex::Real uy_min = 0._rt; - amrex::Real uz_min = 0._rt; - amrex::Real ux_max = 0._rt; - amrex::Real uy_max = 0._rt; - amrex::Real uz_max = 0._rt; - utils::parser::queryWithParser(pp_species_name, "ux_min", ux_min); - utils::parser::queryWithParser(pp_species_name, "uy_min", uy_min); - utils::parser::queryWithParser(pp_species_name, "uz_min", uz_min); - utils::parser::queryWithParser(pp_species_name, "ux_max", ux_max); - utils::parser::queryWithParser(pp_species_name, "uy_max", uy_max); - utils::parser::queryWithParser(pp_species_name, "uz_max", uz_max); - // Construct InjectorMomentum with InjectorMomentumUniform. - h_inj_mom.reset(new InjectorMomentum((InjectorMomentumUniform*)nullptr, - ux_min, uy_min, uz_min, ux_max, uy_max, uz_max)); - } else if (mom_dist_s == "maxwell_boltzmann"){ - h_mom_temp = std::make_unique(pp_species_name); - const GetTemperature getTemp(*h_mom_temp); - h_mom_vel = std::make_unique(pp_species_name); - const GetVelocity getVel(*h_mom_vel); - // Construct InjectorMomentum with InjectorMomentumBoltzmann. - h_inj_mom.reset(new InjectorMomentum((InjectorMomentumBoltzmann*)nullptr, getTemp, getVel)); - } else if (mom_dist_s == "maxwell_juttner"){ - h_mom_temp = std::make_unique(pp_species_name); - const GetTemperature getTemp(*h_mom_temp); - h_mom_vel = std::make_unique(pp_species_name); - const GetVelocity getVel(*h_mom_vel); - // Construct InjectorMomentum with InjectorMomentumJuttner. - h_inj_mom.reset(new InjectorMomentum((InjectorMomentumJuttner*)nullptr, getTemp, getVel)); - } else if (mom_dist_s == "radial_expansion") { - amrex::Real u_over_r = 0._rt; - utils::parser::queryWithParser(pp_species_name, "u_over_r", u_over_r); - // Construct InjectorMomentum with InjectorMomentumRadialExpansion. - h_inj_mom.reset(new InjectorMomentum - ((InjectorMomentumRadialExpansion*)nullptr, u_over_r)); - } else if (mom_dist_s == "parse_momentum_function") { - utils::parser::Store_parserString(pp_species_name, "momentum_function_ux(x,y,z)", - str_momentum_function_ux); - utils::parser::Store_parserString(pp_species_name, "momentum_function_uy(x,y,z)", - str_momentum_function_uy); - utils::parser::Store_parserString(pp_species_name, "momentum_function_uz(x,y,z)", - str_momentum_function_uz); - // Construct InjectorMomentum with InjectorMomentumParser. - ux_parser = std::make_unique( - utils::parser::makeParser(str_momentum_function_ux, {"x","y","z"})); - uy_parser = std::make_unique( - utils::parser::makeParser(str_momentum_function_uy, {"x","y","z"})); - uz_parser = std::make_unique( - utils::parser::makeParser(str_momentum_function_uz, {"x","y","z"})); - h_inj_mom.reset(new InjectorMomentum((InjectorMomentumParser*)nullptr, - ux_parser->compile<3>(), - uy_parser->compile<3>(), - uz_parser->compile<3>())); - } else { - StringParseAbortMessage("Momentum distribution type", mom_dist_s); - } - 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::XDim3 PlasmaInjector::getMomentum (amrex::Real x, amrex::Real y, amrex::Real z) const noexcept diff --git a/Source/Make.WarpX b/Source/Make.WarpX index 3f1702a4214..d1c77482f75 100644 --- a/Source/Make.WarpX +++ b/Source/Make.WarpX @@ -77,6 +77,7 @@ include $(WARPX_HOME)/Source/Diagnostics/Make.package include $(WARPX_HOME)/Source/EmbeddedBoundary/Make.package include $(WARPX_HOME)/Source/FieldSolver/Make.package include $(WARPX_HOME)/Source/Filter/Make.package +include $(WARPX_HOME)/Source/Fluids/Make.package include $(WARPX_HOME)/Source/Initialization/Make.package include $(WARPX_HOME)/Source/Laser/Make.package include $(WARPX_HOME)/Source/Parallelization/Make.package diff --git a/Source/Particles/MultiParticleContainer.H b/Source/Particles/MultiParticleContainer.H index fecd39137d7..af826068806 100644 --- a/Source/Particles/MultiParticleContainer.H +++ b/Source/Particles/MultiParticleContainer.H @@ -79,11 +79,6 @@ public: WarpXParticleContainer& GetParticleContainerFromName (const std::string& name) const; -#ifdef WARPX_USE_OPENPMD - std::unique_ptr& GetUniqueContainer(int index) { - return allcontainers[index]; - } -#endif std::array meanParticleVelocity(int index) { return allcontainers[index]->meanParticleVelocity(); } diff --git a/Source/Particles/Pusher/UpdateMomentumHigueraCary.H b/Source/Particles/Pusher/UpdateMomentumHigueraCary.H index 359fc34d508..dc998caee34 100644 --- a/Source/Particles/Pusher/UpdateMomentumHigueraCary.H +++ b/Source/Particles/Pusher/UpdateMomentumHigueraCary.H @@ -17,49 +17,49 @@ * \brief Push the particle's positions over one timestep, * given the value of its momenta `ux`, `uy`, `uz` */ - +template AMREX_GPU_HOST_DEVICE AMREX_INLINE void UpdateMomentumHigueraCary( - amrex::ParticleReal& ux, amrex::ParticleReal& uy, amrex::ParticleReal& uz, - const amrex::ParticleReal Ex, const amrex::ParticleReal Ey, const amrex::ParticleReal Ez, - const amrex::ParticleReal Bx, const amrex::ParticleReal By, const amrex::ParticleReal Bz, - const amrex::ParticleReal q, const amrex::ParticleReal m, const amrex::Real dt ) + T& ux, T& uy, T& uz, + const T Ex, const T Ey, const T Ez, + const T Bx, const T By, const T Bz, + const T q, const T m, const amrex::Real dt ) { using namespace amrex::literals; // Constants - const amrex::ParticleReal qmt = 0.5_prt*q*dt/m; - constexpr amrex::ParticleReal invclight = 1._prt/PhysConst::c; - constexpr amrex::ParticleReal invclightsq = 1._prt/(PhysConst::c*PhysConst::c); + const T qmt = 0.5_prt*q*dt/m; + constexpr T invclight = 1._prt/PhysConst::c; + constexpr T invclightsq = 1._prt/(PhysConst::c*PhysConst::c); // Compute u_minus - const amrex::ParticleReal umx = ux + qmt*Ex; - const amrex::ParticleReal umy = uy + qmt*Ey; - const amrex::ParticleReal umz = uz + qmt*Ez; + const T umx = ux + qmt*Ex; + const T umy = uy + qmt*Ey; + const T umz = uz + qmt*Ez; // Compute gamma squared of u_minus - amrex::ParticleReal gamma = 1._prt + (umx*umx + umy*umy + umz*umz)*invclightsq; + T gamma = 1._prt + (umx*umx + umy*umy + umz*umz)*invclightsq; // Compute beta and betam squared - const amrex::ParticleReal betax = qmt*Bx; - const amrex::ParticleReal betay = qmt*By; - const amrex::ParticleReal betaz = qmt*Bz; - const amrex::ParticleReal betam = betax*betax + betay*betay + betaz*betaz; + const T betax = qmt*Bx; + const T betay = qmt*By; + const T betaz = qmt*Bz; + const T betam = betax*betax + betay*betay + betaz*betaz; // Compute sigma - const amrex::ParticleReal sigma = gamma - betam; + const T sigma = gamma - betam; // Get u* - const amrex::ParticleReal ust = (umx*betax + umy*betay + umz*betaz)*invclight; + const T ust = (umx*betax + umy*betay + umz*betaz)*invclight; // Get new gamma inversed gamma = 1._prt/std::sqrt(0.5_prt*(sigma + std::sqrt(sigma*sigma + 4._prt*(betam + ust*ust)) )); // Compute t - const amrex::ParticleReal tx = gamma*betax; - const amrex::ParticleReal ty = gamma*betay; - const amrex::ParticleReal tz = gamma*betaz; + const T tx = gamma*betax; + const T ty = gamma*betay; + const T tz = gamma*betaz; // Compute s - const amrex::ParticleReal s = 1._prt/(1._prt+(tx*tx + ty*ty + tz*tz)); + const T s = 1._prt/(1._prt+(tx*tx + ty*ty + tz*tz)); // Compute um dot t - const amrex::ParticleReal umt = umx*tx + umy*ty + umz*tz; + const T umt = umx*tx + umy*ty + umz*tz; // Compute u_plus - const amrex::ParticleReal upx = s*( umx + umt*tx + umy*tz - umz*ty ); - const amrex::ParticleReal upy = s*( umy + umt*ty + umz*tx - umx*tz ); - const amrex::ParticleReal upz = s*( umz + umt*tz + umx*ty - umy*tx ); + const T upx = s*( umx + umt*tx + umy*tz - umz*ty ); + const T upy = s*( umy + umt*ty + umz*tx - umx*tz ); + const T upz = s*( umz + umt*tz + umx*ty - umy*tx ); // Get new u ux = upx + qmt*Ex + upy*tz - upz*ty; uy = upy + qmt*Ey + upz*tx - upx*tz; diff --git a/Source/Python/WarpX.cpp b/Source/Python/WarpX.cpp index 3429f33f5b9..1a5b319577c 100644 --- a/Source/Python/WarpX.cpp +++ b/Source/Python/WarpX.cpp @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/Source/Utils/CMakeLists.txt b/Source/Utils/CMakeLists.txt index 9ded83a0c29..77626c0681e 100644 --- a/Source/Utils/CMakeLists.txt +++ b/Source/Utils/CMakeLists.txt @@ -4,6 +4,7 @@ foreach(D IN LISTS WarpX_DIMS) PRIVATE Interpolate.cpp ParticleUtils.cpp + SpeciesUtils.cpp RelativeCellPosition.cpp WarpXAlgorithmSelection.cpp WarpXMovingWindow.cpp diff --git a/Source/Utils/Make.package b/Source/Utils/Make.package index eb318f8d23c..4b5888ef22b 100644 --- a/Source/Utils/Make.package +++ b/Source/Utils/Make.package @@ -8,6 +8,7 @@ CEXE_sources += Interpolate.cpp CEXE_sources += IntervalsParser.cpp CEXE_sources += RelativeCellPosition.cpp CEXE_sources += ParticleUtils.cpp +CEXE_sources += SpeciesUtils.cpp include $(WARPX_HOME)/Source/Utils/Algorithms/Make.package include $(WARPX_HOME)/Source/Utils/Logo/Make.package diff --git a/Source/Utils/SpeciesUtils.H b/Source/Utils/SpeciesUtils.H new file mode 100644 index 00000000000..b1a2618a002 --- /dev/null +++ b/Source/Utils/SpeciesUtils.H @@ -0,0 +1,39 @@ +/* Copyright 2023 RemiLehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#ifndef WARPX_SPECIES_UTILS_H_ +#define WARPX_SPECIES_UTILS_H_ + +#include +#include "Initialization/InjectorDensity.H" +#include "Initialization/InjectorMomentum.H" +#include "Particles/SpeciesPhysicalProperties.H" + +namespace SpeciesUtils { + + void StringParseAbortMessage(const std::string& var, + const std::string& name); + + void extractSpeciesProperties ( std::string const& species_name, + std::string const& injection_style, amrex::Real& charge, amrex::Real& mass, + PhysicalSpecies& physical_species); + + void parseDensity (std::string const& species_name, + std::unique_ptr& h_inj_rho, + std::unique_ptr& density_parser); + + void parseMomentum (std::string const& species_name, const std::string& style, + std::unique_ptr& h_inj_mom, + std::unique_ptr& ux_parser, + std::unique_ptr& uy_parser, + std::unique_ptr& uz_parser, + std::unique_ptr& h_mom_temp, + std::unique_ptr& h_mom_vel, + int flux_normal_axis=0, int flux_direction=0); + +} + +#endif // WARPX_SPECIES_UTILS_H_ diff --git a/Source/Utils/SpeciesUtils.cpp b/Source/Utils/SpeciesUtils.cpp new file mode 100644 index 00000000000..2ac5879f9d1 --- /dev/null +++ b/Source/Utils/SpeciesUtils.cpp @@ -0,0 +1,240 @@ +/* Copyright 2023 Remi Lehe + * + * This file is part of WarpX. + * + * License: BSD-3-Clause-LBNL + */ +#include "SpeciesUtils.H" +#include +#include "Utils/TextMsg.H" +#include "Utils/Parser/ParserUtils.H" + +namespace SpeciesUtils { + + void StringParseAbortMessage(const std::string& var, + const std::string& name) { + std::stringstream stringstream; + std::string string; + stringstream << var << " string '" << name << "' not recognized."; + string = stringstream.str(); + WARPX_ABORT_WITH_MESSAGE(string); + } + + void extractSpeciesProperties (std::string const& species_name, + std::string const& injection_style, amrex::Real& charge, amrex::Real& mass, + PhysicalSpecies& physical_species ) + { + const amrex::ParmParse pp_species_name(species_name); + std::string physical_species_s; + const bool species_is_specified = pp_species_name.query("species_type", physical_species_s); + if (species_is_specified){ + const auto physical_species_from_string = species::from_string( physical_species_s ); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(physical_species_from_string, + physical_species_s + " does not exist!"); + physical_species = physical_species_from_string.value(); + charge = species::get_charge( physical_species ); + mass = species::get_mass( physical_species ); + } + + // parse charge and mass + const bool charge_is_specified = + utils::parser::queryWithParser(pp_species_name, "charge", charge); + const bool mass_is_specified = + utils::parser::queryWithParser(pp_species_name, "mass", mass); + + if ( charge_is_specified && species_is_specified ){ + ablastr::warn_manager::WMRecordWarning("Species", + "Both '" + species_name + ".charge' and " + + species_name + ".species_type' are specified.\n" + + species_name + ".charge' will take precedence.\n"); + + } + + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + charge_is_specified || + species_is_specified || + (injection_style == "external_file"), + "Need to specify at least one of species_type or charge for species '" + + species_name + "'." + ); + + if ( mass_is_specified && species_is_specified ){ + ablastr::warn_manager::WMRecordWarning("Species", + "Both '" + species_name + ".mass' and " + + species_name + ".species_type' are specified.\n" + + species_name + ".mass' will take precedence.\n"); + } + } + + // Depending on injection type at runtime, initialize inj_rho + // so that inj_rho->getDensity calls + // InjectorPosition[Constant or Predefined or etc.].getDensity. + void parseDensity (std::string const& species_name, + std::unique_ptr& h_inj_rho, + std::unique_ptr& density_parser) + { + const amrex::ParmParse pp_species_name(species_name); + + // parse density information + std::string rho_prof_s; + pp_species_name.get("profile", rho_prof_s); + std::transform(rho_prof_s.begin(), rho_prof_s.end(), + rho_prof_s.begin(), ::tolower); + if (rho_prof_s == "constant") { + amrex::Real density; + utils::parser::getWithParser(pp_species_name, "density", density); + // Construct InjectorDensity with InjectorDensityConstant. + h_inj_rho.reset(new InjectorDensity((InjectorDensityConstant*)nullptr, density)); + } else if (rho_prof_s == "predefined") { + // Construct InjectorDensity with InjectorDensityPredefined. + h_inj_rho.reset(new InjectorDensity((InjectorDensityPredefined*)nullptr,species_name)); + } else if (rho_prof_s == "parse_density_function") { + std::string str_density_function; + utils::parser::Store_parserString( + pp_species_name, "density_function(x,y,z)", str_density_function); + // Construct InjectorDensity with InjectorDensityParser. + density_parser = std::make_unique( + utils::parser::makeParser(str_density_function,{"x","y","z"})); + h_inj_rho.reset(new InjectorDensity((InjectorDensityParser*)nullptr, + density_parser->compile<3>())); + } else { + StringParseAbortMessage("Density profile type", rho_prof_s); + } + } + + // Depending on injection type at runtime, initialize inj_mom + // so that inj_mom->getMomentum calls + // InjectorMomentum[Constant or Gaussian or etc.].getMomentum. + void parseMomentum (std::string const& species_name, const std::string& style, + std::unique_ptr& h_inj_mom, + std::unique_ptr& ux_parser, + std::unique_ptr& uy_parser, + std::unique_ptr& uz_parser, + std::unique_ptr& h_mom_temp, + std::unique_ptr& h_mom_vel, + int flux_normal_axis, int flux_direction) + { + using namespace amrex::literals; + + const amrex::ParmParse pp_species_name(species_name); + + // parse momentum information + std::string mom_dist_s; + pp_species_name.get("momentum_distribution_type", mom_dist_s); + std::transform(mom_dist_s.begin(), + mom_dist_s.end(), + mom_dist_s.begin(), + ::tolower); + if (mom_dist_s == "at_rest") { + constexpr amrex::Real ux = 0._rt; + constexpr amrex::Real uy = 0._rt; + constexpr amrex::Real uz = 0._rt; + // Construct InjectorMomentum with InjectorMomentumConstant. + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumConstant*)nullptr, ux, uy, uz)); + } else if (mom_dist_s == "constant") { + amrex::Real ux = 0._rt; + amrex::Real uy = 0._rt; + amrex::Real uz = 0._rt; + utils::parser::queryWithParser(pp_species_name, "ux", ux); + utils::parser::queryWithParser(pp_species_name, "uy", uy); + utils::parser::queryWithParser(pp_species_name, "uz", uz); + // Construct InjectorMomentum with InjectorMomentumConstant. + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumConstant*)nullptr, ux, uy, uz)); + } else if (mom_dist_s == "gaussian") { + amrex::Real ux_m = 0._rt; + amrex::Real uy_m = 0._rt; + amrex::Real uz_m = 0._rt; + amrex::Real ux_th = 0._rt; + amrex::Real uy_th = 0._rt; + amrex::Real uz_th = 0._rt; + utils::parser::queryWithParser(pp_species_name, "ux_m", ux_m); + utils::parser::queryWithParser(pp_species_name, "uy_m", uy_m); + utils::parser::queryWithParser(pp_species_name, "uz_m", uz_m); + utils::parser::queryWithParser(pp_species_name, "ux_th", ux_th); + utils::parser::queryWithParser(pp_species_name, "uy_th", uy_th); + utils::parser::queryWithParser(pp_species_name, "uz_th", uz_th); + // Construct InjectorMomentum with InjectorMomentumGaussian. + 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 == "gaussianflux") { + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(style == "nfluxpercell", + "Error: gaussianflux can only be used with injection_style = NFluxPerCell"); + amrex::Real ux_m = 0._rt; + amrex::Real uy_m = 0._rt; + amrex::Real uz_m = 0._rt; + amrex::Real ux_th = 0._rt; + amrex::Real uy_th = 0._rt; + amrex::Real uz_th = 0._rt; + utils::parser::queryWithParser(pp_species_name, "ux_m", ux_m); + utils::parser::queryWithParser(pp_species_name, "uy_m", uy_m); + utils::parser::queryWithParser(pp_species_name, "uz_m", uz_m); + utils::parser::queryWithParser(pp_species_name, "ux_th", ux_th); + utils::parser::queryWithParser(pp_species_name, "uy_th", uy_th); + utils::parser::queryWithParser(pp_species_name, "uz_th", uz_th); + // Construct InjectorMomentum with InjectorMomentumGaussianFlux. + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumGaussianFlux*)nullptr, + ux_m, uy_m, uz_m, ux_th, uy_th, uz_th, + flux_normal_axis, flux_direction)); + } else if (mom_dist_s == "uniform") { + amrex::Real ux_min = 0._rt; + amrex::Real uy_min = 0._rt; + amrex::Real uz_min = 0._rt; + amrex::Real ux_max = 0._rt; + amrex::Real uy_max = 0._rt; + amrex::Real uz_max = 0._rt; + utils::parser::queryWithParser(pp_species_name, "ux_min", ux_min); + utils::parser::queryWithParser(pp_species_name, "uy_min", uy_min); + utils::parser::queryWithParser(pp_species_name, "uz_min", uz_min); + utils::parser::queryWithParser(pp_species_name, "ux_max", ux_max); + utils::parser::queryWithParser(pp_species_name, "uy_max", uy_max); + utils::parser::queryWithParser(pp_species_name, "uz_max", uz_max); + // Construct InjectorMomentum with InjectorMomentumUniform. + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumUniform*)nullptr, + ux_min, uy_min, uz_min, ux_max, uy_max, uz_max)); + } else if (mom_dist_s == "maxwell_boltzmann"){ + h_mom_temp = std::make_unique(pp_species_name); + const GetTemperature getTemp(*h_mom_temp); + h_mom_vel = std::make_unique(pp_species_name); + const GetVelocity getVel(*h_mom_vel); + // Construct InjectorMomentum with InjectorMomentumBoltzmann. + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumBoltzmann*)nullptr, getTemp, getVel)); + } else if (mom_dist_s == "maxwell_juttner"){ + h_mom_temp = std::make_unique(pp_species_name); + const GetTemperature getTemp(*h_mom_temp); + h_mom_vel = std::make_unique(pp_species_name); + const GetVelocity getVel(*h_mom_vel); + // Construct InjectorMomentum with InjectorMomentumJuttner. + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumJuttner*)nullptr, getTemp, getVel)); + } else if (mom_dist_s == "radial_expansion") { + amrex::Real u_over_r = 0._rt; + utils::parser::queryWithParser(pp_species_name, "u_over_r", u_over_r); + // Construct InjectorMomentum with InjectorMomentumRadialExpansion. + h_inj_mom.reset(new InjectorMomentum + ((InjectorMomentumRadialExpansion*)nullptr, u_over_r)); + } else if (mom_dist_s == "parse_momentum_function") { + std::string str_momentum_function_ux; + std::string str_momentum_function_uy; + std::string str_momentum_function_uz; + utils::parser::Store_parserString(pp_species_name, "momentum_function_ux(x,y,z)", + str_momentum_function_ux); + utils::parser::Store_parserString(pp_species_name, "momentum_function_uy(x,y,z)", + str_momentum_function_uy); + utils::parser::Store_parserString(pp_species_name, "momentum_function_uz(x,y,z)", + str_momentum_function_uz); + // Construct InjectorMomentum with InjectorMomentumParser. + ux_parser = std::make_unique( + utils::parser::makeParser(str_momentum_function_ux, {"x","y","z"})); + uy_parser = std::make_unique( + utils::parser::makeParser(str_momentum_function_uy, {"x","y","z"})); + uz_parser = std::make_unique( + utils::parser::makeParser(str_momentum_function_uz, {"x","y","z"})); + h_inj_mom.reset(new InjectorMomentum((InjectorMomentumParser*)nullptr, + ux_parser->compile<3>(), + uy_parser->compile<3>(), + uz_parser->compile<3>())); + } else { + StringParseAbortMessage("Momentum distribution type", mom_dist_s); + } + } + +} diff --git a/Source/Utils/WarpXMovingWindow.cpp b/Source/Utils/WarpXMovingWindow.cpp index f968af81c13..62fabef2542 100644 --- a/Source/Utils/WarpXMovingWindow.cpp +++ b/Source/Utils/WarpXMovingWindow.cpp @@ -13,6 +13,8 @@ # include "BoundaryConditions/PML_RZ.H" #endif #include "Particles/MultiParticleContainer.H" +#include "Fluids/MultiFluidContainer.H" +#include "Fluids/WarpXFluidContainer.H" #include "Utils/TextMsg.H" #include "Utils/WarpXConst.H" #include "Utils/WarpXProfilerWrapper.H" @@ -357,6 +359,18 @@ WarpX::MoveWindow (const int step, bool move_j) } } } + + // Shift values of N, NU for each fluid species + if (do_fluid_species) { + const int n_fluid_species = myfl->nSpecies(); + for (int i=0; iGetFluidContainer(i); + shiftMF( *fl.N[lev], geom[lev], num_shift, dir, lev, do_update_cost ); + shiftMF( *fl.NU[lev][0], geom[lev], num_shift, dir, lev, do_update_cost ); + shiftMF( *fl.NU[lev][1], geom[lev], num_shift, dir, lev, do_update_cost ); + shiftMF( *fl.NU[lev][2], geom[lev], num_shift, dir, lev, do_update_cost ); + } + } } // Loop over species (particles and lasers) @@ -410,6 +424,30 @@ WarpX::MoveWindow (const int step, bool move_j) } } + // Continuously inject fluid species in new cells (by default only on level 0) + const int lev = 0; + // Find box in which to initialize new fluid cells + amrex::Box injection_box = geom[lev].Domain(); + injection_box.surroundingNodes(); // get nodal box + // Restrict box in the direction of the moving window, to only include the new cells + if (moving_window_v > 0._rt) + { + injection_box.setSmall( dir, injection_box.bigEnd(dir) - num_shift_base + 1 ); + } + else if (moving_window_v < 0._rt) + { + injection_box.setBig( dir, injection_box.smallEnd(dir) + num_shift_base - 1 ); + } + // Loop over fluid species, and fill the values of the new cells + if (do_fluid_species) { + const int n_fluid_species = myfl->nSpecies(); + const amrex::Real cur_time = t_new[0]; + for (int i=0; iGetFluidContainer(i); + fl.InitData( lev, injection_box, cur_time ); + } + } + return num_shift_base; } diff --git a/Source/WarpX.H b/Source/WarpX.H index 7466bd92496..034c321ba14 100644 --- a/Source/WarpX.H +++ b/Source/WarpX.H @@ -23,6 +23,9 @@ #include "Particles/ParticleBoundaryBuffer_fwd.H" #include "Particles/MultiParticleContainer_fwd.H" #include "Particles/WarpXParticleContainer_fwd.H" +#include "Fluids/MultiFluidContainer_fwd.H" +#include "Fluids/WarpXFluidContainer_fwd.H" + #ifdef WARPX_USE_PSATD # ifdef WARPX_DIM_RZ # include "FieldSolver/SpectralSolver/SpectralSolverRZ_fwd.H" @@ -117,6 +120,7 @@ public: void Evolve (int numsteps = -1); MultiParticleContainer& GetPartContainer () { return *mypc; } + MultiFluidContainer& GetFluidContainer () { return *myfl; } MacroscopicProperties& GetMacroscopicProperties () { return *m_macroscopic_properties; } HybridPICModel& GetHybridPICModel () { return *m_hybrid_pic_model; } MultiDiagnostics& GetMultiDiags () {return *multi_diags;} @@ -503,6 +507,7 @@ public: const amrex::MultiFab& getBfield_avg_cp (int lev, int direction) {return *Bfield_avg_cp[lev][direction];} bool DoPML () const {return do_pml;} + bool DoFluidSpecies () const {return do_fluid_species;} #if (defined WARPX_DIM_RZ) && (defined WARPX_USE_PSATD) const PML_RZ* getPMLRZ() {return pml_rz[0].get();} @@ -1327,6 +1332,10 @@ private: std::unique_ptr mypc; std::unique_ptr multi_diags; + // Fluid container + bool do_fluid_species = 0; + std::unique_ptr myfl; + // // Fields: First array for level, second for direction // diff --git a/Source/WarpX.cpp b/Source/WarpX.cpp index 6857ad85bcf..67b00ab8900 100644 --- a/Source/WarpX.cpp +++ b/Source/WarpX.cpp @@ -30,6 +30,8 @@ #include "FieldSolver/WarpX_FDTD.H" #include "Filter/NCIGodfreyFilter.H" #include "Particles/MultiParticleContainer.H" +#include "Fluids/MultiFluidContainer.H" +#include "Fluids/WarpXFluidContainer.H" #include "Particles/ParticleBoundaryBuffer.H" #include "AcceleratorLattice/AcceleratorLattice.H" #include "Utils/TextMsg.H" @@ -316,6 +318,11 @@ WarpX::WarpX () // Particle Boundary Buffer (i.e., scraped particles on boundary) m_particle_boundary_buffer = std::make_unique(); + // Fluid Container + if (do_fluid_species) { + myfl = std::make_unique(nlevs_max); + } + Efield_aux.resize(nlevs_max); Bfield_aux.resize(nlevs_max); @@ -1019,6 +1026,25 @@ WarpX::ReadParameters () "The number of azimuthal modes (n_rz_azimuthal_modes) must be at least 1"); #endif + // Check whether fluid species will be used + { + const ParmParse pp_fluids("fluids"); + std::vector fluid_species_names = {}; + pp_fluids.queryarr("species_names", fluid_species_names); + if ( fluid_species_names.empty() == false ) do_fluid_species = 1; + if (do_fluid_species) { + WARPX_ALWAYS_ASSERT_WITH_MESSAGE(max_level <= 1, + "Fluid species cannot currently be used with mesh refinement."); + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( + electrostatic_solver_id != ElectrostaticSolverAlgo::Relativistic, + "Fluid species cannot currently be used with the relativistic electrostatic solver."); +#ifdef WARPX_DIM_RZ + WARPX_ALWAYS_ASSERT_WITH_MESSAGE( n_rz_azimuthal_modes <= 1, + "Fluid species cannot be used with more than 1 azimuthal mode."); +#endif + } + } + // Set default parameters with hybrid grid (parsed later below) if (grid_type == GridType::Hybrid) { @@ -2222,6 +2248,14 @@ WarpX::AllocLevelMFs (int lev, const BoxArray& ba, const DistributionMapping& dm ); } + // Allocate extra multifabs needed for fluids + if (do_fluid_species) { + myfl->AllocateLevelMFs(lev, ba, dm); + auto & warpx = GetInstance(); + const amrex::Real cur_time = warpx.gett_new(lev); + myfl->InitData(lev, geom[lev].Domain(),cur_time); + } + if (fft_do_time_averaging) { AllocInitMultiFab(Bfield_avg_fp[lev][0], amrex::convert(ba, Bx_nodal_flag), dm, ncomps, ngEB, lev, "Bfield_avg_fp[x]"); From bd90d1ee2396570820fc41cd3e77b32638f13c97 Mon Sep 17 00:00:00 2001 From: Avigdor Veksler <124003120+aveksler1@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:38:58 -0700 Subject: [PATCH 35/39] RZ m=0 mode support for particle field diagnostics (#4291) * added particle field functionality for RZ modes for openPMD format * removed print statements and fixed field naming to work with openpmd * remove debug statement * documentation and replaced assert with warning * forgot semicolon --- .../ParticleReductionFunctor.cpp | 2 +- Source/Diagnostics/Diagnostics.cpp | 13 +++++++--- Source/Diagnostics/FullDiagnostics.cpp | 25 +++++++++++++++++-- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/Source/Diagnostics/ComputeDiagFunctors/ParticleReductionFunctor.cpp b/Source/Diagnostics/ComputeDiagFunctors/ParticleReductionFunctor.cpp index d3932472c3b..203872b1372 100644 --- a/Source/Diagnostics/ComputeDiagFunctors/ParticleReductionFunctor.cpp +++ b/Source/Diagnostics/ComputeDiagFunctors/ParticleReductionFunctor.cpp @@ -78,7 +78,7 @@ ParticleReductionFunctor::operator() (amrex::MultiFab& mf_dst, const int dcomp, const amrex::ParticleReal x = p.pos(0); const amrex::Real lx = (x - plo[0]) * dxi[0]; ii = static_cast(amrex::Math::floor(lx)); -#if defined(WARPX_DIM_XZ) || defined(WARPX_DIM_3D) +#if defined(WARPX_DIM_XZ) || defined(WARPX_DIM_3D) || defined(WARPX_DIM_RZ) const amrex::ParticleReal y = p.pos(1); const amrex::Real ly = (y - plo[1]) * dxi[1]; jj = static_cast(amrex::Math::floor(ly)); diff --git a/Source/Diagnostics/Diagnostics.cpp b/Source/Diagnostics/Diagnostics.cpp index 7eb574aa0ef..6e74fc2cfde 100644 --- a/Source/Diagnostics/Diagnostics.cpp +++ b/Source/Diagnostics/Diagnostics.cpp @@ -115,11 +115,15 @@ Diagnostics::BaseReadParameters () if (!pfield_varnames_specified){ m_pfield_varnames = {}; } + #ifdef WARPX_DIM_RZ - WARPX_ALWAYS_ASSERT_WITH_MESSAGE( - m_pfield_varnames.empty(), - "Input error: cannot use particle_fields_to_plot with RZ" - ); + if (pfield_varnames_specified){ + ablastr::warn_manager::WMRecordWarning( + "Diagnostics", + "Particle field diagnostics will output the 0th mode only.", + ablastr::warn_manager::WarnPriority::low + ); + } #endif // Get parser strings for particle fields and generate map of parsers @@ -127,6 +131,7 @@ Diagnostics::BaseReadParameters () std::string filter_parser_str; const amrex::ParmParse pp_diag_pfield(m_diag_name + ".particle_fields"); for (const auto& var : m_pfield_varnames) { + bool do_average = true; pp_diag_pfield.query((var + ".do_average").c_str(), do_average); m_pfield_do_average.push_back(do_average); diff --git a/Source/Diagnostics/FullDiagnostics.cpp b/Source/Diagnostics/FullDiagnostics.cpp index 7abe923b316..d5109277de2 100644 --- a/Source/Diagnostics/FullDiagnostics.cpp +++ b/Source/Diagnostics/FullDiagnostics.cpp @@ -187,6 +187,7 @@ FullDiagnostics::InitializeFieldFunctorsRZopenPMD (int lev) const int ncomp = ncomp_multimodefab; // This function is called multiple times, for different values of `lev` // but the `varnames` need only be updated once. + const bool update_varnames = (lev==0); if (update_varnames) { m_varnames.clear(); @@ -194,9 +195,14 @@ FullDiagnostics::InitializeFieldFunctorsRZopenPMD (int lev) m_varnames.reserve(n_rz); } - // Reser field functors + // Add functors for average particle data for each species + const auto nvar = static_cast(m_varnames_fields.size()); + const auto nspec = static_cast(m_pfield_species.size()); + const auto ntot = static_cast(nvar + m_pfield_varnames.size() * nspec); + + // Reset field functors m_all_field_functors[lev].clear(); - m_all_field_functors[lev].resize(m_varnames_fields.size()); + m_all_field_functors[lev].resize(ntot); // Boolean flag for whether the current density should be deposited before // diagnostic output @@ -322,6 +328,20 @@ FullDiagnostics::InitializeFieldFunctorsRZopenPMD (int lev) "Error: " + m_varnames_fields[comp] + " is not a known field output type in RZ geometry"); } } + + // Generate field functors for every particle field diagnostic for every species in m_pfield_species. + // The names of the diagnostics are output in the `[varname]_[species]` format. + for (int pcomp=0; pcomp(nullptr, + lev, m_crse_ratio, m_pfield_strings[pcomp], m_pfield_species_index[ispec], m_pfield_do_average[pcomp], + m_pfield_dofilter[pcomp], m_pfield_filter_strings[pcomp]); + if (update_varnames) { + AddRZModesToOutputNames(std::string(m_pfield_varnames[pcomp]) + "_" + std::string(m_pfield_species[ispec]), ncomp); + } + } + } + // Sum the number of components in input vector m_all_field_functors // and check that it corresponds to the number of components in m_varnames // and m_mf_output @@ -329,6 +349,7 @@ FullDiagnostics::InitializeFieldFunctorsRZopenPMD (int lev) for (int jj=0; jjnComp(); } + AMREX_ALWAYS_ASSERT( ncomp_from_src == m_varnames.size() ); #else amrex::ignore_unused(lev); From 99159dfb47f955936adcb79ac1d3dae15cddcbd6 Mon Sep 17 00:00:00 2001 From: Edoardo Zoni <59625522+EZoni@users.noreply.github.com> Date: Fri, 29 Sep 2023 15:26:14 -0700 Subject: [PATCH 36/39] Fix clang-tidy error [readability-simplify-boolean-expr] (#4328) --- Source/WarpX.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/WarpX.cpp b/Source/WarpX.cpp index 67b00ab8900..348b3125f57 100644 --- a/Source/WarpX.cpp +++ b/Source/WarpX.cpp @@ -1031,7 +1031,7 @@ WarpX::ReadParameters () const ParmParse pp_fluids("fluids"); std::vector fluid_species_names = {}; pp_fluids.queryarr("species_names", fluid_species_names); - if ( fluid_species_names.empty() == false ) do_fluid_species = 1; + if (!fluid_species_names.empty()) do_fluid_species = 1; if (do_fluid_species) { WARPX_ALWAYS_ASSERT_WITH_MESSAGE(max_level <= 1, "Fluid species cannot currently be used with mesh refinement."); From 4cc78e64641a6d6663febfa80576949c97727ec3 Mon Sep 17 00:00:00 2001 From: Grant Johnson <69021085+johnson452@users.noreply.github.com> Date: Sat, 30 Sep 2023 03:28:18 -0700 Subject: [PATCH 37/39] Clean-up docs for PR3991, fluids (#4330) --- Docs/source/theory/Fluid_Loop.png | Bin 300102 -> 0 bytes Docs/source/theory/cold_fluid_model.rst | 6 +++--- Docs/source/usage/parameters.rst | 4 +--- 3 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 Docs/source/theory/Fluid_Loop.png diff --git a/Docs/source/theory/Fluid_Loop.png b/Docs/source/theory/Fluid_Loop.png deleted file mode 100644 index 8d47cd606c5d8ee945d79fea180e6f9ec54a3f48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300102 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 as a placeholder. Also see external fields for how to specify these for fluids as the function names differ. From e137df4d5f39cec78cb78077ca74c717d2e730c2 Mon Sep 17 00:00:00 2001 From: Marco Garten Date: Mon, 2 Oct 2023 15:31:32 +0200 Subject: [PATCH 38/39] Bump up HDF5 version on PM to 1.12.2.7 (#4333) The previous version cray-hdf5-parallel/1.12.2.1 was not recognized anymore. At this moment, 1.12.2.3 and 1.12.2.7 are installed as modules on Perlmutter. --- .../perlmutter-nersc/perlmutter_cpu_warpx.profile.example | 2 +- .../perlmutter-nersc/perlmutter_gpu_warpx.profile.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example b/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example index a722c9a9104..d25b4825dde 100644 --- a/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example +++ b/Tools/machines/perlmutter-nersc/perlmutter_cpu_warpx.profile.example @@ -14,7 +14,7 @@ module load cray-fftw/3.3.10.3 export BOOST_ROOT=/global/common/software/spackecp/perlmutter/e4s-23.05/default/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/boost-1.82.0-ow5r5qrgslcwu33grygouajmuluzuzv3 # optional: for openPMD and PSATD+RZ support -module load cray-hdf5-parallel/1.12.2.1 +module load cray-hdf5-parallel/1.12.2.7 export CMAKE_PREFIX_PATH=${CFS}/${proj}/${USER}/sw/perlmutter/cpu/c-blosc-1.21.1:$CMAKE_PREFIX_PATH export CMAKE_PREFIX_PATH=${CFS}/${proj}/${USER}/sw/perlmutter/cpu/adios2-2.8.3:$CMAKE_PREFIX_PATH export CMAKE_PREFIX_PATH=${CFS}/${proj}/${USER}/sw/perlmutter/cpu/blaspp-master:$CMAKE_PREFIX_PATH diff --git a/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example b/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example index 629a2073a9e..af3e69c1190 100644 --- a/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example +++ b/Tools/machines/perlmutter-nersc/perlmutter_gpu_warpx.profile.example @@ -12,7 +12,7 @@ module load cmake/3.22.0 export BOOST_ROOT=/global/common/software/spackecp/perlmutter/e4s-23.05/default/spack/opt/spack/linux-sles15-zen3/gcc-11.2.0/boost-1.82.0-ow5r5qrgslcwu33grygouajmuluzuzv3 # optional: for openPMD and PSATD+RZ support -module load cray-hdf5-parallel/1.12.2.1 +module load cray-hdf5-parallel/1.12.2.7 export CMAKE_PREFIX_PATH=${CFS}/${proj%_g}/${USER}/sw/perlmutter/gpu/c-blosc-1.21.1:$CMAKE_PREFIX_PATH export CMAKE_PREFIX_PATH=${CFS}/${proj%_g}/${USER}/sw/perlmutter/gpu/adios2-2.8.3:$CMAKE_PREFIX_PATH export CMAKE_PREFIX_PATH=${CFS}/${proj%_g}/${USER}/sw/perlmutter/gpu/blaspp-master:$CMAKE_PREFIX_PATH From 5cc458469157e11a03cd372796f8f0f5c7d9cc70 Mon Sep 17 00:00:00 2001 From: Hannah Klion Date: Mon, 2 Oct 2023 14:22:59 -0700 Subject: [PATCH 39/39] fix compile --- Source/Evolve/WarpXEvolve.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Evolve/WarpXEvolve.cpp b/Source/Evolve/WarpXEvolve.cpp index cecdf3bb0a9..40fc431f4cd 100644 --- a/Source/Evolve/WarpXEvolve.cpp +++ b/Source/Evolve/WarpXEvolve.cpp @@ -1038,6 +1038,7 @@ WarpX::PushParticlesandDepose (int lev, amrex::Real cur_time, DtType a_dt_type, ApplySubcyclingScalingToCurrentDensity(current_fp_vay[lev][0].get(), current_fp_vay[lev][1].get(), current_fp_vay[lev][2].get(), n_subcycle_current, lev); else ApplySubcyclingScalingToCurrentDensity(current_fp[lev][0].get(), current_fp[lev][1].get(), current_fp[lev][2].get(), n_subcycle_current, lev); + } if (do_fluid_species) { myfl->Evolve(lev, *Efield_aux[lev][0],*Efield_aux[lev][1],*Efield_aux[lev][2],