Skip to content

Commit ae5a525

Browse files
committed
self-contact (fric wip)
1 parent d2c6d37 commit ae5a525

5 files changed

Lines changed: 139 additions & 24 deletions

File tree

self_contact/BarrierEnergy.py

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
import math
22
import numpy as np
33

4+
import distance.PointEdgeDistance as PE
5+
import distance.CCD as CCD
6+
7+
import utils
8+
49
dhat = 0.01
510
kappa = 1e5
611

7-
def val(x, n, o, contact_area):
12+
def val(x, n, o, bp, be, contact_area):
813
sum = 0.0
914
# floor:
1015
for i in range(0, len(x)):
@@ -19,9 +24,19 @@ def val(x, n, o, contact_area):
1924
if d < dhat:
2025
s = d / dhat
2126
sum += contact_area[i] * dhat * kappa / 2 * (s - 1) * math.log(s)
27+
# self-contact
28+
dhat_sqr = dhat * dhat
29+
for xI in bp:
30+
for eI in be:
31+
if xI != eI[0] and xI != eI[1]: # do not consider a point and its incident edge
32+
d_sqr = PE.val(x[xI], x[eI[0]], x[eI[1]])
33+
if d_sqr < dhat_sqr:
34+
s = d_sqr / dhat_sqr
35+
# since d_sqr is used, need to divide by 8 not 2 here for consistency to linear elasticity:
36+
sum += contact_area[xI] * dhat * kappa / 8 * (s - 1) * math.log(s)
2237
return sum
2338

24-
def grad(x, n, o, contact_area):
39+
def grad(x, n, o, bp, be, contact_area):
2540
g = np.array([[0.0, 0.0]] * len(x))
2641
# floor:
2742
for i in range(0, len(x)):
@@ -38,9 +53,22 @@ def grad(x, n, o, contact_area):
3853
local_grad = contact_area[i] * dhat * (kappa / 2 * (math.log(s) / dhat + (s - 1) / d)) * n
3954
g[i] += local_grad
4055
g[-1] -= local_grad
56+
# self-contact
57+
dhat_sqr = dhat * dhat
58+
for xI in bp:
59+
for eI in be:
60+
if xI != eI[0] and xI != eI[1]: # do not consider a point and its incident edge
61+
d_sqr = PE.val(x[xI], x[eI[0]], x[eI[1]])
62+
if d_sqr < dhat_sqr:
63+
s = d_sqr / dhat_sqr
64+
# since d_sqr is used, need to divide by 8 not 2 here for consistency to linear elasticity:
65+
local_grad = contact_area[i] * dhat * (kappa / 8 * (math.log(s) / dhat_sqr + (s - 1) / d_sqr)) * PE.grad(x[xI], x[eI[0]], x[eI[1]])
66+
g[xI] += local_grad[0:2]
67+
g[eI[0]] += local_grad[2:4]
68+
g[eI[1]] += local_grad[4:6]
4169
return g
4270

43-
def hess(x, n, o, contact_area):
71+
def hess(x, n, o, bp, be, contact_area):
4472
IJV = [[0] * 0, [0] * 0, np.array([0.0] * 0)]
4573
# floor:
4674
for i in range(0, len(x)):
@@ -66,9 +94,29 @@ def hess(x, n, o, contact_area):
6694
IJV[0].append(index[nI] * 2 + r)
6795
IJV[1].append(index[nJ] * 2 + c)
6896
IJV[2] = np.append(IJV[2], ((-1) ** (nI != nJ)) * local_hess[r, c])
97+
# self-contact
98+
dhat_sqr = dhat * dhat
99+
for xI in bp:
100+
for eI in be:
101+
if xI != eI[0] and xI != eI[1]: # do not consider a point and its incident edge
102+
d_sqr = PE.val(x[xI], x[eI[0]], x[eI[1]])
103+
if d_sqr < dhat_sqr:
104+
d_sqr_grad = PE.grad(x[xI], x[eI[0]], x[eI[1]])
105+
s = d_sqr / dhat_sqr
106+
# since d_sqr is used, need to divide by 8 not 2 here for consistency to linear elasticity:
107+
local_hess = contact_area[i] * dhat * utils.make_PD(kappa / (8 * d_sqr * d_sqr * dhat_sqr) * (d_sqr + dhat_sqr) * np.outer(d_sqr_grad, d_sqr_grad) \
108+
+ (kappa / 8 * (math.log(s) / dhat_sqr + (s - 1) / d_sqr)) * PE.hess(x[xI], x[eI[0]], x[eI[1]]))
109+
index = [xI, eI[0], eI[1]]
110+
for nI in range(0, 3):
111+
for nJ in range(0, 3):
112+
for c in range(0, 2):
113+
for r in range(0, 2):
114+
IJV[0].append(index[nI] * 2 + r)
115+
IJV[1].append(index[nJ] * 2 + c)
116+
IJV[2] = np.append(IJV[2], local_hess[nI * 2 + r, nJ * 2 + c])
69117
return IJV
70118

71-
def init_step_size(x, n, o, p):
119+
def init_step_size(x, n, o, bp, be, p):
72120
alpha = 1
73121
# floor:
74122
for i in range(0, len(x)):
@@ -81,6 +129,14 @@ def init_step_size(x, n, o, p):
81129
p_n = (p[i] - p[-1]).dot(n)
82130
if p_n < 0:
83131
alpha = min(alpha, 0.9 * n.dot(x[i] - x[-1]) / -p_n)
132+
# self-contact
133+
for xI in bp:
134+
for eI in be:
135+
if xI != eI[0] and xI != eI[1]: # do not consider a point and its incident edge
136+
if CCD.bbox_overlap(x[xI], x[eI[0]], x[eI[1]], p[xI], p[eI[0]], p[eI[1]], alpha):
137+
toc = CCD.narrow_phase_CCD(x[xI], x[eI[0]], x[eI[1]], p[xI], p[eI[0]], p[eI[1]], alpha)
138+
if alpha > toc:
139+
alpha = toc
84140
return alpha
85141

86142
def compute_mu_lambda(x, n, o, contact_area, mu):

self_contact/distance/CCD.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from copy import deepcopy
2+
import numpy as np
3+
import math
4+
5+
import distance.PointEdgeDistance as PE
6+
7+
# check whether the bounding box of the trajectory of the point and the edge overlap
8+
def bbox_overlap(p, e0, e1, dp, de0, de1, toc_upperbound):
9+
max_p = np.maximum(p, p + toc_upperbound * dp) # point trajectory bbox top-right
10+
min_p = np.minimum(p, p + toc_upperbound * dp) # point trajectory bbox bottom-left
11+
max_e = np.maximum(np.maximum(e0, e0 + toc_upperbound * de0), np.maximum(e1, e1 + toc_upperbound * de1)) # edge trajectory bbox top-right
12+
min_e = np.minimum(np.minimum(e0, e0 + toc_upperbound * de0), np.minimum(e1, e1 + toc_upperbound * de1)) # edge trajectory bbox bottom-left
13+
if np.any(np.greater(min_p, max_e)) or np.any(np.greater(min_e, max_p)):
14+
return False
15+
else:
16+
return True
17+
18+
# compute the first "time" of contact, or toc,
19+
# return the computed toc only if it is smaller than the previously computed toc_upperbound
20+
def narrow_phase_CCD(_p, _e0, _e1, _dp, _de0, _de1, toc_upperbound):
21+
p = deepcopy(_p)
22+
e0 = deepcopy(_e0)
23+
e1 = deepcopy(_e1)
24+
dp = deepcopy(_dp)
25+
de0 = deepcopy(_de0)
26+
de1 = deepcopy(_de1)
27+
28+
# use relative displacement for faster convergence
29+
mov = (dp + de0 + de1) / 3
30+
de0 -= mov
31+
de1 -= mov
32+
dp -= mov
33+
maxDispMag = np.linalg.norm(dp) + math.sqrt(max(np.dot(de0, de0), np.dot(de1, de1)))
34+
if maxDispMag == 0:
35+
return toc_upperbound
36+
37+
eta = 0.1 # calculate the toc that first brings the distance to 0.1x the current distance
38+
dist2_cur = PE.val(p, e0, e1)
39+
dist_cur = math.sqrt(dist2_cur)
40+
gap = eta * dist_cur
41+
# iteratively move the point and edge towards each other and
42+
# grow the toc estimate without numerical errors
43+
toc = 0
44+
while True:
45+
tocLowerBound = (1 - eta) * dist_cur / maxDispMag
46+
47+
p += tocLowerBound * dp
48+
e0 += tocLowerBound * de0
49+
e1 += tocLowerBound * de1
50+
dist2_cur = PE.val(p, e0, e1)
51+
dist_cur = math.sqrt(dist2_cur)
52+
if toc != 0 and dist_cur < gap:
53+
break
54+
55+
toc += tocLowerBound
56+
if toc > toc_upperbound:
57+
return toc_upperbound
58+
59+
return toc

self_contact/distance/PointEdgeDistance.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22

3-
import PointPointDistance as PP
4-
import PointLineDistance as PL
3+
import distance.PointPointDistance as PP
4+
import distance.PointLineDistance as PL
55

66
def val(p, e0, e1):
77
e = e1 - e0

self_contact/simulator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def screen_projection(x):
8080
pygame.display.flip() # flip the display
8181

8282
# step forward simulation and wait for screen refresh
83-
[x, v] = time_integrator.step_forward(x, e, v, m, vol, IB, mu_lame, lam, ground_n, ground_o, contact_area, mu, is_DBC, DBC, DBC_v, DBC_limit, h, 1e-2)
83+
[x, v] = time_integrator.step_forward(x, e, v, m, vol, IB, mu_lame, lam, ground_n, ground_o, bp, be, contact_area, mu, is_DBC, DBC, DBC_v, DBC_limit, h, 1e-2)
8484
time_step += 1
8585
pygame.time.wait(int(h * 1000))
8686

self_contact/time_integrator.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import FrictionEnergy
1414
import SpringEnergy
1515

16-
def step_forward(x, e, v, m, vol, IB, mu_lame, lam, n, o, contact_area, mu, is_DBC, DBC, DBC_v, DBC_limit, h, tol):
16+
def step_forward(x, e, v, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, mu, is_DBC, DBC, DBC_v, DBC_limit, h, tol):
1717
x_tilde = x + v * h # implicit Euler predictive position
1818
x_n = copy.deepcopy(x)
1919
mu_lambda = BarrierEnergy.compute_mu_lambda(x, n, o, contact_area, mu) # compute mu * lambda for each node using x^n
@@ -27,51 +27,51 @@ def step_forward(x, e, v, m, vol, IB, mu_lame, lam, n, o, contact_area, mu, is_D
2727

2828
# Newton loop
2929
iter = 0
30-
E_last = IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h)
31-
[p, DBC_satisfied] = search_dir(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, (x - x_n) / h, mu_lambda, is_DBC, DBC, DBC_target, DBC_stiff, tol, h)
30+
E_last = IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h)
31+
[p, DBC_satisfied] = search_dir(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, (x - x_n) / h, mu_lambda, is_DBC, DBC, DBC_target, DBC_stiff, tol, h)
3232
while (LA.norm(p, inf) / h > tol) | (sum(DBC_satisfied) != len(DBC)): # also check whether all DBCs are satisfied
3333
print('Iteration', iter, ':')
3434
print('residual =', LA.norm(p, inf) / h)
3535

3636
if (LA.norm(p, inf) / h <= tol) & (sum(DBC_satisfied) != len(DBC)):
3737
# increase DBC stiffness and recompute energy value record
3838
DBC_stiff *= 2
39-
E_last = IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h)
39+
E_last = IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h)
4040

4141
# filter line search
42-
alpha = min(BarrierEnergy.init_step_size(x, n, o, p), NeoHookeanEnergy.init_step_size(x, e, p)) # avoid interpenetration, tunneling, and inversion
43-
while IP_val(x + alpha * p, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h) > E_last:
42+
alpha = min(BarrierEnergy.init_step_size(x, n, o, bp, be, p), NeoHookeanEnergy.init_step_size(x, e, p)) # avoid interpenetration, tunneling, and inversion
43+
while IP_val(x + alpha * p, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h) > E_last:
4444
alpha /= 2
4545
print('step size =', alpha)
4646

4747
x += alpha * p
48-
E_last = IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h)
49-
[p, DBC_satisfied] = search_dir(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, (x - x_n) / h, mu_lambda, is_DBC, DBC, DBC_target, DBC_stiff, tol, h)
48+
E_last = IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, (x - x_n) / h, mu_lambda, DBC, DBC_target, DBC_stiff, h)
49+
[p, DBC_satisfied] = search_dir(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, (x - x_n) / h, mu_lambda, is_DBC, DBC, DBC_target, DBC_stiff, tol, h)
5050
iter += 1
5151

5252
v = (x - x_n) / h # implicit Euler velocity update
5353
return [x, v]
5454

55-
def IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h):
55+
def IP_val(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h):
5656
return InertiaEnergy.val(x, x_tilde, m) + h * h * ( # implicit Euler
5757
NeoHookeanEnergy.val(x, e, vol, IB, mu_lame, lam) +
5858
GravityEnergy.val(x, m) +
59-
BarrierEnergy.val(x, n, o, contact_area) +
59+
BarrierEnergy.val(x, n, o, bp, be, contact_area) +
6060
FrictionEnergy.val(v, mu_lambda, h, n)
6161
) + SpringEnergy.val(x, m, DBC, DBC_target, DBC_stiff)
6262

63-
def IP_grad(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h):
63+
def IP_grad(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h):
6464
return InertiaEnergy.grad(x, x_tilde, m) + h * h * ( # implicit Euler
6565
NeoHookeanEnergy.grad(x, e, vol, IB, mu_lame, lam) +
6666
GravityEnergy.grad(x, m) +
67-
BarrierEnergy.grad(x, n, o, contact_area) +
67+
BarrierEnergy.grad(x, n, o, bp, be, contact_area) +
6868
FrictionEnergy.grad(v, mu_lambda, h, n)
6969
) + SpringEnergy.grad(x, m, DBC, DBC_target, DBC_stiff)
7070

71-
def IP_hess(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h):
71+
def IP_hess(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h):
7272
IJV_In = InertiaEnergy.hess(x, x_tilde, m)
7373
IJV_MS = NeoHookeanEnergy.hess(x, e, vol, IB, mu_lame, lam)
74-
IJV_B = BarrierEnergy.hess(x, n, o, contact_area)
74+
IJV_B = BarrierEnergy.hess(x, n, o, bp, be, contact_area)
7575
IJV_F = FrictionEnergy.hess(v, mu_lambda, h, n)
7676
IJV_S = SpringEnergy.hess(x, m, DBC, DBC_target, DBC_stiff)
7777
IJV_MS[2] *= h * h # implicit Euler
@@ -84,9 +84,9 @@ def IP_hess(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, v, mu_l
8484
H = sparse.coo_matrix((IJV[2], (IJV[0], IJV[1])), shape=(len(x) * 2, len(x) * 2)).tocsr()
8585
return H
8686

87-
def search_dir(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, v, mu_lambda, is_DBC, DBC, DBC_target, DBC_stiff, tol, h):
88-
projected_hess = IP_hess(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h)
89-
reshaped_grad = IP_grad(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h).reshape(len(x) * 2, 1)
87+
def search_dir(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, v, mu_lambda, is_DBC, DBC, DBC_target, DBC_stiff, tol, h):
88+
projected_hess = IP_hess(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h)
89+
reshaped_grad = IP_grad(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, v, mu_lambda, DBC, DBC_target, DBC_stiff, h).reshape(len(x) * 2, 1)
9090
# check whether each DBC is satisfied
9191
DBC_satisfied = [False] * len(x)
9292
for i in range(0, len(DBC)):

0 commit comments

Comments
 (0)