From f49b1a2a3f4187ea3f35ffb3810c9e24a6d93de0 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Mon, 16 Jun 2025 12:23:37 -0400 Subject: [PATCH] docs(pythonInterface): added docs to serif.mfem and serif.polytrope --- .../Coefficient/PyCoefficient.h | 143 ++++++++- .../Operator/Matrix/PyMatrix.h | 216 +++++++++++++- .../PyMFEMTrampolines/Operator/PyOperator.h | 174 ++++++++++- src/python/mfem/bindings.h | 276 +++++++++++++++++- src/python/polytrope/bindings.h | 26 ++ 5 files changed, 823 insertions(+), 12 deletions(-) diff --git a/src/python/mfem/Trampoline/PyMFEMTrampolines/Coefficient/PyCoefficient.h b/src/python/mfem/Trampoline/PyMFEMTrampolines/Coefficient/PyCoefficient.h index f4d160e..ee83a91 100644 --- a/src/python/mfem/Trampoline/PyMFEMTrampolines/Coefficient/PyCoefficient.h +++ b/src/python/mfem/Trampoline/PyMFEMTrampolines/Coefficient/PyCoefficient.h @@ -1,3 +1,11 @@ +/** + * @file PyCoefficient.h + * @brief Defines pybind11 trampoline classes for mfem::Coefficient and mfem::VectorCoefficient. + * + * These trampoline classes allow Python classes to inherit from mfem::Coefficient + * and mfem::VectorCoefficient, enabling Python-defined coefficients to be used + * within the C++ MFEM library. + */ #include #include #include // Needed for std::function @@ -8,20 +16,147 @@ namespace py = pybind11; using namespace mfem; +/** + * @namespace serif::pybind + * @brief Contains pybind11 helper classes and trampoline classes for interfacing C++ with Python. + */ namespace serif::pybind { - // --- Trampoline for the abstract Coefficient base class --- + /** + * @brief Trampoline class for mfem::Coefficient. + * + * This class allows Python classes to inherit from mfem::Coefficient and override + * its virtual methods. This is essential for creating custom coefficients in Python + * that can be used by MFEM's C++ backend. + * + * @see mfem::Coefficient + * + * @par Python Usage Example: + * @code{.py} + * import mfem.ser_ext as mfem + * + * class MyPythonCoefficient(mfem.Coefficient): + * def __init__(self): + * super().__init__() # Call the base C++ constructor + * + * def Eval(self, T, ip): + * # T is an mfem.ElementTransformation + * # ip is an mfem.IntegrationPoint + * # Example: return a constant value + * return 1.0 + * + * def SetTime(self, t): + * # Optionally handle time-dependent coefficients + * super().SetTime(t) # Call base class method + * print(f"Time set to: {t}") + * + * # Using the Python coefficient + * py_coeff = MyPythonCoefficient() + * # py_coeff can now be passed to MFEM functions expecting an mfem::Coefficient + * @endcode + */ class PyCoefficient : public Coefficient { public: - using Coefficient::Coefficient; + using Coefficient::Coefficient; /**< Inherit constructors from mfem::Coefficient. */ + + /** + * @brief Evaluate the coefficient at a given IntegrationPoint in an ElementTransformation. + * + * This method is called by MFEM when the value of the coefficient is needed. + * If a Python class inherits from PyCoefficient, it *must* override this method. + * + * @param T The element transformation. + * @param ip The integration point. + * @return The value of the coefficient at the given point. + * + * @note This method forwards the call to the Python override. + * PYBIND11_OVERRIDE_PURE is used in the .cpp file to handle this. + */ real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override; + + /** + * @brief Set the current time for time-dependent coefficients. + * + * This method is called by MFEM to update the time for time-dependent coefficients. + * Python classes inheriting from PyCoefficient can override this method to implement + * time-dependent behavior. + * + * @param t The current time. + * + * @note This method forwards the call to the Python override if one exists. + * PYBIND11_OVERRIDE is used in the .cpp file to handle this. + */ void SetTime(real_t t) override; }; - // --- Trampoline for the abstract VectorCoefficient base class --- + /** + * @brief Trampoline class for mfem::VectorCoefficient. + * + * This class allows Python classes to inherit from mfem::VectorCoefficient and override + * its virtual methods. This is essential for creating custom vector-valued coefficients + * in Python that can be used by MFEM's C++ backend. + * + * @see mfem::VectorCoefficient + * + * @par Python Usage Example: + * @code{.py} + * import mfem.ser_ext as mfem + * import numpy as np + * + * class MyPythonVectorCoefficient(mfem.VectorCoefficient): + * def __init__(self, dim): + * super().__init__(dim) # Call the base C++ constructor, pass vector dimension + * self.dim = dim + * + * def Eval(self, V, T, ip): + * # V is an mfem.Vector (output parameter, must be filled) + * # T is an mfem.ElementTransformation + * # ip is an mfem.IntegrationPoint + * # Example: return a constant vector [1.0, 2.0, ...] + * for i in range(self.dim): + * V[i] = float(i + 1) + * + * def SetTime(self, t): + * super().SetTime(t) + * print(f"VectorCoefficient time set to: {t}") + * + * # Using the Python vector coefficient + * vec_dim = 2 + * py_vec_coeff = MyPythonVectorCoefficient(vec_dim) + * # py_vec_coeff can now be passed to MFEM functions expecting an mfem::VectorCoefficient + * @endcode + */ class PyVectorCoefficient : public VectorCoefficient { public: - using VectorCoefficient::VectorCoefficient; + using VectorCoefficient::VectorCoefficient; /**< Inherit constructors from mfem::VectorCoefficient. */ + + /** + * @brief Evaluate the vector coefficient at a given IntegrationPoint in an ElementTransformation. + * + * This method is called by MFEM when the value of the vector coefficient is needed. + * If a Python class inherits from PyVectorCoefficient, it *must* override this method. + * The result should be stored in the output Vector @p V. + * + * @param V Output vector to store the result. Its size should match the coefficient's dimension. + * @param T The element transformation. + * @param ip The integration point. + * + * @note This method forwards the call to the Python override. + * PYBIND11_OVERRIDE_PURE is used in the .cpp file to handle this. + */ void Eval(Vector &V, ElementTransformation &T, const IntegrationPoint &ip) override; + + /** + * @brief Set the current time for time-dependent vector coefficients. + * + * This method is called by MFEM to update the time for time-dependent vector coefficients. + * Python classes inheriting from PyVectorCoefficient can override this method to implement + * time-dependent behavior. + * + * @param t The current time. + * + * @note This method forwards the call to the Python override if one exists. + * PYBIND11_OVERRIDE is used in the .cpp file to handle this. + */ void SetTime(real_t t) override; }; } \ No newline at end of file diff --git a/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/Matrix/PyMatrix.h b/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/Matrix/PyMatrix.h index 2007fc7..ad11f6e 100644 --- a/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/Matrix/PyMatrix.h +++ b/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/Matrix/PyMatrix.h @@ -1,3 +1,11 @@ +/** + * @file PyMatrix.h + * @brief Defines a pybind11 trampoline class for mfem::Matrix. + * + * This trampoline class allows Python classes to inherit from mfem::Matrix, + * enabling Python-defined matrices to be used within the C++ MFEM library, + * including overriding methods from its base class mfem::Operator. + */ #pragma once #include "mfem.hpp" @@ -9,7 +17,103 @@ namespace serif::pybind { * @brief A trampoline class for mfem::Matrix. * * This class allows Python classes to inherit from mfem::Matrix and correctly - * override its virtual functions, including those inherited from mfem::Operator. + * override its virtual functions, including those inherited from its base, + * mfem::Operator. This is useful for creating custom matrix types in Python + * that can interact seamlessly with MFEM's C++ components. + * + * @see mfem::Matrix + * @see mfem::Operator + * @see PyOperator + * + * @par Python Usage Example: + * @code{.py} + * import mfem.ser_ext as mfem + * import numpy as np + * + * class MyPythonMatrix(mfem.Matrix): + * def __init__(self, size=0): + * super().__init__(size) # Call base C++ constructor + * if size > 0: + * # Example: Initialize with some data if size is provided + * # Note: Direct data manipulation might be complex due to + * # C++ ownership. This example is conceptual. + * # For a real-world scenario, you might manage data in Python + * # and use Eval or Mult to reflect its state. + * self._py_data = np.zeros((size, size)) + * else: + * self._py_data = None + * + * # --- mfem.Matrix pure virtual overrides --- + * def Elem(self, i, j): + * # This is problematic for direct override for write access + * # due to returning a reference. + * # Typically, you'd manage data and use it in Mult/GetRow etc. + * # For read access (const version), it's more straightforward. + * if self._py_data is not None: + * return self._py_data[i, j] + * raise IndexError("Matrix data not initialized or index out of bounds") + * + * # const Elem version for read access + * # def GetElem(self, i, j): # A helper in Python if Elem is tricky + * # return self._py_data[i,j] + * + * def Inverse(self): + * # Return an mfem.MatrixInverse object + * # This is a simplified example; a real inverse is complex. + * print("MyPythonMatrix.Inverse() called, returning dummy inverse.") + * # For a real implementation, you'd compute and return an actual + * # mfem.MatrixInverse or a Python-derived version of it. + * # This might involve creating a new PyMatrix representing the inverse. + * identity_inv = mfem.DenseMatrix(self.Width()) + * for i in range(self.Width()): + * identity_inv[i,i] = 1.0 + * return mfem.MatrixInverse(identity_inv) # Example + * + * # --- mfem.Matrix regular virtual overrides --- + * def Finalize(self, skip_zeros=1): + * super().Finalize(skip_zeros) # Call base + * print(f"MyPythonMatrix.Finalize({skip_zeros}) called.") + * + * # --- mfem.Operator virtual overrides --- + * def Mult(self, x, y): + * # x is mfem.Vector (input), y is mfem.Vector (output) + * if self._py_data is not None: + * x_np = x.GetDataArray() + * y_np = np.dot(self._py_data, x_np) + * y.SetSize(self._py_data.shape[0]) + * y.Assign(y_np) + * else: + * # Fallback to base class if no Python data + * # super().Mult(x,y) # This might not work as expected if base is abstract + * # or if the C++ mfem.Matrix itself doesn't have data. + * # For a sparse matrix, this would call its sparse Mult. + * # For a dense matrix, it would use its data. + * # If this PyMatrix is purely Python defined, it must implement Mult. + * raise NotImplementedError("Mult not implemented or data not set") + * print("MyPythonMatrix.Mult called.") + * + * # Using the Python Matrix + * mat_size = 3 + * py_mat = MyPythonMatrix(mat_size) + * py_mat._py_data = np.array([[1,2,3],[4,5,6],[7,8,9]], dtype=float) + * + * x_vec = mfem.Vector(mat_size) + * x_vec.Assign(np.array([1,1,1], dtype=float)) + * y_vec = mfem.Vector(mat_size) + * + * py_mat.Mult(x_vec, y_vec) + * print("Result y:", y_vec.GetDataArray()) + * + * # inv_op = py_mat.Inverse() # Be cautious with dummy implementations + * # print("Inverse type:", type(inv_op)) + * + * # Note: Overriding Elem(i,j) to return a C++ reference from Python + * # for write access (mfem::real_t&) is complex and often not directly feasible + * # in a straightforward way with pybind11 for typical Python data structures. + * # Python users would typically interact via methods like Set, Add, or by + * # providing data that the C++ side can access (e.g., via GetData). + * # The const version of Elem (read-only) is more manageable. + * @endcode */ class PyMatrix : public mfem::Matrix { public: @@ -17,24 +121,132 @@ namespace serif::pybind { using mfem::Matrix::Matrix; // --- Trampolines for new mfem::Matrix pure virtual methods --- + /** + * @brief Access element (i,j) for read/write. + * Pure virtual in mfem::Matrix. Must be overridden in Python. + * @param i Row index. + * @param j Column index. + * @return Reference to the matrix element. + * @note Returning a C++ reference from Python for write access can be complex. + * Consider alternative data management strategies in Python. + * PYBIND11_OVERRIDE_PURE is used in the .cpp file. + */ mfem::real_t& Elem(int i, int j) override; + + /** + * @brief Access element (i,j) for read-only. + * Pure virtual in mfem::Matrix. Must be overridden in Python. + * @param i Row index. + * @param j Column index. + * @return Const reference to the matrix element. + * @note PYBIND11_OVERRIDE_PURE is used in the .cpp file. + */ const mfem::real_t& Elem(int i, int j) const override; + + /** + * @brief Get the inverse of the matrix. + * Pure virtual in mfem::Matrix. Must be overridden in Python. + * The caller is responsible for deleting the returned MatrixInverse object. + * @return Pointer to an mfem::MatrixInverse object. + * @note PYBIND11_OVERRIDE_PURE is used in the .cpp file. + */ mfem::MatrixInverse* Inverse() const override; // --- Trampoline for new mfem::Matrix regular virtual methods --- - void Finalize(int) override; + /** + * @brief Finalize matrix assembly. + * For sparse matrices, this typically involves finalizing the sparse structure. + * Can be overridden in Python. + * @param skip_zeros See mfem::SparseMatrix::Finalize documentation. + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ + void Finalize(int skip_zeros) override; // --- Trampolines for inherited mfem::Operator virtual methods --- // These must be repeated here to allow Python classes inheriting from // Matrix to override methods originally from the Operator base class. + + /** + * @brief Perform the operator action: y = A*x. + * Inherited from mfem::Operator. Can be overridden in Python. + * If not overridden, mfem::Matrix's default implementation is used. + * @param x The input vector. + * @param y The output vector (result of A*x). + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ void Mult(const mfem::Vector &x, mfem::Vector &y) const override; + + /** + * @brief Perform the transpose operator action: y = A^T*x. + * Inherited from mfem::Operator. Can be overridden in Python. + * @param x The input vector. + * @param y The output vector (result of A^T*x). + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ void MultTranspose(const mfem::Vector &x, mfem::Vector &y) const override; + + /** + * @brief Perform the action y += a*(A*x). + * Inherited from mfem::Operator. Can be overridden in Python. + * @param x The input vector. + * @param y The vector to which a*(A*x) is added. + * @param a Scalar multiplier (defaults to 1.0). + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ void AddMult(const mfem::Vector &x, mfem::Vector &y, const mfem::real_t a = 1.0) const override; + + /** + * @brief Perform the action y += a*(A^T*x). + * Inherited from mfem::Operator. Can be overridden in Python. + * @param x The input vector. + * @param y The vector to which a*(A^T*x) is added. + * @param a Scalar multiplier (defaults to 1.0). + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ void AddMultTranspose(const mfem::Vector &x, mfem::Vector &y, const mfem::real_t a = 1.0) const override; + + /** + * @brief Get the gradient operator (Jacobian) at a given point x. + * Inherited from mfem::Operator. Can be overridden in Python. + * For a linear matrix operator, the gradient is typically the matrix itself. + * @param x The point at which to evaluate the gradient (often unused for linear operators). + * @return A reference to the gradient operator. + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ mfem::Operator& GetGradient(const mfem::Vector &x) const override; + + /** + * @brief Assemble the diagonal of the operator. + * Inherited from mfem::Operator. Can be overridden in Python. + * @param diag Output vector to store the diagonal entries. + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ void AssembleDiagonal(mfem::Vector &diag) const override; + + /** + * @brief Get the prolongation operator. + * Inherited from mfem::Operator. Can be overridden in Python. + * @return A const pointer to the prolongation operator, or nullptr if not applicable. + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ const mfem::Operator* GetProlongation() const override; + + /** + * @brief Get the restriction operator. + * Inherited from mfem::Operator. Can be overridden in Python. + * @return A const pointer to the restriction operator, or nullptr if not applicable. + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ const mfem::Operator* GetRestriction() const override; + + /** + * @brief Recover the FEM solution. + * Inherited from mfem::Operator. Can be overridden in Python. + * @param X The reduced solution vector. + * @param b The right-hand side vector. + * @param x Output vector for the full FEM solution. + * @note PYBIND11_OVERRIDE is used in the .cpp file. + */ void RecoverFEMSolution(const mfem::Vector &X, const mfem::Vector &b, mfem::Vector &x) override; }; diff --git a/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/PyOperator.h b/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/PyOperator.h index e10d242..ba8ca52 100644 --- a/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/PyOperator.h +++ b/src/python/mfem/Trampoline/PyMFEMTrampolines/Operator/PyOperator.h @@ -1,3 +1,10 @@ +/** + * @file PyOperator.h + * @brief Defines a pybind11 trampoline class for mfem::Operator. + * + * This trampoline class allows Python classes to inherit from mfem::Operator, + * enabling Python-defined operators to be used within the C++ MFEM library. + */ #pragma once #include "mfem.hpp" @@ -6,13 +13,72 @@ namespace py = pybind11; +/** + * @namespace serif::pybind + * @brief Contains pybind11 helper classes and trampoline classes for interfacing C++ with Python. + */ namespace serif::pybind { /** - * @brief A trampoline class for mfem::Operator. - * * This class allows Python classes to inherit from mfem::Operator and correctly + * @brief Trampoline class for mfem::Operator. + * + * This class allows Python classes to inherit from mfem::Operator and correctly * override its virtual functions. When a virtual function is called from C++, * the trampoline ensures the call is forwarded to the Python implementation if one exists. + * This is crucial for integrating Python-defined linear or non-linear operators + * into MFEM's C++-based solvers and algorithms. + * + * @see mfem::Operator + * + * @par Python Usage Example: + * @code{.py} + * import mfem.ser_ext as mfem + * import numpy as np + * + * class MyPythonOperator(mfem.Operator): + * def __init__(self, size): + * super().__init__(size) # Call the base C++ constructor + * # Or super().__init__() if default constructing and setting height/width later + * self.matrix = np.random.rand(size, size) # Example: store a dense matrix + * + * # Must override Mult + * def Mult(self, x, y): + * # x is an mfem.Vector (input) + * # y is an mfem.Vector (output, y = A*x) + * # Ensure y is correctly sized if not already + * if y.Size() != self.Height(): + * y.SetSize(self.Height()) + * + * # Example: y = self.matrix * x + * # This is a conceptual illustration. For actual matrix-vector products + * # with numpy, you'd convert mfem.Vector to numpy array or iterate. + * x_np = x.GetDataArray() # Get a numpy view if configured + * y_np = np.dot(self.matrix, x_np) + * y.Assign(y_np) # Assign result back to mfem.Vector + * print("MyPythonOperator.Mult called") + * + * # Optionally override other methods like MultTranspose, GetGradient, etc. + * def MultTranspose(self, x, y): + * if y.Size() != self.Width(): + * y.SetSize(self.Width()) + * # Example: y = self.matrix.T * x + * x_np = x.GetDataArray() + * y_np = np.dot(self.matrix.T, x_np) + * y.Assign(y_np) + * print("MyPythonOperator.MultTranspose called") + * + * # Using the Python operator + * op_size = 5 + * py_op = MyPythonOperator(op_size) + * + * x_vec = mfem.Vector(op_size) + * x_vec.Assign(np.arange(op_size, dtype=float)) + * y_vec = mfem.Vector(op_size) + * + * py_op.Mult(x_vec, y_vec) + * print("Result y:", y_vec.GetDataArray()) + * # py_op can now be passed to MFEM functions expecting an mfem::Operator + * @endcode */ class PyOperator : public mfem::Operator { public: @@ -22,25 +88,123 @@ namespace serif::pybind { // --- Trampoline declarations for all overridable virtual functions --- - // Pure virtual function (MANDATORY override) + /** + * @brief Perform the operator action: y = A*x. + * + * This is a pure virtual function in mfem::Operator and **must** be overridden + * by any Python class inheriting from PyOperator. + * + * @param x The input vector. + * @param y The output vector (result of A*x). + * @note This method forwards the call to the Python override. + * PYBIND11_OVERRIDE_PURE is used in the .cpp file to handle this. + */ void Mult(const mfem::Vector &x, mfem::Vector &y) const override; - // Regular virtual functions (RECOMMENDED overrides) + /** + * @brief Perform the transpose operator action: y = A^T*x. + * + * Optional override. If not overridden, MFEM's base implementation + * (which may raise an error or be a no-op) will be used. + * + * @param x The input vector. + * @param y The output vector (result of A^T*x). + * @note This method forwards the call to the Python override if one exists. + * PYBIND11_OVERRIDE is used in the .cpp file to handle this. + */ void MultTranspose(const mfem::Vector &x, mfem::Vector &y) const override; + /** + * @brief Perform the action y += a*(A*x). + * + * Optional override. + * + * @param x The input vector. + * @param y The vector to which a*(A*x) is added. + * @param a Scalar multiplier (defaults to 1.0). + * @note This method forwards the call to the Python override if one exists. + */ void AddMult(const mfem::Vector &x, mfem::Vector &y, const mfem::real_t a = 1.0) const override; + /** + * @brief Perform the action y += a*(A^T*x). + * + * Optional override. + * + * @param x The input vector. + * @param y The vector to which a*(A^T*x) is added. + * @param a Scalar multiplier (defaults to 1.0). + * @note This method forwards the call to the Python override if one exists. + */ void AddMultTranspose(const mfem::Vector &x, mfem::Vector &y, const mfem::real_t a = 1.0) const override; + /** + * @brief Get the gradient operator (Jacobian) at a given point x. + * + * For non-linear operators, this method should return the linearization (Jacobian) + * of the operator at the point `x`. The returned Operator is owned by this + * Operator and should not be deleted by the caller. + * Optional override. + * + * @param x The point at which to evaluate the gradient. + * @return A reference to the gradient operator. + * @note This method forwards the call to the Python override if one exists. + */ Operator& GetGradient(const mfem::Vector &x) const override; + /** + * @brief Assemble the diagonal of the operator. + * + * For discrete operators (e.g., matrices), this method should compute and store + * the diagonal entries of the operator in the vector `diag`. + * Optional override. + * + * @param diag Output vector to store the diagonal entries. + * @note This method forwards the call to the Python override if one exists. + */ void AssembleDiagonal(mfem::Vector &diag) const override; + /** + * @brief Get the prolongation operator. + * + * Used in multilevel methods (e.g., AMG). Returns a pointer to the prolongation + * operator (interpolation from a coarser level to this level). + * The returned Operator is typically owned by this Operator. + * Optional override. + * + * @return A const pointer to the prolongation operator, or nullptr if not applicable. + * @note This method forwards the call to the Python override if one exists. + */ const mfem::Operator* GetProlongation() const override; + /** + * @brief Get the restriction operator. + * + * Used in multilevel methods (e.g., AMG). Returns a pointer to the restriction + * operator (projection from this level to a coarser level). + * Typically, this is the transpose of the prolongation operator. + * The returned Operator is typically owned by this Operator. + * Optional override. + * + * @return A const pointer to the restriction operator, or nullptr if not applicable. + * @note This method forwards the call to the Python override if one exists. + */ const mfem::Operator* GetRestriction() const override; + /** + * @brief Recover the FEM solution. + * + * For operators that are part of a system solve (e.g., static condensation), + * this method can be used to reconstruct the full finite element solution `x` + * from a reduced solution `X` and the right-hand side `b`. + * Optional override. + * + * @param X The reduced solution vector. + * @param b The right-hand side vector. + * @param x Output vector for the full FEM solution. + * @note This method forwards the call to the Python override if one exists. + */ void RecoverFEMSolution(const mfem::Vector &X, const mfem::Vector &b, mfem::Vector &x) override; }; -} // namespace serif::pybind::mfem +} // namespace serif::pybind \ No newline at end of file diff --git a/src/python/mfem/bindings.h b/src/python/mfem/bindings.h index 4add1fb..83e2e98 100644 --- a/src/python/mfem/bindings.h +++ b/src/python/mfem/bindings.h @@ -1,33 +1,307 @@ +/** + * @file bindings.h + * @brief Declares functions to register MFEM core library components with pybind11. + * + * This header file lists the functions responsible for creating Python bindings + * for various parts of the MFEM library. Each function typically registers + * a set of related classes, enums, or functionalities to a pybind11::module, + * which is expected to be a submodule named `mfem` within the main `serif` Python module. + * + * @see /Users/tboudreaux/Programming/SERiF/src/python/bindings.cpp for how these are used. + */ #pragma once #include +/** + * @brief Registers all core MFEM bindings to the given Python submodule. + * + * This function serves as the main entry point for exposing MFEM functionalities + * to Python. It calls various other `register_*_bindings` and `bind_*_enum` + * functions to populate the `mfem_submodule`. + * + * @param mfem_submodule The pybind11 module (typically `serif.mfem`) to which + * MFEM bindings will be added. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # Now mfem.Operator, mfem.Vector, mfem.Mesh, etc., are accessible. + * vec = mfem.Vector(10) + * print(vec.Size()) + * @endcode + */ void register_mfem_bindings(pybind11::module &mfem_submodule); +/** + * @brief Registers mfem::Operator and related classes. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # Assuming PyOperator trampoline is bound + * # op = mfem.Operator() # Or a derived class like mfem.DenseMatrix + * @endcode + */ void register_operator_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::Matrix and its derived classes (e.g., mfem::DenseMatrix, mfem::SparseMatrix). + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * mat = mfem.DenseMatrix(2, 2) + * mat[0,0] = 1.0 + * mat[0,1] = 2.0 + * mat[1,0] = 3.0 + * mat[1,1] = 4.0 + * mat.Print() + * @endcode + */ void register_matrix_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::Vector. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * vec = mfem.Vector(5) + * vec[0] = 1.5 + * print(vec.Size(), vec[0]) + * @endcode + */ void register_vector_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::Array. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * arr_int = mfem.intArray(5) # Assuming intArray is a typedef or specific binding + * arr_int[0] = 10 + * print(arr_int.Size(), arr_int[0]) + * @endcode + */ void register_array_bindings(pybind11::module &mfem_submodule); +/** + * @brief Binds the mfem::AssemblyLevel enum. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # level = mfem.AssemblyLevel.LEGACY # Or other enum values + * # print(level) + * @endcode + */ void bind_assembly_level_enum(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::BilinearForm and related functionalities. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # Assuming FiniteElementSpace (fes) is created + * # bform = mfem.BilinearForm(fes) + * # bform.AddDomainIntegrator(mfem.MassIntegrator()) # Assuming MassIntegrator is bound + * # bform.Assemble() + * # A = bform.SpMat() + * @endcode + */ void register_bilinear_form_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::MixedBilinearForm and related functionalities. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # Assuming trial_fes and test_fes are FiniteElementSpaces + * # mbform = mfem.MixedBilinearForm(trial_fes, test_fes) + * # mbform.AddDomainIntegrator(mfem.VectorFEMassIntegrator()) # Example + * # mbform.Assemble() + * @endcode + */ void register_mixed_bilinear_form_bindings(pybind11::module &mfem_submodule); +/** + * @brief Registers mfem::Table. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * table = mfem.Table() + * # ... use table methods ... + * @endcode + */ void register_table_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::Mesh. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * mesh = mfem.Mesh.MakeCartesian1D(10) # Example constructor + * print(mesh.Dimension(), mesh.GetNE()) + * @endcode + */ void register_mesh_bindings(pybind11::module &mfem_submodule); +/** + * @brief Registers mfem::BasisType enum and related constants. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * basis_type = mfem.BasisType.GaussLobatto + * print(basis_type) + * @endcode + */ void register_basis_type_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::FiniteElementCollection base class. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # fec = mfem.FiniteElementCollection() # Typically use derived classes + * @endcode + */ void register_finite_element_collection_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::H1_FECollection. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * order = 1 + * dim = 2 + * fec = mfem.H1_FECollection(order, dim) + * print(fec.GetName()) + * @endcode + */ void register_H1_FECollection_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::RT_FECollection. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * order = 1 + * dim = 2 + * fec = mfem.RT_FECollection(order-1, dim) # RT_FECollection uses p-1 for order p + * print(fec.GetName()) + * @endcode + */ void register_RT_FECollection_bindings(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::ND_FECollection (Nedelec finite elements). + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * order = 1 + * dim = 3 + * fec = mfem.ND_FECollection(order, dim) + * print(fec.GetName()) + * @endcode + */ void register_ND_FECollection_bindings(pybind11::module &mfem_submodule); +/** + * @brief Binds the mfem::Ordering::Type enum. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * ordering = mfem.Ordering.byNODES + * print(ordering) + * @endcode + */ void bind_ordering_enum(pybind11::module &mfem_submodule); + +/** + * @brief Registers mfem::FiniteElementSpace. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * mesh = mfem.Mesh.MakeCartesian1D(5) + * fec = mfem.H1_FECollection(1, mesh.Dimension()) + * fes = mfem.FiniteElementSpace(mesh, fec) + * print(fes.GetNDofs()) + * @endcode + */ void register_finite_element_space_bindings(pybind11::module &mfem_submodule); - +/** + * @brief Registers mfem::Coefficient, mfem::VectorCoefficient and related classes/trampolines. + * @param m The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * const_coeff = mfem.ConstantCoefficient(2.5) + * # vec_coeff = mfem.VectorConstantCoefficient(mfem.Vector([1.0, 2.0])) + * + * # Using a Python-derived coefficient (if PyCoefficient trampoline is bound) + * class MyCoeff(mfem.Coefficient): + * def Eval(self, T, ip): + * return 1.0 + * my_c = MyCoeff() + * @endcode + */ void register_coefficient_bindings(pybind11::module &m); + +/** + * @brief Registers mfem::ElementTransformation. + * @param m The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # ElementTransformation objects are usually obtained from Mesh or FiniteElementSpace + * # mesh = mfem.Mesh.MakeCartesian1D(1) + * # el_trans = mesh.GetElementTransformation(0) + * # print(el_trans.ElementNo) + * @endcode + */ void register_eltrans_bindings(pybind11::module &m); + +/** + * @brief Registers mfem::IntegrationRule and mfem::IntegrationPoint. + * @param m The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * # Get a standard integration rule + * ir = mfem.IntRules.Get(mfem.Geometry.SEGMENT, 3) # Order 3 for a segment + * for i in range(ir.GetNPoints()): + * ip = ir.IntPoint(i) + * # print(f"Point {i}: coords {ip.x}, weight {ip.weight}") + * @endcode + */ void register_intrule_bindings(pybind11::module &m); +/** + * @brief Registers mfem::GridFunction. + * @param mfem_submodule The `serif.mfem` Python submodule. + * @par Python Usage Example: + * @code{.py} + * import serif.mfem as mfem + * mesh = mfem.Mesh.MakeCartesian1D(5) + * fec = mfem.H1_FECollection(1, mesh.Dimension()) + * fes = mfem.FiniteElementSpace(mesh, fec) + * gf = mfem.GridFunction(fes) + * gf.Assign(0.0) # Set all values to 0 + * print(gf.Size()) + * @endcode + */ void register_grid_function_bindings(pybind11::module &mfem_submodule); + diff --git a/src/python/polytrope/bindings.h b/src/python/polytrope/bindings.h index 5c2283f..ef17094 100644 --- a/src/python/polytrope/bindings.h +++ b/src/python/polytrope/bindings.h @@ -1,5 +1,31 @@ +/** + * @file bindings.h + * @brief Declares the function to register polytrope module C++ components with pybind11. + * + * This file contains the declaration for `register_polytrope_bindings`, which is responsible + * for creating Python bindings for classes and functions within the `serif::polytrope` C++ + * namespace. These bindings will be accessible in Python under the `serif.polytrope` submodule. + */ #pragma once #include +/** + * @brief Registers C++ classes and functions from the `serif::polytrope` namespace to Python. + * + * This function takes a pybind11::module object, representing the `serif.polytrope` Python submodule, + * and adds bindings for various components like `PolytropeOperator`, `PolySolver`, etc. + * This allows these C++ components to be instantiated and used directly from Python. + * + * @param polytrope_submodule The pybind11 module (typically `serif.polytrope`) to which + * the polytrope C++ bindings will be added. + * + * @par Python Usage Example: + * After these bindings are registered and the Python module is imported: + * @code{.py} + * from serif.polytrope import PolySolver + * polytrope = PolySolver(1.5, 1) + * polytrope.solve() + * @endcode + */ void register_polytrope_bindings(pybind11::module &polytrope_submodule);