diff --git a/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.cpp b/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.cpp index 825079cf..193745cd 100644 --- a/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.cpp +++ b/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.cpp @@ -6,6 +6,11 @@ #include #include +#include +#include +#include +#include + #include namespace SofaCaribou::forcefield::python { diff --git a/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.h b/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.h index e08741a5..852d4e1c 100644 --- a/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.h +++ b/src/SofaCaribou/Python/Forcefield/HyperelasticForcefield.h @@ -5,20 +5,85 @@ #include #include +#include +#include #include #include +#include +#include + +using namespace pybind11::literals; + namespace SofaCaribou::forcefield::python { +template +class HyperelasticForcefieldTrampoline : public HyperelasticForcefield { +public: + SOFA_CLASS(SOFA_TEMPLATE(HyperelasticForcefieldTrampoline, Element), SOFA_TEMPLATE(HyperelasticForcefield, Element)); + using typename HyperelasticForcefield::DataVecDeriv; + using typename HyperelasticForcefield::DataVecCoord; + + void init() override { + sofapython3::PythonEnvironment::gil acquire; + PYBIND11_OVERLOAD(void, HyperelasticForcefield, init); + } + + void addForce(const sofa::core::MechanicalParams* mparams, DataVecDeriv& f, const DataVecCoord& x, const DataVecDeriv& v) override { + sofapython3::PythonEnvironment::gil acquire; + + // Try to look up the overridden method on the Python side. + pybind11::function override = pybind11::get_override(this, "addForce"); + if (override) { + // python override method is found, call it + pybind11::dict mp = pybind11::dict("time"_a=this->getContext()->getTime(), + "mFactor"_a=mparams->mFactor(), + "bFactor"_a=mparams->bFactor(), + "kFactor"_a=mparams->kFactor(), + "isImplicit"_a=mparams->implicit(), + "energy"_a=mparams->energy()); + override(mp, sofapython3::PythonFactory::toPython(&f), sofapython3::PythonFactory::toPython(&x), sofapython3::PythonFactory::toPython(&v)); + } else { + // No python override, call the generic method + HyperelasticForcefield::addForce(mparams, f, x, v); + } + } + + void addDForce(const sofa::core::MechanicalParams * mparams, DataVecDeriv & df, const DataVecDeriv & dx) override { + sofapython3::PythonEnvironment::gil acquire; + + // Try to look up the overridden method on the Python side. + pybind11::function override = pybind11::get_override(this, "addDForce"); + if (override) { + // python override method is found, call it + pybind11::dict mp = pybind11::dict("time"_a=this->getContext()->getTime(), + "mFactor"_a=mparams->mFactor(), + "bFactor"_a=mparams->bFactor(), + "kFactor"_a=mparams->kFactor(), + "isImplicit"_a=mparams->implicit(), + "energy"_a=mparams->energy()); + override(mp, sofapython3::PythonFactory::toPython(&df), sofapython3::PythonFactory::toPython(&dx)); + } else { + // No python override, call the generic method + HyperelasticForcefield::addDForce(mparams, df, dx); + } + } +}; + template void bind_hyperelastic_forcefield(pybind11::module &m, const std::string & template_name) { pybind11::module::import("Sofa"); - std::string name = "HyperelasticForcefield<" + template_name + ">"; + std::string name = "HyperelasticForcefield_" + template_name; - pybind11::class_, sofa::core::objectmodel::BaseObject, sofapython3::py_shared_ptr>> c(m, name.c_str()); + using Real = typename HyperelasticForcefield::Real; + using VecCoord = typename HyperelasticForcefield::VecCoord; + using VecDeriv = typename HyperelasticForcefield::VecDeriv; + + pybind11::class_, sofa::core::objectmodel::BaseObject, HyperelasticForcefieldTrampoline, sofapython3::py_shared_ptr>> c(m, name.c_str()); + c.def("init", &HyperelasticForcefield::init); c.def("K", &HyperelasticForcefield::K); c.def("cond", &HyperelasticForcefield::cond); c.def("eigenvalues", &HyperelasticForcefield::eigenvalues); @@ -28,6 +93,103 @@ void bind_hyperelastic_forcefield(pybind11::module &m, const std::string & templ c.def("assemble_stiffness", [](HyperelasticForcefield & self, const Eigen::Matrix::Dimension, Eigen::RowMajor> & x) { self.assemble_stiffness(x); }, pybind11::arg("x").noconvert(true)); + c.def("addForce", + [](HyperelasticForcefield &self, + const pybind11::dict &mp, + sofapython3::DataContainer &f, + const sofapython3::DataContainer &x, + const sofapython3::DataContainer &v + ) { + auto *ff = reinterpret_cast *>(&f); + const auto *xx = reinterpret_cast *>(&x); + const auto *vv = reinterpret_cast *>(&v); + sofa::core::MechanicalParams mparams; + if (mp.template contains("mFactor")) { + mparams.setMFactor(pybind11::cast(mp["mFactor"])); + } + if (mp.template contains("bFactor")) { + mparams.setBFactor(pybind11::cast(mp["bFactor"])); + } + if (mp.template contains("kFactor")) { + mparams.setKFactor(pybind11::cast(mp["kFactor"])); + } + if (mp.template contains("isImplicit")) { + mparams.setImplicit(pybind11::cast(mp["isImplicit"])); + } + if (mp.template contains("energy")) { + mparams.setEnergy(pybind11::cast(mp["energy"])); + } + if (mp.template contains("dt")) { + mparams.setDt(pybind11::cast(mp["dt"])); + } + + self.addForce(&mparams, *ff, *xx, *vv); + }, + pybind11::arg("mechanical_params"), + pybind11::arg("f").noconvert(), + pybind11::arg("x").noconvert(), + pybind11::arg("v").noconvert() + ); + + c.def("addDForce", + [](HyperelasticForcefield & self, + const pybind11::dict & mp, + sofapython3::DataContainer &df, + const sofapython3::DataContainer &dx + ) + { + auto * dff = reinterpret_cast*>(&df); + const auto * dxx = reinterpret_cast*>(&dx); + sofa::core::MechanicalParams mparams; + if (mp.template contains("mFactor")) { + mparams.setMFactor(pybind11::cast(mp["mFactor"])); + } + if (mp.template contains("bFactor")) { + mparams.setBFactor(pybind11::cast(mp["bFactor"])); + } + if (mp.template contains("kFactor")) { + mparams.setKFactor(pybind11::cast(mp["kFactor"])); + } + if (mp.template contains("isImplicit")) { + mparams.setImplicit(pybind11::cast(mp["isImplicit"])); + } + if (mp.template contains("energy")) { + mparams.setEnergy(pybind11::cast(mp["energy"])); + } + if (mp.template contains("dt")) { + mparams.setDt(pybind11::cast(mp["dt"])); + } + + self.addDForce(&mparams, *dff, *dxx); + }, + pybind11::arg("mechanical_params"), + pybind11::arg("df").noconvert(), + pybind11::arg("dx").noconvert() + ); + + c.def(pybind11::init([](pybind11::args &args, pybind11::kwargs &kwargs) { + auto ff = sofa::core::sptr> (new HyperelasticForcefieldTrampoline()); + + ff->f_listening.setValue(true); + + if (args.size() == 1) ff->setName(pybind11::cast(args[0])); + + pybind11::object cc = pybind11::cast(ff); + for (auto kv : kwargs) { + auto key = pybind11::cast(kv.first); + auto value = pybind11::reinterpret_borrow(kv.second); + if (key == "name") { + if (!args.empty()) { + throw pybind11::type_error("The name is set twice as a " + "named argument='" + pybind11::cast(value) + "' and as a" + "positional argument='" + + pybind11::cast(args[0]) + "'."); + } + } + sofapython3::BindingBase::SetAttr(cc, key, value); + } + return ff; + })); sofapython3::PythonFactory::registerType>([template_name](sofa::core::objectmodel::Base* o) { return pybind11::cast(dynamic_cast*>(o));