Skip to content

Instantly share code, notes, and snippets.

@FantasyVR
Created June 12, 2022 17:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save FantasyVR/5cd9b76062a9b0fdaa0568a2040797f7 to your computer and use it in GitHub Desktop.
Save FantasyVR/5cd9b76062a9b0fdaa0568a2040797f7 to your computer and use it in GitHub Desktop.
import taichi as ti
import numpy as np
ti.init(arch=ti.gpu)
N = 32
dt = 1e-4
dx = 1 / N
rho = 4e1
NF = 2 * N**2 # number of faces
NV = (N + 1)**2 # number of vertices
E, nu = 4e4, 0.2 # Young's modulus and Poisson's ratio
mu, lam = E / 2 / (1 + nu), E * nu / (1 + nu) / (1 - 2 * nu) # Lame parameters
ball_pos, ball_radius = ti.Vector([0.5, 0.0]), 0.32
gravity = ti.Vector([0, -40])
damping = 12.5
pos = ti.Vector.field(2, float, NV, needs_grad=True)
vel = ti.Vector.field(2, float, NV)
f2v = ti.Vector.field(3, int, NF) # ids of three vertices of each face
B = ti.Matrix.field(2, 2, float, NF)
F = ti.Matrix.field(2, 2, float, NF, needs_grad=True)
V = ti.field(float, NF)
phi = ti.field(float, NF) # potential energy of each face (Neo-Hookean)
U = ti.field(float, (), needs_grad=True) # total potential energy
@ti.kernel
def update_U():
for i in range(NF):
ia, ib, ic = f2v[i]
a, b, c = pos[ia], pos[ib], pos[ic]
V[i] = abs((a - c).cross(b - c))
D_i = ti.Matrix.cols([a - c, b - c])
F[i] = D_i @ B[i]
for i in range(NF):
F_i = F[i]
log_J_i = ti.log(F_i.determinant())
phi_i = mu / 2 * ((F_i.transpose() @ F_i).trace() - 2)
phi_i -= mu * log_J_i
phi_i += lam / 2 * log_J_i**2
phi[i] = phi_i
U[None] += V[i] * phi_i
@ti.kernel
def advance():
for i in range(NV):
acc = -pos.grad[i] / (rho * dx**2)
vel[i] += dt * (acc + gravity)
vel[i] *= ti.exp(-dt * damping)
for i in range(NV):
# ball boundary condition:
disp = pos[i] - ball_pos
disp2 = disp.norm_sqr()
if disp2 <= ball_radius**2:
NoV = vel[i].dot(disp)
if NoV < 0: vel[i] -= NoV * disp / disp2
# rect boundary condition:
cond = (pos[i] < 0) & (vel[i] < 0) | (pos[i] > 1) & (vel[i] > 0)
for j in ti.static(range(pos.n)):
if cond[j]: vel[i][j] = 0
pos[i] += dt * vel[i]
@ti.kernel
def init_pos():
for i, j in ti.ndrange(N + 1, N + 1):
k = i * (N + 1) + j
pos[k] = ti.Vector([i, j]) / N * 0.25 + ti.Vector([0.45, 0.45])
vel[k] = ti.Vector([0, 0])
for i in range(NF):
ia, ib, ic = f2v[i]
a, b, c = pos[ia], pos[ib], pos[ic]
B_i_inv = ti.Matrix.cols([a - c, b - c])
B[i] = B_i_inv.inverse()
@ti.kernel
def init_mesh():
for i, j in ti.ndrange(N, N):
k = (i * N + j) * 2
a = i * (N + 1) + j
b = a + 1
c = a + N + 2
d = a + N + 1
f2v[k + 0] = [a, b, c]
f2v[k + 1] = [c, d, a]
init_mesh()
init_pos()
gui = ti.GUI('FEM99')
frame = 0
series_prefix = "example.ply"
while gui.running:
for e in gui.get_events():
if e.key == gui.ESCAPE:
gui.running = False
elif e.key == 'r':
init_pos()
for i in range(30):
with ti.Tape(loss=U):
update_U()
advance()
gui.circles(pos.to_numpy(), radius=2, color=0xffaa33)
gui.circle(ball_pos, radius=ball_radius * 512, color=0x666666)
gui.show()
# export mesh
num_vertices = NV
np_pos = np.reshape(pos.to_numpy(), (num_vertices, 2))
writer = ti.tools.PLYWriter(num_vertices=num_vertices)
z = np.zeros(num_vertices, np.float32)
writer.add_vertex_pos(np_pos[:, 0], np_pos[:, 1], z)
writer.export_frame_ascii(frame, series_prefix)
frame += 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment