diff --git a/.github/workflows/dependencies/dependencies.sh b/.github/workflows/dependencies/dependencies.sh index d0e86e99..ecaa6eca 100755 --- a/.github/workflows/dependencies/dependencies.sh +++ b/.github/workflows/dependencies/dependencies.sh @@ -13,4 +13,8 @@ sudo apt-get install -y --no-install-recommends\ build-essential \ g++ gfortran \ libopenmpi-dev \ - openmpi-bin + openmpi-bin \ + python3 \ + python3-pip + +python3 -m pip install -U pip setuptools wheel diff --git a/.github/workflows/dependencies/dependencies_clang6.sh b/.github/workflows/dependencies/dependencies_clang6.sh index 19b348b9..596dd8c3 100755 --- a/.github/workflows/dependencies/dependencies_clang6.sh +++ b/.github/workflows/dependencies/dependencies_clang6.sh @@ -11,4 +11,8 @@ sudo apt-get update sudo apt-get install -y \ build-essential \ - clang gfortran + clang gfortran \ + python3 \ + python3-pip + +python3 -m pip install -U pip setuptools wheel diff --git a/.github/workflows/dependencies/dependencies_gcc10.sh b/.github/workflows/dependencies/dependencies_gcc10.sh index 583c5018..66573abc 100755 --- a/.github/workflows/dependencies/dependencies_gcc10.sh +++ b/.github/workflows/dependencies/dependencies_gcc10.sh @@ -14,4 +14,8 @@ sudo apt-get install -y --no-install-recommends \ build-essential \ g++-10 gfortran-10 \ libopenmpi-dev \ - openmpi-bin + openmpi-bin \ + python3 \ + python3-pip + +python3 -m pip install -U pip setuptools wheel diff --git a/.github/workflows/dependencies/dependencies_nofortran.sh b/.github/workflows/dependencies/dependencies_nofortran.sh index 36d759f6..d00c1ff8 100755 --- a/.github/workflows/dependencies/dependencies_nofortran.sh +++ b/.github/workflows/dependencies/dependencies_nofortran.sh @@ -17,4 +17,8 @@ sudo apt-get install -y --no-install-recommends\ build-essential \ g++ \ libopenmpi-dev \ - openmpi-bin + openmpi-bin \ + python3 \ + python3-pip + +python3 -m pip install -U pip setuptools wheel diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 25f93f6e..428d21e8 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -16,17 +16,21 @@ jobs: - name: Build & Install run: | cd ExampleCodes - mkdir build - cd build - cmake .. \ + cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -DAMReX_LINEAR_SOLVERS=ON \ -DAMReX_FORTRAN=ON \ -DAMReX_FORTRAN_INTERFACES=ON \ -DAMReX_EB=ON \ - -DAMReX_PARTICLES=ON - make -j 2 + -DAMReX_PARTICLES=ON \ + -DTUTORIAL_PYTHON=ON + cmake --build build -j 2 + cmake --build build -j 2 --target pyamrex_pip_install + - name: Run Python + run: | + cd GuidedTutorials/MultiFab/ + python main.py # Build all tutorials tutorials_cxx20: @@ -40,9 +44,7 @@ jobs: - name: Build & Install run: | cd ExampleCodes - mkdir build - cd build - cmake .. \ + cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -DAMReX_OMP=ON \ @@ -50,12 +52,18 @@ jobs: -DAMReX_LINEAR_SOLVERS=ON \ -DAMReX_FORTRAN=ON \ -DAMReX_FORTRAN_INTERFACES=ON \ - -DAMReX_EB=ON \ + -DAMReX_EB=ON \ + -DTUTORIAL_PYTHON=ON \ -DCMAKE_CXX_STANDARD=20 \ -DCMAKE_C_COMPILER=$(which gcc-10) \ -DCMAKE_CXX_COMPILER=$(which g++-10) \ -DCMAKE_Fortran_COMPILER=$(which mpif90) - make -j 2 + cmake --build build -j 2 + cmake --build build -j 2 --target pyamrex_pip_install + - name: Run Python + run: | + cd GuidedTutorials/MultiFab/ + python main.py tutorials_clang: name: Clang@6.0 C++14 SP Particles DP Mesh Debug [tutorials] @@ -68,9 +76,7 @@ jobs: - name: Build & Install run: | cd ExampleCodes - mkdir build - cd build - cmake .. \ + cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -DAMReX_MPI=OFF \ @@ -81,11 +87,17 @@ jobs: -DAMReX_EB=ON \ -DAMReX_PRECISION=DOUBLE \ -DAMReX_PARTICLES_PRECISION=SINGLE \ + -DTUTORIAL_PYTHON=ON \ -DCMAKE_CXX_STANDARD=14 \ -DCMAKE_C_COMPILER=$(which clang) \ -DCMAKE_CXX_COMPILER=$(which clang++) \ -DCMAKE_Fortran_COMPILER=$(which gfortran) - make -j 2 + cmake --build build -j 2 + cmake --build build -j 2 --target pyamrex_pip_install + - name: Run Python + run: | + cd GuidedTutorials/MultiFab/ + python main.py # Build all tutorials w/o MPI tutorials-nonmpi: @@ -99,9 +111,7 @@ jobs: - name: Build & Install run: | cd ExampleCodes - mkdir build - cd build - cmake .. \ + cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -DAMReX_MPI=OFF \ @@ -109,8 +119,14 @@ jobs: -DAMReX_FORTRAN=ON \ -DAMReX_FORTRAN_INTERFACES=ON \ -DAMReX_EB=ON \ - -DAMReX_PARTICLES=ON - make -j 2 + -DAMReX_PARTICLES=ON \ + -DTUTORIAL_PYTHON=ON + cmake --build build -j 2 + cmake --build build -j 2 --target pyamrex_pip_install + - name: Run Python + run: | + cd GuidedTutorials/MultiFab/ + python main.py # Build all tutorials tutorials-nofortran: @@ -124,16 +140,20 @@ jobs: - name: Build & Install run: | cd ExampleCodes - mkdir build - cd build - cmake .. \ + cmake -S . -B build \ -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_VERBOSE_MAKEFILE=ON \ -DAMReX_LINEAR_SOLVERS=ON \ -DAMReX_EB=ON \ -DAMReX_PARTICLES=ON \ - -DAMReX_FORTRAN=OFF - make -j 2 + -DAMReX_FORTRAN=OFF \ + -DTUTORIAL_PYTHON=ON + cmake --build build -j 2 + cmake --build build -j 2 --target pyamrex_pip_install + - name: Run Python + run: | + cd GuidedTutorials/MultiFab/ + python main.py # Build all tutorials with CUDA 11.0.2 (recent supported) tutorials-cuda11: diff --git a/Docs/source/Python_Tutorial.rst b/Docs/source/Python_Tutorial.rst new file mode 100644 index 00000000..37c6fc7d --- /dev/null +++ b/Docs/source/Python_Tutorial.rst @@ -0,0 +1,17 @@ +.. _tutorials_python: + +====== +Python +====== + +These examples show how to use AMReX from Python. +AMReX applications can also be interfaced to Python with the same logic. + +In order to build the Python tutorials, add ``-DTUTORIAL_PYTHON=ON`` to the CMake configuration options. +Then install with ``cmake --build build --target pyamrex_pip_install``. + +Examples: + +- ``GuidedTutorials/MultiFab/main.py`` + +Please see `pyAMReX `__ for more details. diff --git a/Docs/source/index.rst b/Docs/source/index.rst index 555b56c3..d9995d95 100644 --- a/Docs/source/index.rst +++ b/Docs/source/index.rst @@ -50,6 +50,7 @@ sorted by the following categories: - :ref:`Linear Solvers` -- Examples of several linear solvers. - :ref:`MUI` -- Incorporates the MxUI/MUI (Multiscale Universal interface) frame into AMReX. - :ref:`Particles` -- Basic usage of AMReX's particle data structures. +- :ref:`Python` -- Using AMReX and interfacing with AMReX applications form Python - via `pyAMReX `__ - :ref:`SDC` -- Example usage of a "Multi-Implicit" Spectral Deferred Corrections (MISDC) integrator to solve a scalar advection-diffusion-reaction equation. - :ref:`SENSEI` -- In situ data analysis and visualization through a unified interface. @@ -71,6 +72,7 @@ sorted by the following categories: ML_Tutorial MUI_Tutorial Particles_Tutorial + Python_Tutorial SDC_Tutorial SENSEI_Tutorial SUNDIALS_Tutorial @@ -95,6 +97,8 @@ sorted by the following categories: .. _`Particles`: Particles_Tutorial.html +.. _`Python`: Python_Tutorial.html + .. _`SDC`: SDC_Tutorial.html .. _`SENSEI`: SENSEI_Tutorial.html diff --git a/ExampleCodes/CMakeLists.txt b/ExampleCodes/CMakeLists.txt index 75e2688b..be729404 100644 --- a/ExampleCodes/CMakeLists.txt +++ b/ExampleCodes/CMakeLists.txt @@ -14,17 +14,34 @@ set( CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake ) # If AMReX_DIR is passed then we link to the library found there. Otherwise # the sources are fetched and compiled from the repo pointed to by AMReX_GIT_REPO # -set ( AMReX_GIT_REPO "https://github.com/AMReX-Codes/amrex.git/" - CACHE STRING "The URL identifying the repo to fetchg AMReX from" ) +set ( AMReX_GIT_REPO "https://github.com/AMReX-Codes/amrex.git" + CACHE STRING "The URL identifying the repo to fetch AMReX from" ) # # Fetch and compile AMReX or link to an existing CMake build install of libamrex. # If AMReX_DIR is passed then we link to the library found there. Otherwise -# the sources are fetched and compiled from the repo pointed to by AMReX_GIT_REPO +# the sources are fetched and compiled from the repo pointed to by pyAMReX_GIT_REPO +# +set ( pyAMReX_GIT_REPO "https://github.com/AMReX-Codes/pyamrex.git" + CACHE STRING "The URL identifying the repo to fetch pyAMReX from" ) +option(TUTORIAL_PYTHON OFF) + +# +# Fetch and compile AMReX or link to an existing CMake build install of libamrex. +# If AMReX_DIR is passed then we link to the library found there. Otherwise +# the sources are fetched and compiled from the repo pointed to by SUNDIALS_GIT_REPO # set ( SUNDIALS_GIT_REPO "https://github.com/LLNL/sundials.git" CACHE STRING "The URL identifying the repo to fetch SUNDIALS from" ) +# +# For Python, we need AMReX as a a sahred library, otherwise we cannot share all +# its global variables between multiple Python modules +# +if(TUTORIAL_PYTHON) + set(AMReX_BUILD_SHARED_LIBS ON CACHE BOOL "Build AMReX shared library" FORCE) +endif() + if( NOT DEFINED AMReX_DIR ) # # Fetch amrex repo @@ -174,6 +191,33 @@ else() endif() endif() +if(TUTORIAL_PYTHON) + if(DEFINED pyAMReX_DIR) + add_subdirectory(${pyAMReX_DIR}) + else() + # + # Fetch pyAMReX repo + # + message(STATUS "Fetching from ${pyAMReX_GIT_REPO} branch ${pyAMReX_GIT_BRANCH}" ) + + set(pyAMReX_GIT_BRANCH "development" CACHE STRING "The pyAMReX branch to checkout") + set(pyAMReX_INSTALL "NO" CACHE INTERNAL "Disable install target for pyAMReX") + + include(FetchContent) + #set(FETCHCONTENT_QUIET OFF) # Verbose ON + + FetchContent_Declare( fetchedpyamrex + GIT_REPOSITORY ${pyAMReX_GIT_REPO} + GIT_TAG ${pyAMReX_GIT_BRANCH} + ) + + if(NOT fetchedpyamrex_POPULATED) + FetchContent_Populate(fetchedpyamrex) + add_subdirectory(${fetchedpyamrex_SOURCE_DIR} ${fetchedpyamrex_BINARY_DIR}) + endif() + endif() +endif() + # # List of subdirectories to search for CMakeLists. # For now, we do not include MUI, SDC, SWFFT diff --git a/GuidedTutorials/MultiFab/main.py b/GuidedTutorials/MultiFab/main.py new file mode 100644 index 00000000..e4418b98 --- /dev/null +++ b/GuidedTutorials/MultiFab/main.py @@ -0,0 +1,99 @@ +import amrex.space3d as amr + +# CPU/GPU logic +if amr.Config.have_gpu: + try: + import cupy as cp + xp = cp + print("Note: found and will use cupy") + except ImportError: + print("Warning: GPU found but cupy not available! Trying managed memory in numpy...") + import numpy as np + xp = np + if amr.Config.gpu_backend == "SYCL": + print("Warning: SYCL GPU backend not yet implemented for Python") + import numpy as np + xp = np + +else: + import numpy as np + xp = np + print("Note: found and will use numpy") + + +# Initialize AMReX +amr.initialize([]) + +if amr.ParallelDescriptor.IOProcessor(): + print(f"Hello world from pyAMReX version {amr.__version__}\n") + +# Goals: +# * Define a MultiFab +# * Fill a MultiFab with data +# * Plot it + +# Parameters + +# Number of data components at each grid point in the MultiFab +ncomp = 1 +# How many grid cells in each direction over the problem domain +n_cell = 32 +# How many grid cells are allowed in each direction over each box +max_grid_size = 16 + +# BoxArray: abstract domain setup + +# Integer vector indicating the lower coordinate bounds +dom_lo = amr.IntVect(0,0,0) +# Integer vector indicating the upper coordinate bounds +dom_hi = amr.IntVect(n_cell-1, n_cell-1, n_cell-1) +# Box containing the coordinates of this domain +domain = amr.Box(dom_lo, dom_hi) + +# Will contain a list of boxes describing the problem domain +ba = amr.BoxArray(domain) + +# Chop the single grid into many small boxes +ba.max_size(max_grid_size) + +# Distribution Mapping +dm = amr.DistributionMapping(ba) + +# Define MultiFab +mf = amr.MultiFab(ba, dm, ncomp, 0) +mf.set_val(0.) + +# Geometry: physical properties for data on our domain +real_box = amr.RealBox([0., 0., 0.], [1. , 1., 1.]) + +coord = 0 # Cartesian +is_per = [0, 0, 0] # periodicity +geom = amr.Geometry(domain, real_box, coord, is_per) + +# Calculate cell sizes +dx = geom.data().CellSize() # dx[0]=dx dx[1]=dy dx[2]=dz + +# Fill a MultiFab with data +mf_val = 0. +for mfi in mf: + bx = mfi.validbox() + # Preferred way to fill array using fast ranged operations: + # - xp.array is indexed in reversed order (n,z,y,x), + # .T creates a view into the AMReX (x,y,z,n) order + # - indices are local (range from 0 to box size) + mf_array = xp.array(mf.array(mfi), copy=False).T + x = (xp.arange(bx.small_end[0], bx.big_end[0]+1)+0.5)*dx[0] + y = (xp.arange(bx.small_end[1], bx.big_end[1]+1)+0.5)*dx[1] + z = (xp.arange(bx.small_end[2], bx.big_end[2]+1)+0.5)*dx[2] + v = (x[xp.newaxis,xp.newaxis,:] + + y[xp.newaxis,:,xp.newaxis]*0.1 + + z[:,xp.newaxis,xp.newaxis]*0.01) + mf_array = 1. + xp.exp(-v) + +# Plot MultiFab data +plotfile = amr.concatenate(root="plt", num=1, mindigits=3) +varnames = amr.Vector_string(["comp0"]) +amr.write_single_level_plotfile(plotfile, mf, varnames, geom, time=0., level_step=0) + +# Finalize AMReX +amr.finalize()