Skip to content

Commit 05cc3d9

Browse files
committed
frictional self-contact
1 parent ae5a525 commit 05cc3d9

4 files changed

Lines changed: 91 additions & 24 deletions

File tree

self_contact/BarrierEnergy.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def grad(x, n, o, bp, be, contact_area):
6262
if d_sqr < dhat_sqr:
6363
s = d_sqr / dhat_sqr
6464
# 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]])
65+
local_grad = contact_area[xI] * dhat * (kappa / 8 * (math.log(s) / dhat_sqr + (s - 1) / d_sqr)) * PE.grad(x[xI], x[eI[0]], x[eI[1]])
6666
g[xI] += local_grad[0:2]
6767
g[eI[0]] += local_grad[2:4]
6868
g[eI[1]] += local_grad[4:6]
@@ -104,7 +104,7 @@ def hess(x, n, o, bp, be, contact_area):
104104
d_sqr_grad = PE.grad(x[xI], x[eI[0]], x[eI[1]])
105105
s = d_sqr / dhat_sqr
106106
# 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) \
107+
local_hess = contact_area[xI] * dhat * utils.make_PD(kappa / (8 * d_sqr * d_sqr * dhat_sqr) * (d_sqr + dhat_sqr) * np.outer(d_sqr_grad, d_sqr_grad) \
108108
+ (kappa / 8 * (math.log(s) / dhat_sqr + (s - 1) / d_sqr)) * PE.hess(x[xI], x[eI[0]], x[eI[1]]))
109109
index = [xI, eI[0], eI[1]]
110110
for nI in range(0, 3):
@@ -139,11 +139,26 @@ def init_step_size(x, n, o, bp, be, p):
139139
alpha = toc
140140
return alpha
141141

142-
def compute_mu_lambda(x, n, o, contact_area, mu):
142+
def compute_mu_lambda(x, n, o, bp, be, contact_area, mu):
143+
# floor:
143144
mu_lambda = np.array([0.0] * len(x))
144145
for i in range(0, len(x)):
145146
d = n.dot(x[i] - o)
146147
if d < dhat:
147148
s = d / dhat
148149
mu_lambda[i] = mu * -contact_area[i] * dhat * (kappa / 2 * (math.log(s) / dhat + (s - 1) / d))
149-
return mu_lambda
150+
# self-contact
151+
mu_lambda_self = []
152+
dhat_sqr = dhat * dhat
153+
for xI in bp:
154+
for eI in be:
155+
if xI != eI[0] and xI != eI[1]: # do not consider a point and its incident edge
156+
d_sqr = PE.val(x[xI], x[eI[0]], x[eI[1]])
157+
if d_sqr < dhat_sqr:
158+
s = d_sqr / dhat_sqr
159+
# since d_sqr is used, need to divide by 8 not 2 here for consistency to linear elasticity
160+
# also, lambda = -\partial b / \partial d = -(\partial b / \partial d^2) * (\partial d^2 / \partial d)
161+
mu_lam = mu * -contact_area[xI] * dhat * (kappa / 8 * (math.log(s) / dhat_sqr + (s - 1) / d_sqr)) * 2 * math.sqrt(d_sqr)
162+
[n, r] = PE.tangent(x[xI], x[eI[0]], x[eI[1]]) # normal and closest point parameterization on the edge
163+
mu_lambda_self.append([xI, eI[0], eI[1], mu_lam, n, r])
164+
return [mu_lambda, mu_lambda_self]

self_contact/FrictionEnergy.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,46 @@ def f_hess_term(vbarnorm, epsv):
2323
else:
2424
return -1.0 / (epsv * epsv)
2525

26-
def val(v, mu_lambda, hhat, n):
26+
def val(v, mu_lambda, mu_lambda_self, hhat, n):
2727
sum = 0.0
28+
# floor:
2829
T = np.identity(2) - np.outer(n, n) # tangent of slope is constant
2930
for i in range(0, len(v)):
3031
if mu_lambda[i] > 0:
3132
vbar = np.transpose(T).dot(v[i])
3233
sum += mu_lambda[i] * f0(np.linalg.norm(vbar), epsv, hhat)
34+
# self-contact:
35+
for i in range(0, len(mu_lambda_self)):
36+
[xI, eI0, eI1, mu_lam, n, r] = mu_lambda_self[i]
37+
T = np.identity(2) - np.outer(n, n)
38+
rel_v = v[xI] - ((1 - r) * v[eI0] + r * v[eI1])
39+
vbar = np.transpose(T).dot(rel_v)
40+
sum += mu_lam * f0(np.linalg.norm(vbar), epsv, hhat)
3341
return sum
3442

35-
def grad(v, mu_lambda, hhat, n):
43+
def grad(v, mu_lambda, mu_lambda_self, hhat, n):
3644
g = np.array([[0.0, 0.0]] * len(v))
45+
# floor:
3746
T = np.identity(2) - np.outer(n, n) # tangent of slope is constant
3847
for i in range(0, len(v)):
3948
if mu_lambda[i] > 0:
4049
vbar = np.transpose(T).dot(v[i])
4150
g[i] = mu_lambda[i] * f1_div_vbarnorm(np.linalg.norm(vbar), epsv) * T.dot(vbar)
51+
# self-contact:
52+
for i in range(0, len(mu_lambda_self)):
53+
[xI, eI0, eI1, mu_lam, n, r] = mu_lambda_self[i]
54+
T = np.identity(2) - np.outer(n, n)
55+
rel_v = v[xI] - ((1 - r) * v[eI0] + r * v[eI1])
56+
vbar = np.transpose(T).dot(rel_v)
57+
g_rel_v = mu_lam * f1_div_vbarnorm(np.linalg.norm(vbar), epsv) * T.dot(vbar)
58+
g[xI] += g_rel_v
59+
g[eI0] += g_rel_v * -(1 - r)
60+
g[eI1] += g_rel_v * -r
4261
return g
4362

44-
def hess(v, mu_lambda, hhat, n):
63+
def hess(v, mu_lambda, mu_lambda_self, hhat, n):
4564
IJV = [[0] * 0, [0] * 0, np.array([0.0] * 0)]
65+
# floor:
4666
T = np.identity(2) - np.outer(n, n) # tangent of slope is constant
4767
for i in range(0, len(v)):
4868
if mu_lambda[i] > 0:
@@ -57,4 +77,24 @@ def hess(v, mu_lambda, hhat, n):
5777
IJV[0].append(i * 2 + r)
5878
IJV[1].append(i * 2 + c)
5979
IJV[2] = np.append(IJV[2], local_hess[r, c])
80+
# self-contact:
81+
for i in range(0, len(mu_lambda_self)):
82+
[xI, eI0, eI1, mu_lam, n, r] = mu_lambda_self[i]
83+
T = np.identity(2) - np.outer(n, n)
84+
rel_v = v[xI] - ((1 - r) * v[eI0] + r * v[eI1])
85+
vbar = np.transpose(T).dot(rel_v)
86+
vbarnorm = np.linalg.norm(vbar)
87+
inner_term = f1_div_vbarnorm(vbarnorm, epsv) * np.identity(2)
88+
if vbarnorm != 0:
89+
inner_term += f_hess_term(vbarnorm, epsv) / vbarnorm * np.outer(vbar, vbar)
90+
hess_rel_v = mu_lam * T.dot(utils.make_PD(inner_term)).dot(np.transpose(T)) / hhat
91+
index = [xI, eI0, eI1]
92+
d_rel_v_dv = [1, -(1 - r), -r]
93+
for nI in range(0, 3):
94+
for nJ in range(0, 3):
95+
for c in range(0, 2):
96+
for r in range(0, 2):
97+
IJV[0].append(index[nI] * 2 + r)
98+
IJV[1].append(index[nJ] * 2 + c)
99+
IJV[2] = np.append(IJV[2], d_rel_v_dv[nI] * d_rel_v_dv[nJ] * hess_rel_v[r, c])
60100
return IJV

self_contact/distance/PointEdgeDistance.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,16 @@ def hess(p, e0, e1):
4545
np.reshape([H_PP[2, 0:2], np.array([0.0, 0.0]), H_PP[2, 2:4]], (1, 6))[0], \
4646
np.reshape([H_PP[3, 0:2], np.array([0.0, 0.0]), H_PP[3, 2:4]], (1, 6))[0]])
4747
else: # point(p)-line(e0e1) expression
48-
return PL.hess(p, e0, e1)
48+
return PL.hess(p, e0, e1)
49+
50+
# compute normal and the parameterization of the closest point on the edge
51+
def tangent(p, e0, e1):
52+
e = e1 - e0
53+
ratio = np.dot(e, p - e0) / np.dot(e, e)
54+
if ratio < 0: # point(p)-point(e0) expression
55+
n = p - e0
56+
elif ratio > 1: # point(p)-point(e1) expression
57+
n = p - e1
58+
else: # point(p)-line(e0e1) expression
59+
n = p - ((1 - ratio) * e0 + ratio * e1)
60+
return [n / np.linalg.norm(n), ratio]

self_contact/time_integrator.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
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)
19-
mu_lambda = BarrierEnergy.compute_mu_lambda(x, n, o, contact_area, mu) # compute mu * lambda for each node using x^n
19+
[mu_lambda, mu_lambda_self] = BarrierEnergy.compute_mu_lambda(x, n, o, bp, be, contact_area, mu) # compute mu * lambda for each node using x^n
2020
DBC_target = [] # target position of each DBC in the current time step
2121
for i in range(0, len(DBC)):
2222
if (DBC_limit[i] - x_n[DBC[i]]).dot(DBC_v[i]) > 0:
@@ -27,52 +27,52 @@ def step_forward(x, e, v, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area,
2727

2828
# Newton loop
2929
iter = 0
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)
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, mu_lambda_self, 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, mu_lambda_self, 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, bp, be, 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, mu_lambda_self, DBC, DBC_target, DBC_stiff, h)
4040

4141
# filter line search
4242
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:
43+
while IP_val(x + alpha * p, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, (x + alpha * p - x_n) / h, mu_lambda, mu_lambda_self, 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, 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)
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, mu_lambda_self, 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, mu_lambda_self, 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, bp, be, 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, mu_lambda_self, 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) +
5959
BarrierEnergy.val(x, n, o, bp, be, contact_area) +
60-
FrictionEnergy.val(v, mu_lambda, h, n)
60+
FrictionEnergy.val(v, mu_lambda, mu_lambda_self, 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, bp, be, 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, mu_lambda_self, 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) +
6767
BarrierEnergy.grad(x, n, o, bp, be, contact_area) +
68-
FrictionEnergy.grad(v, mu_lambda, h, n)
68+
FrictionEnergy.grad(v, mu_lambda, mu_lambda_self, 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, bp, be, 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, mu_lambda_self, 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)
7474
IJV_B = BarrierEnergy.hess(x, n, o, bp, be, contact_area)
75-
IJV_F = FrictionEnergy.hess(v, mu_lambda, h, n)
75+
IJV_F = FrictionEnergy.hess(v, mu_lambda, mu_lambda_self, h, n)
7676
IJV_S = SpringEnergy.hess(x, m, DBC, DBC_target, DBC_stiff)
7777
IJV_MS[2] *= h * h # implicit Euler
7878
IJV_B[2] *= h * h # implicit Euler
@@ -84,9 +84,9 @@ def IP_hess(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area,
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, 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)
87+
def search_dir(x, e, x_tilde, m, vol, IB, mu_lame, lam, n, o, bp, be, contact_area, v, mu_lambda, mu_lambda_self, 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, mu_lambda_self, 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, mu_lambda_self, 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)