Skip to content

Enhancement of Pybind11 interface: Trampolines, more default arguments, and more#880

Open
hverhelst wants to merge 3 commits into
devfrom
feature/pybind11_trampolines
Open

Enhancement of Pybind11 interface: Trampolines, more default arguments, and more#880
hverhelst wants to merge 3 commits into
devfrom
feature/pybind11_trampolines

Conversation

@hverhelst

Copy link
Copy Markdown
Member

Among many other improvement of the Python interface (see list below), this PR introduces Trampolines to Python.
An example (see python_examples/function_trampoline.py):

One can overload gs.core.gsFunction inside Python. This creates a Python class inheriting from the (trampoline of the) C++ class gsFunction, which can therefore directly be used in assemblers, projections etc defined inside Python.

class MyFunction(gs.core.gsFunction):
    def __init__(self):
        super().__init__()

    def domainDim(self):
        return 2

    def targetDim(self):
        return 1

    def eval_into(self, u: np.ndarray, result: np.ndarray) -> None:
        x = u[0, :]
        y = u[1, :]
        result[:] = np.sin(np.pi * x) * np.sin(np.pi * y)
        print(result)

    def deriv_into(self, u: np.ndarray, result: np.ndarray) -> None:
        x = u[0, :]
        y = u[1, :]
        result[0, :] = np.pi * np.cos(np.pi * x) * np.sin(np.pi * y)  # df/dx
        result[1, :] = np.pi * np.sin(np.pi * x) * np.cos(np.pi * y)  # df/dy

NEW:
Add pybind11 interface for gsBasis::uniformRefine/Coarsen_withTransfer
Add pybind11 interface for gsConstantFunction
Add pybind11 interface for gsField
Add pybind11 interface for gsFunctionExpr::set_u
Add pybind11 trampolines for gsFunctionSet and gsFunction
Add pybind11 interface for refinement, degree elevation etc in gsMultiBasis
Add pybind11 interface for writing Python dictionary from gsOptionList
Add pybind11 interface for SciPy sparse matrices from gsSparseMatrix
Add assembly of full system, only matrix or only rhs in gsL2Projection + bindings
Add pybind11 interface for gsQuasiInterpolate

IMPROVED:
More concise bindings for dimension-based template classes (e.g., gsHTensorBasis, gsTHBSpline)
Improve argument names and default args of gsWriteParaview bindings
SparseMatrix Row/Col Major template exposure to Pybind (for transfer matrix)


Please consider the following checklist before issuing a pull
request:

  • Have you added an explanation of what your changes do and why
    you'd like us to include them?
  • Have you documented any new codes using Doxygen comments?
  • Have you written new tests or examples for your changes?

- gsBasis::uniformRefine/Coarsen_withTransfer
- gsConstantFunction
- gsField
- gsFunctionExpr::set_u
- trampoline for gsFunctionSet and gsFunction
- Extra functions (refinement, degree elevation etc) for gsMultiBasis
- Improvements of templated basis bindings (gsHTensorBasis, gsTHBSpline)
- gsOptionList: add function that writes to python dict
- Improve argument names and default args of gsWriteParaview bindings
- SparseMatrix Major template exposure to Pybind (for transfer matrix)
- SparseMatrix cast to CSC Python structure
- System, matrix and rhs independent assembly code for gsL2Projection
- Exposure of quasi interpolation
@hverhelst hverhelst requested review from a team and filiatra as code owners May 5, 2026 14:30
Copilot AI review requested due to automatic review settings May 5, 2026 14:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR significantly expands the G+Smo Python (pybind11) surface area, primarily by adding trampoline support for overriding core function interfaces in Python, exposing additional core/util classes (e.g., gsField, gsConstantFunction, gsL2Projection, gsQuasiInterpolate), and improving/templating several dimension-dependent bindings.

Changes:

  • Add pybind11 trampolines for gsFunction / gsFunctionSet to enable Python-side subclassing and overrides.
  • Add new Python bindings for utilities and core objects (e.g., gsL2Projection system/matrix/rhs assembly, gsQuasiInterpolate, gsField, gsConstantFunction) and improve argument defaults/names across multiple bindings.
  • Extend sparse matrix + IO interoperability (SciPy sparse integration helpers, XML template updates, labeled gsFileData access).

Reviewed changes

Copilot reviewed 42 out of 42 changed files in this pull request and generated 15 comments.

Show a summary per file
File Description
src/misc/gsPyBind11.cpp Registers new submodules/bindings; templates dimension bindings
src/gsUtils/gsQuasiInterpolate.h Declares pybind init hook
src/gsUtils/gsQuasiInterpolate_.cpp Implements gsQuasiInterpolate Python bindings
src/gsUtils/gsL2Projection.hpp Adds _system/_matrix/_rhs assembly helpers
src/gsUtils/gsL2Projection.h Adds public system/matrix/rhs overload set + pybind hook
src/gsUtils/gsL2Projection_.cpp Exposes gsL2Projection API to Python (project/system/matrix/rhs)
src/gsPde/gsBoundaryConditions_.cpp Adds __str__ binding
src/gsNurbs/gsNurbsCreator_.cpp Fixes docstring + adds default args
src/gsNurbs/gsKnotVector_.cpp Adds default arg + set_degree binding
src/gsMSplines/gsMappedSingleBasis.h Switches to templated pybind init decl
src/gsMSplines/gsMappedBasis.h Switches to templated pybind init decl
src/gsMSplines/gsMappedBasis_.cpp Refactors bindings into templates
src/gsModeling/gsBarrierPatch.h Switches to templated pybind init decl
src/gsModeling/gsBarrierPatch_.cpp Refactors bindings into templates
src/gsMatrix/gsSparseMatrix.h Adds SciPy accessors + pybind type_caster override
src/gsMatrix/gsMatrix.h Minor binding cleanup (comment removal)
src/gsIO/gsXmlUtils.hpp Generalizes XML support for sparse matrix template params
src/gsIO/gsXmlInstance_.cpp Instantiates XML for Row/Col major sparse matrices
src/gsIO/gsXml.hpp Generalizes sparse XML helpers for template params
src/gsIO/gsXml.h Generalizes sparse XML helper declaration
src/gsIO/gsXml_.cpp Instantiates sparse XML helper for Row/Col major
src/gsIO/gsWriteParaview_.cpp Improves arg names/defaults + adds multipatch/field bindings
src/gsIO/gsOptionList.cpp Adds Python-dict constructor
src/gsIO/gsFileData.h Adds getLabelTag helper
src/gsIO/gsFileData_.cpp Adds label-based read/write helpers in Python binding
src/gsHSplines/gsTHBSplineBasis.h Switches to templated pybind init decl
src/gsHSplines/gsTHBSpline.h Switches to templated pybind init decl
src/gsHSplines/gsTHBSpline_.cpp Refactors hspline bindings into templates
src/gsHSplines/gsHTensorBasis.h Switches to templated pybind init decl
src/gsCore/gsMultiBasis_.cpp Adds more mutators + reference return policies
src/gsCore/gsFunctionSet_.cpp Adds gsFunctionSet trampoline + updates bindings
src/gsCore/gsFunctionExpr_.cpp Adds set_u binding
src/gsCore/gsFunction_.cpp Adds gsFunction trampoline + updates bindings
src/gsCore/gsField.hpp Removes unused numEvals params in distance methods
src/gsCore/gsField.h Updates distance method signatures + adds pybind init hook
src/gsCore/gsField_.cpp Adds gsField Python bindings
src/gsCore/gsConstantFunction.h Adds pybind init hook
src/gsCore/gsConstantFunction_.cpp Adds gsConstantFunction Python bindings
src/gsCore/gsBasis_.cpp Adds refine/coarsen transfer-matrix bindings + makeGeometry
setup.py Enables OpenMP; modifies install requirements (needs correction)
python_examples/function_trampoline.py Adds example for Python-side function override
pyproject.toml Pins pybind11 build requirement (>= 2.12)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread setup.py
Comment on lines 240 to 242
install_requires=[
"numpy<2",
"",
],
py::gil_scoped_acquire acquire;
py::function overload = py::get_override(static_cast<const Base*>(this), "deriv2_into");
if (overload) {
result.resize(this->targetDim() * this->domainDim() * 2, u.cols());
}

// Fallback: If no Python clone exists, this is an error for a trampoline
GISMO_ERROR("A Python-derived gsBasis must implement a clone() method "
}

// Fallback: If no Python clone exists, this is an error for a trampoline
GISMO_ERROR("A Python-derived gsBasis must implement a clone() method "
Comment thread src/gsCore/gsField_.cpp
Comment on lines +35 to +38
.def("distanceH2", &Class::distanceH2, "Computes the H2-seminorm of the difference between a field and a function", py::arg("func"), py::arg("isFunc_param")=false)
.def("distanceDG", &Class::distanceDG, "Computes the DG-distance between a field and a function", py::arg("func"), py::arg("isFunc_param")=false)
.def("__str__", &Class::print, "Returns the string representation of the field")
.def("parDim", &Class::parDim, "Returns the parametric dimension of the field")
Comment on lines +258 to +261
* @param options The options that control the projection process
*
* @return The L2 error of the projection
*/
Comment thread src/gsIO/gsOptionList.cpp
Comment on lines +571 to +591
// Iterate over dict items and try to set them as int, real, or switch
for (auto& item : dict_opts)
{
std::string key = pybind11::cast<std::string>(item.first);
py::handle val_handle = item.second;

// Try to interpret value as int, real, or bool, in that order
try {
opt->addInt(key, "", pybind11::cast<int>(val_handle));
}
catch (...) {
try {
opt->addReal(key, "", pybind11::cast<real_t>(val_handle));
}
catch (...) {
try {
opt->addSwitch(key, "", pybind11::cast<bool>(val_handle));
}
catch (...) {
// Skip entries that can't be converted
}
Comment on lines +675 to +679
return pybind11::array_t<Index>(
{static_cast<pybind11::ssize_t>(s.nonZeros())},
{sizeof(Index)},
const_cast<Index*>(s.innerIndexPtr()),
pybind11::cast(s));
Comment thread src/gsCore/gsBasis_.cpp
Comment on lines +54 to +58
.def("uniformRefine_withTransfer",
[](Class& self, int numKnots, int mul) {
gsSparseMatrix<real_t, RowMajor> transfer;
self.uniformRefine_withTransfer(transfer, numKnots, mul);
return transfer;
Comment on lines +262 to +266
static void system( const gsMultiBasis<T> & projectionBasis,
const gsMultiPatch<T> & geometryMap,
gsSparseMatrix<T> & systemMatrix,
gsMatrix<T> & rhs,
const gsOptionList & options = gsOptionList())
hverhelst added 2 commits May 5, 2026 17:10
* add factories like `gsTensorBSplineBasis` which do not need a number for dim
* add vararg constructors for `gsTensorBSplineBasis`instead of hardcoded constructors
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants