Skip to content

Commit 5c23226

Browse files
committed
Introduce bindings to assemble components matrices
1 parent 33070ab commit 5c23226

3 files changed

Lines changed: 112 additions & 9 deletions

File tree

bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ForceField.cpp

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
#include <sofa/core/behavior/MechanicalState.h>
2929
#include <sofa/core/behavior/ForceField.h>
3030
#include <sofa/core/MechanicalParams.h>
31-
#include <sofa/core/behavior/MultiMatrixAccessor.h>
31+
#include <sofa/core/behavior/DefaultMultiMatrixAccessor.h>
32+
#include <sofa/linearalgebra/CompressedRowSparseMatrix.h>
33+
34+
#include <pybind11/eigen.h>
3235

3336
#include <SofaPython3/PythonEnvironment.h>
3437
using sofapython3::PythonEnvironment;
@@ -177,10 +180,10 @@ namespace sofapython3
177180

178181

179182
template<class TDOFType>
180-
void declare_forcefield(py::module &m, std::string typestr) {
181-
std::string pyclass_name = std::string("ForceField") + typestr;
183+
void declare_forcefield(py::module &m) {
184+
const std::string pyclass_name = std::string("ForceField") + TDOFType::Name();
182185
py::class_<ForceField<TDOFType>, BaseObject, ForceField_Trampoline<TDOFType>, py_shared_ptr<ForceField<TDOFType>>> f(m, pyclass_name.c_str(), py::dynamic_attr(), py::multiple_inheritance(), sofapython3::doc::forceField::forceFieldClass);
183-
186+
184187
f.def(py::init([](py::args &args, py::kwargs &kwargs) {
185188
auto ff = sofa::core::sptr<ForceField_Trampoline<TDOFType>> (new ForceField_Trampoline<TDOFType>());
186189

@@ -204,15 +207,50 @@ namespace sofapython3
204207
}
205208
return ff;
206209
}));
210+
211+
using Real = typename TDOFType::Real;
212+
using EigenSparseMatrix = Eigen::SparseMatrix<typename TDOFType::Real, Eigen::RowMajor>;
213+
using EigenMatrixMap = Eigen::Map<EigenSparseMatrix>;
214+
215+
f.def("assembleKMatrix", [](ForceField<TDOFType>& self) -> EigenSparseMatrix
216+
{
217+
sofa::linearalgebra::CompressedRowSparseMatrix<Real> matrix;
218+
219+
if (const auto* mstate = self.getMState())
220+
{
221+
const auto matrixSize = static_cast<sofa::linearalgebra::BaseMatrix::Index>(mstate->getMatrixSize());
222+
matrix.resize(matrixSize, matrixSize);
223+
224+
sofa::core::behavior::DefaultMultiMatrixAccessor accessor;
225+
accessor.addMechanicalState(mstate);
226+
accessor.setGlobalMatrix(&matrix);
227+
228+
auto mparams = *MechanicalParams::defaultInstance();
229+
mparams.setKFactor(1.).setMFactor(0.).setBFactor(0.);
230+
231+
self.addKToMatrix(&mparams, &accessor);
232+
}
233+
matrix.compress();
234+
235+
return EigenMatrixMap(matrix.rows(), matrix.cols(), matrix.getColsValue().size(),
236+
(typename EigenMatrixMap::StorageIndex*)matrix.rowBegin.data(),
237+
(typename EigenMatrixMap::StorageIndex*)matrix.colsIndex.data(),
238+
matrix.colsValue.data());
239+
}, sofapython3::doc::forceField::assembleKMatrix);
240+
241+
PythonFactory::registerType<ForceField<TDOFType>>([](sofa::core::objectmodel::Base* object)
242+
{
243+
return py::cast(dynamic_cast<ForceField<TDOFType>*>(object));
244+
});
207245
}
208246

209247

210248
void moduleAddForceField(py::module &m) {
211-
declare_forcefield<Vec3dTypes>(m, "Vec3d");
212-
declare_forcefield<Vec2dTypes>(m, "Vec2d");
213-
declare_forcefield<Vec1dTypes>(m, "Vec1d");
214-
declare_forcefield<Rigid3dTypes>(m, "Rigid3d");
215-
declare_forcefield<Rigid2dTypes>(m, "Rigid2d");
249+
declare_forcefield<Vec3dTypes>(m);
250+
declare_forcefield<Vec2dTypes>(m);
251+
declare_forcefield<Vec1dTypes>(m);
252+
declare_forcefield<Rigid3dTypes>(m);
253+
declare_forcefield<Rigid2dTypes>(m);
216254
}
217255

218256
} // namespace sofapython3

bindings/Sofa/src/SofaPython3/Sofa/Core/Binding_ForceField_doc.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,20 @@ namespace sofapython3::doc::forceField
2525
static auto forceFieldClass = R"(
2626
An overridable class to create your own customized force field.
2727
)";
28+
29+
static constexpr const char* assembleKMatrix = R"(
30+
Assemble the stiffness matrix of a force field.
31+
32+
Note that this function is not free. It assembles the stiffness matrix whether or not it has been assembled previously
33+
to add it into the global system matrix. Besides, the function does not prevent side effects of a second matrix assembly
34+
in a single time step.
35+
36+
Typical usage example:
37+
FEM = root.addObject('HexahedronFEMForceField', name="FEM", youngModulus="4000", poissonRatio="0.3", method="large")
38+
...
39+
stiffness_matrix = self.force_field.assembleKMatrix()
40+
41+
Returns:
42+
A scipy.sparse.csr_matrix object representing the stiffness matrix of the force field
43+
)";
2844
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Required import for python
2+
import Sofa
3+
import SofaRuntime
4+
from Sofa import SofaLinearSolver
5+
from scipy import sparse
6+
from scipy import linalg
7+
8+
# Function called when the scene graph is being created
9+
def createScene(root):
10+
11+
root.addObject('VisualStyle', displayFlags="showBehaviorModels showForceFields")
12+
13+
root.addObject('DefaultAnimationLoop')
14+
root.addObject('DefaultVisualManagerLoop')
15+
16+
root.addObject('EulerImplicitSolver', rayleighStiffness="0.1", rayleighMass="0.1")
17+
root.addObject('SparseLDLSolver', applyPermutation="false", template="CompressedRowSparseMatrixd")
18+
19+
root.addObject('MechanicalObject', name="DoFs")
20+
root.addObject('UniformMass', name="mass", totalMass="320")
21+
root.addObject('RegularGridTopology', name="grid", nx="4", ny="4", nz="20", xmin="-9", xmax="-6", ymin="0", ymax="3", zmin="0", zmax="19")
22+
root.addObject('BoxROI', name="box", box="-10 -1 -0.0001 -5 4 0.0001")
23+
root.addObject('FixedConstraint', indices="@box.indices")
24+
FEM = root.addObject('HexahedronFEMForceField', name="FEM", youngModulus="4000", poissonRatio="0.3", method="large")
25+
26+
root.addObject(MatrixAccessController('MatrixAccessor', name='matrixAccessor', force_field=FEM))
27+
28+
return root
29+
30+
31+
class MatrixAccessController(Sofa.Core.Controller):
32+
33+
34+
def __init__(self, *args, **kwargs):
35+
Sofa.Core.Controller.__init__(self, *args, **kwargs)
36+
self.force_field = kwargs.get("force_field")
37+
38+
def onAnimateEndEvent(self, event):
39+
stiffness_matrix = self.force_field.assembleKMatrix()
40+
41+
print('====================================')
42+
print('Stiffness matrix')
43+
print('====================================')
44+
print('dtype: ' + str(stiffness_matrix.dtype))
45+
print('shape: ' + str(stiffness_matrix.shape))
46+
print('ndim: ' + str(stiffness_matrix.ndim))
47+
print('nnz: ' + str(stiffness_matrix.nnz))
48+
print('norm: ' + str(sparse.linalg.norm(stiffness_matrix)))
49+

0 commit comments

Comments
 (0)