Skip to content

Commit f9d4811

Browse files
g1n0stliminchen
andauthored
Add mpm code examples (#5)
* [add] add sand mpm code * [update] update section number and add anchors for references * [update] move mpm_sand from 10 to 11 * [add] add two colliding elastic blocks in 2d case * [add] add anchor for grid update code * [update] add anchors * updated comments --------- Co-authored-by: Minchen Li <minchernl@gmail.com>
1 parent 841176f commit f9d4811

6 files changed

Lines changed: 476 additions & 0 deletions

File tree

10_mpm_elasticity/readme.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Two Colliding Elastic Blocks in 2D
2+
3+
Two elastic blocks with opposite initial velocities collide head-on using the simplest PIC transfer scheme, illustrating a minimal yet complete implementation of the Material Point Method (MPM).
4+
5+
![results](results.gif)
6+
7+
## Dependencies
8+
```
9+
pip install numpy taichi
10+
```
11+
12+
## Run
13+
```
14+
python simulator.py
15+
```

10_mpm_elasticity/results.gif

448 KB
Loading

10_mpm_elasticity/simulator.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
# Material Point Method Simulation
2+
import numpy as np # numpy for linear algebra
3+
import taichi as ti # taichi for fast and parallelized computation
4+
5+
ti.init(arch=ti.cpu)
6+
7+
# ANCHOR: property_def
8+
# simulation setup
9+
grid_size = 128 # background Eulerian grid's resolution, in 2D is [128, 128]
10+
dx = 1.0 / grid_size # the domain size is [1m, 1m] in 2D, so dx for each cell is (1/128)m
11+
dt = 2e-4 # time step size in second
12+
ppc = 8 # average particles per cell
13+
14+
density = 1000 # mass density, unit: kg / m^3
15+
E, nu = 1e4, 0.3 # block's Young's modulus and Poisson's ratio
16+
mu, lam = E / (2 * (1 + nu)), E * nu / ((1 + nu) * (1 - 2 * nu)) # Lame parameters
17+
# ANCHOR_END: property_def
18+
19+
# ANCHOR: setting
20+
# uniformly sampling material particles
21+
def uniform_grid(x0, y0, x1, y1, dx):
22+
xx, yy = np.meshgrid(np.arange(x0, x1 + dx, dx), np.arange(y0, y1 + dx, dx))
23+
return np.column_stack((xx.ravel(), yy.ravel()))
24+
25+
box1_samples = uniform_grid(0.2, 0.4, 0.4, 0.6, dx / np.sqrt(ppc))
26+
box1_velocities = np.tile(np.array([10.0, 0]), (len(box1_samples), 1))
27+
box2_samples = uniform_grid(0.6, 0.4, 0.8, 0.6, dx / np.sqrt(ppc))
28+
box2_velocities = np.tile(np.array([-10.0, 0]), (len(box1_samples), 1))
29+
all_samples = np.concatenate([box1_samples, box2_samples], axis=0)
30+
all_velocities = np.concatenate([box1_velocities, box2_velocities], axis=0)
31+
# ANCHOR_END: setting
32+
33+
# ANCHOR: data_def
34+
# material particles data
35+
N_particles = len(all_samples)
36+
x = ti.Vector.field(2, float, N_particles) # the position of particles
37+
x.from_numpy(all_samples)
38+
v = ti.Vector.field(2, float, N_particles) # the velocity of particles
39+
v.from_numpy(all_velocities)
40+
vol = ti.field(float, N_particles) # the volume of particle
41+
vol.fill(0.2 * 0.4 / N_particles) # get the volume of each particle as V_rest / N_particles
42+
m = ti.field(float, N_particles) # the mass of particle
43+
m.fill(vol[0] * density)
44+
F = ti.Matrix.field(2, 2, float, N_particles) # the deformation gradient of particles
45+
F.from_numpy(np.tile(np.eye(2), (N_particles, 1, 1)))
46+
47+
# grid data
48+
grid_m = ti.field(float, (grid_size, grid_size))
49+
grid_v = ti.Vector.field(2, float, (grid_size, grid_size))
50+
# ANCHOR_END: data_def
51+
52+
# ANCHOR: reset_grid
53+
def reset_grid():
54+
# after each transfer, the grid is reset
55+
grid_m.fill(0)
56+
grid_v.fill(0)
57+
# ANCHOR_END: reset_grid
58+
59+
################################
60+
# Stvk Hencky Elasticity
61+
################################
62+
# ANCHOR: stvk
63+
@ti.func
64+
def StVK_Hencky_PK1_2D(F):
65+
U, sig, V = ti.svd(F)
66+
inv_sig = sig.inverse()
67+
e = ti.Matrix([[ti.log(sig[0, 0]), 0], [0, ti.log(sig[1, 1])]])
68+
return U @ (2 * mu * inv_sig @ e + lam * e.trace() * inv_sig) @ V.transpose()
69+
# ANCHOR_END: stvk
70+
71+
# Particle-to-Grid (P2G) Transfers
72+
# ANCHOR: p2g
73+
@ti.kernel
74+
def particle_to_grid_transfer():
75+
for p in range(N_particles):
76+
base = (x[p] / dx - 0.5).cast(int)
77+
fx = x[p] / dx - base.cast(float)
78+
# quadratic B-spline interpolating functions (Section 26.2)
79+
w = [0.5 * (1.5 - fx) ** 2, 0.75 - (fx - 1) ** 2, 0.5 * (fx - 0.5) ** 2]
80+
# gradient of the interpolating function (Section 26.2)
81+
dw_dx = [fx - 1.5, 2 * (1.0 - fx), fx - 0.5]
82+
83+
P = StVK_Hencky_PK1_2D(F[p])
84+
for i in ti.static(range(3)):
85+
for j in ti.static(range(3)):
86+
offset = ti.Vector([i, j])
87+
weight = w[i][0] * w[j][1]
88+
grad_weight = ti.Vector([(1. / dx) * dw_dx[i][0] * w[j][1],
89+
w[i][0] * (1. / dx) * dw_dx[j][1]])
90+
91+
grid_m[base + offset] += weight * m[p] # mass transfer
92+
grid_v[base + offset] += weight * m[p] * v[p] # momentum Transfer, PIC formulation
93+
# internal force (stress) transfer
94+
fi = -vol[p] * P @ F[p].transpose() @ grad_weight
95+
grid_v[base + offset] += dt * fi
96+
# ANCHOR_END: p2g
97+
98+
# Grid Update
99+
# ANCHOR: grid_update
100+
@ti.kernel
101+
def update_grid():
102+
for i, j in grid_m:
103+
if grid_m[i, j] > 0:
104+
grid_v[i, j] = grid_v[i, j] / grid_m[i, j] # extract updated nodal velocity from transferred nodal momentum
105+
106+
# Dirichlet BC near the bounding box
107+
if i <= 3 or i > grid_size - 3 or j <= 2 or j > grid_size - 3:
108+
grid_v[i, j] = 0
109+
# ANCHOR_END: grid_update
110+
111+
112+
# Grid-to-Particle (G2P) Transfers
113+
# ANCHOR: g2p
114+
@ti.kernel
115+
def grid_to_particle_transfer():
116+
for p in range(N_particles):
117+
base = (x[p] / dx - 0.5).cast(int)
118+
fx = x[p] / dx - base.cast(float)
119+
# quadratic B-spline interpolating functions (Section 26.2)
120+
w = [0.5 * (1.5 - fx) ** 2, 0.75 - (fx - 1) ** 2, 0.5 * (fx - 0.5) ** 2]
121+
# gradient of the interpolating function (Section 26.2)
122+
dw_dx = [fx - 1.5, 2 * (1.0 - fx), fx - 0.5]
123+
124+
new_v = ti.Vector.zero(float, 2)
125+
v_grad_wT = ti.Matrix.zero(float, 2, 2)
126+
for i in ti.static(range(3)):
127+
for j in ti.static(range(3)):
128+
offset = ti.Vector([i, j])
129+
weight = w[i][0] * w[j][1]
130+
grad_weight = ti.Vector([(1. / dx) * dw_dx[i][0] * w[j][1],
131+
w[i][0] * (1. / dx) * dw_dx[j][1]])
132+
133+
new_v += weight * grid_v[base + offset]
134+
v_grad_wT += grid_v[base + offset].outer_product(grad_weight)
135+
136+
v[p] = new_v
137+
F[p] = (ti.Matrix.identity(float, 2) + dt * v_grad_wT) @ F[p]
138+
# ANCHOR_END: g2p
139+
140+
# Deformation Gradient and Particle State Update
141+
# ANCHOR: particle_update
142+
@ti.kernel
143+
def update_particle_state():
144+
for p in range(N_particles):
145+
x[p] += dt * v[p] # advection
146+
# ANCHOR_END: particle_update
147+
148+
# ANCHOR: time_step
149+
def step():
150+
# a single time step of the Material Point Method (MPM) simulation
151+
reset_grid()
152+
particle_to_grid_transfer()
153+
update_grid()
154+
grid_to_particle_transfer()
155+
update_particle_state()
156+
# ANCHOR_END: time_step
157+
158+
################################
159+
# Main
160+
################################
161+
gui = ti.GUI("2D MPM Elasticity", res = 512, background_color = 0xFFFFFF)
162+
while True:
163+
for s in range(50): step()
164+
165+
gui.circles(x.to_numpy()[:len(box1_samples)], radius=3, color=0xFF0000)
166+
gui.circles(x.to_numpy()[len(box1_samples):], radius=3, color=0x0000FF)
167+
gui.show()

11_mpm_sand/readme.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# 2D Sand with a Sphere Collider
2+
3+
A 2D sand block falling onto a static red sphere collider simulated by Material Point Method (MPM). The sand undergoes irreversible deformation and splashing upon impact, demonstrating granular flow and frictional boundary response.
4+
5+
![results](results.gif)
6+
7+
## Dependencies
8+
```
9+
pip install numpy taichi
10+
```
11+
12+
## Run
13+
```
14+
python simulator.py
15+
```

11_mpm_sand/results.gif

4.03 MB
Loading

0 commit comments

Comments
 (0)