# Motion integration¶

Motion integration consists in computing accelerations from forces/torques acting on nodes carrying mass and updating their positions/oritentations. Velocities are update based on accelerations, and positions are advanced.

Note

Let us repeat: It must be carefully observed for which time-point is any value valid; this is show by using time-indices \(\prev{x}\equiv x^{(t-\Dt)}\), \(\pprev{x}\equiv x^{(t-\Dt/2)}\), \(\curr{x}\equiv x^{(t)}\), \(\nnext{x} \equiv x^{(t+\Dt/2)}\), \(\next{x} \equiv x^{(t+\Dt)}\). Because of the leap-frog integration scheme, even derivatives of position (position, acceleration, forces, torques) are known at full-steps while odd derivatives (velocity) are known at mid-steps.

Motion integration is implemented in `woo.dem.Leapfrog`

.

The summary force \(\vec{F}_{\Sigma}\) and torque \(\vec{T}_{\Sigma}\) include contributions from all relevant contacts, fields (such as gravity) and internal forces (e.g. from Membrane element).

Todo

Mention how are forces from contacts summed up.

## Position¶

Integrating position consists in using current acceleration \(\curr{\vec{a}}\equiv\curr{\ddot{\vec{u}}}\) on a node to update its position from the current value of `position`

\(\curr{\vec{u}}\) to its value at the next timestep \(\next{\vec{u}}\); we know the node’s `velocity`

\(\pprev{\vec{v}}\equiv\pprev{\dot{\vec{u}}}\). Computation of acceleration, knowing the `current force`

\(\vec{F}_{\Sigma}\) acting on the node in question and its `mass`

\(m\), is simply

Using the 2-nd order finite difference approximation with step \(\Dt\), we obtain

from which we express

Typically, \(\prev{\vec{u}}\) is already not known (only \(\curr{\vec{u}}\) is); we notice, however, that

i.e. the mean velocity during the previous step, which is known. Plugging this approximate into the \((\dagger)\) term, we also notice that mean velocity during the current step can be approximated as

which is \((\dagger)\); we arrive finally at

The algorithm can then be written down by first computing current mean velocity \(\nnext{\vec{v}}\) which we need to store for the next step (just as we use its old value \(\pprev{\vec{v}}\) now), then computing the position for the next time step \(\next{\vec{u}}\):

Positions are known at times \(i\Delta t\) (if \(\Delta t\) is constant) while velocities are known at \(i\Delta t+\frac{\Delta t}{2}\). The fact that they interleave (jump over each other) in such way gave rise to the colloquial name “leap-frog”.

Note

The initial (often zero) nodal velocity `DemData.vel`

, before motion is integrated for the first time, is usually understood to be \(\curr{\vec{v}}\) rather than \(\pprev{\vec{v}}\). In that case, (28) is modified to read \(\nnext{\vec{v}}=\curr{\vec{v}}+\curr{\vec{a}}\Dt/2\). The `DemData.velMidstep`

bit is used to know whether the node has already been encountered by the integrator or not. This adjustment can be turned off by unsetting `woo.dem.Leapfrog.adjVel0`

.

## Orientation¶

### Spherical nodes¶

The basic integration procedure applies to nodes which have inertia tensor such that \(\tens{I}_{11}=\tens{I}_{22}=\tens{I}_{33}\) (this tensor is always diagonal, since local node coordinates coincide with principal axes, and is stored as a 3-vector in `woo.dem.DemData.inertia`

; the sphericity can be queried via `isAspherical`

).

Note

As with linear velocity, the first motion integration modifies the equation to read \(\nnext{\vec{\omega}}=\curr{\vec{\omega}}+\curr{\dot{\vec{\omega}}}\Dt/2\), under the conditions explained above.

### Aspherical nodes¶

Aspherical nodes have different moment of inertia along each principal axis. Their positions are integrated in the same ways as with spherical nodes.

Integrating rotation is considerably more complicated as the local reference frame is not inertial. Rotation of rigid body in the local frame, where inertia tensor \(\mat{I}\) is diagonal, is described in the continuous form by Euler’s equations (\(i\in\{1,2,3\}\), and \(i\), \(j\), \(k\) being sequential):

Due to the presence of the current values of both \(\vec{\omega}\) and \(\dot{\vec{\omega}}\), they cannot be solved using the standard leapfrog algorithm.

The algorithm presented here is described by [AT89] (pg. 84–89) and was designed by Fincham for molecular dynamics problems; it is based on extending the leapfrog algorithm by mid-step/on-step estimators of quantities known at on-step/mid-step points in the basic formulation. Although it has received criticism and more precise algorithms are known ([Ome99], [NB06], [JWC08]), this one is currently implemented in Woo for its relative simplicity.

Each node has its local coordinate system aligned with the principal axes of inertia; we use \(\tilde{\bullet}\) to denote vectors in local coordinates. The orientation of the local system is given by the current `node orientation`

\(\curr{q}\) as a quaternion; this quaternion can be expressed as the (current) rotation matrix \(\mat{A}\). Therefore, every vector \(\vec{a}\) is transformed as \(\tilde{\vec{a}}=q\vec{a}q^{*}=\mat{A}\vec{a}\). Since \(\mat{A}\) is a rotation (orthogonal) matrix, the inverse rotation \(\mat{A}^{-1}=\mat{A}^{T}\).

For the node in question, we know

- \(\curr{\tilde{\mat{I}}}\) (constant)
`inertia tensor diagonal`

(non-diagonal items are zero, since local coordinates are principal), - \(\curr{\vec{T}}\) external
`torque`

, - \(\curr{q}\) current
`orientation (`

and its equivalent rotation matrix \(\mat{A}\)), - \(\pprev{\vec{\omega}}\) mid-step
`angular velocity`

, - \(\pprev{\vec{L}}\) mid-step
`angular momentum`

; this is an auxiliary variable that must be tracked additionally for use in this algorithm. It will be zero in the initial step.

Our goal is to compute new values of the latter three, i.e. \(\nnext{\vec{L}}\), \(\next{q}\), \(\nnext{\vec{\omega}}\). We first estimate current angular momentum and compute current local angular velocity:

Then we compute \(\curr{\dot{q}}\) (see Quaternion differentiation), using \(\curr{q}\) and \(\curr{\tilde{\vec{\omega}}}\):

We evaluate \(\nnext{\dot{q}}\) from \(\nnext{q}\) and \(\nnext{\tilde{\vec{\omega}}}\) in the same way as in (30) but shifted by \(\Dt/2\) ahead. Then we can finally compute the desired values

## Motion in uniformly deforming space¶

In some simulations, nodes can be considered as moving within uniformly deforming medium with velocity gradient tensor \(\tens{L}\), being stationary at origin. This functionality is provided by the `woo.core.Cell`

class (`Scene.cell`

), along with periodic boundaries. The equations written here only valid when `Cell.homoDeform`

has the value `Cell.homoGradV2`

, which is the default.

Note

It is (currently) not possible to simulate deforming space without periodicity. As workaround, set the periodic space big enough to encompass the whole simulation.

Note

The node’s `linear`

and `angular`

velocities comprise both space and fluctuation velocity. It is (almost) only in the integrator that the two must be distinguished.

Spin is skew-symmetric (rotational) part of velocity gradient \(\tens{W}=\frac{1}{2}(\tens{L}-\tens{L}^T)\); its Hodge dual vector (noted \(*\bullet\)) is position-independent medium angular velocity. Local medium velocities (in cartesian coordinates) read

where \(\vec{\omega}_L\) is written in cartesian coordinates component-wise as

where \(\epsilon_{ijk}\) is the Levi-Civita symbol (also known as “permutation tensor”).

### Linear velocity¶

Velocity \(\vec{v}\) of each particle is sum of fluctuation velocity \(\vec{v}_f\) and local medium velocity \(\vec{v}_L\). \(\vec{v}\) evolves not only by virtue of acceleration, but also of \(\tens{L}\), defined at mid-step time-points. We add this term to (28)

with

This equation can be rearranged as

showing that the correction \(\Dt\curr{\vec{\dot v}_L}\) corresponds to subtracting the previous field velocity and adding the current field velocity.

Going back to (32), we write the unknown on-step velocity as \(\curr{\vec{v}}\approx (\pprev{\vec{v}}+\nnext{\vec{v}})/2\) and substitute \(\Dt\curr{\vec{\dot v}_L}\) into (31) obtaining

The position-independent terms are stored in `ImLL4hInv`

, `LmL`

, `IpLL4h`

and are updated at each timestep. Both \(\pprev{\tens{L}}\) and \(\nnext{\tens{L}}\) must be know in this equations; they are stored in `woo.core.Cell.gradV`

and `woo.core.Cell.nextGradV`

respectively.

### Angular velocity¶

As \(\vec{\omega}_L\) is only a function of time (not of space, unlike \(\vec{v}_L\)), (29) simply becomes

where the \((\dagger)\) term is stored as `deltaSpinVec`

.

Warning

Uniformly deforming medium integration is not implemented for aspherical particles, their rotation will be integrated pretending \(\tens{W}\) is zero.

### Periodic space¶

Periodic space is defined as parallelepiped located at origin, expressed as `matrix`

\(\mat{H}\) of which columns are its side vectors. Periodic space volume is given by \(|\det\mat{H}|\).

\(\mat{H}\) follows motion of the space which can be written down as motion of a point \(p\) with zero fluctuation velocity and acceleration; linearizing its motion around mid-step, we obtain its next position as

from which \(\next{\vec{p}}\) is found as

The \(\mat{H}\) matrix is updated in the same way, i.e.

The `transformation matrix`

accumulates deformation from \(\tens{L}\) and is updated as

If \(\tens{L}\) is diagonal, then the (initially zero) \(\mat{T}\) is also diagonal and is a strain matrix. Diagonal contains the Hencky strain along respective axes.

## Clumps¶

Todo

Write about clump motion integration; write about `woo.dem.ClumpSpherePack`

and how its properties are computed. Perhaps move clumps to a separate file.

## Timestep¶

Todo

Write on \(\Dt\), how `Scene`

queries `fields`

and `engines`

on their timestep, about `woo.core.Scene.dtSafety`

, about `woo.dem.DynDt`

.

## Numerical damping¶

Warning

This damping scheme is only suitable for quasi-static simulation where the dynamics is only auxiliary and not important as such. Using this damping scheme in dynamic simulations will lead to gross errors, such as free fall not happening correctly.

In simulations of quasi-static phenomena, it it desirable to dissipate kinetic energy of particles. Since some contact laws (such as Linear (Cundall) contact model) do not include velocity-based damping (such as one in [DAddettaKunRammHerrmann01]), it is possible to use artificial numerical damping. The formulation is described in the manual of the PFC3D code, but the version used here is slightly adapted.

The basic idea is to decrease forces which increase the particle velocities and increase forces which decrease particle velocities by \((\Delta F)_d\), comparing the current acceleration sense and particle velocity sense. This is done component-by-component, which makes the damping scheme clearly non-physical, as it is not invariant with respect to coordinate system rotation; on the other hand, it is very easy to compute. Cundall proposed the form (we omit particle indices $i$ since it applies to all of them separately):

where \(\lambda_d\) is the damping coefficient. This formulation has several advantages [Hen03]:

- it acts on forces (accelerations), not constraining uniform motion;
- it is independent of eigenfrequencies of particles, they will be all damped equally;
- it needs only the dimensionless parameter \(\lambda_d\) which does not have to be scaled.

Woo uses the adapted form

where we replaced the previous mid-step velocity \(\pprev{\dot{u}}\) by its on-step estimate in parentheses. This is to avoid locked-in forces that appear if the velocity changes its sign due to force application at each step, i.e. when the particle in question oscillates around the position of equilibrium with \(2\Dt\) period.

Equation (34) is implemented in `Leapfrog`

; the damping coefficient \(\lambda_d\) is `Leapfrog.damping`

.

Tip

Ask questions or report issues at github.