Integration Schemes
All dynamic simulations assume to discretize the temporal evolution of the system through small time steps. This time step is usually noted dt. An integration scheme is the numerical method describing how to find the approximate solution for ordinary differential equations (ODE).
They are usually called ODESolver in SOFA.
Let's write our ordinary differential equation of a function y as follows:
ODESolver defines how to go from the current time step (t) to the next (t + dt), which will structure the linear system
- explicit contributions depending on the degrees of freedom (DOFs) at the current time step
will contribute to the vector - while implicit contributions depending on the degrees of freedom (DOFs) at the next step
(unknown) will contribute to .
Two categories
Two main categories of integration schemes exist: explicit and implicit schemes. A combination of explicit and implicit methods are also possible, it is called semi-implicit or semi-explicit schemes.
Explicit scheme
An explicit scheme means that the new time step (t + dt) is computed based on information of the previous time step (t):
For instance, in mechanics, internal or external forces would be computed on current known positions
Explicit schemes are usually known as being fast to solve (since the created linear system is lighter) but they require very small time steps, unless they may undergo stability issues. They are known to efficiently solve non-stiff problems.
Explicit ODESolvers in SOFA:
Implicit scheme
An implicit scheme means that the new time step (t + dt) is computed based on information of this next time step (t + dt):
For instance, in mechanics, internal or external forces would be computed on unknown positions at the next time step
Implicit schemes are known as being slower to solve (the outcoming linear system is more complex) but they are way more stable than explicit schemes. Stiff differential equations require the use of implicit schemes.
Implicit ODESolvers in SOFA:
In the SOFA code
The integration scheme is described in the solve()
function of the ODESolver. This solve() function is called by the AnimationLoop (through a dedicated visitor) and builds the complete linear system
Specification of the scheme
The construction of the linear system changes whether the integration scheme is explicit or implicit, which is specified by:
- for explicit cases
- for implicit cases
Build the linear matrix system
The left hand side matrix
MechanicalMatrix
will trigger different visitors, thus different functions to compute the system matrix The right hand side vector b is built through the function:
Again, Depending on the scheme (explicit or implicit, see previous paragraph), the abstract function computeForce
will trigger different visitors, thus different functions to accumulate the forces into the vector
State vectors in ODESolver
In order to build the linear matrix system, the ODESolver uses information contained in state vectors (like DOFs and their derivatives) within the scope of the ODESolver. The ODESolver does not access the state vectors directly. It accesses the state vectors remotely using visitors, which traverse the graph starting from the node which contains the solver. This keeps the implementation of the solver independent of the simulated objects and their types.
Each type of solver may use different auxiliary state vectors to implement their simulation method. State vectors (MultiVec) are allocated and processed in the scope of the solver in a thread-safe way using an instance of simulation::common::VectorOperations. For instance, a Runge-Kutta algorithms needs to save the result of previous time steps.
To create an auxiliary vector, this can be done as follows:
MultiVecCoord pos(&vop, core::VecCoordId::position() ); // standard position vector
MultiVecDeriv acc(&vop); // auxiliary vector
MultiVecCoord previousPos(&vop, previousPosID); // additional vector
Compute the solution
In most cases, the matrix system
Some simple matrix cases provides a diagonal matrix
Rayleigh damping
The Rayleigh damping is a numerical damping. This damping has therefore no physical meaning and must not be mixed up with physical damping (like DiagonalVelocityDampingForceField in SOFA). The Rayleigh damping corresponds to a damping matrix that is proportional to the mass or/and stiffness matrices using coefficients, respectively Rayleigh stiffness factor
When Rayleigh damping is used, the damping matrix becomes the sum of the physical and the numerical (Rayleigh) damping:
You can see the use of Rayleigh mass and stiffness dampings in the solve()
function of the EulerImplicit class (see EulerImplicitSolver.cpp).