jaxdem.forces.spring#

Linear spring force model.

Classes

FacetFacetSpringForce([laws, ...])

Linear spring contact between facets.

SphereFacetSpringForce([laws, face, edge, ...])

Linear spring contact between spheres and facets.

SpringForce([laws])

Linear spring-like interaction between particles.

class jaxdem.forces.spring.SpringForce(laws: tuple[ForceModel, ...] = ())#

Bases: ForceModel

Linear spring-like interaction between particles.

Notes

  • The model reads the ‘effective Young’s modulus’ (\(k_{eff,\; ij}\)) from the jaxdem.System.mat_table using the material IDs of the interacting particles.

  • The force is zero if \(i == j\).

  • The model computes distances and normals with the zero-safe double-where helpers in jaxdem.utils.linalg. The force and its gradients stay finite when particles are perfectly co-located.

The penetration \(\delta\) (overlap) between two particles \(i\) and \(j\) is:

\[\delta = \max\left(0, (R_i + R_j) - r\right)\]

where \(R_i\) and \(R_j\) are the radii of particles \(i\) and \(j\) respectively, and \(r = ||r_{ij}||\) is the distance between their centers.

The force \(F_{ij}\) acting on particle \(i\) due to particle \(j\) is:

\[F_{ij} = k_{eff,\; ij}\, \delta\, \hat{n}_{ij}\]

where \(\hat{n}_{ij} = \vec{r}_{ij} / r\) is the unit vector from particle \(j\) to particle \(i\).

The potential energy \(E_{ij}\) of the interaction is:

\[E_{ij} = \frac{1}{2} k_{eff,\; ij} \delta^2\]

where \(k_{eff,\; ij}\) is the effective Young’s modulus for the particle pair.

search_radii(state: State, system: System) → jax.Array[source]#

Conservative search extent for this law’s finite interaction range.

static force(i: int, j: int, pos: jax.Array, state: State, system: System, history: jax.Array, *, advance_history: bool = True, quasistatic: bool = False) → tuple[jax.Array, jax.Array, jax.Array][source]#

Compute the linear spring force on particle \(i\) from particle \(j\).

Returns zero when \(i = j\).

Parameters:
  • i (int) – Index of the first particle.

  • j (int) – Index of the second particle.

  • pos (jax.Array) – Particle positions.

  • state (State) – Current state of the simulation.

  • system (System) – Simulation system configuration.

  • history (jax.Array) – Pair history, returned unchanged by this stateless law.

  • advance_history (bool, optional) – Unused by this stateless law.

  • quasistatic (bool, optional) – Unused by this stateless law.

Returns:

(force, torque, history). Force and torque have shapes (dim,) and (ang_dim,) for a single pair. Torque is zero.

Return type:

tuple[jax.Array, jax.Array, jax.Array]

static energy(i: int, j: int, pos: jax.Array, state: State, system: System, history: jax.Array) → jax.Array[source]#

Compute the linear spring potential energy between particle \(i\) and particle \(j\).

Returns zero when \(i = j\).

Parameters:
  • i (int) – Index of the first particle.

  • j (int) – Index of the second particle.

  • pos (jax.Array) – Particle positions.

  • state (State) – Current state of the simulation.

  • system (System) – Simulation system configuration.

Returns:

Scalar potential energy of the interaction between particles \(i\) and \(j\).

Return type:

jax.Array

property required_material_properties: tuple[str, ...][source]#

Names of the material properties this force model needs.

Each name (for example ‘young_eff’ or ‘restitution’) must be present in System.mat_table. Used for validation.

class jaxdem.forces.spring.SphereFacetSpringForce(laws: tuple[ForceModel, ...] = (), face: bool = True, edge: bool = True, vertex: bool = True)#

Bases: ForceModel

Linear spring contact between spheres and facets.

Contacts are evaluated through vertex keys, using the primary key for rigid facets. Search bounds automatically include the current facet extent and contact thickness. state._rad supplies an optional conservative lower bound on that search radius.

Shared faces accumulate loads on the same physical vertex keys. Flexible nodal loads use closest-point barycentric weights. Penalties add per face, including multiple faces contacted near a shared edge or vertex.

Contact thickness is state.rad at each face’s first vertex (set through State.add_facet(thickness=...) or retained when attaching faces to existing nodes). The force model itself has no thickness parameter. Zero-distance normals are force conventions, so physical relaxation requires a gradient-only optimizer. Value-based optimization requires a custom energy target differentiated through AD.

face, edge, and vertex enable contacts whose closest point is on that feature. Disabling a feature suppresses its contacts without substituting a farther feature. In 2D, the open segment is an edge and its endpoints are vertex contacts; face has no effect. All switches default to true and are static JAX configuration, so changing them recompiles the kernel and omits disabled feature calculations.

face: bool#
edge: bool#
vertex: bool#
property supports_analytical_energy_gradient: bool[source]#

Zero-distance contact normals are force conventions, not energy gradients.

property required_material_properties: tuple[str, ...][source]#

Require the effective Young modulus for the contact spring.

search_radii(state: State, system: System) → jax.Array[source]#

Include current facet extent and contact thickness in search bounds.

static force(i: int, j: int, pos: jax.Array, state: State, system: System, history: jax.Array, *, advance_history: bool = True, quasistatic: bool = False) → tuple[jax.Array, jax.Array, jax.Array][source]#
static energy(i: int, j: int, pos: jax.Array, state: State, system: System, history: jax.Array) → jax.Array[source]#
class jaxdem.forces.spring.FacetFacetSpringForce(laws: tuple[ForceModel, ...] = (), vertex_triangle: bool = True, edge_edge: bool = True, edge_triangle: bool = True)#

Bases: ForceModel

Linear spring contact between facets.

Contacts are evaluated through vertex keys, using the primary key for rigid facets. Search bounds automatically include the current facet extent and contact thickness. state._rad supplies an optional conservative lower bound on that search radius.

Shared faces accumulate loads on the same physical vertex keys. Flexible nodal loads use closest-point barycentric weights. Penalties add per face, including multiple faces contacted near a shared edge or vertex.

Contact thickness is state.rad at each face’s first vertex (set through State.add_facet(thickness=...) or retained when attaching faces to existing nodes). The force model itself has no thickness parameter. Zero-distance normals are force conventions, so physical relaxation requires a gradient-only optimizer. Value-based optimization requires a custom energy target differentiated through AD.

Faces sharing any physical node are excluded from facet-facet contact.

Static switches select geometry query groups: vertex_triangle measures all vertices against the opposite triangle, edge_edge measures all edge pairs, and edge_triangle detects edge piercings of the opposite face. The minimum remaining candidate defines the contact. These groups overlap physical feature types: vertex-triangle queries include boundary contacts, and parallel face-face separation can be found by edge queries. In 2D, only edge_edge is used. All switches default to true; disabled groups are omitted when JAX compiles the kernel.

vertex_triangle: bool#
edge_edge: bool#
edge_triangle: bool#
property supports_analytical_energy_gradient: bool[source]#

Zero-distance contact normals are force conventions, not energy gradients.

property required_material_properties: tuple[str, ...][source]#

Require the effective Young modulus for the contact spring.

search_radii(state: State, system: System) → jax.Array[source]#

Include current facet extent and contact thickness in search bounds.

static force(i: int, j: int, pos: jax.Array, state: State, system: System, history: jax.Array, *, advance_history: bool = True, quasistatic: bool = False) → tuple[jax.Array, jax.Array, jax.Array][source]#
static energy(i: int, j: int, pos: jax.Array, state: State, system: System, history: jax.Array) → jax.Array[source]#