Skip to content

Commit f0ba660

Browse files
committed
Add Binding_Mapping
1 parent 3ec609c commit f0ba660

File tree

4 files changed

+371
-0
lines changed

4 files changed

+371
-0
lines changed
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/******************************************************************************
2+
* SofaPython3 plugin *
3+
* (c) 2021 CNRS, University of Lille, INRIA *
4+
* *
5+
* This program is free software; you can redistribute it and/or modify it *
6+
* under the terms of the GNU Lesser General Public License as published by *
7+
* the Free Software Foundation; either version 2.1 of the License, or (at *
8+
* your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, but WITHOUT *
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
13+
* for more details. *
14+
* *
15+
* You should have received a copy of the GNU Lesser General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
*******************************************************************************
18+
* Contact information: [email protected] *
19+
******************************************************************************/
20+
21+
#include <pybind11/pybind11.h>
22+
#include <pybind11/numpy.h>
23+
#include <SofaPython3/Sofa/Core/Binding_Base.h>
24+
#include <SofaPython3/Sofa/Core/Binding_Mapping.h>
25+
#include <SofaPython3/Sofa/Core/Binding_Mapping_doc.h>
26+
#include <SofaPython3/PythonFactory.h>
27+
28+
#include <sofa/core/ConstraintParams.h>
29+
30+
#include <sofa/core/behavior/MechanicalState.h>
31+
#include <sofa/core/MechanicalParams.h>
32+
#include <sofa/core/behavior/DefaultMultiMatrixAccessor.h>
33+
#include <sofa/linearalgebra/CompressedRowSparseMatrix.h>
34+
35+
#include <pybind11/eigen.h>
36+
37+
#include <SofaPython3/PythonEnvironment.h>
38+
using sofapython3::PythonEnvironment;
39+
40+
/// Makes an alias for the pybind11 namespace to increase readability.
41+
namespace py { using namespace pybind11; }
42+
/// To bring in the `_a` literal
43+
using namespace pybind11::literals;
44+
45+
namespace sofapython3
46+
{
47+
using sofa::core::Mapping;
48+
using sofa::core::objectmodel::BaseObject;
49+
using sofa::core::objectmodel::ComponentState;
50+
using sofa::core::behavior::MechanicalState;
51+
using sofa::core::MechanicalParams;
52+
using sofa::core::behavior::MultiMatrixAccessor;
53+
using sofa::defaulttype::Vec3dTypes;
54+
using sofa::defaulttype::Vec2dTypes;
55+
using sofa::defaulttype::Vec1dTypes;
56+
using sofa::defaulttype::Vec6dTypes;
57+
using sofa::defaulttype::Rigid3dTypes;
58+
using sofa::defaulttype::Rigid2dTypes;
59+
60+
template<class In, class Out>
61+
Mapping_Trampoline<In, Out>::Mapping_Trampoline() = default;
62+
63+
template<class In, class Out>
64+
Mapping_Trampoline<In, Out>::~Mapping_Trampoline() = default;
65+
66+
template<class In, class Out>
67+
std::string Mapping_Trampoline<In, Out>::getClassName() const
68+
{
69+
PythonEnvironment::gil acquire {"getClassName"};
70+
71+
// Get the actual class name from python.
72+
return py::str(py::cast(this).get_type().attr("__name__"));
73+
}
74+
75+
template<class In, class Out>
76+
void Mapping_Trampoline<In, Out>::init()
77+
{
78+
PythonEnvironment::gil acquire;
79+
80+
Inherit1::init();
81+
}
82+
83+
template<class In, class Out>
84+
void Mapping_Trampoline<In, Out>::apply( const MechanicalParams* mparams, OutDataVecCoord& out, const InDataVecCoord& in){
85+
PythonEnvironment::gil acquire;
86+
87+
// pass bFactor, kFactor, energy
88+
py::dict mp = py::dict("time"_a=this->getContext()->getTime(),
89+
"mFactor"_a=mparams->mFactor(),
90+
"bFactor"_a=mparams->bFactor(),
91+
"kFactor"_a=mparams->kFactor(),
92+
"isImplicit"_a=mparams->implicit(),
93+
"energy"_a=mparams->energy());
94+
95+
PYBIND11_OVERLOAD_PURE(void, Inherit1, apply, mp,
96+
PythonFactory::toPython(&out), PythonFactory::toPython(&in));
97+
}
98+
99+
template<class In, class Out>
100+
void Mapping_Trampoline<In, Out>::applyJ( const MechanicalParams* mparams, OutDataVecDeriv& out, const InDataVecDeriv& in){
101+
PythonEnvironment::gil acquire;
102+
103+
// pass bFactor, kFactor, energy
104+
py::dict mp = py::dict("time"_a=getContext()->getTime(),
105+
"mFactor"_a=mparams->mFactor(),
106+
"bFactor"_a=mparams->bFactor(),
107+
"kFactor"_a=mparams->kFactor(),
108+
"isImplicit"_a=mparams->implicit(),
109+
"energy"_a=mparams->energy());
110+
111+
PYBIND11_OVERLOAD_PURE(void, Inherit1, applyJ, mp,
112+
PythonFactory::toPython(&out), PythonFactory::toPython(&in));
113+
}
114+
115+
template<class In, class Out>
116+
void Mapping_Trampoline<In, Out>::applyJT( const MechanicalParams* mparams, InDataVecDeriv& out, const OutDataVecDeriv& in){
117+
PythonEnvironment::gil acquire;
118+
119+
py::dict mp = py::dict("time"_a=getContext()->getTime(),
120+
"mFactor"_a=mparams->mFactor(),
121+
"bFactor"_a=mparams->bFactor(),
122+
"kFactor"_a=mparams->kFactor(),
123+
"isImplicit"_a=mparams->implicit(),
124+
"energy"_a=mparams->energy());
125+
126+
PYBIND11_OVERLOAD_PURE(void, Inherit1, applyJT, mp,
127+
PythonFactory::toPython(&out), PythonFactory::toPython(&in));
128+
}
129+
130+
template<class In, class Out>
131+
void Mapping_Trampoline<In, Out>::applyJT( const ConstraintParams* cparams,
132+
InDataMatrixDeriv& out, const OutDataMatrixDeriv& in)
133+
{
134+
PythonEnvironment::gil acquire;
135+
136+
py::dict mp = py::dict("time"_a=getContext()->getTime());
137+
138+
PYBIND11_OVERLOAD_PURE(void, Inherit1, applyConstrainsJT, mp,
139+
PythonFactory::toPython(&out), PythonFactory::toPython(&in));
140+
}
141+
142+
template<class In, class Out>
143+
void declareMapping(py::module &m) {
144+
const std::string pyclass_name = std::string("Mapping_") + In::Name()+ "_" + Out::Name();
145+
146+
py::class_<Mapping<In, Out>, BaseObject, Mapping_Trampoline<In, Out>,
147+
py_shared_ptr<Mapping<In, Out>>> f(m, pyclass_name.c_str(), py::dynamic_attr(), py::multiple_inheritance(),
148+
sofapython3::doc::mapping::mappingClass);
149+
150+
f.def(py::init([](py::args &args, py::kwargs &kwargs) {
151+
auto ff = sofa::core::sptr<Mapping_Trampoline<In, Out>> (new Mapping_Trampoline<In, Out>());
152+
153+
ff->f_listening.setValue(true);
154+
155+
if (args.size() == 1)
156+
ff->setName(py::cast<std::string>(args[0]));
157+
158+
py::object cc = py::cast(ff);
159+
for (auto kv : kwargs) {
160+
std::string key = py::cast<std::string>(kv.first);
161+
py::object value = py::reinterpret_borrow<py::object>(kv.second);
162+
if (key == "name") {
163+
if (args.size() != 0) {
164+
throw py::type_error("The name is set twice as a "
165+
"named argument='" + py::cast<std::string>(value) + "' and as a"
166+
"positional argument='" +
167+
py::cast<std::string>(args[0]) + "'.");
168+
}
169+
}
170+
BindingBase::SetAttr(cc, key, value);
171+
}
172+
return ff;
173+
}));
174+
}
175+
176+
void moduleAddMapping(py::module &m) {
177+
declareMapping<Rigid3dTypes, Vec3dTypes>(m);
178+
declareMapping<Vec3dTypes, Vec3dTypes>(m);
179+
}
180+
181+
} // namespace sofapython3
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/******************************************************************************
2+
* SofaPython3 plugin *
3+
* (c) 2021 CNRS, University of Lille, INRIA *
4+
* *
5+
* This program is free software; you can redistribute it and/or modify it *
6+
* under the terms of the GNU Lesser General Public License as published by *
7+
* the Free Software Foundation; either version 2.1 of the License, or (at *
8+
* your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, but WITHOUT *
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
13+
* for more details. *
14+
* *
15+
* You should have received a copy of the GNU Lesser General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
*******************************************************************************
18+
* Contact information: [email protected] *
19+
******************************************************************************/
20+
21+
#pragma once
22+
23+
#include <sofa/core/BaseMapping.h>
24+
#include <sofa/core/Mapping.h>
25+
#include <sofa/defaulttype/VecTypes.h>
26+
#include <sofa/core/MechanicalParams.h>
27+
#include <pybind11/pybind11.h>
28+
29+
namespace sofapython3 {
30+
using sofa::core::ConstraintParams;
31+
using sofa::core::MechanicalParams;
32+
33+
template<class In, class Out>
34+
class Mapping_Trampoline : public sofa::core::Mapping<In, Out> {
35+
public:
36+
SOFA_CLASS(SOFA_TEMPLATE2(Mapping_Trampoline, In, Out) ,
37+
SOFA_TEMPLATE2(sofa::core::Mapping, In, Out));
38+
using sofa::core::Mapping<In,Out>::getContext;
39+
using typename sofa::core::Mapping<In,Out>::OutDataVecCoord;
40+
using typename sofa::core::Mapping<In,Out>::OutDataVecDeriv;
41+
using typename sofa::core::Mapping<In,Out>::InDataVecCoord;
42+
using typename sofa::core::Mapping<In,Out>::InDataVecDeriv;
43+
using typename sofa::core::Mapping<In,Out>::OutDataMatrixDeriv;
44+
using typename sofa::core::Mapping<In,Out>::InDataMatrixDeriv;
45+
46+
Mapping_Trampoline();
47+
~Mapping_Trampoline() override;
48+
49+
void init() override;
50+
std::string getClassName() const override;
51+
52+
void apply( const MechanicalParams* mparams, OutDataVecCoord& out, const InDataVecCoord& in) override;
53+
void applyJ( const MechanicalParams* mparams, OutDataVecDeriv& out, const InDataVecDeriv& in) override;
54+
void applyJT( const MechanicalParams* mparams, InDataVecDeriv& out, const OutDataVecDeriv& in) override;
55+
void applyJT( const ConstraintParams* mparams, InDataMatrixDeriv& out, const OutDataMatrixDeriv& in) override;
56+
57+
};
58+
59+
void moduleAddMapping(pybind11::module &m);
60+
61+
} /// namespace sofapython3
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/******************************************************************************
2+
* SofaPython3 plugin *
3+
* (c) 2021 CNRS, University of Lille, INRIA *
4+
* *
5+
* This program is free software; you can redistribute it and/or modify it *
6+
* under the terms of the GNU Lesser General Public License as published by *
7+
* the Free Software Foundation; either version 2.1 of the License, or (at *
8+
* your option) any later version. *
9+
* *
10+
* This program is distributed in the hope that it will be useful, but WITHOUT *
11+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
12+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License *
13+
* for more details. *
14+
* *
15+
* You should have received a copy of the GNU Lesser General Public License *
16+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
17+
*******************************************************************************
18+
* Contact information: [email protected] *
19+
******************************************************************************/
20+
21+
#pragma once
22+
23+
namespace sofapython3::doc::mapping
24+
{
25+
static auto mappingClass = R"(
26+
Overridable class to create your own customized mapping
27+
)";
28+
29+
}

examples/example-mapping.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""Implementation of an identity mapping in python"""
2+
# coding: utf8
3+
import Sofa
4+
import numpy as np
5+
6+
class IdentityMapping(Sofa.Core.Mapping_Vec3d_Vec3d):
7+
"""Implementation of a Mapping in python"""
8+
def __init__(self, *args, **kwargs):
9+
10+
## This is just an ugly trick to transform pointer to object into sofa linkpath.
11+
for k,v in kwargs.items():
12+
if k == "input":
13+
kwargs[k] = v.linkpath
14+
elif k == "output":
15+
kwargs[k] = v.linkpath
16+
17+
Sofa.Core.Mapping_Vec3d_Vec3d.__init__(self, *args, **kwargs)
18+
19+
# let's compute the positions initial delta.
20+
input_position = kwargs["input"].target().position.value.copy()
21+
output_position = kwargs["output"].target().position.value.copy()
22+
self.delta = output_position - input_position
23+
24+
def apply(self, m, outCoord, inCoord):
25+
print("PYTHON(🐍) APPLY ", outCoord, inCoord)
26+
with outCoord.writeableArray() as wa:
27+
wa[:] = inCoord.value + self.delta
28+
29+
def applyJ(self, m, outDeriv, inDeriv):
30+
print("PYTHON(🐍) APPLY-J", outDeriv, inDeriv)
31+
32+
def applyJT(self, m, outDeriv, inDeriv):
33+
print("PYTHON(🐍) APPLY-JT", outDeriv, inDeriv)
34+
35+
def applyConstrainsJT(self, m, outDeriv, inDeriv):
36+
print("PYTHON(🐍) APPLY-JT for constraints, data are [⋱]", m, outDeriv, inDeriv)
37+
print("Constraints ", inDeriv.value)
38+
39+
40+
41+
42+
def createScene(root):
43+
root.addObject("RequiredPlugin", pluginName=["Sofa.GL.Component",
44+
"Sofa.Component.ODESolver.Backward",
45+
"Sofa.Component.LinearSolver.Direct",
46+
"Sofa.Component.LinearSolver.Iterative",
47+
"Sofa.Component.Mass",
48+
"Sofa.Component.StateContainer",
49+
"Sofa.Component.Visual"
50+
])
51+
52+
root.addObject("LineAxis")
53+
root.addObject("DefaultAnimationLoop", name="loop")
54+
55+
root.addChild("Modelling")
56+
57+
######### OBJECT #####################
58+
o = root.Modelling.addChild("Object")
59+
c = o.addObject("MechanicalObject", name="mechanical", position=[0.0,0.0,0.0, 1.0,0.0,0.0])
60+
c.showObject = True
61+
c.showColor = [1.0,0.0,0.0,1.0]
62+
c.drawMode = 1
63+
o.addObject("UniformMass", name="mass", totalMass=1.0)
64+
65+
######### OBJECT #####################
66+
m = o.addChild("MappedDof")
67+
sm = m.addObject("MechanicalObject", name="mapped_quantities", position=[0.0,0.5,0.0, 1.0, 0.5, 0.0])
68+
sm.showObject = True
69+
sm.showColor = [1.0,0.0,1.0,0.7]
70+
sm.drawMode = 1
71+
m.addObject( IdentityMapping(name="CPPObject", input=c, output=sm ) )
72+
73+
root.addChild("Simulation")
74+
root.Simulation.addObject("EulerImplicitSolver")
75+
root.Simulation.addObject("CGLinearSolver", tolerance=1e-12, threshold=1e-12, iterations=25)
76+
root.Simulation.addChild(root.Modelling)
77+
78+
return root
79+
80+
81+
def main():
82+
import SofaRuntime
83+
import Sofa.Gui
84+
import SofaQt
85+
86+
root=Sofa.Core.Node("root")
87+
createScene(root)
88+
Sofa.Simulation.initRoot(root)
89+
90+
Sofa.Gui.GUIManager.Init("myscene", "qglviewer")
91+
Sofa.Gui.GUIManager.createGUI(root, __file__)
92+
Sofa.Gui.GUIManager.SetDimension(1080, 1080)
93+
Sofa.Gui.GUIManager.MainLoop(root)
94+
Sofa.Gui.GUIManager.closeGUI()
95+
96+
print("End of simulation.")
97+
98+
99+
if __name__ == '__main__':
100+
main()

0 commit comments

Comments
 (0)