OGS
ogs_mpl.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
4#include <pybind11/eigen.h>
5#include <pybind11/pybind11.h>
6#include <pybind11/stl.h>
7
8#include <range/v3/range/conversion.hpp>
9#include <range/v3/view/transform.hpp>
10
11#include "BaseLib/Logging.h"
12#include "BaseLib/MPI.h"
16#include "MathLib/Point3d.h"
18
19namespace py = pybind11;
20
21using namespace MaterialPropertyLib;
22using namespace ParameterLib;
23
24namespace
25{
26// Convert NumPy array to Point3d if not None. This function is there to avoid
27// pulling the MeshLib::Point3d object into python, but use std::array (which
28// has all automatic conversions in pybind11).
29std::optional<MathLib::Point3d> pythonToSpatialPositionCoords(
30 py::object const& coordinates)
31{
32 if (coordinates.is_none())
33 {
34 return std::nullopt;
35 }
36 return std::make_optional<MathLib::Point3d>(
37 py::cast<std::array<double, 3>>(coordinates));
38}
39
40// Convert SpatialPositon to python array if coordinates are set. This function
41// is there to avoid pulling the MeshLib::Point3d object into python, but use
42// python arrays.
44{
45 auto const& opt_coords = pos.getCoordinates();
46 if (opt_coords)
47 {
48 return py::array_t<double>(3, opt_coords->data());
49 }
50 return py::none();
51}
52
53void bindSpatialPosition(py::module_& m)
54{
55 py::class_<SpatialPosition>(m, "SpatialPosition",
56 R"pbdoc(
57 Describes a spatial position within a mesh or domain.
58
59 A SpatialPosition may refer to a node (via node ID), an element (via element ID),
60 or a physical point in space (via coordinates). It is typically used when evaluating
61 material properties that depend on the spatial context within a simulation.
62
63 The position may be partially specified. For example:
64 - Only coordinates for geometric evaluation
65 - Only node ID for nodal properties
66 - Only element ID for element-wise values
67 - Or any combination of the above
68
69 The class is used as a parameter to property evaluations in the OGS material properties library.
70)pbdoc")
71 .def(py::init(
72 [](std::optional<std::size_t> node_id,
73 std::optional<std::size_t>
74 element_id,
75 py::object const& coordinates)
76 {
77 return SpatialPosition(
78 node_id, element_id,
80 }),
81 py::arg("node_id") = std::nullopt,
82 py::arg("element_id") = std::nullopt,
83 py::arg("coords") = py::none(),
84 R"pbdoc(
85 SpatialPosition(node_id=None, element_id=None, coords=None)
86
87 Parameters:
88 node_id (int, optional): Node ID
89 element_id (int, optional): Element ID
90 coords (array-like of 3 floats, optional): Coordinates
91 )pbdoc")
92
93 .def_property(
94 "node_id",
95 [](SpatialPosition const& pos) { return pos.getNodeID(); },
96 [](SpatialPosition& pos, std::size_t const id)
97 { pos.setNodeID(id); },
98 R"pbdoc(
99 Node ID of the spatial position.
100
101 This property can be read and set from Python. Setting to None is not supported.
102 )pbdoc")
103
104 .def_property(
105 "element_id",
106 [](SpatialPosition const& pos) { return pos.getElementID(); },
107 [](SpatialPosition& pos, std::size_t const id)
108 { pos.setElementID(id); },
109 R"pbdoc(
110 Element ID of the spatial position.
111
112 This property can be read and set from Python. Setting to None is not supported.
113 )pbdoc")
114
115 .def_property(
116 "coordinates",
118 [](SpatialPosition& pos, py::object const& coordinates)
119 {
120 pos.setCoordinates(
121 pythonToSpatialPositionCoords(coordinates).value());
122 },
123 R"pbdoc(
124 Coordinates of the spatial position as a 3-element array.
125
126 This property can be read and set from Python. Use a 3-element list, tuple,
127 or NumPy array. Setting to None is not supported.
128 )pbdoc")
129
130 .def(
131 "__repr__",
132 [](SpatialPosition const& pos)
133 {
134 auto const node_id =
135 pos.getNodeID() ? std::to_string(*pos.getNodeID()) : "None";
136 auto const element_id =
137 pos.getElementID() ? std::to_string(*pos.getElementID())
138 : "None";
139 auto const coords = spatialPositionCoordsToPython(pos);
140
141 return "<SpatialPosition(" + node_id + ", " + element_id +
142 ", " + py::str(coords).cast<std::string>() + ")>";
143 },
144 R"pbdoc(
145 Return string representation of the SpatialPosition.
146 )pbdoc");
147}
148
149void bindVariableArray(py::module_& m)
150{
151 py::class_<VariableArray>(m, "VariableArray")
152 .def(py::init<>())
153 .def_readwrite("capillary_pressure", &VariableArray::capillary_pressure)
154 .def_readwrite("concentration", &VariableArray::concentration)
155 .def_readwrite("deformation_gradient",
157 .def_readwrite("density", &VariableArray::density)
158 .def_readwrite("effective_pore_pressure",
160 .def_readwrite("enthalpy", &VariableArray::enthalpy)
161 .def_readwrite("enthalpy_of_evaporation",
163 .def_readwrite("equivalent_plastic_strain",
165 .def_readwrite("fracture_aperture", &VariableArray::fracture_aperture)
166 .def_readwrite("grain_compressibility",
168 .def_readwrite("liquid_phase_pressure",
170 .def_readwrite("liquid_saturation", &VariableArray::liquid_saturation)
171 .def_readwrite("mechanical_strain", &VariableArray::mechanical_strain)
172 .def_readwrite("molar_mass", &VariableArray::molar_mass)
173 .def_readwrite("molar_mass_derivative",
175 .def_readwrite("molar_fraction", &VariableArray::molar_fraction)
176 .def_readwrite("gas_phase_pressure", &VariableArray::gas_phase_pressure)
177 .def_readwrite("porosity", &VariableArray::porosity)
178 .def_readwrite("solid_grain_pressure",
180 .def_readwrite("stress", &VariableArray::stress)
181 .def_readwrite("temperature", &VariableArray::temperature)
182 .def_readwrite("total_strain", &VariableArray::total_strain)
183 .def_readwrite("total_stress", &VariableArray::total_stress)
184 .def_readwrite("transport_porosity", &VariableArray::transport_porosity)
185 .def_readwrite("vapour_pressure", &VariableArray::vapour_pressure)
186 .def_readwrite("volumetric_strain", &VariableArray::volumetric_strain);
187}
188
189void bindVariableEnum(py::module_& m)
190{
191 py::enum_<Variable>(m, "Variable")
192 .value("capillary_pressure", Variable::capillary_pressure)
193 .value("concentration", Variable::concentration)
194 .value("deformation_gradient", Variable::deformation_gradient)
195 .value("density", Variable::density)
196 .value("effective_pore_pressure", Variable::effective_pore_pressure)
197 .value("enthalpy", Variable::enthalpy)
198 .value("enthalpy_of_evaporation", Variable::enthalpy_of_evaporation)
199 .value("equivalent_plastic_strain", Variable::equivalent_plastic_strain)
200 .value("fracture_aperture", Variable::fracture_aperture)
201 .value("grain_compressibility", Variable::grain_compressibility)
202 .value("liquid_phase_pressure", Variable::liquid_phase_pressure)
203 .value("liquid_saturation", Variable::liquid_saturation)
204 .value("mechanical_strain", Variable::mechanical_strain)
205 .value("molar_mass", Variable::molar_mass)
206 .value("molar_mass_derivative", Variable::molar_mass_derivative)
207 .value("molar_fraction", Variable::molar_fraction)
208 .value("gas_phase_pressure", Variable::gas_phase_pressure)
209 .value("porosity", Variable::porosity)
210 .value("solid_grain_pressure", Variable::solid_grain_pressure)
211 .value("stress", Variable::stress)
212 .value("temperature", Variable::temperature)
213 .value("total_strain", Variable::total_strain)
214 .value("total_stress", Variable::total_stress)
215 .value("transport_porosity", Variable::transport_porosity)
216 .value("vapour_pressure", Variable::vapour_pressure)
217 .value("volumetric_strain", Variable::volumetric_strain)
218 .export_values();
219}
220
221void bindProperty(py::module_& m)
222{
223 py::class_<Property>(m, "Property", R"pbdoc(
224 Base class for material properties.
225 )pbdoc")
226 .def(
227 "value",
228 [](const Property& p, const VariableArray& va,
229 const SpatialPosition& pos, double t, double dt)
230 { return p.value(va, pos, t, dt); },
231 py::arg("variable_array"),
232 py::arg("pos"),
233 py::arg("t"),
234 py::arg("dt"),
235 R"pbdoc(
236 Evaluate the property value.
237
238 Parameters:
239 variable_array: Current time step values.
240 pos: Spatial position.
241 t: Current time.
242 dt: Time step size.
243 )pbdoc")
244
245 .def(
246 "value",
247 [](const Property& p, const VariableArray& va,
248 const VariableArray& vap, const SpatialPosition& pos, double t,
249 double dt) { return p.value(va, vap, pos, t, dt); },
250 py::arg("variable_array"),
251 py::arg("variable_array_prev"),
252 py::arg("pos"),
253 py::arg("t"),
254 py::arg("dt"),
255 R"pbdoc(
256 Evaluate the property value with previous time step data.
257
258 Parameters:
259 variable_array: Current time step values.
260 variable_array_prev: Previous time step values.
261 pos: Spatial position.
262 t: Current time.
263 dt: Time step size.
264 )pbdoc")
265
266 .def(
267 "dValue",
268 [](const Property& p, const VariableArray& va,
269 const VariableArray& vap, Variable var,
270 const SpatialPosition& pos, double t, double dt)
271 { return p.dValue(va, vap, var, pos, t, dt); },
272 py::arg("variable_array"),
273 py::arg("variable_array_prev"),
274 py::arg("variable"),
275 py::arg("pos"),
276 py::arg("t"),
277 py::arg("dt"),
278 R"pbdoc(
279 Evaluate the derivative of the property with respect to a variable,
280 using previous time step data.
281
282 Parameters:
283 variable_array: Current time step values.
284 variable_array_prev: Previous time step values.
285 variable: Variable to differentiate with respect to.
286 pos: Spatial position.
287 t: Current time.
288 dt: Time step size.
289 )pbdoc")
290
291 .def(
292 "dValue",
293 [](const Property& p, const VariableArray& va, Variable var,
294 const SpatialPosition& pos, double t, double dt)
295 { return p.dValue(va, var, pos, t, dt); },
296 py::arg("variable_array"),
297 py::arg("variable"),
298 py::arg("pos"),
299 py::arg("t"),
300 py::arg("dt"),
301 R"pbdoc(
302 Evaluate the derivative of the property with respect to a variable.
303
304 Parameters:
305 variable_array: Current time step values.
306 variable: Variable to differentiate with respect to.
307 pos: Spatial position.
308 t: Current time.
309 dt: Time step size.
310 )pbdoc");
311}
312
313void bindConstant(py::module_& m)
314{
315 py::class_<Constant, Property>(m, "Constant", R"pbdoc(
316 Constant property with a fixed value.
317 )pbdoc")
318 .def(py::init<std::string, PropertyDataType>(),
319 py::arg("name"),
320 py::arg("value"),
321 R"pbdoc(
322 Construct a Constant property.
323
324 Parameters:
325 name: Name of the property.
326 value: Constant value.
327 )pbdoc");
328}
329
330void bindLinear(py::module_& m)
331{
332 py::class_<Linear, Property>(m, "Linear", R"pbdoc(
333 Linearly interpolated property dependent on independent variables.
334 )pbdoc")
335 .def(py::init(
336 [](std::string name, PropertyDataType reference_value,
337 std::vector<std::tuple<Variable, VariableType,
338 VariableType>> const& ivs)
339 {
340 auto makeIV = [](std::tuple<Variable, VariableType,
341 VariableType> const& iv)
342 { return std::make_from_tuple<IndependentVariable>(iv); };
343
344 auto independent_variables =
345 ivs | ranges::views::transform(makeIV) |
346 ranges::to<std::vector>();
347 return std::make_unique<Linear>(name, reference_value,
348 independent_variables);
349 }),
350 py::arg("name"),
351 py::arg("reference_value"),
352 py::arg("independent_variables"),
353 R"pbdoc(
354 Construct a Linear property.
355
356 Parameters:
357 name: Name of the property.
358 reference_value: Base property value.
359 independent_variables: List of tuples (variable, reference condition, slope), each defining a linear dependency on a variable.
360 )pbdoc");
361}
362} // namespace
363
365{
366#ifndef NDEBUG
368#else // NDEBUG
370#endif // NDEBUG
371
372 m.attr("__name__") = "ogs.mpl";
373 m.doc() = "pybind11 ogs MPL bindings";
374
378 bindProperty(m);
379 bindConstant(m);
380 bindLinear(m);
381}
virtual PropertyDataType value() const
virtual PropertyDataType dValue(VariableArray const &variable_array, VariableArray const &variable_array_prev, Variable const variable, ParameterLib::SpatialPosition const &pos, double const t, double const dt) const
DeformationGradient deformation_gradient
std::optional< std::size_t > getNodeID() const
void setNodeID(std::size_t node_id)
std::optional< std::size_t > getElementID() const
void setCoordinates(MathLib::Point3d const &coordinates)
void setElementID(std::size_t element_id)
std::optional< MathLib::Point3d > const getCoordinates() const
void initOGSLogger(std::string const &log_level)
Definition Logging.cpp:56
std::variant< std::monostate, double, Eigen::Vector< double, 4 >, Eigen::Vector< double, 5 >, Eigen::Vector< double, 6 >, Eigen::Vector< double, 9 > > VariableType
std::variant< double, Eigen::Matrix< double, 2, 1 >, Eigen::Matrix< double, 3, 1 >, Eigen::Matrix< double, 2, 2 >, Eigen::Matrix< double, 3, 3 >, Eigen::Matrix< double, 4, 1 >, Eigen::Matrix< double, 6, 1 >, Eigen::MatrixXd > PropertyDataType
void bindSpatialPosition(py::module_ &m)
Definition ogs_mpl.cpp:53
void bindConstant(py::module_ &m)
Definition ogs_mpl.cpp:238
void bindLinear(py::module_ &m)
Definition ogs_mpl.cpp:247
void bindVariableArray(py::module_ &m)
Definition ogs_mpl.cpp:113
void bindVariableEnum(py::module_ &m)
Definition ogs_mpl.cpp:153
std::optional< MathLib::Point3d > pythonToSpatialPositionCoords(py::object const &coordinates)
Definition ogs_mpl.cpp:29
py::object spatialPositionCoordsToPython(SpatialPosition const &pos)
Definition ogs_mpl.cpp:43
void bindProperty(py::module_ &m)
Definition ogs_mpl.cpp:185
PYBIND11_MODULE(mpl, m)
Definition ogs_mpl.cpp:364