Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions doc/source/modules/pydefix.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,30 @@ Your python script should define functions that will be called while Idefix is r
* The signature of the ``initflow`` function should be ``(data)`` where ``data`` is a python structure matching Idefix's ``DataBlockHost`` class.
* 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

Access to input parameters
^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``pydefix`` module exposes the full set of Idefix input parameters as a global variable ``input_parameters``. This variable is a nested dictionary with the structure ``dict[str, dict[str, list[str]]]``, where:

* The first level keys are the block names from the input file (e.g., ``Grid``, ``Hydro``, ``TimeIntegrator``, etc.)
* The second level keys are the parameter names within each block
* The values are lists of strings containing the parameter values as specified in the input file

For example, to access the grid parameters from your Python script:

.. code-block:: python

from pydefix import input_parameters

# Access Grid block parameters
grid_params = input_parameters["Grid"]
x1_grid = grid_params["X1-grid"] # Returns a list like ["1", "0.0", "256", "u", "1.0"]

# Access TimeIntegrator parameters
cfl = input_parameters["TimeIntegrator"]["CFL"][0] # Returns "0.6" as a string

This allows your Python functions to access and use any parameter from the input file without needing to pass them explicitly.

MPI parallelism
+++++++++++++++
When Idefix runs with MPI parallelism enabled, a python interpreter and script is launched by each MPI process. Each of these script is independent
Expand Down
4 changes: 4 additions & 0 deletions src/arrays.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ template <typename T> using IdefixArray3D =
Kokkos::View<T***, Layout, Device>;
template <typename T> using IdefixArray4D =
Kokkos::View<T****, Layout, Device>;
template <typename T> using IdefixArray5D =
Kokkos::View<T*****, Layout, Device>;

template <typename T> using IdefixHostArray1D =
Kokkos::View<T*, Kokkos::LayoutRight, Kokkos::HostSpace>;
Expand All @@ -33,6 +35,8 @@ template <typename T> using IdefixHostArray3D =
Kokkos::View<T***, Kokkos::LayoutRight, Kokkos::HostSpace>;
template <typename T> using IdefixHostArray4D =
Kokkos::View<T****, Kokkos::LayoutRight, Kokkos::HostSpace>;
template <typename T> using IdefixHostArray5D =
Kokkos::View<T*****, Kokkos::LayoutRight, Kokkos::HostSpace>;

// Atomic arrays
template <typename T> using IdefixAtomicArray1D =
Expand Down
34 changes: 29 additions & 5 deletions src/fluid/fluid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ class Fluid {
IdefixArray4D<real> J; // Electrical current
// (only defined when non-ideal MHD effects are enabled)

// For muli-fluid simulations, we need to store the variables of all fluids in a single array
IdefixArray5D<real> AllVc; // All cell-centered primitive variables index
IdefixArray5D<real> AllUc; // All face-centered varariables

// Name of the fields (used in outputs)
std::vector<std::string> VcName;
std::vector<std::string> VsName;
Expand Down Expand Up @@ -532,11 +536,31 @@ Fluid<Phys>::Fluid(Grid &grid, Input &input, DataBlock *datain, int n) {
// ALLOCATION SECION ///////////////////
/////////////////////////////////////////

// We now allocate the fields required by the hydro solver
Vc = IdefixArray4D<real>(prefix+"_Vc", Phys::nvar+nTracer,
data->np_tot[KDIR], data->np_tot[JDIR], data->np_tot[IDIR]);
Uc = IdefixArray4D<real>(prefix+"_Uc", Phys::nvar+nTracer,
data->np_tot[KDIR], data->np_tot[JDIR], data->np_tot[IDIR]);
if(std::string(Phys::prefix).compare("Dust") == 0) {
// Ensure Vc and Vs are subviews of the a parent array (own by the primary dust fluid object)
if(instanceNumber == 0) {
// Allocate AllVc
int nSpecies = input.Get<int>("Dust","nSpecies",0);
AllVc = IdefixArray5D<real>("Dust_AllVc", nSpecies, Phys::nvar+nTracer,
data->np_tot[KDIR], data->np_tot[JDIR], data->np_tot[IDIR]);
AllUc = IdefixArray5D<real>("Dust_AllUc", nSpecies, Phys::nvar+nTracer,
data->np_tot[KDIR], data->np_tot[JDIR], data->np_tot[IDIR]);
} else {
AllVc = data->dust[0]->AllVc;
AllUc = data->dust[0]->AllUc;
}
// Create subviews for this dust specie
Vc = Kokkos::subview(AllVc, instanceNumber,
Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL());
Uc = Kokkos::subview(AllUc, instanceNumber,
Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL(), Kokkos::ALL());
} else {
// We now allocate the fields required by the hydro solver
Vc = IdefixArray4D<real>(prefix+"_Vc", Phys::nvar+nTracer,
data->np_tot[KDIR], data->np_tot[JDIR], data->np_tot[IDIR]);
Uc = IdefixArray4D<real>(prefix+"_Uc", Phys::nvar+nTracer,
data->np_tot[KDIR], data->np_tot[JDIR], data->np_tot[IDIR]);
}

data->states["current"].PushArray(Uc, State::center, prefix+"_Uc");

Expand Down
4 changes: 4 additions & 0 deletions src/input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,3 +431,7 @@ void Input::PrintVersion() {
idfx::cout << "Compiler version: " << CompilerInfo::version << std::endl;
idfx::cout << "Compiler path: " << CompilerInfo::path << std::endl;
}

const IdefixInputContainer& Input::GetInputParameters() const {
return inputParameters;
}
1 change: 1 addition & 0 deletions src/input.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class Input {
void PrintLogo();
void PrintOptions();
void PrintVersion();
const IdefixInputContainer& GetInputParameters() const;

bool restartRequested{false}; //< Should we restart?
int restartFileNumber; //< if yes, from which file?
Expand Down
13 changes: 12 additions & 1 deletion src/pydefix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ namespace py = pybind11;

int Pydefix::ninstance = 0;

// Global pointer to input parameters for Python access
static const IdefixInputContainer* globalInputParameters = nullptr;


namespace PydefixTools {
// Functions provided by Idefix in Pydefix for user convenience
Expand Down Expand Up @@ -250,6 +253,11 @@ PYBIND11_EMBEDDED_MODULE(pydefix, m) {
m.attr("prank") = idfx::prank;
m.attr("psize") = idfx::psize;

// Expose input parameters as a global module attribute
if (globalInputParameters != nullptr) {
m.attr("input_parameters") = py::cast(*globalInputParameters);
}

m.def("GatherIdefixArray",&PydefixTools::GatherIdefixArray,
py::arg("in"),
py::arg("data"),
Expand Down Expand Up @@ -277,7 +285,10 @@ void Pydefix::CallScript(std::string scriptName, std::string funcName, Ts... arg
}


Pydefix::Pydefix(Input &input) {
Pydefix::Pydefix(Input &input) : inputPtr(&input) {
// Store reference to input parameters for Python access
globalInputParameters = &(input.GetInputParameters());

// Check that the input has a [python] block
if(input.CheckBlock("Python")) {
this->isActive = true;
Expand Down
1 change: 1 addition & 0 deletions src/pydefix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class Pydefix {
template<typename... Ts>
void CallScript(std::string, std::string, Ts...);
static int ninstance;
Input* inputPtr; // Store pointer to active Input instance
std::string scriptFilename;
std::string outputFunctionName;
std::string initflowFunctionName;
Expand Down
31 changes: 31 additions & 0 deletions test/IO/pydefix/pydefix_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,34 @@
from pydefix import *


def test_input_parameters_access():
"""Test that input_parameters are accessible from Python."""
# Access the global input_parameters dict
params = input_parameters

# Verify it's a dict
assert isinstance(params, dict), "input_parameters should be a dict"

# Verify we can access Grid parameters
assert "Grid" in params, "Grid block should be in input_parameters"
grid_params = params["Grid"]
assert isinstance(grid_params, dict), "Grid should be a dict"

# Verify we can access specific parameters
assert "X1-grid" in grid_params, "X1-grid should be in Grid params"
x1_grid = grid_params["X1-grid"]
assert isinstance(x1_grid, list), "X1-grid should be a list"
assert len(x1_grid) > 0, "X1-grid should have values"

# Verify we can read actual values from the ini file
# From idefix.ini: X1-grid is "1 0.0 256 u 1.0"
assert x1_grid[0] == "1", f"Expected '1', got {x1_grid[0]}"
assert x1_grid[1] == "0.0", f"Expected '0.0', got {x1_grid[1]}"
assert x1_grid[2] == "256", f"Expected '256', got {x1_grid[2]}"

print("✓ input_parameters access test passed!")


# The output function
# the only argument is dataBlockHost python object, wrapping a dataBlockHost Idefix object
def output(data, grid, n):
Expand All @@ -44,6 +72,9 @@ def output(data, grid, n):


def initflow(data):
# Test input_parameters access on first call
test_input_parameters_access()

# Field amplitude
B0 = 1 / np.sqrt(4 * np.pi)

Expand Down
Loading