jaxdem.bonded_forces#

Bonded-force interfaces independent of the collider.

Classes

BondedForceModel()

Abstract interface for bonded interaction containers.

class jaxdem.bonded_forces.BondedForceModel#

Bases: Factory, ABC

Abstract interface for bonded interaction containers.

Design intent#

  • System stores one concrete bonded container instance.

  • The container exposes bonded force/energy callables through force_and_energy_fns.

  • The ForceManager gets and runs these callables to compute bonded contributions at each time step.

  • The bonded data stays accessible through System, so the force/energy callables can read what they need.

property force_and_energy_fns: tuple[ForceFunction, EnergyFunction, bool][source]#

Build the bonded force/energy callables for the force manager.

Returns:

(force_fn, energy_fn, is_com_force) where:

  • force_fn computes bonded force and torque contributions.

  • energy_fn computes bonded potential-energy contributions.

  • is_com_force tells where the force acts: True for the center of mass, False for the contact point. It has no effect on spheres.

Return type:

Tuple[ForceFunction, EnergyFunction, bool]

abstractmethod compute_potential_energy(pos: jax.Array, state: State, system: System) jax.Array[source]#

Compute the total bonded potential energy of the system.

abstractmethod static merge(model1: BondedForceModel, model2: BondedForceModel | Sequence[BondedForceModel]) BondedForceModel[source]#

Merge two or more bonded-force models into one.

The merge concatenates topology, reference, and coefficient arrays. It shifts vertex indices and body IDs automatically so references stay consistent. When one side has a term that the other does not, the merge pads missing coefficients with 0 and missing reference values with 1.

Parameters:
Returns:

A new model containing all bodies from both sides.

Return type:

BondedForceModel

abstractmethod static add(model: BondedForceModel, **kwargs: Any) BondedForceModel[source]#

Create a new body from raw arrays and merge it into an existing model.

This is a convenience wrapper equivalent to calling the concrete Create constructor followed by merge().

Parameters:
  • model (BondedForceModel) – Existing model to extend.

  • **kwargs – Constructor arguments forwarded to the concrete Create method (e.g. vertices, elements, coefficients, …).

Returns:

The extended model.

Return type:

BondedForceModel

classmethod stack(models: Sequence[BondedT]) BondedT[source]#
classmethod unstack(model: BondedT) list[BondedT][source]#
class jaxdem.bonded_forces.DeformableParticleModel(elements: Array | None, edges: Array | None, element_adjacency: Array | None, element_adjacency_edges: Array | None, elements_id: Array | None, initial_body_contents: Array | None, initial_element_measures: Array | None, initial_edge_lengths: Array | None, initial_bendings: Array | None, w_b: Array | None, em: Array | None, ec: Array | None, eb: Array | None, el: Array | None, gamma: Array | None)#

Bases: BondedForceModel

Model for 2D and 3D deformable bodies built from surface meshes.

The mesh is triangular for 3D bodies and made of line segments for 2D bodies. The mesh does not need to be closed, but content-based forces fail on an open mesh. Order the vertices consistently (e.g., CCW) to define positive normals and bending angles.

The container stores the mesh connectivity (elements, edges, etc.) and the reference properties (initial measures, contents, lengths, angles) that the force computation needs.

The general form of the deformable particle potential energy per particle is:

\[\begin{split}E_K &= E_{K,measure} + E_{K,content} + E_{K,bending} + E_{K,edge} + E_{K,surface} \\ E_{K,measure} &= \sum_{m} \frac{em_m}{2{\mathcal{M}_{m,0}}} \left(\mathcal{M}_m - \mathcal{M}_{m,0}\right)^2 \\ E_{K,surface} &= \sum_{m} \gamma_m \mathcal{M}_m \\ E_{K,content} &= \frac{e_c}{2{\mathcal{C}_{K,0}}} \left(\mathcal{C}_K - \mathcal{C}_{K,0}\right)^2 \\ E_{K,bending} &= \frac{1}{2} \sum_{a} eb_a wb_{a} \left(\theta_a -\theta_{a,0}\right)^2 \\ E_{K,edge} &= \frac{1}{2} \sum_{e} el_e \left(L_e - L_{e,0}\right)^2\end{split}\]

Definitions per Dimension:

  • 3D: Measure ($mathcal{M}$) is Face Area. Content ($mathcal{C}$) is Volume. Elements are Triangles.

  • 2D: Measure ($mathcal{M}$) is Segment Length. Content ($mathcal{C}$) is Enclosed Area. Elements are Segments.

The constructor precomputes the bending normalization from the reference configuration:

  • 3D: \(w_b = l_0 / h_0\), where \(l_0\) is the initial shared-edge (hinge) length and \(h_0\) is the initial distance between adjacent face centroids.

  • 2D: \(w_b = 1/h_0\), with \(h_0 = 0.5 (L_{\text{left},0}+L_{\text{right},0})\), where \(L_{\text{left},0}\) and \(L_{\text{right},0}\) are initial lengths of adjacent segments.

Shapes:

  • K: Number of deformable bodies. Vertices and connectivity (elements, edges, etc.) define each body.

  • M: Number of boundary elements (\(K \sum_K m_K\))

  • E: Number of unique edges (\(K \sum_K e_K\))

  • A: Number of element adjacencies (\(K \sum_K a_K\))

The model concatenates the mesh properties of all bodies along axis=0. Only the content term needs the body ID of each element: it sums the content contributions per body. elements_id maps each element to its body.

Coefficient broadcasting:

  • Create broadcasts scalar coefficients to the matching geometric entities. em and gamma become shape (M,), eb becomes (A,), and el becomes (E,).

  • ec is special: its shape is (K,), one value per body. K is the number of unique IDs in elements_id. elements_id[m] gives the body index that reads ec for element m.

elements: Array | None#

Array of vertex indices forming the boundary elements. Shape: (M, 3) for 3D (Triangles) or (M, 2) for 2D (Segments). Indices refer to the particle index. Vertices correspond to State.pos.

edges: Array | None#

(E, 2). Each row contains the indices of the two vertices forming the edge. Note: In 2D, the set of edges often overlaps with the set of elements (segments).

Type:

Array of vertex indices forming the edges. Shape

element_adjacency: Array | None#

(A, 2). Each row contains the indices of the two elements sharing a connection.

Type:

Array of element adjacency pairs (for bending/dihedral angles). Shape

element_adjacency_edges: Array | None#

(A, 2). This can be independent from edges because we could have extra edge springs that do not correspond to the mesh connectivity.

Type:

Array of vertex IDs forming the shared edge for each adjacency. Shape

elements_id: Array | None#

(M,). elements_id[i] == k means element i belongs to body k.

Type:

Array of body IDs for each boundary element. Shape

initial_body_contents: Array | None#

(K,). Represents Volume in 3D or Area in 2D.

Type:

Array of reference (stress-free) bulk content for each body. Shape

initial_element_measures: Array | None#

(M,). Represents Area in 3D or Length in 2D.

Type:

Array of reference (stress-free) measures for each element. Shape

initial_edge_lengths: Array | None#

(E,).

Type:

Array of reference (stress-free) lengths for each unique edge. Shape

initial_bendings: Array | None#

(A,). Represents Dihedral Angle in 3D or Vertex Angle in 2D.

Type:

Array of reference (stress-free) bending angles for each adjacency. Shape

w_b: Array | None#

(A,). 3D: hinge_length0 / dual_length0 with dual_length0 between face centers. 2D: 1 / dual_length0 with dual_length0 = 0.5 * (L_left0 + L_right0).

Type:

Precomputed bending normalization coefficient per adjacency. Shape

em: Array | None#

(M,). (Controls area stiffness in 3D and length stiffness in 2D).

Type:

Measure elasticity coefficient for each element. Shape

ec: Array | None#

(K,). (Controls volume stiffness in 3D and area stiffness in 2D).

Type:

Content elasticity coefficient for each body. Shape

eb: Array | None#

(A,).

Type:

Bending elasticity coefficient for each hinge. Shape

el: Array | None#

(E,).

Type:

Edge length elasticity coefficient for each edge. Shape

gamma: Array | None#

(M,).

Type:

Surface/Line tension coefficient for each element. Shape

classmethod Create(*, vertices: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements: Array | ndarray | bool | number | bool | int | float | complex | None = None, edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency_edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements_id: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_body_contents: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_element_measures: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_edge_lengths: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_bendings: Array | ndarray | bool | number | bool | int | float | complex | None = None, em: Array | ndarray | bool | number | bool | int | float | complex | None = None, ec: Array | ndarray | bool | number | bool | int | float | complex | None = None, eb: Array | ndarray | bool | number | bool | int | float | complex | None = None, el: Array | ndarray | bool | number | bool | int | float | complex | None = None, gamma: Array | ndarray | bool | number | bool | int | float | complex | None = None, w_b: Array | ndarray | bool | number | bool | int | float | complex | None = None) Self[source]#
static merge(model1: BondedForceModel, model2: BondedForceModel | Sequence[BondedForceModel]) BondedForceModel[source]#

Merge two or more bonded-force models into one.

The merge concatenates topology, reference, and coefficient arrays. It shifts vertex indices and body IDs automatically so references stay consistent. When one side has a term that the other does not, the merge pads missing coefficients with 0 and missing reference values with 1.

Parameters:
Returns:

A new model containing all bodies from both sides.

Return type:

BondedForceModel

static add(model: BondedForceModel, **kwargs: Any) BondedForceModel[source]#

Create a new body from raw arrays and merge it into an existing model.

This is a convenience wrapper equivalent to calling the concrete Create constructor followed by merge().

Parameters:
  • model (BondedForceModel) – Existing model to extend.

  • **kwargs – Constructor arguments forwarded to the concrete Create method (e.g. vertices, elements, coefficients, …).

Returns:

The extended model.

Return type:

BondedForceModel

static compute_potential_energy_w_aux(pos: jax.Array, state: State, system: System) tuple[jax.Array, dict[str, jax.Array]][source]#
static compute_potential_energy(pos: jax.Array, state: State, system: System) jax.Array[source]#
update_reference_state(pos: jax.Array, state: State, system: System) DeformableParticleModel[source]#

Return a model with the plastically updated reference configuration.

This is a pure functional update: the update never mutates the model. The base (elastic) model has no plastic flow and returns self. Plastic subclasses override this hook.

static compute_forces(pos: jax.Array, state: State, system: System) tuple[jax.Array, jax.Array]#
property force_and_energy_fns: tuple[ForceFunction, EnergyFunction, bool][source]#

Build the bonded force/energy callables for the force manager.

Returns:

(force_fn, energy_fn, is_com_force) where:

  • force_fn computes bonded force and torque contributions.

  • energy_fn computes bonded potential-energy contributions.

  • is_com_force tells where the force acts: True for the center of mass, False for the contact point. It has no effect on spheres.

Return type:

Tuple[ForceFunction, EnergyFunction, bool]

class jaxdem.bonded_forces.PlasticBendingDeformableParticleModel(elements: Array | None, edges: Array | None, element_adjacency: Array | None, element_adjacency_edges: Array | None, elements_id: Array | None, initial_body_contents: Array | None, initial_element_measures: Array | None, initial_edge_lengths: Array | None, initial_bendings: Array | None, w_b: Array | None, em: Array | None, ec: Array | None, eb: Array | None, el: Array | None, gamma: Array | None, tau_s: Array | None = None)#

Bases: DeformableParticleModel

Deformable particle model with plastic bending angles.

Elastic forces match DeformableParticleModel. The model keeps the edge-spring, measure, content, bending, and surface-tension terms. See the DeformableParticleModel docstring for the full energy definition and shape conventions. The only difference is the plastic update rule for the reference bending angles.

Instead of relaxing each edge rest length (as in PlasticDeformableParticleModel), the reference bending angle of each adjacency relaxes toward the current bending angle:

\[\theta_{a,0}(t+dt) = \theta_{a,0}(t) + \frac{1}{\tau_{s,a}} (\theta_a(t) - \theta_{a,0}(t))\,dt\]

where \(\theta_{a,0}\) is the reference (rest) bending angle (initial_bendings), \(\theta_a\) is the current bending angle, \(\tau_{s,a}\) is the relaxation time (tau_s), and \(dt\) is the simulation time step.

Shapes (see DeformableParticleModel for definitions of K, M, E, A):

  • tau_s: (A,) — one relaxation time per adjacency (bending hinge).

tau_s: Array | None#

(A,).

Type:

Plastic relaxation time for each adjacency (bending hinge). Shape

classmethod Create(*, vertices: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements: Array | ndarray | bool | number | bool | int | float | complex | None = None, edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency_edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements_id: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_body_contents: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_element_measures: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_edge_lengths: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_bendings: Array | ndarray | bool | number | bool | int | float | complex | None = None, em: Array | ndarray | bool | number | bool | int | float | complex | None = None, ec: Array | ndarray | bool | number | bool | int | float | complex | None = None, eb: Array | ndarray | bool | number | bool | int | float | complex | None = None, el: Array | ndarray | bool | number | bool | int | float | complex | None = None, gamma: Array | ndarray | bool | number | bool | int | float | complex | None = None, tau_s: Array | ndarray | bool | number | bool | int | float | complex | None = None, w_b: Array | ndarray | bool | number | bool | int | float | complex | None = None) Self[source]#
update_reference_state(pos: jax.Array, state: State, system: System) DeformableParticleModel[source]#

Relax each reference bending angle toward the current bending angle.

class jaxdem.bonded_forces.PlasticDeformableParticleModel(elements: Array | None, edges: Array | None, element_adjacency: Array | None, element_adjacency_edges: Array | None, elements_id: Array | None, initial_body_contents: Array | None, initial_element_measures: Array | None, initial_edge_lengths: Array | None, initial_bendings: Array | None, w_b: Array | None, em: Array | None, ec: Array | None, eb: Array | None, el: Array | None, gamma: Array | None, tau_s: Array | None = None)#

Bases: DeformableParticleModel

Deformable particle model with per-edge plasticity.

Elastic forces, topology, reference configuration, and coefficient broadcasting match DeformableParticleModel. See its docstring for the full energy definition and shape conventions. The only difference is the plastic update rule.

Plasticity:

Each force calculation integrates a spring-dashpot equation for the reference length of each edge:

\[L_{e,0}(t+dt) = L_{e,0}(t) + \frac{1}{\tau_{s,e}} (L_e(t) - L_{e,0}(t)) dt\]

where \(L_{e,0}\) is the initial (reference) edge length (initial_edge_lengths), \(L_e\) is the current edge length, \(\tau_{s,e}\) is the relaxation time (tau_s), and \(dt\) is the simulation time step.

Shapes (see DeformableParticleModel for definitions of K, M, E, A):

  • tau_s: (E,) — one relaxation time per edge.

tau_s: Array | None#

(E,).

Type:

Plastic relaxation time for each edge. Shape

classmethod Create(*, vertices: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements: Array | ndarray | bool | number | bool | int | float | complex | None = None, edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency_edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements_id: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_body_contents: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_element_measures: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_edge_lengths: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_bendings: Array | ndarray | bool | number | bool | int | float | complex | None = None, em: Array | ndarray | bool | number | bool | int | float | complex | None = None, ec: Array | ndarray | bool | number | bool | int | float | complex | None = None, eb: Array | ndarray | bool | number | bool | int | float | complex | None = None, el: Array | ndarray | bool | number | bool | int | float | complex | None = None, gamma: Array | ndarray | bool | number | bool | int | float | complex | None = None, tau_s: Array | ndarray | bool | number | bool | int | float | complex | None = None, w_b: Array | ndarray | bool | number | bool | int | float | complex | None = None) Self[source]#
update_reference_state(pos: jax.Array, state: State, system: System) DeformableParticleModel[source]#

Relax each reference edge length toward the current edge length.

class jaxdem.bonded_forces.PlasticPerimeterDeformableParticleModel(elements: Array | None, edges: Array | None, element_adjacency: Array | None, element_adjacency_edges: Array | None, elements_id: Array | None, initial_body_contents: Array | None, initial_element_measures: Array | None, initial_edge_lengths: Array | None, initial_bendings: Array | None, w_b: Array | None, em: Array | None, ec: Array | None, eb: Array | None, el: Array | None, gamma: Array | None, edges_id: Array | None = None, tau_s: Array | None = None)#

Bases: DeformableParticleModel

Deformable particle model with perimeter-level plasticity.

Elastic forces match DeformableParticleModel. The model keeps the edge-spring, measure, content, bending, and surface-tension terms. See the DeformableParticleModel docstring for the full energy definition and shape conventions. The only difference is the plastic update rule.

Instead of relaxing each edge rest length independently (as in PlasticDeformableParticleModel), the total reference perimeter of each body relaxes toward the current total perimeter. Uniform rescaling then distributes the change back to the individual edges:

\[\begin{split}P_{K,0}^{\text{new}} &= P_{K,0} + \frac{1}{\tau_{s,K}} (P_K - P_{K,0})\,dt \\ L_{e,0}^{\text{new}} &= L_{e,0}\;\frac{P_{K,0}^{\text{new}}}{P_{K,0}} \qquad \forall\, e \in K\end{split}\]

where \(P_K = \sum_{e \in K} L_e\) is the current perimeter, \(P_{K,0} = \sum_{e \in K} L_{e,0}\) the reference perimeter, \(\tau_{s,K}\) the per-body relaxation time, and \(dt\) the time step. Uniform rescaling preserves relative edge proportions within each body.

This model requires an edges_id mapping (shape (E,)) that assigns each edge to its body, analogous to elements_id for elements. For a single body without edges_id, all edges belong to body 0.

Shapes (see DeformableParticleModel for definitions of K, M, E, A):

  • edges_id: (E,)

  • tau_s: (K,)

edges_id: Array | None#

(E,). edges_id[e] == k means edge e belongs to body k. Required for perimeter-level plasticity.

Type:

Array of body IDs for each edge. Shape

tau_s: Array | None#

(K,). Controls how fast the total reference perimeter of each body relaxes toward the current perimeter.

Type:

Plastic relaxation time for each body. Shape

classmethod Create(*, vertices: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements: Array | ndarray | bool | number | bool | int | float | complex | None = None, edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency: Array | ndarray | bool | number | bool | int | float | complex | None = None, element_adjacency_edges: Array | ndarray | bool | number | bool | int | float | complex | None = None, elements_id: Array | ndarray | bool | number | bool | int | float | complex | None = None, edges_id: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_body_contents: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_element_measures: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_edge_lengths: Array | ndarray | bool | number | bool | int | float | complex | None = None, initial_bendings: Array | ndarray | bool | number | bool | int | float | complex | None = None, em: Array | ndarray | bool | number | bool | int | float | complex | None = None, ec: Array | ndarray | bool | number | bool | int | float | complex | None = None, eb: Array | ndarray | bool | number | bool | int | float | complex | None = None, el: Array | ndarray | bool | number | bool | int | float | complex | None = None, gamma: Array | ndarray | bool | number | bool | int | float | complex | None = None, tau_s: Array | ndarray | bool | number | bool | int | float | complex | None = None, w_b: Array | ndarray | bool | number | bool | int | float | complex | None = None) Self[source]#
update_reference_state(pos: jax.Array, state: State, system: System) DeformableParticleModel[source]#

Relax each body’s reference perimeter toward its current perimeter.

Modules

deformable_particle

Implementation of the deformable particle model.

plastic_bending_deformable_particle

Implementation of the deformable particle model with plastic bending angles.

plastic_deformable_particle

Implementation of the plastic deformable particle model.

plastic_perimeter_deformable_particle

Implementation of the plastic perimeter deformable particle model.