Note
Go to the end to download the full example code.
Materials, Matchmakers, and Material Tables.#
JaxDEM separates material definitions from force laws:
Materialstores per-material scalar properties.MaterialMatchmakerdefines how to mix two material properties into an effective pair property.MaterialTablestores both scalar properties and precomputed pair tables used by force models.
This guide shows how to create materials, choose a matchmaker, and pass a
material table into System. For how force models
consume these properties, see Force Models..
Creating Materials#
Materials are created through the factory interface. Each registered material type defines its own required fields.
import jax.numpy as jnp
import jaxdem as jdem
elastic = jdem.Material.create(
"elastic",
density=2500.0,
young=2.0e5,
poisson=0.25,
)
frictional = jdem.Material.create(
"elasticfrict",
density=1200.0,
young=8.0e4,
poisson=0.35,
mu=0.6,
e=1.0,
)
lj = jdem.Material.create(
"lj",
density=1.0,
epsilon=1.5,
)
print("elastic type:", elastic.type_name)
print("frictional type:", frictional.type_name)
print("lj type:", lj.type_name)
elastic type: elastic
frictional type: elasticfrict
lj type: lj
You can inspect available material types in the factory registry:
print("Registered materials:", list(jdem.Material._registry.keys()))
Registered materials: ['elastic', 'elasticfrict', 'lj']
Creating a MaterialTable#
from_materials() converts a list of material objects into a
Structure-of-Arrays representation and computes effective pair properties.
When some materials do not define a property, fill is used.
harmonic_matcher = jdem.MaterialMatchmaker.create("harmonic")
mat_table = jdem.MaterialTable.from_materials(
[elastic, frictional],
matcher=harmonic_matcher,
fill=0.0,
)
print("Number of materials:", len(mat_table))
print("Stored scalar keys:", sorted(mat_table.props.keys()))
print("Stored pair keys:", sorted(mat_table.pair.keys()))
# Scalar per-material arrays (shape: (M,))
print("young:", mat_table.young)
print("mu (elastic filled with 0.0):", mat_table.mu)
# Effective pair arrays (shape: (M, M))
print("young_eff:\n", mat_table.young_eff)
print("poisson_eff:\n", mat_table.poisson_eff)
print("mu_eff:\n", mat_table.mu_eff)
Number of materials: 2
Stored scalar keys: ['density', 'e', 'mu', 'mu_r', 'poisson', 'young']
Stored pair keys: ['density_eff', 'e_eff', 'mu_eff', 'mu_r_eff', 'poisson_eff', 'young_eff']
young: [200000. 80000.]
mu (elastic filled with 0.0): [0. 0.6]
young_eff:
[[200000. 114285.71428571]
[114285.71428571 80000. ]]
poisson_eff:
[[0.25 0.29166667]
[0.29166667 0.35 ]]
mu_eff:
[[0. 0. ]
[0. 0.6]]
Matchmakers#
A MaterialMatchmaker controls how
effective pair properties are computed.
linear_matcher = jdem.MaterialMatchmaker.create("linear")
mat_table_linear = jdem.MaterialTable.from_materials(
[elastic, frictional],
matcher=linear_matcher,
)
print("Registered matchmakers:", list(jdem.MaterialMatchmaker._registry.keys()))
print("harmonic young_eff[0,1] =", mat_table.young_eff[0, 1])
print("linear young_eff[0,1] =", mat_table_linear.young_eff[0, 1])
Registered matchmakers: ['harmonic', 'linear']
harmonic young_eff[0,1] = 114285.71428571428
linear young_eff[0,1] = 140000.0
Using Material IDs in a Simulation#
Particles select their material via state.mat_id.
Force models then query pair values from system.mat_table.
state = jdem.State.create(
pos=jnp.array([[0.0, 0.0], [1.5, 0.0]]),
rad=jnp.array([1.0, 1.0]),
mat_id=jnp.array([0, 1]), # particle 0 uses elastic, particle 1 uses frictional
)
system = jdem.System.create(
state.shape,
force_model_type="spring",
mat_table=mat_table,
)
print(
"Required properties for spring:", system.force_model.required_material_properties
)
print("Effective stiffness used by pair (0,1):", system.mat_table.young_eff[0, 1])
state, system = system.step(state, system)
print("Resulting forces:\n", state.force)
Required properties for spring: ('young_eff',)
Effective stiffness used by pair (0,1): 114285.71428571428
Resulting forces:
[[-57142.85714286 0. ]
[ 57142.85714286 0. ]]
Default MaterialTable in System.create#
If mat_table is omitted, create()
builds a default single-material table.
default_system = jdem.System.create(state.shape, force_model_type="spring")
print("Default number of materials:", len(default_system.mat_table))
print("Default matcher:", default_system.mat_table.matcher.type_name)
print("Default scalar keys:", sorted(default_system.mat_table.props.keys()))
Default number of materials: 1
Default matcher: harmonic
Default scalar keys: ['density', 'poisson', 'young']
Total running time of the script: (0 minutes 0.663 seconds)