Skip to content

Commit 5a1748f

Browse files
committed
[Bindings] Add SofaSimulationCore module and VectorOperations bindings
Signed-off-by: Jean-Nicolas Brunet <[email protected]>
1 parent cfe3ebc commit 5a1748f

File tree

7 files changed

+231
-3
lines changed

7 files changed

+231
-3
lines changed

bindings/Modules/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
project(Bindings.Modules)
22

3-
set(MODULEBINDINGS_MODULE_LIST SofaBaseTopology SofaDeformable)
3+
set(MODULEBINDINGS_MODULE_LIST SofaBaseTopology SofaDeformable SofaSimulationCore)
44

55
find_package(Sofa.GL QUIET)
66
if(Sofa.GL_FOUND)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
23+
#include <sofa/simulation/config.h>
24+
#include <sofa/simulation/VectorOperations.h>
25+
26+
namespace py { using namespace pybind11; }
27+
28+
namespace sofapython3 {
29+
30+
void moduleAddVectorOperations(pybind11::module& m)
31+
{
32+
using namespace sofa::simulation::common;
33+
34+
py::class_<VectorOperations> c (m, "VectorOperations");
35+
c.def(py::init([](sofa::core::objectmodel::BaseContext* context, bool precomputedTraversalOrder = false) {
36+
return VectorOperations(sofa::core::ExecParams::defaultInstance(), context, precomputedTraversalOrder);
37+
}), py::arg("context"), py::arg("precomputedTraversalOrder") = false);
38+
39+
c.def("v_alloc", py::overload_cast<sofa::core::MultiVecCoordId&>(&VectorOperations::v_alloc), py::arg("multi_vector_id"));
40+
c.def("v_alloc", py::overload_cast<sofa::core::MultiVecDerivId&>(&VectorOperations::v_alloc), py::arg("multi_vector_id"));
41+
42+
c.def("v_free", py::overload_cast<sofa::core::MultiVecCoordId&, bool, bool>(&VectorOperations::v_free), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false);
43+
c.def("v_free", py::overload_cast<sofa::core::MultiVecDerivId&, bool, bool>(&VectorOperations::v_free), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false);
44+
45+
c.def("v_realloc", py::overload_cast<sofa::core::MultiVecCoordId&, bool, bool>(&VectorOperations::v_realloc), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false);
46+
c.def("v_realloc", py::overload_cast<sofa::core::MultiVecDerivId&, bool, bool>(&VectorOperations::v_realloc), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false);
47+
48+
c.def("v_clear", &VectorOperations::v_clear, py::arg("multi_vector_id"));
49+
c.def("v_size", &VectorOperations::v_size, py::arg("multi_vector_id"));
50+
51+
c.def("v_eq", py::overload_cast<sofa::core::MultiVecId, sofa::core::ConstMultiVecId>(&VectorOperations::v_eq), py::arg("v").noconvert(false), py::arg("a").noconvert(false), "v = a");
52+
c.def("v_eq", [](VectorOperations & self, sofa::core::MultiVecId &v, sofa::core::ConstMultiVecId & a){
53+
self.v_eq(v, a);
54+
});
55+
c.def("v_eq", py::overload_cast<sofa::core::MultiVecId, sofa::core::ConstMultiVecId, SReal>(&VectorOperations::v_eq), py::arg("v").noconvert(false), py::arg("a").noconvert(false), py::arg("f").noconvert(false), "v = f*a");
56+
c.def("v_peq", &VectorOperations::v_peq, py::arg("v"), py::arg("a"), py::arg("f")=1.0, "v += f*a");
57+
c.def("v_teq", &VectorOperations::v_teq, py::arg("v"), py::arg("f"), "v *= f");
58+
c.def("v_op", &VectorOperations::v_op, py::arg("v"), py::arg("a"), py::arg("b"), py::arg("f"), "v=a+b*f");
59+
60+
c.def("v_dot", &VectorOperations::v_dot, py::arg("a"), py::arg("b"), "a dot b ( get result using finish )");
61+
c.def("v_norm", &VectorOperations::v_norm, py::arg("a"), py::arg("l"), "Compute the norm of a vector ( get result using finish ). The type of norm is set by parameter l. Use 0 for the infinite norm. Note that the 2-norm is more efficiently computed using the square root of the dot product.");
62+
c.def("finish", &VectorOperations::finish);
63+
64+
c.def("v_threshold", &VectorOperations::v_threshold, py::arg("a"), py::arg("threshold"), "Nullify the values below the given threshold.");
65+
}
66+
67+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
project(Bindings.Modules.SofaSimulationCore)
2+
3+
set(SOURCE_FILES
4+
${CMAKE_CURRENT_SOURCE_DIR}/Binding_VectorOperations.cpp
5+
${CMAKE_CURRENT_SOURCE_DIR}/Module_SofaSimulationCore.cpp
6+
)
7+
8+
set(HEADER_FILES
9+
)
10+
11+
find_package(Sofa.SimulationCore REQUIRED)
12+
13+
SP3_add_python_module(
14+
TARGET ${PROJECT_NAME}
15+
PACKAGE Bindings
16+
MODULE SofaSimulationCore
17+
DESTINATION Sofa
18+
SOURCES ${SOURCE_FILES}
19+
HEADERS ${HEADER_FILES}
20+
DEPENDS SofaSimulationCore SofaPython3::Plugin
21+
)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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 <sofa/simulation/init.h>
23+
24+
namespace py { using namespace pybind11; }
25+
26+
namespace sofapython3
27+
{
28+
29+
void moduleAddVectorOperations(pybind11::module& m);
30+
31+
PYBIND11_MODULE(SofaSimulationCore, m)
32+
{
33+
moduleAddVectorOperations(m);
34+
}
35+
36+
} // namespace sofapython3

bindings/Modules/tests/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ set(SOURCE_FILES
77
set(PYTHON_FILES
88
${CMAKE_CURRENT_SOURCE_DIR}/SofaDeformable/LinearSpring.py
99
${CMAKE_CURRENT_SOURCE_DIR}/SofaDeformable/SpringForceField.py
10+
${CMAKE_CURRENT_SOURCE_DIR}/SofaSimulationCore/VectorOperations.py
1011
)
1112

1213
find_package(SofaGTestMain REQUIRED)
@@ -26,7 +27,7 @@ sofa_auto_set_target_rpath(
2627

2728
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
2829

29-
set(DIR_BINDING_LIST SofaBaseTopology SofaDeformable)
30+
set(DIR_BINDING_LIST SofaBaseTopology SofaDeformable SofaSimulationCore)
3031
get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
3132
foreach(dir_binding ${DIR_BINDING_LIST})
3233
if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir_binding}")
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import unittest
2+
import Sofa
3+
from Sofa import SofaSimulationCore
4+
import numpy as np
5+
6+
7+
class VectorOperations(unittest.TestCase):
8+
def test_alloc_coord(self):
9+
root = Sofa.Core.Node()
10+
create_scene(root)
11+
Sofa.Simulation.init(root)
12+
13+
vop = SofaSimulationCore.VectorOperations(root)
14+
15+
# Create a first multivector
16+
multi_vec_id_1 = Sofa.Core.MultiVecCoordId()
17+
self.assertEqual(multi_vec_id_1.defaultId.index, 0)
18+
self.assertTrue(multi_vec_id_1.isNull())
19+
vop.v_alloc(multi_vec_id_1)
20+
self.assertFalse(multi_vec_id_1.isNull())
21+
self.assertEqual(multi_vec_id_1.getId(root.node_1.mo).index, 5)
22+
self.assertEqual(multi_vec_id_1.getId(root.node_2.mo).index, 5)
23+
24+
# Create a second one
25+
multi_vec_id_2 = Sofa.Core.MultiVecCoordId()
26+
vop.v_alloc(multi_vec_id_2)
27+
self.assertEqual(multi_vec_id_2.getId(root.node_1.mo).index, 6)
28+
self.assertEqual(multi_vec_id_2.getId(root.node_2.mo).index, 6)
29+
30+
# Create a third one, only in node_2
31+
vop2 = SofaSimulationCore.VectorOperations(root.node_2)
32+
multi_vec_id_3 = Sofa.Core.MultiVecCoordId()
33+
vop2.v_alloc(multi_vec_id_3)
34+
self.assertEqual(multi_vec_id_3.getId(root.node_1.mo).index, 0)
35+
self.assertEqual(multi_vec_id_3.getId(root.node_2.mo).index, 7)
36+
37+
# Free
38+
vop.v_free(multi_vec_id_1)
39+
vop.v_free(multi_vec_id_2)
40+
vop2.v_free(multi_vec_id_3)
41+
42+
def test_alloc_deriv(self):
43+
root = Sofa.Core.Node()
44+
create_scene(root)
45+
Sofa.Simulation.init(root)
46+
47+
vop = SofaSimulationCore.VectorOperations(root)
48+
49+
# Create a first multivector
50+
multi_vec_id_1 = Sofa.Core.MultiVecDerivId()
51+
self.assertEqual(multi_vec_id_1.defaultId.index, 0)
52+
self.assertTrue(multi_vec_id_1.isNull())
53+
vop.v_alloc(multi_vec_id_1)
54+
self.assertFalse(multi_vec_id_1.isNull())
55+
self.assertEqual(multi_vec_id_1.getId(root.node_1.mo).index, 9)
56+
self.assertEqual(multi_vec_id_1.getId(root.node_2.mo).index, 9)
57+
58+
# Create a second one
59+
multi_vec_id_2 = Sofa.Core.MultiVecDerivId()
60+
vop.v_alloc(multi_vec_id_2)
61+
self.assertEqual(multi_vec_id_2.getId(root.node_1.mo).index, 10)
62+
self.assertEqual(multi_vec_id_2.getId(root.node_2.mo).index, 10)
63+
64+
# Create a third one, only in node_2
65+
vop2 = SofaSimulationCore.VectorOperations(root.node_2)
66+
multi_vec_id_3 = Sofa.Core.MultiVecDerivId()
67+
vop2.v_alloc(multi_vec_id_3)
68+
self.assertEqual(multi_vec_id_3.getId(root.node_1.mo).index, 0)
69+
self.assertEqual(multi_vec_id_3.getId(root.node_2.mo).index, 11)
70+
71+
# Free
72+
vop.v_free(multi_vec_id_1)
73+
vop.v_free(multi_vec_id_2)
74+
vop2.v_free(multi_vec_id_3)
75+
76+
def test_op(self):
77+
root = Sofa.Core.Node()
78+
create_scene(root)
79+
Sofa.Simulation.init(root)
80+
81+
vop = SofaSimulationCore.VectorOperations(root)
82+
83+
# Create a temporary multivector
84+
v = Sofa.Core.MultiVecCoordId()
85+
vop.v_alloc(v)
86+
87+
x_id = Sofa.Core.VecCoordId.position()
88+
vop.v_eq(v, x_id) # v = x
89+
vop.v_peq(v, x_id, 2) # v += 2*x
90+
vop.v_dot(v, v) # n = sqrt(v dot v)
91+
n = np.sqrt(vop.finish())
92+
93+
x = np.concatenate((root.node_1.mo.position.array(), root.node_2.mo.position.array()), axis=None).flatten()
94+
self.assertEqual(n, np.linalg.norm(x + (2*x)))
95+
96+
97+
def create_scene(root):
98+
root.addObject('RequiredPlugin', pluginName='SofaBaseMechanics')
99+
root.addChild('node_1')
100+
root.node_1.addObject('MechanicalObject', name='mo', template='Vec3', position=[[1,1,1], [2,2,2], [3,3,3]])
101+
102+
root.addChild('node_2')
103+
root.node_2.addObject('MechanicalObject', name='mo', template='Vec2', position=[[1,1], [2,2], [3,3], [4, 4]])

bindings/Modules/tests/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ static struct Tests : public sofapython3::PythonTestExtractor
3636
MessageDispatcher::addHandler(&MainPerComponentLoggingMessageHandler::getInstance()) ;
3737

3838
const std::string executable_directory = sofa::helper::Utils::getExecutableDirectory();
39-
addTestDirectory(executable_directory+"/SofaBaseTopology", "SofaBaseTopology_");
4039
addTestDirectory(executable_directory+"/SofaDeformable", "SofaDeformable_");
40+
addTestDirectory(executable_directory+"/SofaSimulationCore", "SofaSimulationCore_");
4141
}
4242
} python_tests;
4343

0 commit comments

Comments
 (0)