MeshMatrixMass
This component belongs to the category of Masses. In the dynamic equation (see Physics integration page), the mass density results from the first derivative in time of the momentum term. The MeshMatrixMass computes the integral of this mass density over the volume of the object geometry. To do so and for any given topology (triangles, quads, tetrahedra or hexahedra), the MeshMatrixMass integrates the mass density inside each elements and sums the mass matrix \(\mathbf{M}\) in the system matrix \(\mathbf{A}\).
Volume integration
As detailed in the Physics integration page, the left hand side part of the linear momentum conservation equals \(\rho\dot{v}\). To integrate over the domain, its weak form will result in the mass matrix:
where \(\phi_j\) are the test functions, which are basis functions ensuring the existence of a solution. Since no exact integration can be performed on a random domain \(\Omega\), the MeshMatrixMass relies on the Finite Element Method (FEM) and accumulates the result of the integral over each finite element (triangles, quads, tetrahedra or hexahedra):
The FEM relies on simple geometries in which any field can be interpolated using shape functions \(\phi_i\) (see FEM at a glance). Note that the same basis functions are chosen for both the test and the shape functions. The interpolation of the acceleration term \(\dot{v}\) thus gives:
By change of variables, the computation of the mass matrix results in solving the following integration of the shape functions \(\phi\) in each element:
Case of a linear tetrahedron
In the case of a linear tetrahedron, the shape functions are:
By replacing the shape functions, we therefore obtain:
We can note that the matrix is symmetric. The integration in the reference (or parent) space \(\boldsymbol{\xi}\) can be numerically computed using a Gauss quadrature (or Gauss point integration). The resulting mass matrix is:
API
Depending on the type of LinearSolver used:
- for iterative solvers, the result of the multiplication between the mass matrix \(\mathbf{M}\) and an approximated solution is computed by the function:
template <class DataTypes, class MassType>
void MeshMatrixMass<DataTypes, MassType>::addMDx(const core::MechanicalParams*, DataVecDeriv& vres, const DataVecDeriv& vdx, SReal factor)
{
const MassVector &vertexMass= d_vertexMassInfo.getValue();
const MassVector &edgeMass= d_edgeMassInfo.getValue();
helper::WriteAccessor< DataVecDeriv > res = vres;
helper::ReadAccessor< DataVecDeriv > dx = vdx;
size_t v0,v1,nbEdges=_topology->getNbEdges();
for (unsigned int i=0; i<dx.size(); i++)
{
res[i] += dx[i] * vertexMass[i] * (Real)factor;
}
for (unsigned int j=0; j<nbEdges; ++j)
{
v0=_topology->getEdge(j)[0];
v1=_topology->getEdge(j)[1];
res[v0] += dx[v1] * edgeMass[j] * (Real)factor;
res[v1] += dx[v0] * edgeMass[j] * (Real)factor;
}
}
- for direct solvers, the mass matrix \(\mathbf{M}\) is built by the function:
template <class DataTypes, class MassType>
void MeshMatrixMass<DataTypes, MassType>::addMToMatrix(const core::MechanicalParams *mparams, const sofa::core::behavior::MultiMatrixAccessor* matrix)
{
const MassVector &vertexMass= d_vertexMassInfo.getValue();
const MassVector &edgeMass= d_edgeMassInfo.getValue();
size_t v0,v1,nbEdges=_topology->getNbEdges();
const int N = defaulttype::DataTypeInfo<Deriv>::size();
AddMToMatrixFunctor<Deriv,MassType> calc;
sofa::core::behavior::MultiMatrixAccessor::MatrixRef r = matrix->getMatrix(this->mstate);
sofa::defaulttype::BaseMatrix* mat = r.matrix;
Real mFactor = (Real)mparams->mFactorIncludingRayleighDamping(this->rayleighMass.getValue());
for (size_t i = 0; i < vertexMass.size(); i++)
{
calc(r.matrix, vertexMass[i], r.offset + N*i, mFactor);
}
for (size_t j = 0; j < nbEdges; ++j)
{
v0 = _topology->getEdge(j)[0];
v1 = _topology->getEdge(j)[1];
calc(r.matrix, edgeMass[j], r.offset + N*v0, r.offset + N*v1, mFactor);
calc(r.matrix, edgeMass[j], r.offset + N*v1, r.offset + N*v0, mFactor);
}
}
Case of mass lumping
Note that using the optional data lumping, it is possible to simply the mass matrix by making it diagonal. This is called mass lumping and it consists in summing all mass values of a line on the diagonal. The DiagonalMass is an optimized version of this mass lumping approach. In case of a linear tetrahedron, if the data lumping is true, the (lumped) mass matrix becomes:
Use lumping with caution since it is a numerical approximation, thus decreasing the accuracy of the integration.
Usage
The MeshMatrixMass requires a MechanicalObject to store the degrees of freedom associated to the nodes, as well as a Topology. An integration scheme and a solver are also necessary to solve the linear system at each time step.
Several topologies are handled by the MeshMatrixMass, namely: triangles, quads, tetrahedra or hexahedra. Only the beam model (edge topology) is not handled by this component.
Compute a mass matrix resulting from the space integration of a density over a domain.
Vec1d,Vec1d
Templates:
- Vec1d,Vec1d
Target: Sofa.Component.Mass
namespace: sofa::component::mass
parents:
- Mass
Data
Name | Description | Default value |
---|---|---|
name | object name | unnamed |
printLog | if true, emits extra messages at runtime. | 0 |
tags | list of the subsets the object belongs to | |
bbox | this object bounding box | |
componentState | The state of the component among (Dirty, Valid, Undefined, Loading, Invalid). | Undefined |
listening | if true, handle the events, otherwise ignore the events | 0 |
rayleighStiffness | Rayleigh damping - stiffness matrix coefficient | 0 |
separateGravity | add separately gravity to velocity computation | 0 |
rayleighMass | Rayleigh damping - mass matrix coefficient | 0 |
massDensity | Specify real and strictly positive value(s) for the mass density. If unspecified or wrongly set, the totalMass information is used. | |
totalMass | Specify the total mass resulting from all particles. If unspecified or wrongly set, the default value is used: totalMass = 1.0 | 1 |
vertexMass | internal values of the particles masses on vertices, supporting topological changes | |
edgeMass | internal values of the particles masses on edges, supporting topological changes | |
computeMassOnRest | If true, the mass of every element is computed based on the rest position rather than the position | 0 |
lumping | If true, the mass matrix is lumped, meaning the mass matrix becomes diagonal (summing all mass values of a line on the diagonal) | 0 |
printMass | boolean if you want to check the mass conservation | 0 |
graph | Graph of the controlled potential | |
Visualization | ||
showGravityCenter | display the center of gravity of the system | 0 |
showAxisSizeFactor | factor length of the axis displayed (only used for rigids) | 1 |
Links
Name | Description | Destination type name |
---|---|---|
context | Graph Node containing this object (or BaseContext::getDefault() if no graph is used) | BaseContext |
slaves | Sub-objects used internally by this object | BaseObject |
master | nullptr for regular objects, or master object for which this object is one sub-objects | BaseObject |
mechanicalStates | List of mechanical states to which this component is associated | BaseMechanicalState |
mstate | MechanicalState used by this component | MechanicalState<Vec1d> |
topology | link to the topology container | BaseMeshTopology |
geometryState | link to the MechanicalObject associated with the geometry | MechanicalState<Vec1d> |
Vec1d,Vec2d
Templates:
- Vec1d,Vec2d
Target: Sofa.Component.Mass
namespace: sofa::component::mass
parents:
- Mass
Data
Name | Description | Default value |
---|---|---|
name | object name | unnamed |
printLog | if true, emits extra messages at runtime. | 0 |
tags | list of the subsets the object belongs to | |
bbox | this object bounding box | |
componentState | The state of the component among (Dirty, Valid, Undefined, Loading, Invalid). | Undefined |
listening | if true, handle the events, otherwise ignore the events | 0 |
rayleighStiffness | Rayleigh damping - stiffness matrix coefficient | 0 |
separateGravity | add separately gravity to velocity computation | 0 |
rayleighMass | Rayleigh damping - mass matrix coefficient | 0 |
massDensity | Specify real and strictly positive value(s) for the mass density. If unspecified or wrongly set, the totalMass information is used. | |
totalMass | Specify the total mass resulting from all particles. If unspecified or wrongly set, the default value is used: totalMass = 1.0 | 1 |
vertexMass | internal values of the particles masses on vertices, supporting topological changes | |
edgeMass | internal values of the particles masses on edges, supporting topological changes | |
computeMassOnRest | If true, the mass of every element is computed based on the rest position rather than the position | 0 |
lumping | If true, the mass matrix is lumped, meaning the mass matrix becomes diagonal (summing all mass values of a line on the diagonal) | 0 |
printMass | boolean if you want to check the mass conservation | 0 |
graph | Graph of the controlled potential | |
Visualization | ||
showGravityCenter | display the center of gravity of the system | 0 |
showAxisSizeFactor | factor length of the axis displayed (only used for rigids) | 1 |
Links
Name | Description | Destination type name |
---|---|---|
context | Graph Node containing this object (or BaseContext::getDefault() if no graph is used) | BaseContext |
slaves | Sub-objects used internally by this object | BaseObject |
master | nullptr for regular objects, or master object for which this object is one sub-objects | BaseObject |
mechanicalStates | List of mechanical states to which this component is associated | BaseMechanicalState |
mstate | MechanicalState used by this component | MechanicalState<Vec1d> |
topology | link to the topology container | BaseMeshTopology |
geometryState | link to the MechanicalObject associated with the geometry | MechanicalState<Vec2d> |
Vec1d,Vec3d
Templates:
- Vec1d,Vec3d
Target: Sofa.Component.Mass
namespace: sofa::component::mass
parents:
- Mass
Data
Name | Description | Default value |
---|---|---|
name | object name | unnamed |
printLog | if true, emits extra messages at runtime. | 0 |
tags | list of the subsets the object belongs to | |
bbox | this object bounding box | |
componentState | The state of the component among (Dirty, Valid, Undefined, Loading, Invalid). | Undefined |
listening | if true, handle the events, otherwise ignore the events | 0 |
rayleighStiffness | Rayleigh damping - stiffness matrix coefficient | 0 |
separateGravity | add separately gravity to velocity computation | 0 |
rayleighMass | Rayleigh damping - mass matrix coefficient | 0 |
massDensity | Specify real and strictly positive value(s) for the mass density. If unspecified or wrongly set, the totalMass information is used. | |
totalMass | Specify the total mass resulting from all particles. If unspecified or wrongly set, the default value is used: totalMass = 1.0 | 1 |
vertexMass | internal values of the particles masses on vertices, supporting topological changes | |
edgeMass | internal values of the particles masses on edges, supporting topological changes | |
computeMassOnRest | If true, the mass of every element is computed based on the rest position rather than the position | 0 |
lumping | If true, the mass matrix is lumped, meaning the mass matrix becomes diagonal (summing all mass values of a line on the diagonal) | 0 |
printMass | boolean if you want to check the mass conservation | 0 |
graph | Graph of the controlled potential | |
Visualization | ||
showGravityCenter | display the center of gravity of the system | 0 |
showAxisSizeFactor | factor length of the axis displayed (only used for rigids) | 1 |
Links
Name | Description | Destination type name |
---|---|---|
context | Graph Node containing this object (or BaseContext::getDefault() if no graph is used) | BaseContext |
slaves | Sub-objects used internally by this object | BaseObject |
master | nullptr for regular objects, or master object for which this object is one sub-objects | BaseObject |
mechanicalStates | List of mechanical states to which this component is associated | BaseMechanicalState |
mstate | MechanicalState used by this component | MechanicalState<Vec1d> |
topology | link to the topology container | BaseMeshTopology |
geometryState | link to the MechanicalObject associated with the geometry | MechanicalState<Vec3d> |
Vec2d,Vec2d
Templates:
- Vec2d,Vec2d
Target: Sofa.Component.Mass
namespace: sofa::component::mass
parents:
- Mass
Data
Name | Description | Default value |
---|---|---|
name | object name | unnamed |
printLog | if true, emits extra messages at runtime. | 0 |
tags | list of the subsets the object belongs to | |
bbox | this object bounding box | |
componentState | The state of the component among (Dirty, Valid, Undefined, Loading, Invalid). | Undefined |
listening | if true, handle the events, otherwise ignore the events | 0 |
rayleighStiffness | Rayleigh damping - stiffness matrix coefficient | 0 |
separateGravity | add separately gravity to velocity computation | 0 |
rayleighMass | Rayleigh damping - mass matrix coefficient | 0 |
massDensity | Specify real and strictly positive value(s) for the mass density. If unspecified or wrongly set, the totalMass information is used. | |
totalMass | Specify the total mass resulting from all particles. If unspecified or wrongly set, the default value is used: totalMass = 1.0 | 1 |
vertexMass | internal values of the particles masses on vertices, supporting topological changes | |
edgeMass | internal values of the particles masses on edges, supporting topological changes | |
computeMassOnRest | If true, the mass of every element is computed based on the rest position rather than the position | 0 |
lumping | If true, the mass matrix is lumped, meaning the mass matrix becomes diagonal (summing all mass values of a line on the diagonal) | 0 |
printMass | boolean if you want to check the mass conservation | 0 |
graph | Graph of the controlled potential | |
Visualization | ||
showGravityCenter | display the center of gravity of the system | 0 |
showAxisSizeFactor | factor length of the axis displayed (only used for rigids) | 1 |
Links
Name | Description | Destination type name |
---|---|---|
context | Graph Node containing this object (or BaseContext::getDefault() if no graph is used) | BaseContext |
slaves | Sub-objects used internally by this object | BaseObject |
master | nullptr for regular objects, or master object for which this object is one sub-objects | BaseObject |
mechanicalStates | List of mechanical states to which this component is associated | BaseMechanicalState |
mstate | MechanicalState used by this component | MechanicalState<Vec2d> |
topology | link to the topology container | BaseMeshTopology |
geometryState | link to the MechanicalObject associated with the geometry | MechanicalState<Vec2d> |
Vec2d,Vec3d
Templates:
- Vec2d,Vec3d
Target: Sofa.Component.Mass
namespace: sofa::component::mass
parents:
- Mass
Data
Name | Description | Default value |
---|---|---|
name | object name | unnamed |
printLog | if true, emits extra messages at runtime. | 0 |
tags | list of the subsets the object belongs to | |
bbox | this object bounding box | |
componentState | The state of the component among (Dirty, Valid, Undefined, Loading, Invalid). | Undefined |
listening | if true, handle the events, otherwise ignore the events | 0 |
rayleighStiffness | Rayleigh damping - stiffness matrix coefficient | 0 |
separateGravity | add separately gravity to velocity computation | 0 |
rayleighMass | Rayleigh damping - mass matrix coefficient | 0 |
massDensity | Specify real and strictly positive value(s) for the mass density. If unspecified or wrongly set, the totalMass information is used. | |
totalMass | Specify the total mass resulting from all particles. If unspecified or wrongly set, the default value is used: totalMass = 1.0 | 1 |
vertexMass | internal values of the particles masses on vertices, supporting topological changes | |
edgeMass | internal values of the particles masses on edges, supporting topological changes | |
computeMassOnRest | If true, the mass of every element is computed based on the rest position rather than the position | 0 |
lumping | If true, the mass matrix is lumped, meaning the mass matrix becomes diagonal (summing all mass values of a line on the diagonal) | 0 |
printMass | boolean if you want to check the mass conservation | 0 |
graph | Graph of the controlled potential | |
Visualization | ||
showGravityCenter | display the center of gravity of the system | 0 |
showAxisSizeFactor | factor length of the axis displayed (only used for rigids) | 1 |
Links
Name | Description | Destination type name |
---|---|---|
context | Graph Node containing this object (or BaseContext::getDefault() if no graph is used) | BaseContext |
slaves | Sub-objects used internally by this object | BaseObject |
master | nullptr for regular objects, or master object for which this object is one sub-objects | BaseObject |
mechanicalStates | List of mechanical states to which this component is associated | BaseMechanicalState |
mstate | MechanicalState used by this component | MechanicalState<Vec2d> |
topology | link to the topology container | BaseMeshTopology |
geometryState | link to the MechanicalObject associated with the geometry | MechanicalState<Vec3d> |
Vec3d,Vec3d
Templates:
- Vec3d,Vec3d
Target: Sofa.Component.Mass
namespace: sofa::component::mass
parents:
- Mass
Data
Name | Description | Default value |
---|---|---|
name | object name | unnamed |
printLog | if true, emits extra messages at runtime. | 0 |
tags | list of the subsets the object belongs to | |
bbox | this object bounding box | |
componentState | The state of the component among (Dirty, Valid, Undefined, Loading, Invalid). | Undefined |
listening | if true, handle the events, otherwise ignore the events | 0 |
rayleighStiffness | Rayleigh damping - stiffness matrix coefficient | 0 |
separateGravity | add separately gravity to velocity computation | 0 |
rayleighMass | Rayleigh damping - mass matrix coefficient | 0 |
massDensity | Specify real and strictly positive value(s) for the mass density. If unspecified or wrongly set, the totalMass information is used. | |
totalMass | Specify the total mass resulting from all particles. If unspecified or wrongly set, the default value is used: totalMass = 1.0 | 1 |
vertexMass | internal values of the particles masses on vertices, supporting topological changes | |
edgeMass | internal values of the particles masses on edges, supporting topological changes | |
computeMassOnRest | If true, the mass of every element is computed based on the rest position rather than the position | 0 |
lumping | If true, the mass matrix is lumped, meaning the mass matrix becomes diagonal (summing all mass values of a line on the diagonal) | 0 |
printMass | boolean if you want to check the mass conservation | 0 |
graph | Graph of the controlled potential | |
Visualization | ||
showGravityCenter | display the center of gravity of the system | 0 |
showAxisSizeFactor | factor length of the axis displayed (only used for rigids) | 1 |
Links
Name | Description | Destination type name |
---|---|---|
context | Graph Node containing this object (or BaseContext::getDefault() if no graph is used) | BaseContext |
slaves | Sub-objects used internally by this object | BaseObject |
master | nullptr for regular objects, or master object for which this object is one sub-objects | BaseObject |
mechanicalStates | List of mechanical states to which this component is associated | BaseMechanicalState |
mstate | MechanicalState used by this component | MechanicalState<Vec3d> |
topology | link to the topology container | BaseMeshTopology |
geometryState | link to the MechanicalObject associated with the geometry | MechanicalState<Vec3d> |
Examples
MeshMatrixMass.scn
<?xml version="1.0" ?>
<Node name="root" dt="0.005">
<RequiredPlugin name="Sofa.Component.Collision.Detection.Algorithm"/> <!-- Needed to use components [BVHNarrowPhase BruteForceBroadPhase CollisionPipeline] -->
<RequiredPlugin name="Sofa.Component.Collision.Detection.Intersection"/> <!-- Needed to use components [DiscreteIntersection] -->
<RequiredPlugin name="Sofa.Component.Collision.Geometry"/> <!-- Needed to use components [SphereCollisionModel] -->
<RequiredPlugin name="Sofa.Component.Collision.Response.Contact"/> <!-- Needed to use components [CollisionResponse] -->
<RequiredPlugin name="Sofa.Component.Constraint.Projective"/> <!-- Needed to use components [FixedProjectiveConstraint] -->
<RequiredPlugin name="Sofa.Component.IO.Mesh"/> <!-- Needed to use components [MeshGmshLoader MeshOBJLoader SphereLoader] -->
<RequiredPlugin name="Sofa.Component.LinearSolver.Iterative"/> <!-- Needed to use components [CGLinearSolver] -->
<RequiredPlugin name="Sofa.Component.Mapping.Linear"/> <!-- Needed to use components [BarycentricMapping] -->
<RequiredPlugin name="Sofa.Component.Mass"/> <!-- Needed to use components [MeshMatrixMass] -->
<RequiredPlugin name="Sofa.Component.ODESolver.Backward"/> <!-- Needed to use components [EulerImplicitSolver] -->
<RequiredPlugin name="Sofa.Component.SolidMechanics.FEM.Elastic"/> <!-- Needed to use components [TetrahedralCorotationalFEMForceField] -->
<RequiredPlugin name="Sofa.Component.StateContainer"/> <!-- Needed to use components [MechanicalObject] -->
<RequiredPlugin name="Sofa.Component.Topology.Container.Dynamic"/> <!-- Needed to use components [TetrahedronSetGeometryAlgorithms TetrahedronSetTopologyContainer] -->
<RequiredPlugin name="Sofa.Component.Visual"/> <!-- Needed to use components [VisualStyle] -->
<RequiredPlugin name="Sofa.GL.Component.Rendering3D"/> <!-- Needed to use components [OglModel] -->
<VisualStyle displayFlags="showBehaviorModels showForceFields" />
<DefaultAnimationLoop/>
<CollisionPipeline verbose="0" name="CollisionPipeline" />
<BruteForceBroadPhase/>
<BVHNarrowPhase/>
<CollisionResponse response="PenalityContactForceField" name="collision response" />
<DiscreteIntersection />
<MeshGmshLoader name="MeshLoader" filename="mesh/liver.msh" />
<MeshOBJLoader name="LiverSurface" filename="mesh/liver-smooth.obj" />
<Node name="Liver">
<EulerImplicitSolver name="integration scheme" />
<CGLinearSolver name="linear solver" iterations="1000" tolerance="1e-9" threshold="1e-9"/>
<MechanicalObject name="dofs" src="@../MeshLoader"/>
<!-- Container for the tetrahedra-->
<TetrahedronSetTopologyContainer name="TetraTopo" src="@../MeshLoader"/>
<TetrahedronSetGeometryAlgorithms name="GeomAlgo" />
<MeshMatrixMass totalMass="60" name="SparseMass" topology="@TetraTopo" />
<TetrahedralCorotationalFEMForceField template="Vec3" name="FEM" method="large" poissonRatio="0.45" youngModulus="5000" />
<FixedProjectiveConstraint name="FixedProjectiveConstraint" indices="3 39 64" />
<Node name="Visu" >
<OglModel name="VisualModel" src="@../../LiverSurface" color="cyan"/>
<BarycentricMapping name="VisualMapping" input="@../dofs" output="@VisualModel" />
</Node>
<Node name="Surf" >
<SphereLoader filename="mesh/liver.sph" />
<MechanicalObject name="spheres" position="@[-1].position" />
<SphereCollisionModel name="CollisionModel" listRadius="@[-2].listRadius"/>
<BarycentricMapping name="CollisionMapping" input="@../dofs" output="@spheres" />
</Node>
</Node>
</Node>
def createScene(root_node):
root = root_node.addChild('root', dt="0.005")
root.addObject('RequiredPlugin', name="Sofa.Component.Collision.Detection.Algorithm")
root.addObject('RequiredPlugin', name="Sofa.Component.Collision.Detection.Intersection")
root.addObject('RequiredPlugin', name="Sofa.Component.Collision.Geometry")
root.addObject('RequiredPlugin', name="Sofa.Component.Collision.Response.Contact")
root.addObject('RequiredPlugin', name="Sofa.Component.Constraint.Projective")
root.addObject('RequiredPlugin', name="Sofa.Component.IO.Mesh")
root.addObject('RequiredPlugin', name="Sofa.Component.LinearSolver.Iterative")
root.addObject('RequiredPlugin', name="Sofa.Component.Mapping.Linear")
root.addObject('RequiredPlugin', name="Sofa.Component.Mass")
root.addObject('RequiredPlugin', name="Sofa.Component.ODESolver.Backward")
root.addObject('RequiredPlugin', name="Sofa.Component.SolidMechanics.FEM.Elastic")
root.addObject('RequiredPlugin', name="Sofa.Component.StateContainer")
root.addObject('RequiredPlugin', name="Sofa.Component.Topology.Container.Dynamic")
root.addObject('RequiredPlugin', name="Sofa.Component.Visual")
root.addObject('RequiredPlugin', name="Sofa.GL.Component.Rendering3D")
root.addObject('VisualStyle', displayFlags="showBehaviorModels showForceFields")
root.addObject('DefaultAnimationLoop', )
root.addObject('CollisionPipeline', verbose="0", name="CollisionPipeline")
root.addObject('BruteForceBroadPhase', )
root.addObject('BVHNarrowPhase', )
root.addObject('CollisionResponse', response="PenalityContactForceField", name="collision response")
root.addObject('DiscreteIntersection', )
root.addObject('MeshGmshLoader', name="MeshLoader", filename="mesh/liver.msh")
root.addObject('MeshOBJLoader', name="LiverSurface", filename="mesh/liver-smooth.obj")
liver = root.addChild('Liver')
liver.addObject('EulerImplicitSolver', name="integration scheme")
liver.addObject('CGLinearSolver', name="linear solver", iterations="1000", tolerance="1e-9", threshold="1e-9")
liver.addObject('MechanicalObject', name="dofs", src="@../MeshLoader")
liver.addObject('TetrahedronSetTopologyContainer', name="TetraTopo", src="@../MeshLoader")
liver.addObject('TetrahedronSetGeometryAlgorithms', name="GeomAlgo")
liver.addObject('MeshMatrixMass', totalMass="60", name="SparseMass", topology="@TetraTopo")
liver.addObject('TetrahedralCorotationalFEMForceField', template="Vec3", name="FEM", method="large", poissonRatio="0.45", youngModulus="5000")
liver.addObject('FixedProjectiveConstraint', name="FixedProjectiveConstraint", indices="3 39 64")
visu = Liver.addChild('Visu')
visu.addObject('OglModel', name="VisualModel", src="@../../LiverSurface", color="cyan")
visu.addObject('BarycentricMapping', name="VisualMapping", input="@../dofs", output="@VisualModel")
surf = Liver.addChild('Surf')
surf.addObject('SphereLoader', filename="mesh/liver.sph")
surf.addObject('MechanicalObject', name="spheres", position="@[-1].position")
surf.addObject('SphereCollisionModel', name="CollisionModel", listRadius="@[-2].listRadius")
surf.addObject('BarycentricMapping', name="CollisionMapping", input="@../dofs", output="@spheres")