Skip to content

Commit 32a3ef2

Browse files
committed
ground contact
1 parent 7ef5c63 commit 32a3ef2

4 files changed

Lines changed: 79 additions & 24 deletions

File tree

contact/BarrierEnergy.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import math
2+
import numpy as np
3+
4+
y_ground = -1
5+
dhat = 0.01
6+
kappa = 1e5
7+
8+
def val(x, contact_area):
9+
sum = 0.0
10+
for i in range(0, len(x)):
11+
d = x[i][1] - y_ground
12+
if d < dhat:
13+
s = d / dhat
14+
sum += contact_area[i] * dhat * kappa / 2 * (s - 1) * math.log(s)
15+
return sum
16+
17+
def grad(x, contact_area):
18+
g = np.array([[0.0, 0.0]] * len(x))
19+
for i in range(0, len(x)):
20+
d = x[i][1] - y_ground
21+
if d < dhat:
22+
s = d / dhat
23+
g[i][1] = contact_area[i] * dhat * (kappa / 2 * (math.log(s) / dhat + (s - 1) / d))
24+
return g
25+
26+
def hess(x, contact_area):
27+
IJV = [[0] * len(x), [0] * len(x), np.array([0.0] * len(x))]
28+
for i in range(0, len(x)):
29+
IJV[0][i] = i * 2 + 1
30+
IJV[1][i] = i * 2 + 1
31+
d = x[i][1] - y_ground
32+
if d < dhat:
33+
IJV[2][i] = contact_area[i] * dhat * kappa / (2 * d * d * dhat) * (d + dhat)
34+
else:
35+
IJV[2][i] = 0.0
36+
return IJV
37+
38+
def init_step_size(x, p):
39+
alpha = 1
40+
for i in range(0, len(x)):
41+
if p[i][1] < 0:
42+
alpha = min(alpha, 0.9 * (y_ground - x[i][1]) / p[i][1])
43+
return alpha

contact/GravityEnergy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import numpy as np
22

3-
gravity = [0.0, 9.81]
3+
gravity = [0.0, -9.81]
44

55
def val(x, m):
66
sum = 0.0

contact/simulator.py

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
# simulation setup
1111
side_len = 1
1212
rho = 1000 # density of square
13-
k = 1e3 # spring stiffness
13+
k = 2e4 # spring stiffness
1414
n_seg = 4 # num of segments per side of the square
15-
h = 0.02 # time step size in s
16-
DBC = [0, (n_seg + 1) * n_seg] # fix the left and right top nodes
15+
h = 0.01 # time step size in s
16+
DBC = [] # no nodes need to be fixed
1717

1818
# initialize simulation
1919
[x, e] = square_mesh.generate(side_len, n_seg) # node positions and edge node indices
@@ -29,10 +29,17 @@
2929
is_DBC = [False] * len(x)
3030
for i in DBC:
3131
is_DBC[i] = True
32+
contact_area = [side_len / n_seg] * len(x) # perimeter split to each node
3233

3334
# simulation with visualization
35+
resolution = np.array([900, 900])
36+
offset = resolution / 2
37+
scale = 200
38+
def screen_projection(x):
39+
return [offset[0] + scale * x[0], resolution[1] - (offset[1] + scale * x[1])]
40+
3441
time_step = 0
35-
screen = pygame.display.set_mode([900, 900])
42+
screen = pygame.display.set_mode(resolution)
3643
running = True
3744
while running:
3845
# run until the user asks to quit
@@ -44,15 +51,16 @@
4451

4552
# fill the background and draw the square
4653
screen.fill((255, 255, 255))
54+
pygame.draw.aaline(screen, (0, 0, 255), [0, offset[1] + 1 * scale], [resolution[1], offset[1] + 1 * scale]) # ground
4755
for eI in e:
48-
pygame.draw.aaline(screen, (0, 0, 255), 450 + x[eI[0]] * 200, 450 + x[eI[1]] * 200)
56+
pygame.draw.aaline(screen, (0, 0, 255), screen_projection(x[eI[0]]), screen_projection(x[eI[1]]))
4957
for xI in x:
50-
pygame.draw.circle(screen, (0, 0, 255), 450 + xI * 200, 0.1 * side_len / n_seg * 200)
58+
pygame.draw.circle(screen, (0, 0, 255), screen_projection(xI), 0.1 * side_len / n_seg * scale)
5159

5260
pygame.display.flip() # flip the display
5361

5462
# step forward simulation and wait for screen refresh
55-
[x, v] = time_integrator.step_forward(x, e, v, m, l2, k, is_DBC, h, 1e-2)
63+
[x, v] = time_integrator.step_forward(x, e, v, m, l2, k, contact_area, is_DBC, h, 1e-2)
5664
time_step += 1
5765
pygame.time.wait(int(h * 1000))
5866

contact/time_integrator.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,50 +9,54 @@
99
import InertiaEnergy
1010
import MassSpringEnergy
1111
import GravityEnergy
12+
import BarrierEnergy
1213

13-
def step_forward(x, e, v, m, l2, k, is_DBC, h, tol):
14+
def step_forward(x, e, v, m, l2, k, contact_area, is_DBC, h, tol):
1415
x_tilde = x + v * h # implicit Euler predictive position
1516
x_n = copy.deepcopy(x)
1617

1718
# Newton loop
1819
iter = 0
19-
E_last = IP_val(x, e, x_tilde, m, l2, k, h)
20-
p = search_dir(x, e, x_tilde, m, l2, k, is_DBC, h)
20+
E_last = IP_val(x, e, x_tilde, m, l2, k, contact_area, h)
21+
p = search_dir(x, e, x_tilde, m, l2, k, contact_area, is_DBC, h)
2122
while LA.norm(p, inf) / h > tol:
2223
print('Iteration', iter, ':')
2324
print('residual =', LA.norm(p, inf) / h)
2425

2526
# line search
26-
alpha = 1
27-
while IP_val(x + alpha * p, e, x_tilde, m, l2, k, h) > E_last:
27+
alpha = BarrierEnergy.init_step_size(x, p) # filter line search to avoid interpenetration and tunneling
28+
while IP_val(x + alpha * p, e, x_tilde, m, l2, k, contact_area, h) > E_last:
2829
alpha /= 2
2930
print('step size =', alpha)
3031

3132
x += alpha * p
32-
E_last = IP_val(x, e, x_tilde, m, l2, k, h)
33-
p = search_dir(x, e, x_tilde, m, l2, k, is_DBC, h)
33+
E_last = IP_val(x, e, x_tilde, m, l2, k, contact_area, h)
34+
p = search_dir(x, e, x_tilde, m, l2, k, contact_area, is_DBC, h)
3435
iter += 1
3536

3637
v = (x - x_n) / h # implicit Euler velocity update
3738
return [x, v]
3839

39-
def IP_val(x, e, x_tilde, m, l2, k, h):
40-
return InertiaEnergy.val(x, x_tilde, m) + h * h * (MassSpringEnergy.val(x, e, l2, k) + GravityEnergy.val(x, m)) # implicit Euler
40+
def IP_val(x, e, x_tilde, m, l2, k, contact_area, h):
41+
return InertiaEnergy.val(x, x_tilde, m) + h * h * (MassSpringEnergy.val(x, e, l2, k) + GravityEnergy.val(x, m) + BarrierEnergy.val(x, contact_area)) # implicit Euler
4142

42-
def IP_grad(x, e, x_tilde, m, l2, k, h):
43-
return InertiaEnergy.grad(x, x_tilde, m) + h * h * (MassSpringEnergy.grad(x, e, l2, k) + GravityEnergy.grad(x, m)) # implicit Euler
43+
def IP_grad(x, e, x_tilde, m, l2, k, contact_area, h):
44+
return InertiaEnergy.grad(x, x_tilde, m) + h * h * (MassSpringEnergy.grad(x, e, l2, k) + GravityEnergy.grad(x, m) + BarrierEnergy.grad(x, contact_area)) # implicit Euler
4445

45-
def IP_hess(x, e, x_tilde, m, l2, k, h):
46+
def IP_hess(x, e, x_tilde, m, l2, k, contact_area, h):
4647
IJV_In = InertiaEnergy.hess(x, x_tilde, m)
4748
IJV_MS = MassSpringEnergy.hess(x, e, l2, k)
49+
IJV_B = BarrierEnergy.hess(x, contact_area)
4850
IJV_MS[2] *= h * h # implicit Euler
49-
IJV = np.append(IJV_In, IJV_MS, axis=1)
51+
IJV_B[2] *= h * h # implicit Euler
52+
IJV_In_MS = np.append(IJV_In, IJV_MS, axis=1)
53+
IJV = np.append(IJV_In_MS, IJV_B, axis=1)
5054
H = sparse.coo_matrix((IJV[2], (IJV[0], IJV[1])), shape=(len(x) * 2, len(x) * 2)).tocsr()
5155
return H
5256

53-
def search_dir(x, e, x_tilde, m, l2, k, is_DBC, h):
54-
projected_hess = IP_hess(x, e, x_tilde, m, l2, k, h)
55-
reshaped_grad = IP_grad(x, e, x_tilde, m, l2, k, h).reshape(len(x) * 2, 1)
57+
def search_dir(x, e, x_tilde, m, l2, k, contact_area, is_DBC, h):
58+
projected_hess = IP_hess(x, e, x_tilde, m, l2, k, contact_area, h)
59+
reshaped_grad = IP_grad(x, e, x_tilde, m, l2, k, contact_area, h).reshape(len(x) * 2, 1)
5660
# eliminate DOF by modifying gradient and Hessian for DBC:
5761
for i, j in zip(*projected_hess.nonzero()):
5862
if is_DBC[int(i / 2)] | is_DBC[int(j / 2)]:

0 commit comments

Comments
 (0)