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#
You create materials 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 a material does not define a property, fill supplies the value.
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
the material table computes effective pair properties.
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.71428571429
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.71428571429
Resulting forces:
[[-57142.85714286 0. ]
[ 57142.85714286 0. ]]
Default MaterialTable in System.create#
If you omit mat_table, 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.635 seconds)