Skip to content

Commit 2c170d5

Browse files
authored
Merge pull request #14 from chahak13/traction
Add particle surface traction
2 parents e517025 + 2e4d5ae commit 2c170d5

12 files changed

Lines changed: 276 additions & 19 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# The `meta` group contains top level attributes that govern the
2+
# behaviour of the MPM Solver.
3+
#
4+
# Attributes:
5+
# title: The title of the experiment. This is just for the user's
6+
# reference.
7+
# type: The type of simulation to be used. Allowed values are
8+
# {"MPMExplicit"}
9+
# scheme: The MPM Scheme used for simulation. Allowed values are
10+
# {"usl", "usf"}
11+
# dt: Timestep used in the simulation.
12+
# nsteps: Number of steps to run the simulation for.
13+
[meta]
14+
title = "uniaxial-particle-traction"
15+
type = "MPMExplicit"
16+
dimension = 2
17+
scheme = "usf"
18+
dt = 0.001
19+
nsteps = 1000
20+
velocity_update = true
21+
22+
[output]
23+
format = "npz"
24+
folder = "results/"
25+
step_frequency = 10
26+
27+
[mesh]
28+
# type = "file"
29+
# file = "mesh-1d.txt"
30+
# boundary_nodes = "boundary-1d.txt"
31+
# particle_element_ids = "particles-elements.txt"
32+
type = "generator"
33+
nelements = [3, 1]
34+
element_length = [0.1, 0.1]
35+
particle_element_ids = [0]
36+
element = "Quadrilateral4Node"
37+
38+
[[mesh.constraints]]
39+
node_ids = [0, 4]
40+
dir = 0
41+
velocity = 0.0
42+
43+
[[materials]]
44+
id = 0
45+
density = 1000
46+
poisson_ratio = 0
47+
youngs_modulus = 1000000
48+
type = "LinearElastic"
49+
50+
[[particles]]
51+
file = "particles-2d-particle-traction.json"
52+
material_id = 0
53+
init_velocity = 0.0
54+
55+
[external_loading]
56+
gravity = [0, 0]
57+
58+
[[external_loading.particle_surface_traction]]
59+
pset = [0]
60+
pids = [[5, 11]]
61+
math_function_id = 0
62+
dir = 0
63+
traction = 1
64+
65+
[[math_functions]]
66+
type = "Linear"
67+
xvalues = [0.0, 0.5, 1.0]
68+
fxvalues = [0.0, 1.0, 1.0]
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
[
2+
[
3+
[
4+
0.025,
5+
0.025
6+
]
7+
],
8+
[
9+
[
10+
0.075,
11+
0.025
12+
]
13+
],
14+
[
15+
[
16+
0.125,
17+
0.025
18+
]
19+
],
20+
[
21+
[
22+
0.175,
23+
0.025
24+
]
25+
],
26+
[
27+
[
28+
0.225,
29+
0.025
30+
]
31+
],
32+
[
33+
[
34+
0.275,
35+
0.025
36+
]
37+
],
38+
[
39+
[
40+
0.025,
41+
0.075
42+
]
43+
],
44+
[
45+
[
46+
0.075,
47+
0.075
48+
]
49+
],
50+
[
51+
[
52+
0.125,
53+
0.075
54+
]
55+
],
56+
[
57+
[
58+
0.175,
59+
0.075
60+
]
61+
],
62+
[
63+
[
64+
0.225,
65+
0.075
66+
]
67+
],
68+
[
69+
[
70+
0.275,
71+
0.075
72+
]
73+
]
74+
]
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import os
2+
from pathlib import Path
3+
import jax.numpy as jnp
4+
from diffmpm.solver import MPM
5+
6+
curr_filepath = Path(__file__).absolute()
7+
curr_dir = curr_filepath.parent
8+
os.chdir(curr_dir)
9+
mpm = MPM("mpm-particle-traction.toml")
10+
mpm.solve()
11+
result = jnp.load("results/uniaxial-particle-traction/particles_0300.npz")
12+
## Step 300
13+
assert jnp.round(result["stress"][0, :, 0].min() - 0.4450086768966724, 5) == 0.0
14+
assert jnp.round(result["stress"][0, :, 0].max() - 0.5966527842046769, 5) == 0.0
15+
16+
## Step 510
17+
result = jnp.load("results/uniaxial-particle-traction/particles_0510.npz")
18+
assert jnp.round(result["stress"][0, :, 0].min() - 0.7528092313640623, 5) == 0.0
19+
assert jnp.round(result["stress"][0, :, 0].max() - 1.0109599915279937, 5) == 0.0
20+
21+
## Step 750
22+
result = jnp.load("results/uniaxial-particle-traction/particles_0750.npz")
23+
assert jnp.round(result["stress"][0, :, 0].min() - 0.7500090055681591, 5) == 0.0
24+
assert jnp.round(result["stress"][0, :, 0].max() - 1.0000224746314728, 5) == 0.0
25+
26+
## Step 990
27+
result = jnp.load("results/uniaxial-particle-traction/particles_0990.npz")
28+
assert jnp.round(result["stress"][0, :, 0].min() - 0.750002924022295, 5) == 0.0
29+
assert jnp.round(result["stress"][0, :, 0].max() - 0.9999997782938734, 5) == 0.0

diffmpm/element.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,17 @@ def apply_concentrated_nodal_forces(self, particles, curr_time):
239239
factor * cnf.force
240240
)
241241

242+
def apply_particle_traction_forces(self, particles):
243+
def _step(pid, args):
244+
f_ext, ptraction, mapped_pos, el_nodes = args
245+
f_ext = f_ext.at[el_nodes[pid]].add(mapped_pos[pid] @ ptraction[pid])
246+
return f_ext, ptraction, mapped_pos, el_nodes
247+
248+
mapped_positions = self.shapefn(particles.reference_loc)
249+
mapped_nodes = vmap(self.id_to_node_ids)(particles.element_ids).squeeze(-1)
250+
args = (self.nodes.f_ext, particles.traction, mapped_positions, mapped_nodes)
251+
self.nodes.f_ext, _, _, _ = lax.fori_loop(0, len(particles), _step, args)
252+
242253
def update_nodal_acceleration_velocity(self, particles, dt: float, *args):
243254
"""Update the nodal momentum based on total force on nodes."""
244255
total_force = self.nodes.get_total_force()

diffmpm/forces.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
NodalForce = namedtuple("NodalForce", ("node_ids", "function", "dir", "force"))
55
ParticleTraction = namedtuple(
6-
"ParticleTraction", ("pset", "function", "dir", "traction")
6+
"ParticleTraction", ("pset", "pids", "function", "dir", "traction")
77
)
88
register_pytree_node(
99
NodalForce,

diffmpm/io.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def _parse_external_loading(self, config):
8080
external_loading = {}
8181
external_loading["gravity"] = jnp.array(config["external_loading"]["gravity"])
8282
external_loading["concentrated_nodal_forces"] = []
83-
external_loading["particle_surface_traction"] = []
83+
particle_surface_traction = []
8484
if "concentrated_nodal_forces" in config["external_loading"]:
8585
cnf_list = []
8686
for cnfconfig in config["external_loading"]["concentrated_nodal_forces"]:
@@ -102,17 +102,23 @@ def _parse_external_loading(self, config):
102102
if "particle_surface_traction" in config["external_loading"]:
103103
pst_list = []
104104
for pstconfig in config["external_loading"]["particle_surface_traction"]:
105-
pst = ParticleTraction(
106-
pset=jnp.array(pstconfig["pset"]),
107-
function=self.parsed_config["math_functions"][
105+
if "math_function_id" in pstconfig:
106+
fn = self.parsed_config["math_functions"][
108107
pstconfig["math_function_id"]
109-
],
108+
]
109+
else:
110+
fn = Unit(-1)
111+
pst = ParticleTraction(
112+
pset=pstconfig["pset"],
113+
pids=jnp.array(pstconfig["pids"]),
114+
function=fn,
110115
dir=pstconfig["dir"],
111116
traction=pstconfig["traction"],
112117
)
113118
pst_list.append(pst)
114-
external_loading["particle_surface_traction"] = pst_list
119+
particle_surface_traction.extend(pst_list)
115120
self.parsed_config["external_loading"] = external_loading
121+
self.parsed_config["particle_surface_traction"] = particle_surface_traction
116122

117123
def _parse_mesh(self, config):
118124
element_cls = getattr(mpel, config["mesh"]["element"])

diffmpm/mesh.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ def __init__(self, config: dict):
2121
"""Initialize mesh using configuration."""
2222
self.particles: Iterable[Particles, ...] = config["particles"]
2323
self.elements: _Element = config["elements"]
24+
self.particle_tractions = config["particle_surface_traction"]
25+
26+
@property
27+
@abc.abstractmethod
28+
def ndim(self):
29+
...
2430

2531
# TODO: Convert to using jax directives for loop
2632
def apply_on_elements(self, function, args=()):
@@ -34,15 +40,33 @@ def apply_on_particles(self, function, args=()):
3440
f = getattr(particle_set, function)
3541
f(self.elements, *args)
3642

43+
def apply_traction_on_particles(self, curr_time):
44+
self.apply_on_particles("zero_traction")
45+
for ptraction in self.particle_tractions:
46+
factor = ptraction.function.value(curr_time)
47+
traction_val = factor * ptraction.traction
48+
for i, pset_id in enumerate(ptraction.pset):
49+
self.particles[pset_id].assign_traction(
50+
ptraction.pids[i], ptraction.dir, traction_val
51+
)
52+
53+
# breakpoint()
54+
self.apply_on_elements("apply_particle_traction_forces")
55+
3756
def tree_flatten(self):
3857
children = (self.particles, self.elements)
39-
aux_data = tuple()
58+
aux_data = self.particle_tractions
4059
return (children, aux_data)
4160

4261
@classmethod
4362
def tree_unflatten(cls, aux_data, children):
44-
del aux_data
45-
return cls({"particles": children[0], "elements": children[1]})
63+
return cls(
64+
{
65+
"particles": children[0],
66+
"elements": children[1],
67+
"particle_surface_traction": aux_data,
68+
}
69+
)
4670

4771

4872
@register_pytree_node_class
@@ -61,6 +85,10 @@ def __init__(self, config: dict):
6185
"""
6286
super().__init__(config)
6387

88+
@property
89+
def ndim(self):
90+
return 1
91+
6492

6593
@register_pytree_node_class
6694
class Mesh2D(_MeshBase):
@@ -78,6 +106,10 @@ def __init__(self, config: dict):
78106
"""
79107
super().__init__(config)
80108

109+
@property
110+
def ndim(self):
111+
return 2
112+
81113

82114
if __name__ == "__main__":
83115
from diffmpm.element import Linear1D

diffmpm/particle.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def __init__(
5555
jnp.ones_like(self.mass) * self.material.properties["density"]
5656
)
5757
self.volume = jnp.ones_like(self.mass)
58+
self.size = jnp.zeros_like(self.loc)
5859
self.velocity = jnp.zeros_like(self.loc)
5960
self.acceleration = jnp.zeros_like(self.loc)
6061
self.momentum = jnp.zeros_like(self.loc)
@@ -63,13 +64,15 @@ def __init__(
6364
self.strain_rate = jnp.zeros((self.loc.shape[0], 6, 1))
6465
self.dstrain = jnp.zeros((self.loc.shape[0], 6, 1))
6566
self.f_ext = jnp.zeros_like(self.loc)
67+
self.traction = jnp.zeros_like(self.loc)
6668
self.reference_loc = jnp.zeros_like(self.loc)
6769
self.volumetric_strain_centroid = jnp.zeros((self.loc.shape[0], 1))
6870
else:
6971
(
7072
self.mass,
7173
self.density,
7274
self.volume,
75+
self.size,
7376
self.velocity,
7477
self.acceleration,
7578
self.momentum,
@@ -78,6 +81,7 @@ def __init__(
7881
self.strain_rate,
7982
self.dstrain,
8083
self.f_ext,
84+
self.traction,
8185
self.reference_loc,
8286
self.volumetric_strain_centroid,
8387
) = data
@@ -91,6 +95,7 @@ def tree_flatten(self):
9195
self.mass,
9296
self.density,
9397
self.volume,
98+
self.size,
9499
self.velocity,
95100
self.acceleration,
96101
self.momentum,
@@ -99,6 +104,7 @@ def tree_flatten(self):
99104
self.strain_rate,
100105
self.dstrain,
101106
self.f_ext,
107+
self.traction,
102108
self.reference_loc,
103109
self.volumetric_strain_centroid,
104110
)
@@ -152,6 +158,7 @@ def compute_volume(self, elements, total_elements):
152158
/ particles_per_element[self.element_ids]
153159
)
154160
self.volume = self.volume.at[:, 0, 0].set(vol)
161+
self.size = self.size.at[:].set(self.volume ** (1 / self.size.shape[-1]))
155162
self.mass = self.mass.at[:, 0, 0].set(vol * self.density.squeeze())
156163

157164
def update_natural_coords(self, elements: _Element):
@@ -304,6 +311,14 @@ def update_volume(self, *args):
304311
self.volume = self.volume.at[:, 0, :].multiply(1 + self.dvolumetric_strain)
305312
self.density = self.density.at[:, 0, :].divide(1 + self.dvolumetric_strain)
306313

314+
def assign_traction(self, pids, dir, traction_):
315+
self.traction = self.traction.at[pids, 0, dir].add(
316+
traction_ * self.volume[pids, 0, 0] / self.size[pids, 0, dir]
317+
)
318+
319+
def zero_traction(self, *args):
320+
self.traction = self.traction.at[:].set(0)
321+
307322

308323
if __name__ == "__main__":
309324
from diffmpm.material import SimpleMaterial

diffmpm/scheme.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ def compute_stress_strain(self):
2525
def compute_forces(self, gravity, step):
2626
self.mesh.apply_on_elements("compute_external_force")
2727
self.mesh.apply_on_elements("compute_body_force", args=(gravity,))
28+
self.mesh.apply_traction_on_particles(step * self.dt)
2829
self.mesh.apply_on_elements(
2930
"apply_concentrated_nodal_forces", args=(step * self.dt,)
3031
)

0 commit comments

Comments
 (0)