From 98216e0091c461b19bacf1d24cb8116dd29809f2 Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Wed, 26 May 2021 22:27:38 +0200 Subject: [PATCH 1/9] [Bindings] Add BaseState bindings Signed-off-by: Jean-Nicolas Brunet --- .../Sofa/Core/Binding_BaseState.cpp | 45 +++++++++++++++++++ .../SofaPython3/Sofa/Core/Binding_BaseState.h | 28 ++++++++++++ .../src/SofaPython3/Sofa/Core/CMakeLists.txt | 2 + .../SofaPython3/Sofa/Core/Submodule_Core.cpp | 2 + 4 files changed, 77 insertions(+) create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.cpp create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.h diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.cpp new file mode 100644 index 00000000..312b2fa9 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.cpp @@ -0,0 +1,45 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include + +#include + +namespace py { using namespace pybind11; } + +namespace sofapython3 { + +void moduleAddBaseState(py::module& m) { + using namespace sofa::core; + py::class_> (m, "BaseState") + .def("getSize", &BaseState::getSize) + .def("resize", &BaseState::getSize) + ; + + /// register the BaseState binding in the downcasting subsystem + PythonFactory::registerType([](sofa::core::objectmodel::Base* object) + { + return py::cast(dynamic_cast(object)); + }); +} + +} // namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.h new file mode 100644 index 00000000..be1fde0d --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_BaseState.h @@ -0,0 +1,28 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once +#include + +namespace sofapython3 { + +void moduleAddBaseState(pybind11::module &m); + +} /// namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt index 14b41b24..76a23cc2 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt @@ -10,6 +10,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseObject_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseCamera.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseContext.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseState.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ContactListener.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ContactListener_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Context.h @@ -49,6 +50,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseObject.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseCamera.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseContext.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseState.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ContactListener.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Context.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Controller.cpp diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp index 2e8d6bac..5de158e9 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp @@ -28,6 +28,7 @@ using sofa::helper::logging::Message; #include #include #include +#include #include #include #include @@ -126,6 +127,7 @@ PYBIND11_MODULE(Core, core) moduleAddDataVectorString(core); moduleAddBaseObject(core); moduleAddBaseCamera(core); + moduleAddBaseState(core); moduleAddContactListener(core); moduleAddContext(core); moduleAddController(core); From 4247aa5f8f33838408bf462f94ec72d5d9203f00 Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Wed, 26 May 2021 22:30:39 +0200 Subject: [PATCH 2/9] [Bindings] Add MechanicalParams bindings Signed-off-by: Jean-Nicolas Brunet --- .../Sofa/Core/Binding_MechanicalParams.cpp | 63 +++++++++++++++++++ .../Sofa/Core/Binding_MechanicalParams.h | 28 +++++++++ .../Sofa/Core/Binding_MechanicalParams_doc.h | 32 ++++++++++ .../src/SofaPython3/Sofa/Core/CMakeLists.txt | 3 + .../SofaPython3/Sofa/Core/Submodule_Core.cpp | 2 + bindings/Sofa/tests/CMakeLists.txt | 1 + bindings/Sofa/tests/Core/MechanicalParams.py | 49 +++++++++++++++ 7 files changed, 178 insertions(+) create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.h create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams_doc.h create mode 100644 bindings/Sofa/tests/Core/MechanicalParams.py diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp new file mode 100644 index 00000000..4f1ad1da --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp @@ -0,0 +1,63 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include + +namespace py { using namespace pybind11; } + +namespace sofapython3 { + +void moduleAddMechanicalParams(py::module& m) { + using namespace sofa::core; + py::class_ c (m, "MechanicalParams", doc::mechanicalparams::MechanicalParamsClass); + c.def(py::init()); + + // Dt + c.def_property("dt", &MechanicalParams::dt, &MechanicalParams::setDt); + c.def("setDt", &MechanicalParams::setDt, py::arg("dt")); + + // Implicit + c.def_property("implicit", &MechanicalParams::implicit, &MechanicalParams::setImplicit, "Specify if the time integration scheme is implicit."); + c.def("setImplicit", &MechanicalParams::setImplicit, py::arg("v"), "Specify if the time integration scheme is implicit."); + + // Mass factor + c.def_property("mFactor", &MechanicalParams::mFactor, &MechanicalParams::setMFactor, "Set Mass matrix contributions factor (for implicit schemes)."); + c.def("setMFactor", &MechanicalParams::setMFactor, py::arg("m"), "Set Mass matrix contributions factor (for implicit schemes)."); + + // Damping factor + c.def_property("bFactor", &MechanicalParams::bFactor, &MechanicalParams::setBFactor, "Set Damping matrix contributions factor (for implicit schemes)."); + c.def("setBFactor", &MechanicalParams::setBFactor, py::arg("b"), "Set Damping matrix contributions factor (for implicit schemes)."); + + // Stiffness factor + c.def_property("kFactor", &MechanicalParams::kFactor, &MechanicalParams::setKFactor, "Set Stiffness matrix contributions factor (for implicit schemes)."); + c.def("setKFactor", &MechanicalParams::setKFactor, py::arg("k"), "Set Stiffness matrix contributions factor (for implicit schemes)."); + + // Symmetric + c.def_property("symmetricMatrix", &MechanicalParams::symmetricMatrix, &MechanicalParams::setSymmetricMatrix, "Set the symmetric matrix flag (for implicit schemes), for solvers specialized on symmetric matrices"); + c.def("setSymmetricMatrix", &MechanicalParams::setSymmetricMatrix, "Set the symmetric matrix flag (for implicit schemes), for solvers specialized on symmetric matrices"); + + // Energy + c.def_property("energy", &MechanicalParams::energy, &MechanicalParams::setEnergy, "Specify if the potential and kinematic energies should be computed."); + c.def("setEnergy", &MechanicalParams::setEnergy, "Specify if the potential and kinematic energies should be computed."); +} + +} // namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.h new file mode 100644 index 00000000..0cbcd507 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.h @@ -0,0 +1,28 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once +#include + +namespace sofapython3 { + +void moduleAddMechanicalParams(pybind11::module &m); + +} /// namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams_doc.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams_doc.h new file mode 100644 index 00000000..9868e224 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams_doc.h @@ -0,0 +1,32 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once + +namespace sofapython3::doc::mechanicalparams +{ + +static auto MechanicalParamsClass = +R"( +Class gathering parameters use by mechanical components methods, and transmitted by mechanical visitors +)"; + + +} diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt index 76a23cc2..4406f276 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt @@ -22,6 +22,8 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory_doc.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_NodeIterator.h @@ -61,6 +63,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Data/Binding_DataVectorString.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_NodeIterator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Prefab.cpp diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp index 5de158e9..876766f2 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp @@ -35,6 +35,7 @@ using sofa::helper::logging::Message; #include #include #include +#include #include #include #include @@ -134,6 +135,7 @@ PYBIND11_MODULE(Core, core) moduleAddDataEngine(core); moduleAddForceField(core); moduleAddObjectFactory(core); + moduleAddMechanicalParams(core); moduleAddNode(core); moduleAddNodeIterator(core); moduleAddPrefab(core); diff --git a/bindings/Sofa/tests/CMakeLists.txt b/bindings/Sofa/tests/CMakeLists.txt index 7f97f59b..c3139e34 100644 --- a/bindings/Sofa/tests/CMakeLists.txt +++ b/bindings/Sofa/tests/CMakeLists.txt @@ -18,6 +18,7 @@ set(PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Core/ForceField.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/DataEngine.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/MyRestShapeForceField.py + ${CMAKE_CURRENT_SOURCE_DIR}/Core/MechanicalParams.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/PythonRestShapeForceField.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/BaseLink.py ${CMAKE_CURRENT_SOURCE_DIR}/Helper/Message.py diff --git a/bindings/Sofa/tests/Core/MechanicalParams.py b/bindings/Sofa/tests/Core/MechanicalParams.py new file mode 100644 index 00000000..828009df --- /dev/null +++ b/bindings/Sofa/tests/Core/MechanicalParams.py @@ -0,0 +1,49 @@ +import Sofa +import unittest + + +class Test(unittest.TestCase): + def test_creation(self): + mparams = Sofa.Core.MechanicalParams() + + # dt + mparams.dt = 1.25 + self.assertEqual(mparams.dt, 1.25) + mparams.setDt(1.50) + self.assertEqual(mparams.dt, 1.50) + + # Implicit + mparams.implicit = True + self.assertTrue(mparams.implicit) + mparams.setImplicit(False) + self.assertFalse(mparams.implicit) + + # Mass factor + mparams.mFactor = 1.30 + self.assertEqual(mparams.mFactor, 1.30) + mparams.setMFactor(1.35) + self.assertEqual(mparams.mFactor, 1.35) + + # Damping factor + mparams.bFactor = 1.35 + self.assertEqual(mparams.bFactor, 1.35) + mparams.setBFactor(1.40) + self.assertEqual(mparams.bFactor, 1.40) + + # Stiffness factor + mparams.kFactor = 1.45 + self.assertEqual(mparams.kFactor, 1.45) + mparams.setKFactor(1.50) + self.assertEqual(mparams.kFactor, 1.50) + + # Symmetric + mparams.symmetricMatrix = True + self.assertTrue(mparams.symmetricMatrix) + mparams.setSymmetricMatrix(False) + self.assertFalse(mparams.symmetricMatrix) + + # Energy + mparams.energy = True + self.assertTrue(mparams.energy) + mparams.setEnergy(False) + self.assertFalse(mparams.energy) From 83fae7dffb13928c33e6623a26bb9b598ece64e9 Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Wed, 26 May 2021 22:32:09 +0200 Subject: [PATCH 3/9] [Bindings] Add VecId bindings Signed-off-by: Jean-Nicolas Brunet --- .../SofaPython3/Sofa/Core/Binding_VecId.cpp | 107 +++++++++++++ .../src/SofaPython3/Sofa/Core/Binding_VecId.h | 28 ++++ .../src/SofaPython3/Sofa/Core/CMakeLists.txt | 2 + .../SofaPython3/Sofa/Core/Submodule_Core.cpp | 2 + bindings/Sofa/tests/CMakeLists.txt | 1 + bindings/Sofa/tests/Core/VecId.py | 149 ++++++++++++++++++ 6 files changed, 289 insertions(+) create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.cpp create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.h create mode 100644 bindings/Sofa/tests/Core/VecId.py diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.cpp new file mode 100644 index 00000000..aec2d272 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.cpp @@ -0,0 +1,107 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include + +namespace py { using namespace pybind11; } +using namespace sofa::core; + +namespace sofapython3 { + +template +void bindVecId(py::module& m, const std::string & name) { + + using StandardVec = TStandardVec; + py::class_ s (m, ("Standard"+name).c_str()); + if constexpr (vtype == VecType::V_COORD || vtype == VecType::V_ALL) { + s.def_static("position", &StandardVec::position); + s.def_static("restPosition", &StandardVec::restPosition); + s.def_static("freePosition", &StandardVec::freePosition); + s.def_static("resetPosition", &StandardVec::resetPosition); + } + if constexpr (vtype == VecType::V_DERIV || vtype == VecType::V_ALL) { + s.def_static("velocity", &StandardVec::velocity); + s.def_static("resetVelocity", &StandardVec::resetVelocity); + s.def_static("freeVelocity", &StandardVec::freeVelocity); + s.def_static("normal", &StandardVec::normal); + s.def_static("force", &StandardVec::force); + s.def_static("externalForce", &StandardVec::externalForce); + s.def_static("dx", &StandardVec::dx); + s.def_static("dforce", &StandardVec::dforce); + } + if constexpr(vtype == VecType::V_MATDERIV || vtype == VecType::V_ALL) { + s.def_static("constraintJacobian", &StandardVec::constraintJacobian); + s.def_static("mappingJacobian", &StandardVec::mappingJacobian); + } + + if constexpr (vtype == VecType::V_ALL) { + s.def_static("getFirstDynamicIndex", &StandardVec::getFirstDynamicIndex, py::arg("t")); + } + + using VecId = TVecId; + py::class_ v (m, name.c_str()); + v.def(py::init()); + if constexpr (vtype != V_ALL) { + py::implicitly_convertible, TVecId>(); + v.def(py::init()); + } + v.def("getName", &VecId::getName); + v.def("__str__", &VecId::getName); +} + +void moduleAddVecId(py::module& m) { + + // VecType + py::enum_ (m, "VecType") + .value("V_ALL", VecType::V_ALL) + .value("V_COORD", VecType::V_COORD) + .value("V_DERIV", VecType::V_DERIV) + .value("V_MATDERIV", VecType::V_MATDERIV) + .export_values() + ; + + // VecAccess + py::enum_ (m, "VecAccess") + .value("V_READ", VecAccess::V_READ) + .value("V_WRITE", VecAccess::V_WRITE) + .export_values() + ; + + // BaseVecId + py::class_ (m, "BaseVecId") + .def_property_readonly("index", &BaseVecId::getIndex) + .def("getIndex", &BaseVecId::getIndex) + .def_property_readonly("type", &BaseVecId::getType) + .def("getType", &BaseVecId::getType) + ; + + // VecId + bindVecId (m, "ConstVecId"); + bindVecId (m, "VecId"); + bindVecId (m, "ConstVecCoordId"); + bindVecId(m, "VecCoordId"); + bindVecId (m, "ConstVecDerivId"); + bindVecId(m, "VecDerivId"); + bindVecId (m, "ConstMatrixDerivId"); + bindVecId(m, "MatrixDerivId"); +} + +} // namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.h new file mode 100644 index 00000000..d518acdc --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_VecId.h @@ -0,0 +1,28 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once +#include + +namespace sofapython3 { + +void moduleAddVecId(pybind11::module &m); + +} /// namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt index 4406f276..bd00785b 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt @@ -42,6 +42,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseData_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseCamera_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Topology.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_VecId.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseMeshTopology.h ) @@ -71,6 +72,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_PythonScriptEvent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseLink.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Topology.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_VecId.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_BaseMeshTopology.cpp ) diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp index 876766f2..1b747ea2 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp @@ -42,6 +42,7 @@ using sofa::helper::logging::Message; #include #include #include +#include #include #include @@ -141,6 +142,7 @@ PYBIND11_MODULE(Core, core) moduleAddPrefab(core); moduleAddBaseLink(core); moduleAddTopology(core); + moduleAddVecId(core); moduleAddBaseMeshTopology(core); } diff --git a/bindings/Sofa/tests/CMakeLists.txt b/bindings/Sofa/tests/CMakeLists.txt index c3139e34..7a929431 100644 --- a/bindings/Sofa/tests/CMakeLists.txt +++ b/bindings/Sofa/tests/CMakeLists.txt @@ -21,6 +21,7 @@ set(PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Core/MechanicalParams.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/PythonRestShapeForceField.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/BaseLink.py + ${CMAKE_CURRENT_SOURCE_DIR}/Core/VecId.py ${CMAKE_CURRENT_SOURCE_DIR}/Helper/Message.py ${CMAKE_CURRENT_SOURCE_DIR}/Types/RGBAColor.py ${CMAKE_CURRENT_SOURCE_DIR}/Types/Vec3.py diff --git a/bindings/Sofa/tests/Core/VecId.py b/bindings/Sofa/tests/Core/VecId.py new file mode 100644 index 00000000..6e9f4e90 --- /dev/null +++ b/bindings/Sofa/tests/Core/VecId.py @@ -0,0 +1,149 @@ +import Sofa +import unittest + + +class VecId(unittest.TestCase): + def test_coord(self): + # Const Coord + vec = Sofa.Core.ConstVecCoordId() + self.assertEqual(vec.index, 0) + self.assertEqual(vec.getIndex(), 0) + self.assertEqual(vec.type, Sofa.Core.VecType.V_COORD) + + vec = Sofa.Core.ConstVecCoordId(15) + self.assertEqual(vec.index, 15) + self.assertEqual(vec.getIndex(), 15) + self.assertEqual(vec.type, Sofa.Core.VecType.V_COORD) + + # Coord + vec = Sofa.Core.VecCoordId() + self.assertEqual(vec.index, 0) + self.assertEqual(vec.getIndex(), 0) + self.assertEqual(vec.type, Sofa.Core.VecType.V_COORD) + + vec = Sofa.Core.VecCoordId(15) + self.assertEqual(vec.index, 15) + self.assertEqual(vec.getIndex(), 15) + self.assertEqual(vec.type, Sofa.Core.VecType.V_COORD) + + def test_deriv(self): + # Const Deriv + vec = Sofa.Core.ConstVecDerivId() + self.assertEqual(vec.index, 0) + self.assertEqual(vec.getIndex(), 0) + self.assertEqual(vec.type, Sofa.Core.VecType.V_DERIV) + + vec = Sofa.Core.ConstVecDerivId(15) + self.assertEqual(vec.index, 15) + self.assertEqual(vec.getIndex(), 15) + self.assertEqual(vec.type, Sofa.Core.VecType.V_DERIV) + + # Deriv + vec = Sofa.Core.VecDerivId() + self.assertEqual(vec.index, 0) + self.assertEqual(vec.getIndex(), 0) + self.assertEqual(vec.type, Sofa.Core.VecType.V_DERIV) + + vec = Sofa.Core.VecDerivId(15) + self.assertEqual(vec.index, 15) + self.assertEqual(vec.getIndex(), 15) + self.assertEqual(vec.type, Sofa.Core.VecType.V_DERIV) + + def test_default_coord_vectors(self): + # Position + self.assertEqual(Sofa.Core.VecCoordId.position().index, 1) + self.assertEqual(Sofa.Core.VecCoordId.position().getName(), "position(V_COORD)") + self.assertEqual(Sofa.Core.ConstVecCoordId.position().index, 1) + self.assertEqual(Sofa.Core.ConstVecCoordId.position().getName(), "position(V_COORD)") + + # restPosition + self.assertEqual(Sofa.Core.VecCoordId.restPosition().index, 2) + self.assertEqual(Sofa.Core.VecCoordId.restPosition().getName(), "restPosition(V_COORD)") + self.assertEqual(Sofa.Core.ConstVecCoordId.restPosition().index, 2) + self.assertEqual(Sofa.Core.ConstVecCoordId.restPosition().getName(), "restPosition(V_COORD)") + + # freePosition + self.assertEqual(Sofa.Core.VecCoordId.freePosition().index, 3) + self.assertEqual(Sofa.Core.VecCoordId.freePosition().getName(), "freePosition(V_COORD)") + self.assertEqual(Sofa.Core.ConstVecCoordId.freePosition().index, 3) + self.assertEqual(Sofa.Core.ConstVecCoordId.freePosition().getName(), "freePosition(V_COORD)") + + # resetPosition + self.assertEqual(Sofa.Core.VecCoordId.resetPosition().index, 4) + self.assertEqual(Sofa.Core.VecCoordId.resetPosition().getName(), "resetPosition(V_COORD)") + self.assertEqual(Sofa.Core.ConstVecCoordId.resetPosition().index, 4) + self.assertEqual(Sofa.Core.ConstVecCoordId.resetPosition().getName(), "resetPosition(V_COORD)") + + # Dynamic offset + self.assertEqual(Sofa.Core.VecId.getFirstDynamicIndex(Sofa.Core.VecType.V_COORD), 5) + self.assertEqual(Sofa.Core.ConstVecId.getFirstDynamicIndex(Sofa.Core.VecType.V_COORD), 5) + + def test_default_deriv_vectors(self): + # velocity + self.assertEqual(Sofa.Core.VecDerivId.velocity().index, 1) + self.assertEqual(Sofa.Core.VecDerivId.velocity().getName(), "velocity(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.velocity().index, 1) + self.assertEqual(Sofa.Core.ConstVecDerivId.velocity().getName(), "velocity(V_DERIV)") + + # resetVelocity + self.assertEqual(Sofa.Core.VecDerivId.resetVelocity().index, 2) + self.assertEqual(Sofa.Core.VecDerivId.resetVelocity().getName(), "resetVelocity(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.resetVelocity().index, 2) + self.assertEqual(Sofa.Core.ConstVecDerivId.resetVelocity().getName(), "resetVelocity(V_DERIV)") + + # freeVelocity + self.assertEqual(Sofa.Core.VecDerivId.freeVelocity().index, 3) + self.assertEqual(Sofa.Core.VecDerivId.freeVelocity().getName(), "freeVelocity(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.freeVelocity().index, 3) + self.assertEqual(Sofa.Core.ConstVecDerivId.freeVelocity().getName(), "freeVelocity(V_DERIV)") + + # normal + self.assertEqual(Sofa.Core.VecDerivId.normal().index, 4) + self.assertEqual(Sofa.Core.VecDerivId.normal().getName(), "normal(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.normal().index, 4) + self.assertEqual(Sofa.Core.ConstVecDerivId.normal().getName(), "normal(V_DERIV)") + + # force + self.assertEqual(Sofa.Core.VecDerivId.force().index, 5) + self.assertEqual(Sofa.Core.VecDerivId.force().getName(), "force(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.force().index, 5) + self.assertEqual(Sofa.Core.ConstVecDerivId.force().getName(), "force(V_DERIV)") + + # externalForce + self.assertEqual(Sofa.Core.VecDerivId.externalForce().index, 6) + self.assertEqual(Sofa.Core.VecDerivId.externalForce().getName(), "externalForce(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.externalForce().index, 6) + self.assertEqual(Sofa.Core.ConstVecDerivId.externalForce().getName(), "externalForce(V_DERIV)") + + # dx + self.assertEqual(Sofa.Core.VecDerivId.dx().index, 7) + self.assertEqual(Sofa.Core.VecDerivId.dx().getName(), "dx(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.dx().index, 7) + self.assertEqual(Sofa.Core.ConstVecDerivId.dx().getName(), "dx(V_DERIV)") + + # dforce + self.assertEqual(Sofa.Core.VecDerivId.dforce().index, 8) + self.assertEqual(Sofa.Core.VecDerivId.dforce().getName(), "dforce(V_DERIV)") + self.assertEqual(Sofa.Core.ConstVecDerivId.dforce().index, 8) + self.assertEqual(Sofa.Core.ConstVecDerivId.dforce().getName(), "dforce(V_DERIV)") + + # Dynamic offset + self.assertEqual(Sofa.Core.VecId.getFirstDynamicIndex(Sofa.Core.VecType.V_DERIV), 9) + self.assertEqual(Sofa.Core.ConstVecId.getFirstDynamicIndex(Sofa.Core.VecType.V_DERIV), 9) + + def test_default_matderiv_vectors(self): + # constraintJacobian + self.assertEqual(Sofa.Core.MatrixDerivId.constraintJacobian().index, 1) + self.assertEqual(Sofa.Core.MatrixDerivId.constraintJacobian().getName(), "holonomic(V_MATDERIV)") + self.assertEqual(Sofa.Core.ConstMatrixDerivId.constraintJacobian().index, 1) + self.assertEqual(Sofa.Core.ConstMatrixDerivId.constraintJacobian().getName(), "holonomic(V_MATDERIV)") + + # mappingJacobian + self.assertEqual(Sofa.Core.MatrixDerivId.mappingJacobian().index, 2) + self.assertEqual(Sofa.Core.MatrixDerivId.mappingJacobian().getName(), "nonHolonomic(V_MATDERIV)") + self.assertEqual(Sofa.Core.ConstMatrixDerivId.mappingJacobian().index, 2) + self.assertEqual(Sofa.Core.ConstMatrixDerivId.mappingJacobian().getName(), "nonHolonomic(V_MATDERIV)") + + # Dynamic offset + self.assertEqual(Sofa.Core.VecId.getFirstDynamicIndex(Sofa.Core.VecType.V_MATDERIV), 3) + self.assertEqual(Sofa.Core.ConstVecId.getFirstDynamicIndex(Sofa.Core.VecType.V_MATDERIV), 3) From d908822f5c4db07a4c58015ff057235030b78d7d Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Wed, 26 May 2021 22:33:06 +0200 Subject: [PATCH 4/9] [Bindings] Add MultiVecId bindings Signed-off-by: Jean-Nicolas Brunet --- .../Sofa/Core/Binding_MultiVecId.cpp | 125 ++++++++++++++++++ .../Sofa/Core/Binding_MultiVecId.h | 28 ++++ .../src/SofaPython3/Sofa/Core/CMakeLists.txt | 2 + .../SofaPython3/Sofa/Core/Submodule_Core.cpp | 2 + bindings/Sofa/tests/CMakeLists.txt | 1 + bindings/Sofa/tests/Core/MultiVecId.py | 38 ++++++ 6 files changed, 196 insertions(+) create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.cpp create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.h create mode 100644 bindings/Sofa/tests/Core/MultiVecId.py diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.cpp new file mode 100644 index 00000000..91787c97 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.cpp @@ -0,0 +1,125 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include + +namespace py { using namespace pybind11; } +using namespace sofa::core; + +namespace sofapython3 { + +template +void bindMultiVecId(py::module& m, const std::string & name) { + using MultiVecId = TMultiVecId; + py::class_ c (m, name.c_str()); + + // Constructors + c.def(py::init()); + + // Read-only can be constructed from both read-only and read-write multi vectors + if constexpr (vaccess == V_READ) { + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + if constexpr(vtype != V_ALL) { + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + } else { + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + } + } + + // Construct from the same access type + c.def(py::init&>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init&>()); + + // Construct from a ALL vector type + if constexpr(vtype != V_ALL) { + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + } else { + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + + c.def(py::init &>()); + py::implicitly_convertible, TMultiVecId>(); + } + + // Public methods/properties + c.def_property_readonly("defaultId", &MultiVecId::getDefaultId); + c.def("getDefaultId", &MultiVecId::getDefaultId); + + c.def("getName", &MultiVecId::getName); + c.def("isNull", &MultiVecId::isNull); + c.def("__str__", &MultiVecId::getName); + c.def("getId", &MultiVecId::getId, py::arg("state")); + + // Static methods/properties + c.def_static("null", &MultiVecId::null); +} + +void moduleAddMultiVecId(py::module& m) { + bindMultiVecId (m, "ConstMultiVecId"); + bindMultiVecId (m, "MultiVecId"); + bindMultiVecId (m, "ConstMultiVecCoordId"); + bindMultiVecId (m, "MultiVecCoordId"); + bindMultiVecId (m, "ConstMultiVecDerivId"); + bindMultiVecId (m, "MultiVecDerivId"); + bindMultiVecId (m, "ConstMultiMatrixDerivId"); + bindMultiVecId (m, "MultiMatrixDerivId"); +} + +} // namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.h new file mode 100644 index 00000000..05b79993 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MultiVecId.h @@ -0,0 +1,28 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once +#include + +namespace sofapython3 { + +void moduleAddMultiVecId(pybind11::module &m); + +} /// namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt index bd00785b..bf1c43b7 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt @@ -24,6 +24,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams_doc.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MultiVecId.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_NodeIterator.h @@ -65,6 +66,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MultiVecId.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_NodeIterator.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Prefab.cpp diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp index 1b747ea2..67a47045 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp @@ -36,6 +36,7 @@ using sofa::helper::logging::Message; #include #include #include +#include #include #include #include @@ -137,6 +138,7 @@ PYBIND11_MODULE(Core, core) moduleAddForceField(core); moduleAddObjectFactory(core); moduleAddMechanicalParams(core); + moduleAddMultiVecId(core); moduleAddNode(core); moduleAddNodeIterator(core); moduleAddPrefab(core); diff --git a/bindings/Sofa/tests/CMakeLists.txt b/bindings/Sofa/tests/CMakeLists.txt index 7a929431..54e62f24 100644 --- a/bindings/Sofa/tests/CMakeLists.txt +++ b/bindings/Sofa/tests/CMakeLists.txt @@ -19,6 +19,7 @@ set(PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Core/DataEngine.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/MyRestShapeForceField.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/MechanicalParams.py + ${CMAKE_CURRENT_SOURCE_DIR}/Core/MultiVecId.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/PythonRestShapeForceField.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/BaseLink.py ${CMAKE_CURRENT_SOURCE_DIR}/Core/VecId.py diff --git a/bindings/Sofa/tests/Core/MultiVecId.py b/bindings/Sofa/tests/Core/MultiVecId.py new file mode 100644 index 00000000..5cf84aff --- /dev/null +++ b/bindings/Sofa/tests/Core/MultiVecId.py @@ -0,0 +1,38 @@ +import Sofa +import unittest + + +class MultiVecId(unittest.TestCase): + """ + Test the creation of MultiVecId + + Note that this test case will not test the allocation inside mechanical object. This is instead tested inside + the vector operations (Sofa.SimulationCore.VectorOperations) test case. + """ + def test_coord(self): + # Const Coord + vec = Sofa.Core.ConstMultiVecCoordId() + self.assertEqual(vec.defaultId.index, 0) + self.assertEqual(vec.getDefaultId().getIndex(), 0) + self.assertEqual(vec.defaultId.type, Sofa.Core.VecType.V_COORD) + + vec = Sofa.Core.ConstMultiVecCoordId(Sofa.Core.ConstVecCoordId(15)) + self.assertEqual(vec.defaultId.index, 15) + self.assertEqual(vec.getDefaultId().getIndex(), 15) + self.assertEqual(vec.defaultId.type, Sofa.Core.VecType.V_COORD) + + vec = Sofa.Core.ConstMultiVecCoordId(Sofa.Core.VecCoordId(15)) + self.assertEqual(vec.defaultId.index, 15) + self.assertEqual(vec.getDefaultId().getIndex(), 15) + self.assertEqual(vec.defaultId.type, Sofa.Core.VecType.V_COORD) + + # Coord + vec = Sofa.Core.MultiVecCoordId() + self.assertEqual(vec.defaultId.index, 0) + self.assertEqual(vec.getDefaultId().getIndex(), 0) + self.assertEqual(vec.defaultId.type, Sofa.Core.VecType.V_COORD) + + vec = Sofa.Core.MultiVecCoordId(Sofa.Core.VecCoordId(15)) + self.assertEqual(vec.defaultId.index, 15) + self.assertEqual(vec.getDefaultId().getIndex(), 15) + self.assertEqual(vec.defaultId.type, Sofa.Core.VecType.V_COORD) From 5350c380a5942b2dcb9045503b7f9c9e1605a91d Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Wed, 26 May 2021 22:34:58 +0200 Subject: [PATCH 5/9] [Bindings] Add SofaSimulationCore module and VectorOperations bindings Signed-off-by: Jean-Nicolas Brunet --- bindings/Modules/CMakeLists.txt | 2 +- .../Binding_VectorOperations.cpp | 67 ++++++++++++ .../SofaSimulationCore/CMakeLists.txt | 21 ++++ .../Module_SofaSimulationCore.cpp | 36 ++++++ bindings/Modules/tests/CMakeLists.txt | 3 +- .../SofaSimulationCore/VectorOperations.py | 103 ++++++++++++++++++ bindings/Modules/tests/main.cpp | 2 +- 7 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_VectorOperations.cpp create mode 100644 bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt create mode 100644 bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp create mode 100644 bindings/Modules/tests/SofaSimulationCore/VectorOperations.py diff --git a/bindings/Modules/CMakeLists.txt b/bindings/Modules/CMakeLists.txt index d653248e..1527fd76 100644 --- a/bindings/Modules/CMakeLists.txt +++ b/bindings/Modules/CMakeLists.txt @@ -1,6 +1,6 @@ project(Bindings.Modules) -set(MODULEBINDINGS_MODULE_LIST SofaBaseTopology SofaDeformable) +set(MODULEBINDINGS_MODULE_LIST SofaBaseTopology SofaDeformable SofaSimulationCore) find_package(Sofa.GL QUIET) if(Sofa.GL_FOUND) diff --git a/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_VectorOperations.cpp b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_VectorOperations.cpp new file mode 100644 index 00000000..9ac478ed --- /dev/null +++ b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_VectorOperations.cpp @@ -0,0 +1,67 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include + +#include +#include + +namespace py { using namespace pybind11; } + +namespace sofapython3 { + +void moduleAddVectorOperations(pybind11::module& m) +{ + using namespace sofa::simulation::common; + + py::class_ c (m, "VectorOperations"); + c.def(py::init([](sofa::core::objectmodel::BaseContext* context, bool precomputedTraversalOrder = false) { + return VectorOperations(sofa::core::ExecParams::defaultInstance(), context, precomputedTraversalOrder); + }), py::arg("context"), py::arg("precomputedTraversalOrder") = false); + + c.def("v_alloc", py::overload_cast(&VectorOperations::v_alloc), py::arg("multi_vector_id")); + c.def("v_alloc", py::overload_cast(&VectorOperations::v_alloc), py::arg("multi_vector_id")); + + c.def("v_free", py::overload_cast(&VectorOperations::v_free), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false); + c.def("v_free", py::overload_cast(&VectorOperations::v_free), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false); + + c.def("v_realloc", py::overload_cast(&VectorOperations::v_realloc), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false); + c.def("v_realloc", py::overload_cast(&VectorOperations::v_realloc), py::arg("multi_vector_id"), py::arg("interactionForceField") = false, py::arg("propagate") = false); + + c.def("v_clear", &VectorOperations::v_clear, py::arg("multi_vector_id")); + c.def("v_size", &VectorOperations::v_size, py::arg("multi_vector_id")); + + c.def("v_eq", py::overload_cast(&VectorOperations::v_eq), py::arg("v").noconvert(false), py::arg("a").noconvert(false), "v = a"); + c.def("v_eq", [](VectorOperations & self, sofa::core::MultiVecId &v, sofa::core::ConstMultiVecId & a){ + self.v_eq(v, a); + }); + c.def("v_eq", py::overload_cast(&VectorOperations::v_eq), py::arg("v").noconvert(false), py::arg("a").noconvert(false), py::arg("f").noconvert(false), "v = f*a"); + c.def("v_peq", &VectorOperations::v_peq, py::arg("v"), py::arg("a"), py::arg("f")=1.0, "v += f*a"); + c.def("v_teq", &VectorOperations::v_teq, py::arg("v"), py::arg("f"), "v *= f"); + c.def("v_op", &VectorOperations::v_op, py::arg("v"), py::arg("a"), py::arg("b"), py::arg("f"), "v=a+b*f"); + + c.def("v_dot", &VectorOperations::v_dot, py::arg("a"), py::arg("b"), "a dot b ( get result using finish )"); + 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."); + c.def("finish", &VectorOperations::finish); + + c.def("v_threshold", &VectorOperations::v_threshold, py::arg("a"), py::arg("threshold"), "Nullify the values below the given threshold."); +} + +} \ No newline at end of file diff --git a/bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt b/bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt new file mode 100644 index 00000000..61c04e8e --- /dev/null +++ b/bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt @@ -0,0 +1,21 @@ +project(Bindings.Modules.SofaSimulationCore) + +set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_VectorOperations.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Module_SofaSimulationCore.cpp +) + +set(HEADER_FILES +) + +find_package(Sofa.SimulationCore REQUIRED) + +SP3_add_python_module( + TARGET ${PROJECT_NAME} + PACKAGE Bindings + MODULE SofaSimulationCore + DESTINATION Sofa + SOURCES ${SOURCE_FILES} + HEADERS ${HEADER_FILES} + DEPENDS SofaSimulationCore SofaPython3::Plugin +) diff --git a/bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp new file mode 100644 index 00000000..40c26618 --- /dev/null +++ b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp @@ -0,0 +1,36 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include + +namespace py { using namespace pybind11; } + +namespace sofapython3 +{ + +void moduleAddVectorOperations(pybind11::module& m); + +PYBIND11_MODULE(SofaSimulationCore, m) +{ + moduleAddVectorOperations(m); +} + +} // namespace sofapython3 \ No newline at end of file diff --git a/bindings/Modules/tests/CMakeLists.txt b/bindings/Modules/tests/CMakeLists.txt index 27632d42..a18eb58d 100644 --- a/bindings/Modules/tests/CMakeLists.txt +++ b/bindings/Modules/tests/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCE_FILES set(PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/SofaDeformable/LinearSpring.py ${CMAKE_CURRENT_SOURCE_DIR}/SofaDeformable/SpringForceField.py + ${CMAKE_CURRENT_SOURCE_DIR}/SofaSimulationCore/VectorOperations.py ) find_package(SofaGTestMain REQUIRED) @@ -26,7 +27,7 @@ sofa_auto_set_target_rpath( add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) -set(DIR_BINDING_LIST SofaBaseTopology SofaDeformable) +set(DIR_BINDING_LIST SofaBaseTopology SofaDeformable SofaSimulationCore) get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) foreach(dir_binding ${DIR_BINDING_LIST}) if (NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${dir_binding}") diff --git a/bindings/Modules/tests/SofaSimulationCore/VectorOperations.py b/bindings/Modules/tests/SofaSimulationCore/VectorOperations.py new file mode 100644 index 00000000..e92973aa --- /dev/null +++ b/bindings/Modules/tests/SofaSimulationCore/VectorOperations.py @@ -0,0 +1,103 @@ +import unittest +import Sofa +from Sofa import SofaSimulationCore +import numpy as np + + +class VectorOperations(unittest.TestCase): + def test_alloc_coord(self): + root = Sofa.Core.Node() + create_scene(root) + Sofa.Simulation.init(root) + + vop = SofaSimulationCore.VectorOperations(root) + + # Create a first multivector + multi_vec_id_1 = Sofa.Core.MultiVecCoordId() + self.assertEqual(multi_vec_id_1.defaultId.index, 0) + self.assertTrue(multi_vec_id_1.isNull()) + vop.v_alloc(multi_vec_id_1) + self.assertFalse(multi_vec_id_1.isNull()) + self.assertEqual(multi_vec_id_1.getId(root.node_1.mo).index, 5) + self.assertEqual(multi_vec_id_1.getId(root.node_2.mo).index, 5) + + # Create a second one + multi_vec_id_2 = Sofa.Core.MultiVecCoordId() + vop.v_alloc(multi_vec_id_2) + self.assertEqual(multi_vec_id_2.getId(root.node_1.mo).index, 6) + self.assertEqual(multi_vec_id_2.getId(root.node_2.mo).index, 6) + + # Create a third one, only in node_2 + vop2 = SofaSimulationCore.VectorOperations(root.node_2) + multi_vec_id_3 = Sofa.Core.MultiVecCoordId() + vop2.v_alloc(multi_vec_id_3) + self.assertEqual(multi_vec_id_3.getId(root.node_1.mo).index, 0) + self.assertEqual(multi_vec_id_3.getId(root.node_2.mo).index, 7) + + # Free + vop.v_free(multi_vec_id_1) + vop.v_free(multi_vec_id_2) + vop2.v_free(multi_vec_id_3) + + def test_alloc_deriv(self): + root = Sofa.Core.Node() + create_scene(root) + Sofa.Simulation.init(root) + + vop = SofaSimulationCore.VectorOperations(root) + + # Create a first multivector + multi_vec_id_1 = Sofa.Core.MultiVecDerivId() + self.assertEqual(multi_vec_id_1.defaultId.index, 0) + self.assertTrue(multi_vec_id_1.isNull()) + vop.v_alloc(multi_vec_id_1) + self.assertFalse(multi_vec_id_1.isNull()) + self.assertEqual(multi_vec_id_1.getId(root.node_1.mo).index, 9) + self.assertEqual(multi_vec_id_1.getId(root.node_2.mo).index, 9) + + # Create a second one + multi_vec_id_2 = Sofa.Core.MultiVecDerivId() + vop.v_alloc(multi_vec_id_2) + self.assertEqual(multi_vec_id_2.getId(root.node_1.mo).index, 10) + self.assertEqual(multi_vec_id_2.getId(root.node_2.mo).index, 10) + + # Create a third one, only in node_2 + vop2 = SofaSimulationCore.VectorOperations(root.node_2) + multi_vec_id_3 = Sofa.Core.MultiVecDerivId() + vop2.v_alloc(multi_vec_id_3) + self.assertEqual(multi_vec_id_3.getId(root.node_1.mo).index, 0) + self.assertEqual(multi_vec_id_3.getId(root.node_2.mo).index, 11) + + # Free + vop.v_free(multi_vec_id_1) + vop.v_free(multi_vec_id_2) + vop2.v_free(multi_vec_id_3) + + def test_op(self): + root = Sofa.Core.Node() + create_scene(root) + Sofa.Simulation.init(root) + + vop = SofaSimulationCore.VectorOperations(root) + + # Create a temporary multivector + v = Sofa.Core.MultiVecCoordId() + vop.v_alloc(v) + + x_id = Sofa.Core.VecCoordId.position() + vop.v_eq(v, x_id) # v = x + vop.v_peq(v, x_id, 2) # v += 2*x + vop.v_dot(v, v) # n = sqrt(v dot v) + n = np.sqrt(vop.finish()) + + x = np.concatenate((root.node_1.mo.position.array(), root.node_2.mo.position.array()), axis=None).flatten() + self.assertEqual(n, np.linalg.norm(x + (2*x))) + + +def create_scene(root): + root.addObject('RequiredPlugin', pluginName='SofaBaseMechanics') + root.addChild('node_1') + root.node_1.addObject('MechanicalObject', name='mo', template='Vec3', position=[[1,1,1], [2,2,2], [3,3,3]]) + + root.addChild('node_2') + root.node_2.addObject('MechanicalObject', name='mo', template='Vec2', position=[[1,1], [2,2], [3,3], [4, 4]]) diff --git a/bindings/Modules/tests/main.cpp b/bindings/Modules/tests/main.cpp index 9248d2e0..bba2992a 100644 --- a/bindings/Modules/tests/main.cpp +++ b/bindings/Modules/tests/main.cpp @@ -36,8 +36,8 @@ static struct Tests : public sofapython3::PythonTestExtractor MessageDispatcher::addHandler(&MainPerComponentLoggingMessageHandler::getInstance()) ; const std::string executable_directory = sofa::helper::Utils::getExecutableDirectory(); - addTestDirectory(executable_directory+"/SofaBaseTopology", "SofaBaseTopology_"); addTestDirectory(executable_directory+"/SofaDeformable", "SofaDeformable_"); + addTestDirectory(executable_directory+"/SofaSimulationCore", "SofaSimulationCore_"); } } python_tests; From eda25cea735a172235a5d70ced8cf5fc464efe1c Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Thu, 27 May 2021 16:21:36 +0200 Subject: [PATCH 6/9] [Bindings] Add ExecParams bindings Signed-off-by: Jean-Nicolas Brunet --- .../Sofa/Core/Binding_ExecParams.cpp | 53 +++++++++++++++++++ .../Sofa/Core/Binding_ExecParams.h | 28 ++++++++++ .../Sofa/Core/Binding_MechanicalParams.cpp | 2 +- .../src/SofaPython3/Sofa/Core/CMakeLists.txt | 2 + .../SofaPython3/Sofa/Core/Submodule_Core.cpp | 2 + 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.cpp create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.h diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.cpp new file mode 100644 index 00000000..b0f04793 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.cpp @@ -0,0 +1,53 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include + +namespace py { using namespace pybind11; } + +namespace sofapython3 { + +void moduleAddExecParams(py::module& m) { + using namespace sofa::core; + py::class_ c (m, "ExecParams", "Class gathering parameters use by most components methods, and transmitted by all visitors"); + c.def(py::init()); + + py::enum_ (c, "ExecMode") + .value("EXEC_NONE", ExecParams::ExecMode::EXEC_NONE) + .value("EXEC_DEFAULT", ExecParams::ExecMode::EXEC_DEFAULT) + .value("EXEC_DEBUG", ExecParams::ExecMode::EXEC_DEBUG) + .value("EXEC_GPU", ExecParams::ExecMode::EXEC_GPU) + .value("EXEC_GRAPH", ExecParams::ExecMode::EXEC_GRAPH) + .export_values() + ; + + // Public properties/methods + c.def("checkValidStorage", &ExecParams::checkValidStorage); + c.def("execMode", &ExecParams::execMode, "Mode of execution requested"); + c.def("threadID", &ExecParams::threadID, "Index of current thread (0 corresponding to the only thread in sequential mode, or first thread in parallel mode)"); + c.def("nbThreads", &ExecParams::nbThreads, "Number of threads currently known to Sofa"); + c.def("update", &ExecParams::update, "Make sure this instance is up-to-date relative to the current thread"); + c.def("setExecMode", &ExecParams::setExecMode, "Request a specific mode of execution"); + c.def("setThreadID", &ExecParams::setThreadID, "Specify the index of the current thread"); + +} + +} // namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.h new file mode 100644 index 00000000..c4cc3ef8 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ExecParams.h @@ -0,0 +1,28 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once +#include + +namespace sofapython3 { + +void moduleAddExecParams(pybind11::module &m); + +} /// namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp index 4f1ad1da..ee56c14a 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_MechanicalParams.cpp @@ -28,7 +28,7 @@ namespace sofapython3 { void moduleAddMechanicalParams(py::module& m) { using namespace sofa::core; - py::class_ c (m, "MechanicalParams", doc::mechanicalparams::MechanicalParamsClass); + py::class_ c (m, "MechanicalParams", doc::mechanicalparams::MechanicalParamsClass); c.def(py::init()); // Dt diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt index bf1c43b7..66208502 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt @@ -18,6 +18,7 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Controller_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_DataEngine.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_DataEngine_doc.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ExecParams.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.h @@ -63,6 +64,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Data/Binding_DataString.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Data/Binding_DataLink.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Data/Binding_DataVectorString.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ExecParams.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams.cpp diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp index 67a47045..8a607567 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp @@ -34,6 +34,7 @@ using sofa::helper::logging::Message; #include #include #include +#include #include #include #include @@ -135,6 +136,7 @@ PYBIND11_MODULE(Core, core) moduleAddContext(core); moduleAddController(core); moduleAddDataEngine(core); + moduleAddExecParams(core); moduleAddForceField(core); moduleAddObjectFactory(core); moduleAddMechanicalParams(core); From 99919d63281c6d01cf6cf1b6b5f10b4b708a2927 Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Thu, 27 May 2021 16:23:11 +0200 Subject: [PATCH 7/9] [Bindings] Add OdeSolver bindings Signed-off-by: Jean-Nicolas Brunet --- .../Sofa/Core/Binding_OdeSolver.cpp | 64 +++++++++++++++++++ .../SofaPython3/Sofa/Core/Binding_OdeSolver.h | 28 ++++++++ .../Sofa/Core/Binding_OdeSolver_doc.h | 45 +++++++++++++ .../src/SofaPython3/Sofa/Core/CMakeLists.txt | 3 + .../SofaPython3/Sofa/Core/Submodule_Core.cpp | 2 + 5 files changed, 142 insertions(+) create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.cpp create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.h create mode 100644 bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver_doc.h diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.cpp new file mode 100644 index 00000000..84325136 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.cpp @@ -0,0 +1,64 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +namespace py { using namespace pybind11; } + +namespace sofapython3 { +using namespace sofa::core::behavior; + +class PyOdeSolver : public OdeSolver { +public: + SOFA_CLASS(PyOdeSolver, OdeSolver); + void solve(const sofa::core::ExecParams* params, SReal dt, sofa::core::MultiVecCoordId xResult, sofa::core::MultiVecDerivId vResult) final { + PythonEnvironment::gil acquire; + PYBIND11_OVERLOAD_PURE(void, OdeSolver, solve, params, dt, xResult, vResult); + } +}; + +void moduleAddOdeSolver(py::module& m) { + py::class_> (m, "OdeSolver", doc::odesolver::OdeSolverClass) + .def(py::init([](py::args & /*args*/, py::kwargs & kwargs) { + auto o = sofa::core::sptr (new PyOdeSolver()); + for (auto kv : kwargs) { + auto key = py::cast(kv.first); + auto value = py::reinterpret_borrow(kv.second); + BindingBase::SetAttr(*o.get(), key, value); + } + return o; + })) + ; + + /// register the BaseState binding in the downcasting subsystem + PythonFactory::registerType([](sofa::core::objectmodel::Base* object) + { + return py::cast(dynamic_cast(object)); + }); +} + +} // namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.h new file mode 100644 index 00000000..1e8b6df1 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver.h @@ -0,0 +1,28 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once +#include + +namespace sofapython3 { + +void moduleAddOdeSolver(pybind11::module &m); + +} /// namespace sofapython3 diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver_doc.h b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver_doc.h new file mode 100644 index 00000000..ffccde27 --- /dev/null +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_OdeSolver_doc.h @@ -0,0 +1,45 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#pragma once + +namespace sofapython3::doc::odesolver +{ + +static auto OdeSolverClass = +R"( +Component responsible for timestep integration, i.e. advancing the state from time t to t+dt. + +This class currently control both the integration scheme (explicit, +implicit, static, etc), and the linear system resolution algorithm +(conjugate gradient, matrix direct inversion, etc). Those two aspect will +propably be separated in a future version. + +While all computations required to do the integration step are handled by +this object, they should not be implemented directly in it, but instead +the solver propagates orders (or Visitor) to the other components in the +scenegraph that will locally execute them. This allow for greater +flexibility (the solver can just ask for the forces to be computed without +knowing what type of forces are present), as well as performances +(some computations can be executed in parallel). +)"; + + +} diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt index 66208502..06f5af0a 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/CMakeLists.txt @@ -22,6 +22,8 @@ set(HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_OdeSolver.h + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_OdeSolver_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory_doc.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams.h ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams_doc.h @@ -67,6 +69,7 @@ set(SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ExecParams.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ForceField.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_ObjectFactory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_OdeSolver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalParams.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MultiVecId.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_Node.cpp diff --git a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp index 8a607567..c3b803ac 100644 --- a/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp +++ b/bindings/Sofa/src/SofaPython3/Sofa/Core/Submodule_Core.cpp @@ -36,6 +36,7 @@ using sofa::helper::logging::Message; #include #include #include +#include #include #include #include @@ -139,6 +140,7 @@ PYBIND11_MODULE(Core, core) moduleAddExecParams(core); moduleAddForceField(core); moduleAddObjectFactory(core); + moduleAddOdeSolver(core); moduleAddMechanicalParams(core); moduleAddMultiVecId(core); moduleAddNode(core); From b00ec3826f5c0ff32e17603a81689ef920255314 Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Thu, 27 May 2021 16:23:36 +0200 Subject: [PATCH 8/9] [Bindings] Add MechanicalOperations bindings Signed-off-by: Jean-Nicolas Brunet --- .../Binding_MechanicalOperations.cpp | 85 +++++++++++++++++++ .../SofaSimulationCore/CMakeLists.txt | 1 + .../Module_SofaSimulationCore.cpp | 2 + bindings/Modules/tests/CMakeLists.txt | 1 + .../MechanicalOperations.py | 85 +++++++++++++++++++ examples/StaticSolver.py | 69 +++++++++++++++ 6 files changed, 243 insertions(+) create mode 100644 bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp create mode 100644 bindings/Modules/tests/SofaSimulationCore/MechanicalOperations.py create mode 100644 examples/StaticSolver.py diff --git a/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp new file mode 100644 index 00000000..a78fa143 --- /dev/null +++ b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp @@ -0,0 +1,85 @@ +/****************************************************************************** +* SofaPython3 plugin * +* (c) 2021 CNRS, University of Lille, INRIA * +* * +* This program is free software; you can redistribute it and/or modify it * +* under the terms of the GNU Lesser General Public License as published by * +* the Free Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, but WITHOUT * +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * +* for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this program. If not, see . * +******************************************************************************* +* Contact information: contact@sofa-framework.org * +******************************************************************************/ + +#include + +#include + +namespace py { using namespace pybind11; } + +namespace sofapython3 { + +void moduleAddMechanicalOperations(pybind11::module& m) +{ + using namespace sofa::simulation::common; + + py::class_ c (m, "MechanicalOperations"); + c.def(py::init(), py::arg("mparams"), py::arg("ctx"), py::arg("precomputedTraversalOrder")=false); + + // Mechanical Vector operations + c.def("propagateDx", &MechanicalOperations::propagateDx, py::arg("dx"), py::arg("ignore_flag") = false, "Propagate the given displacement through all mappings"); + c.def("propagateDxAndResetDf", &MechanicalOperations::propagateDxAndResetDf, py::arg("dx"), py::arg("df"), "Propagate the given displacement through all mappings and reset the current force delta"); + c.def("propagateX", &MechanicalOperations::propagateX, py::arg("x"), "Propagate the given position through all mappings"); + c.def("propagateV", &MechanicalOperations::propagateV, py::arg("v"), "Propagate the given velocity through all mappings"); + c.def("propagateXAndV", &MechanicalOperations::propagateXAndV, py::arg("x"), py::arg("v"), "Propagate the given position and velocity through all mappings"); + c.def("propagateXAndResetF", &MechanicalOperations::propagateXAndResetF, py::arg("x"), py::arg("f"), "Propagate the given position through all mappings and reset the current force delta"); + c.def("projectPosition", &MechanicalOperations::projectPosition, py::arg("x"), py::arg("time") = 0., "Apply projective constraints to the given position vector"); + c.def("projectVelocity", &MechanicalOperations::projectVelocity, py::arg("v"), py::arg("time") = 0., "Apply projective constraints to the given velocity vector"); + c.def("projectResponse", [](MechanicalOperations & self, sofa::core::MultiVecDerivId dx) { + self.projectResponse(dx); + }, py::arg("dx"), "Apply projective constraints to the given vector"); + c.def("projectPositionAndVelocity", &MechanicalOperations::projectPositionAndVelocity, py::arg("x"), py::arg("v"), py::arg("time") = 0., "Apply projective constraints to the given position and velocity vectors"); + c.def("addMdx", &MechanicalOperations::addMdx, py::arg("res"), py::arg("dx"), py::arg("factor") = 1.0, "res += factor M.dx"); + c.def("integrateVelocity", &MechanicalOperations::integrateVelocity, py::arg("res"), py::arg("x"), py::arg("v"), py::arg("dt"), "res = x + v.dt"); + c.def("accFromF", &MechanicalOperations::accFromF, py::arg("a"), py::arg("f"), "a = M^-1 . f"); + c.def("computeEnergy", &MechanicalOperations::computeEnergy, py::arg("kineticEnergy"), py::arg("potentialEnergy"), "Compute Energy"); + c.def("computeForce", py::overload_cast(&MechanicalOperations::computeForce), py::arg("result"), py::arg("clear")=true, py::arg("accumulate")=true, py::arg("neglectingCompliance")=true, "Compute the current force (given the latest propagated position and velocity)"); + c.def("computeForce", py::overload_cast(&MechanicalOperations::computeForce), py::arg("t"), py::arg("f"), py::arg("x"), py::arg("v"), py::arg("neglectingCompliance")=true, "Compute f(x,v) at time t. Parameters x and v not const due to propagation through mappings."); + c.def("computeDf", &MechanicalOperations::computeDf, py::arg("df"), py::arg("clear")=true, py::arg("accumulate")=true, "Compute the current force delta (given the latest propagated displacement)"); + c.def("computeDfV", &MechanicalOperations::computeDfV, py::arg("df"), py::arg("clear")=true, py::arg("accumulate")=true, "Compute the current force delta (given the latest propagated velocity)"); + c.def("addMBKdx", &MechanicalOperations::addMBKdx, py::arg("df"), py::arg("m"), py::arg("b"), py::arg("k"), py::arg("clear")=true, py::arg("accumulate")=true, "accumulate $ df += (m M + b B + k K) dx $ (given the latest propagated displacement)"); + c.def("addMBKv", &MechanicalOperations::addMBKv, py::arg("df"), py::arg("m"), py::arg("b"), py::arg("k"), py::arg("clear")=true, py::arg("accumulate")=true, "accumulate $ df += (m M + b B + k K) velocity $"); + c.def("addSeparateGravity", &MechanicalOperations::addSeparateGravity, py::arg("dt"), py::arg("result") = sofa::core::VecDerivId::velocity(), "Add dt*Gravity to the velocity"); + c.def("computeContactForce", &MechanicalOperations::computeContactForce, py::arg("result")); + c.def("computeContactDf", &MechanicalOperations::computeContactDf, py::arg("df")); + c.def("computeAcc", &MechanicalOperations::computeAcc, py::arg("t"), py::arg("a"), py::arg("x"), py::arg("v"), "Compute a(x,v) at time t. Parameters x and v not const due to propagation through mappings."); + c.def("computeContactAcc", &MechanicalOperations::computeContactAcc, py::arg("t"), py::arg("a"), py::arg("x"), py::arg("v"), "Parameters x and v not const due to propagation through mappings."); + + // Matrix operations using LinearSolver components + c.def("m_resetSystem", &MechanicalOperations::m_resetSystem); + c.def("m_setSystemMBKMatrix", &MechanicalOperations::m_setSystemMBKMatrix, py::arg("m"), py::arg("b"), py::arg("k")); + c.def("m_setSystemRHVector", &MechanicalOperations::m_setSystemRHVector, py::arg("f")); + c.def("m_setSystemLHVector", &MechanicalOperations::m_setSystemLHVector, py::arg("dx")); + c.def("m_solveSystem", &MechanicalOperations::m_solveSystem); + + // Constraints + c.def("solveConstraint", &MechanicalOperations::solveConstraint, py::arg("id"), py::arg("order")); + + // Matrix operations + c.def("getMatrixDimension", py::overload_cast(&MechanicalOperations::getMatrixDimension), py::arg("nrows"), py::arg("ncols"), py::arg("matrix") = nullptr); + c.def("getMatrixDimension", py::overload_cast(&MechanicalOperations::getMatrixDimension), py::arg("matrix")); + c.def("addMBK_ToMatrix", &MechanicalOperations::addMBK_ToMatrix, py::arg("matrix"), py::arg("mFact"), py::arg("bFact"), py::arg("kFact")); + c.def("addSubMBK_ToMatrix", &MechanicalOperations::addSubMBK_ToMatrix, py::arg("matrix"), py::arg("subMatrixIndex"), py::arg("mFact"), py::arg("bFact"), py::arg("kFact")); + c.def("multiVector2BaseVector", &MechanicalOperations::multiVector2BaseVector, py::arg("src"), py::arg("dest"), py::arg("matrix")); + c.def("baseVector2MultiVector", &MechanicalOperations::baseVector2MultiVector, py::arg("src"), py::arg("dest"), py::arg("matrix")); + c.def("multiVectorPeqBaseVector", &MechanicalOperations::multiVectorPeqBaseVector, py::arg("dest"), py::arg("src"), py::arg("matrix")); +} + +} \ No newline at end of file diff --git a/bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt b/bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt index 61c04e8e..bf261d97 100644 --- a/bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt +++ b/bindings/Modules/src/SofaPython3/SofaSimulationCore/CMakeLists.txt @@ -1,6 +1,7 @@ project(Bindings.Modules.SofaSimulationCore) set(SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/Binding_MechanicalOperations.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Binding_VectorOperations.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Module_SofaSimulationCore.cpp ) diff --git a/bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp index 40c26618..b222b774 100644 --- a/bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp +++ b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Module_SofaSimulationCore.cpp @@ -26,10 +26,12 @@ namespace py { using namespace pybind11; } namespace sofapython3 { +void moduleAddMechanicalOperations(pybind11::module& m); void moduleAddVectorOperations(pybind11::module& m); PYBIND11_MODULE(SofaSimulationCore, m) { + moduleAddMechanicalOperations(m); moduleAddVectorOperations(m); } diff --git a/bindings/Modules/tests/CMakeLists.txt b/bindings/Modules/tests/CMakeLists.txt index a18eb58d..01374e6d 100644 --- a/bindings/Modules/tests/CMakeLists.txt +++ b/bindings/Modules/tests/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCE_FILES set(PYTHON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/SofaDeformable/LinearSpring.py ${CMAKE_CURRENT_SOURCE_DIR}/SofaDeformable/SpringForceField.py + ${CMAKE_CURRENT_SOURCE_DIR}/SofaSimulationCore/MechanicalOperations.py ${CMAKE_CURRENT_SOURCE_DIR}/SofaSimulationCore/VectorOperations.py ) diff --git a/bindings/Modules/tests/SofaSimulationCore/MechanicalOperations.py b/bindings/Modules/tests/SofaSimulationCore/MechanicalOperations.py new file mode 100644 index 00000000..c03a3f6a --- /dev/null +++ b/bindings/Modules/tests/SofaSimulationCore/MechanicalOperations.py @@ -0,0 +1,85 @@ +import unittest +import Sofa +from Sofa import SofaSimulationCore +import numpy as np + +""" +In order to test out a good subset of the Mechanical operations, let's do here do a Newton-Raphson solver in python. +""" + + +class StaticOdeSolver (Sofa.Core.OdeSolver): + def solve(self, _, dt, X, V): + mparams = Sofa.Core.MechanicalParams() + vop = SofaSimulationCore.VectorOperations(self.getContext()) + mop = SofaSimulationCore.MechanicalOperations(mparams, self.getContext()) + + # Allocate the solution vector + dx = Sofa.Core.VecId.dx() + vop.v_realloc(dx, interactionForceField=True, propagate=True) + vop.v_clear(dx) + + # Compute the residual + F = Sofa.Core.VecId.force() + mop.computeForce(result=F, clear=True, accumulate=True); + mop.projectResponse(F) + vop.v_dot(F, F) + F_norm = np.sqrt(vop.finish()) + + # Assemble the system + mop.m_setSystemMBKMatrix(m=0, b=0, k=-1) + mop.m_setSystemRHVector(F) + mop.m_setSystemLHVector(dx) + + # Solve the system + mop.m_solveSystem() + vop.v_dot(dx, dx) + dx_norm = np.sqrt(vop.finish()) + + # Propagate the solution + vop.v_peq(X, dx) # X += dx + + # Solve the constraints + # todo: Bind ConstraintParams + # mop.solveConstraint(X, ConstraintParams.ConstOrder.POS) + + print(f"Solved with |F| = {F_norm} and |dx| = {dx_norm}") + + +class MechanicalOperations(unittest.TestCase): + def test_static_solver(self): + root = Sofa.Core.Node() + createScene(root) + Sofa.Simulation.init(root) + for _ in range(5): + Sofa.Simulation.animate(root, root.dt.value) + + middle_node_index = 52 + solution_of_middle_node = np.array([0, 10.0953, -0.285531]) + self.assertLess(np.linalg.norm(solution_of_middle_node - root.mechanics.neumann.mo.position.array()[middle_node_index])/np.linalg.norm(solution_of_middle_node), 1e-5) + + +def createScene(root): + w, l = 2, 10 + nx, ny = 3, 9 + dx, dy = w/nx/2, l/ny/2 + root.addObject('RequiredPlugin', pluginName='SofaLoader SofaBoundaryCondition SofaEngine SofaSimpleFem SofaSparseSolver SofaTopologyMapping') + root.addObject('MeshObjLoader', name='surface', filename='mesh/cylinder.obj') + root.addObject('SparseGridTopology', src='@surface', name='grid', n=[nx, ny, nx]) + root.addChild('mechanics') + root.mechanics.addObject(StaticOdeSolver(name='solver')) + root.mechanics.addObject('SparseLDLSolver') + root.mechanics.addObject('MechanicalObject', name='mo', src='@../grid') + root.mechanics.addObject('HexahedronSetTopologyContainer', name='hexa_topology', hexahedra='@../grid.hexahedra') + root.mechanics.addObject('HexahedronFEMForceField', youngModulus=3000, poissonRatio=0.3) + root.mechanics.addObject('BoxROI', name='fixed_roi', box=[-1-dx,0-dx,-1-dx, 1+dx, 0+dx, 1+dx], drawBoxes=True) + root.mechanics.addObject('FixedConstraint', indices='@fixed_roi.indices') + root.mechanics.addChild('neumann') + root.mechanics.neumann.addObject('MechanicalObject', name='mo') + root.mechanics.neumann.addObject('QuadSetTopologyContainer', name='quad_topology') + root.mechanics.neumann.addObject('QuadSetTopologyModifier') + root.mechanics.neumann.addObject('QuadSetGeometryAlgorithms') + root.mechanics.neumann.addObject('Hexa2QuadTopologicalMapping', input='@../hexa_topology', output='@quad_topology') + root.mechanics.neumann.addObject('SubsetMapping', applyRestPosition=True, input='@../mo', output='@./mo', indices='@quad_topology.points') + root.mechanics.neumann.addObject('BoxROI', name='top_roi', quad='@quad_topology.quads', src='@mo', box=[-1-dx,10-dx,-1-dx, 1+dx, 10+dx, 1+dx], drawBoxes=True) + root.mechanics.neumann.addObject('QuadPressureForceField', pressure=[0, 0, -1], quadList='@top_roi.quadIndices') diff --git a/examples/StaticSolver.py b/examples/StaticSolver.py new file mode 100644 index 00000000..25b95682 --- /dev/null +++ b/examples/StaticSolver.py @@ -0,0 +1,69 @@ +import Sofa +from Sofa import SofaSimulationCore +import numpy as np + +class StaticOdeSolver (Sofa.Core.OdeSolver): + def solve(self, _, dt, X, V): + mparams = Sofa.Core.MechanicalParams() + vop = SofaSimulationCore.VectorOperations(self.getContext()) + mop = SofaSimulationCore.MechanicalOperations(mparams, self.getContext()) + + # Allocate the solution vector + dx = Sofa.Core.VecId.dx() + vop.v_realloc(dx, interactionForceField=True, propagate=True) + vop.v_clear(dx) + + # Compute the residual + F = Sofa.Core.VecId.force() + mop.computeForce(result=F, clear=True, accumulate=True); + mop.projectResponse(F) + vop.v_dot(F, F) + F_norm = np.sqrt(vop.finish()) + + # Assemble the system + mop.m_setSystemMBKMatrix(m=0, b=0, k=-1) + mop.m_setSystemRHVector(F) + mop.m_setSystemLHVector(dx) + + # Solve the system + mop.m_solveSystem() + vop.v_dot(dx, dx) + dx_norm = np.sqrt(vop.finish()) + + # Propagate the solution + vop.v_peq(X, dx) # X += dx + + # Solve the constraints + # todo: Bind ConstraintParams + # mop.solveConstraint(X, ConstraintParams.ConstOrder.POS) + + print(f"Solved with |F| = {F_norm} and |dx| = {dx_norm}") + +def createScene(root): + w, l = 2, 10 + nx, ny = 3, 9 + dx, dy = w/nx/2, l/ny/2 + root.addObject('RequiredPlugin', pluginName='SofaLoader SofaBoundaryCondition SofaEngine SofaSimpleFem SofaImplicitOdeSolver SofaOpenglVisual SofaSparseSolver SofaTopologyMapping') + root.addObject('MeshObjLoader', name='surface', filename='mesh/cylinder.obj') + root.addObject('SparseGridTopology', src='@surface', name='grid', n=[nx, ny, nx]) + root.addChild('mechanics') +# root.mechanics.addObject('StaticSolver', name='solver', printLog=False) + root.mechanics.addObject(StaticOdeSolver(name='solver')) + root.mechanics.addObject('SparseLDLSolver') + root.mechanics.addObject('MechanicalObject', name='mo', src='@../grid') + root.mechanics.addObject('HexahedronSetTopologyContainer', name='hexa_topology', hexahedra='@../grid.hexahedra') + root.mechanics.addObject('HexahedronFEMForceField', youngModulus=3000, poissonRatio=0.3) + root.mechanics.addObject('BoxROI', name='fixed_roi', box=[-1-dx,0-dx,-1-dx, 1+dx, 0+dx, 1+dx], drawBoxes=True) + root.mechanics.addObject('FixedConstraint', indices='@fixed_roi.indices') + root.mechanics.addChild('visual') + root.mechanics.visual.addObject('OglModel', name='vm', src='@/surface') + root.mechanics.visual.addObject('BarycentricMapping', applyRestPosition=True) + root.mechanics.addChild('neumann') + root.mechanics.neumann.addObject('MechanicalObject', name='mo') + root.mechanics.neumann.addObject('QuadSetTopologyContainer', name='quad_topology') + root.mechanics.neumann.addObject('QuadSetTopologyModifier') + root.mechanics.neumann.addObject('QuadSetGeometryAlgorithms') + root.mechanics.neumann.addObject('Hexa2QuadTopologicalMapping', input='@../hexa_topology', output='@quad_topology') + root.mechanics.neumann.addObject('SubsetMapping', applyRestPosition=True, input='@../mo', output='@./mo', indices='@quad_topology.points') + root.mechanics.neumann.addObject('BoxROI', name='top_roi', quad='@quad_topology.quads', src='@mo', box=[-1-dx,10-dx,-1-dx, 1+dx, 10+dx, 1+dx], drawBoxes=True) + root.mechanics.neumann.addObject('QuadPressureForceField', pressure=[0, 0, -1], quadList='@top_roi.quadIndices') From 99713c48c1ad3afce5091f3b18a7ea9a72b96768 Mon Sep 17 00:00:00 2001 From: Jean-Nicolas Brunet Date: Thu, 26 Aug 2021 02:43:11 +0200 Subject: [PATCH 9/9] Remove addSubMBK_ToMatrix binding --- .../SofaSimulationCore/Binding_MechanicalOperations.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp index a78fa143..1ac2f990 100644 --- a/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp +++ b/bindings/Modules/src/SofaPython3/SofaSimulationCore/Binding_MechanicalOperations.cpp @@ -76,10 +76,9 @@ void moduleAddMechanicalOperations(pybind11::module& m) c.def("getMatrixDimension", py::overload_cast(&MechanicalOperations::getMatrixDimension), py::arg("nrows"), py::arg("ncols"), py::arg("matrix") = nullptr); c.def("getMatrixDimension", py::overload_cast(&MechanicalOperations::getMatrixDimension), py::arg("matrix")); c.def("addMBK_ToMatrix", &MechanicalOperations::addMBK_ToMatrix, py::arg("matrix"), py::arg("mFact"), py::arg("bFact"), py::arg("kFact")); - c.def("addSubMBK_ToMatrix", &MechanicalOperations::addSubMBK_ToMatrix, py::arg("matrix"), py::arg("subMatrixIndex"), py::arg("mFact"), py::arg("bFact"), py::arg("kFact")); c.def("multiVector2BaseVector", &MechanicalOperations::multiVector2BaseVector, py::arg("src"), py::arg("dest"), py::arg("matrix")); c.def("baseVector2MultiVector", &MechanicalOperations::baseVector2MultiVector, py::arg("src"), py::arg("dest"), py::arg("matrix")); c.def("multiVectorPeqBaseVector", &MechanicalOperations::multiVectorPeqBaseVector, py::arg("dest"), py::arg("src"), py::arg("matrix")); } -} \ No newline at end of file +}