Skip to content

Commit 42b474f

Browse files
authored
Merge pull request #277 from idefix-code/pythonBinding
Add python binding for initial conditions and outputs
2 parents 7b2910c + f89fc65 commit 42b474f

24 files changed

Lines changed: 1074 additions & 44 deletions

.pre-commit-config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ repos:
3434
- B # flake8-bugbear
3535
- I # isort
3636
- NPY # numpy-specific rules
37+
- --ignore
38+
- F405
39+
- --ignore
40+
- F403 # ignore import *
3741

3842
- repo: https://github.com/neutrinoceros/inifix
3943
rev: v5.0.2

CMakeLists.txt

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ option(Idefix_HIGH_ORDER_FARGO "Force Fargo to use a PPM reconstruction scheme"
1515
option(Idefix_DEBUG "Enable Idefix debug features (makes the code very slow)" OFF)
1616
option(Idefix_RUNTIME_CHECKS "Enable runtime sanity checks" OFF)
1717
option(Idefix_WERROR "Treat compiler warnings as errors" OFF)
18+
option(Idefix_PYTHON "Enable python bindings (requires pybind11)" OFF)
1819
set(Idefix_CXX_FLAGS "" CACHE STRING "Additional compiler/linker flag")
1920
set(Idefix_DEFS "definitions.hpp" CACHE FILEPATH "Problem definition header file")
2021
option(Idefix_CUSTOM_EOS "Use custom equation of state" OFF)
@@ -77,7 +78,7 @@ add_subdirectory(src build)
7778
if(EXISTS ${PROJECT_BINARY_DIR}/setup.cpp)
7879
target_sources(idefix PUBLIC ${PROJECT_BINARY_DIR}/setup.cpp)
7980
else()
80-
message(WARNING "No specific setup.cpp found in the problem directory")
81+
message(WARNING "No specific setup.cpp found in the problem directory (this message can be ignored if using python to define your problem)")
8182
endif()
8283

8384
# If a CMakeLists.txt is in the problem dir (for problem-specific source files)
@@ -115,12 +116,26 @@ if(Idefix_HDF5)
115116
)
116117
find_package(HDF5 REQUIRED)
117118
target_link_libraries(idefix "${HDF5_LIBRARIES}")
118-
target_include_directories(idefix PUBLIC "${HDF5_INCLUDE_DIRS}")
119+
target_include_directories(idefix "${HDF5_INCLUDE_DIRS}")
119120
message(STATUS "XDMF (hdf5+xmf) dumps enabled")
120121
else()
121122
set(Idefix_HDF5 OFF)
122123
endif()
123124

125+
if(Idefix_PYTHON)
126+
add_compile_definitions("WITH_PYTHON")
127+
if (NOT DEFINED Python_FIND_FRAMEWORK)
128+
set(Python_FIND_FRAMEWORK "LAST") # Use Apple's python only at last resort on Macos
129+
endif ()
130+
set(PYBIND11_FINDPYTHON ON CACHE BOOL "Idefix requires python" FORCE)
131+
find_package(pybind11 REQUIRED)
132+
target_link_libraries(idefix pybind11::embed)
133+
target_sources(idefix
134+
PUBLIC src/pydefix.cpp
135+
PUBLIC src/pydefix.hpp
136+
)
137+
endif()
138+
124139
if(Idefix_DEBUG)
125140
add_compile_definitions("DEBUG")
126141
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g")
@@ -232,6 +247,7 @@ else()
232247
endif()
233248
message(STATUS " MPI: ${Idefix_MPI}")
234249
message(STATUS " HDF5: ${Idefix_HDF5}")
250+
message(STATUS " Python: ${Idefix_PYTHON}")
235251
message(STATUS " Reconstruction: ${Idefix_RECONSTRUCTION}")
236252
message(STATUS " Precision: ${Idefix_PRECISION}")
237253
message(STATUS " Version: ${Idefix_VERSION}")

doc/source/index.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ MPI library
3333
When using MPI parallelisation, *Idefix* relies on an external MPI library. *Idefix* has been tested successfully with OpenMPI and IntelMPI libraries. When used on GPU architectures, *Idefix* assumes that
3434
the MPI library is GPU-Aware. If unsure, check this last point with your system administrator.
3535

36+
Python
37+
When using *Idefix* with its python interface through the module `Pydefix`, *Idefix* relies on an external python>=3.8 interpreter with the module `pybind11 <https://pybind11.readthedocs.io>`_
38+
installed.
39+
3640
================
3741
Features
3842
================

doc/source/modules.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ In this section, you will find a more detailed documentation about each module t
2828
:ref:`gridCoarseningModule`
2929
The grid coarsening module, that allows to derefine the grid in particular locations to speed up the computation.
3030

31+
:ref:`pydefixModule`
32+
The Python interaction module, that allows Idefix to interact directly with a python interpreter.
33+
3134

3235
.. toctree::
3336
:maxdepth: 2
@@ -40,3 +43,4 @@ In this section, you will find a more detailed documentation about each module t
4043
modules/selfGravity.rst
4144
modules/braginskii.rst
4245
modules/gridCoarsening.rst
46+
modules/pydefix.rst

doc/source/modules/pydefix.rst

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
.. _pydefixModule:
2+
3+
Pydefix module
4+
==============
5+
6+
The Pydefix module allows Idefix to talk directly to a Python interpreter while running. It can be used to create your own initial condition
7+
and/or for custom outputs produced from Python. Pydefix relies on the pybind11 python package
8+
9+
The essence of Pydefix is to allows the user to have a direct access to Idefix data structure from Python without writing/accessing any file. In particular, IdefixArrays are viewed as numpy arrays in Python.
10+
Note however that to keep things simple, Pydefix works on the host memory space only, and hence sync data to/from the GPU (if used) before invoking Python functions. Hence, using Pydefix for outputs induces
11+
a significant loss of performances.
12+
13+
14+
Before you start
15+
----------------
16+
Pybind11 installation
17+
+++++++++++++++++++++
18+
19+
In order to use pydefix, you need to be working in a python>=3.8 environement that includes `pybind11 <https://pybind11.readthedocs.io>`_. Follow the instruction of your package manager to install pybind11>=2.12.
20+
21+
Pydefix usage
22+
-------------
23+
Idefix Configuration
24+
++++++++++++++++++++
25+
26+
In order to use Pydefix, you need to switch on ``Idefix_PYTHON`` in cmake. This will auto-detect Python and check that pybind11 can be used effectively.
27+
28+
29+
Run Idefix with Pydefix
30+
+++++++++++++++++++++++
31+
32+
Pydefix is typically enabled from your input file `idefix.ini` in the block ``[Python]``. The following parameters are available:
33+
34+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
35+
| Entry name | Parameter type | Comment |
36+
+========================+=======================+===========================================================================================================+
37+
| script | string | | (Mandatory) Filename (*without ".py"!*) of the python script that Idefix should use. |
38+
| | | | The script should be in the same location as the Idefix executable file. |
39+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
40+
| output_function | string | | (Optional) Name of the function that will be called for each output event (the function should be |
41+
| | | | defined in the python script above). When ommited, pydefix output functions are disabled. |
42+
| | | | The periodicity of the pydefix output routine is set in the block:entry `[Output]:python` |
43+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
44+
| initflow_function | string | | (Optional) Name of the python function that will be called to initialize the flow in place of the C++ |
45+
| | | | function ``Setup::InitFlow``. Revert to ``Setup::Initflow`` when ommited. |
46+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
47+
48+
Python script
49+
+++++++++++++
50+
51+
When using Pydefix, idefix expects a python script to be specified in the input file (see ``script`` parameter above). To be fully functionnal, you should import the ``pydefix`` module at the beginning
52+
of your python script (you can also import other python module, as any python script).
53+
54+
Your python script should define functions that will be called while Idefix is running:
55+
* The signature of the ``initflow`` function should be ``(data)`` where ``data`` is a python structure matching Idefix's ``DataBlockHost`` class.
56+
* The signature of the ``output`` function should be ``(data,grid,n)`` where ``data`` is a python structure matching Idefix's ``DataBlockHost`` class, ``grid`` is Idefix's ``GridHost`` class, and ``n`` is an integer representing the current number of the output
57+
58+
MPI parallelism
59+
+++++++++++++++
60+
When Idefix runs with MPI parallelism enabled, a python interpreter and script is launched by each MPI process. Each of these script is independent
61+
and have access to its local ``dataBlockHost``. The `pydefix` module however gives access to the local rank ``prank`` and total MPI size ``psize``. In addition,
62+
pydefix provides the function ``GatherIdefixArray`` to gather the data distributed among each process without invoking MPI directly in python. This function
63+
expects a 3D distributed IdefixArray in entry following the signature
64+
65+
.. code-block:: c++
66+
67+
GatherIdefixArray(IdefixHostArray3D<real> in, // 3D distributed array
68+
DataBlockHost dataHost, // dataBlock structure
69+
bool keepBoundaries = true, // Whether we keep the ghost zones in the returned array
70+
bool broadcast = true) // Whether the returned array is available only in proc #0 or in every proc (caution! possibly requires lots of memory)
71+
72+
This function is used as follows:
73+
74+
.. code-block:: python
75+
76+
import pydefix as pdfx # mandatory
77+
import numpy as np
78+
import matplotlib.pyplot as plt
79+
80+
def output(data,grid,n):
81+
# Gather pressure field from every process in process #0 (set broadcast to True to distribute the result to all processes)
82+
prs = pdfx.GatherIdefixArray(data.Vc[pdfx.PRS,:,:,:],data,broadcast=False)
83+
84+
# Only root process performs this
85+
if pdfx.prank==0:
86+
x=grid.x[pdfx.IDIR] # The grid contains the full domain size, while the datablock contains only local information
87+
y=grid.x[pdfx.JDIR]
88+
plt.figure()
89+
plt.pcolormesh(x,y,prs[0,:,:],cmap='plasma')
90+
91+
92+
.. note::
93+
For more advanced usage, it is also possibly to directly call MPI routines from python using the `Mpi4py <https://pypi.org/project/mpi4py/>`_ module.
94+
95+
Example
96+
+++++++
97+
98+
An example is provided in the directory `test/IO/python`. This example shows how to use Idefix with pure python initial conditions and outputs.
99+
It reproduces the 2D OrszagTang vortex available in MHD/OrszagTang without requiring any single line of C++ code from the user.
100+
101+
The python script `pydefix_example.py` initializes the initial condition of the OT test (``initflow``) and produces a series of PNG files through matplotlib (`output`).
102+
103+
Troubleshooting
104+
---------------
105+
106+
It during configuration stage, you get::
107+
108+
CMake Error at CMakeLists.txt:122 (find_package):
109+
By not providing "Findpybind11.cmake" in CMAKE_MODULE_PATH this project has
110+
asked CMake to find a package configuration file provided by "pybind11",
111+
but CMake did not find one.
112+
113+
It means that cmake cannot find the location of pybind11 (this typically happens on MacOs). In order to locate pybind11, open a python interpreter, and get pybind11 install dir through:
114+
115+
.. code-block:: python
116+
117+
import pybind11
118+
print(pybind11.__file__)
119+
120+
You can then exit the interpreter and set the pybind11_DIR environement variable to the right path:
121+
122+
.. code-block:: bash
123+
124+
export pybind11_DIR=env/lib/python3.10/site-packages/pybind11
125+
126+
you can then run cmake which should be able to find pybind11, and compile the code.

doc/source/reference/idefix.ini.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,23 @@ and ``X1-end``, ``X2-end``, ``X3-end`` for the right boundaries. ``X2`` boundari
358358
| | | (see :ref:`userdefBoundaries`) |
359359
+----------------+------------------------------------------------------------------------------------------------------------------+
360360

361+
``Python`` section
362+
------------------
363+
364+
This section describes the python script and function that can interact with Idefix while running using the Pydefix module (see :ref:`pydefixModule`)
365+
366+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
367+
| Entry name | Parameter type | Comment |
368+
+========================+=======================+===========================================================================================================+
369+
| script | string | | (Mandatory) Filename (*without ".py"!*) of the python script that Idefix should use. |
370+
| | | | The script should be in location of Idefix executable file |
371+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
372+
| output_function | string | | (Optional) Name of the function that will be called for each output event (the function should be |
373+
| | | | defined in the python script above). When ommited, pydefix output functions are disabled. |
374+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
375+
| initflow_function | string | | (optional) Name of the python function that will be called to initialize the flow in place of the C++ |
376+
| | | | function `Setup::InitFlow`. Revert to `Setup::Initflow`` when ommited. |
377+
+------------------------+-----------------------+-----------------------------------------------------------------------------------------------------------+
361378

362379
.. _outputSection:
363380

@@ -411,6 +428,9 @@ This section describes the outputs *Idefix* produces. For more details about eac
411428
| | | | (see :ref:`functionEnrollment`). The user-defined variables defined by this function |
412429
| | | | are then written as new variables in vtk and/or xdmf outputs. |
413430
+----------------+-------------------------+--------------------------------------------------------------------------------------------------+
431+
| python | float | | Time interval between pydefix outputs, in code units. |
432+
| | | | If negative, periodic pydefix outputs are disabled. |
433+
+----------------+-------------------------+--------------------------------------------------------------------------------------------------+
414434

415435
.. note::
416436
Even if dumps are not mentionned in your input file (and are therefore disabled), dump files are still produced when *Idefix* captures a signal

doc/source/reference/makefile.rst

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,18 +108,11 @@ We recommend the following modules and environement variables on AdAstra:
108108

109109
.. code-block:: bash
110110
111-
module load cpe/23.12
111+
module load cpe/24.07
112112
module load craype-accel-amd-gfx90a craype-x86-trento
113113
module load PrgEnv-cray
114-
module load amd-mixed/5.7.1
115-
module load rocm/5.7.1 # nécessaire a cause d'un bug de path pas encore fix..
116-
export HIPCC_COMPILE_FLAGS_APPEND="-isystem ${CRAY_MPICH_PREFIX}/include"
117-
export HIPCC_LINK_FLAGS_APPEND="-L${CRAY_MPICH_PREFIX}/lib -lmpi ${PE_MPICH_GTL_DIR_amd_gfx90a} ${PE_MPICH_GTL_LIBS_amd_gfx90a} -lstdc++fs"
118-
export CXX=hipcc
119-
export CC=hipcc
120-
121-
The `-lstdc++fs` option being there to guarantee the link to the HIP library and the access to specific
122-
C++17 <filesystem> functions.
114+
module load amd-mixed
115+
module load cray-python/3.11.7
123116
124117
Finally, *Idefix* can be configured to run on Mi250 by enabling HIP and the desired architecture with the following options to ccmake:
125118

doc/source/reference/outputs.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Output formats
77
--------------
88

99
*Idefix* uses several types of outputs you may want for your setup. By default, *Idefix* allows
10-
for 4 kinds of outputs:
10+
for 5 kinds of outputs:
1111

1212
* logs which essentially tells the user what *Idefix* is currently doing. When running in serial, logs are sent to stdout, but when
1313
MPI is enabled, only the logs of the rank 0 process is sent to stdout, and each process (including rank 0) simultaneously writes a
@@ -21,17 +21,20 @@ for 4 kinds of outputs:
2121
or `Visit <https://wci.llnl.gov/simulation/computer-codes/visit>`_. The XDMF format relies on the HDF5 format and therefore requires *Idefix* to be configured with HDF5 support.
2222
* user-defined analysis files. These are totally left to the user. They usually consist of ascii tables defined by the user, but they can
2323
be anything.
24+
* python script, that relies on the :ref:`pydefixModule`. This launches a user-defined python function fed with Idefix data. One can then directly plot or interact with Idefix outputs from python.
2425

2526
The output periodicity and the userdef variables should all be declared in the input file, as described in :ref:`outputSection`.
2627

2728
Defining your own outputs
2829
-------------------------
2930

30-
*Idefix* provides two ways to define your own outputs: analysis, which are used to make your
31-
own output file (e.g. an ascii-tabulated file); and user variables, which are written by *Idefix* output routines.
31+
*Idefix* provides three ways to define your own outputs: analysis, which are used to make your
32+
own output file (e.g. an ascii-tabulated file); user variables, which are written by *Idefix* output routines, and python user-defined
33+
functions that process Idefix's data.
3234

3335
Both analysis and uservar requires the definition of a user function which needs to be enrolled following the procedure described
34-
in :ref:`functionEnrollment` and using the function signatures declared in `output.hpp`.
36+
in :ref:`functionEnrollment` and using the function signatures declared in `output.hpp`. The python outputs are described
37+
in the :ref:`pydefixModule`.
3538

3639
We provide below an example of a setup using both analysis outputs and uservar outputs
3740

src/dataBlock/dataBlockHost.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ DataBlockHost::DataBlockHost(DataBlock& datain) {
3434

3535
nghost = data->nghost;
3636

37+
lbound = data->lbound;
38+
rbound = data->rbound;
39+
3740
xbeg = data->xbeg;
3841
xend = data->xend;
3942
beg = data->beg;
@@ -95,13 +98,18 @@ DataBlockHost::DataBlockHost(DataBlock& datain) {
9598
this->haveplanetarySystem = data->haveplanetarySystem;
9699
this->planetarySystem = data->planetarySystem.get();
97100

101+
this->t = data->t;
102+
this->dt = data->dt;
103+
98104
idfx::popRegion();
99105
}
100106

101107
// Synchronisation routines of Data (*Only*)
102108
void DataBlockHost::SyncToDevice() {
103109
idfx::pushRegion("DataBlockHost::SyncToDevice()");
104110

111+
data->t = this->t;
112+
data->dt = this->dt;
105113
Kokkos::deep_copy(data->hydro->Vc,Vc);
106114
Kokkos::deep_copy(data->hydro->InvDt,InvDt);
107115

@@ -138,6 +146,9 @@ void DataBlockHost::SyncToDevice() {
138146

139147
void DataBlockHost::SyncFromDevice() {
140148
idfx::pushRegion("DataBlockHost::SyncFromDevice()");
149+
this->t = data->t;
150+
this->dt = data->dt;
151+
141152
Kokkos::deep_copy(Vc,data->hydro->Vc);
142153
Kokkos::deep_copy(InvDt,data->hydro->InvDt);
143154

0 commit comments

Comments
 (0)