Skip to content

Commit 760b740

Browse files
committed
self_contact init
1 parent 7eae7cf commit 760b740

11 files changed

Lines changed: 642 additions & 0 deletions

self_contact/BarrierEnergy.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import math
2+
import numpy as np
3+
4+
dhat = 0.01
5+
kappa = 1e5
6+
7+
def val(x, n, o, contact_area):
8+
sum = 0.0
9+
# floor:
10+
for i in range(0, len(x)):
11+
d = n.dot(x[i] - o)
12+
if d < dhat:
13+
s = d / dhat
14+
sum += contact_area[i] * dhat * kappa / 2 * (s - 1) * math.log(s)
15+
# ceil:
16+
n = np.array([0.0, -1.0])
17+
for i in range(0, len(x) - 1):
18+
d = n.dot(x[i] - x[-1])
19+
if d < dhat:
20+
s = d / dhat
21+
sum += contact_area[i] * dhat * kappa / 2 * (s - 1) * math.log(s)
22+
return sum
23+
24+
def grad(x, n, o, contact_area):
25+
g = np.array([[0.0, 0.0]] * len(x))
26+
# floor:
27+
for i in range(0, len(x)):
28+
d = n.dot(x[i] - o)
29+
if d < dhat:
30+
s = d / dhat
31+
g[i] = contact_area[i] * dhat * (kappa / 2 * (math.log(s) / dhat + (s - 1) / d)) * n
32+
# ceil:
33+
n = np.array([0.0, -1.0])
34+
for i in range(0, len(x) - 1):
35+
d = n.dot(x[i] - x[-1])
36+
if d < dhat:
37+
s = d / dhat
38+
local_grad = contact_area[i] * dhat * (kappa / 2 * (math.log(s) / dhat + (s - 1) / d)) * n
39+
g[i] += local_grad
40+
g[-1] -= local_grad
41+
return g
42+
43+
def hess(x, n, o, contact_area):
44+
IJV = [[0] * 0, [0] * 0, np.array([0.0] * 0)]
45+
# floor:
46+
for i in range(0, len(x)):
47+
d = n.dot(x[i] - o)
48+
if d < dhat:
49+
local_hess = contact_area[i] * dhat * kappa / (2 * d * d * dhat) * (d + dhat) * np.outer(n, n)
50+
for c in range(0, 2):
51+
for r in range(0, 2):
52+
IJV[0].append(i * 2 + r)
53+
IJV[1].append(i * 2 + c)
54+
IJV[2] = np.append(IJV[2], local_hess[r, c])
55+
# ceil:
56+
n = np.array([0.0, -1.0])
57+
for i in range(0, len(x) - 1):
58+
d = n.dot(x[i] - x[-1])
59+
if d < dhat:
60+
local_hess = contact_area[i] * dhat * kappa / (2 * d * d * dhat) * (d + dhat) * np.outer(n, n)
61+
index = [i, len(x) - 1]
62+
for nI in range(0, 2):
63+
for nJ in range(0, 2):
64+
for c in range(0, 2):
65+
for r in range(0, 2):
66+
IJV[0].append(index[nI] * 2 + r)
67+
IJV[1].append(index[nJ] * 2 + c)
68+
IJV[2] = np.append(IJV[2], ((-1) ** (nI != nJ)) * local_hess[r, c])
69+
return IJV
70+
71+
def init_step_size(x, n, o, p):
72+
alpha = 1
73+
# floor:
74+
for i in range(0, len(x)):
75+
p_n = p[i].dot(n)
76+
if p_n < 0:
77+
alpha = min(alpha, 0.9 * n.dot(x[i] - o) / -p_n)
78+
# ceil:
79+
n = np.array([0.0, -1.0])
80+
for i in range(0, len(x) - 1):
81+
p_n = (p[i] - p[-1]).dot(n)
82+
if p_n < 0:
83+
alpha = min(alpha, 0.9 * n.dot(x[i] - x[-1]) / -p_n)
84+
return alpha
85+
86+
def compute_mu_lambda(x, n, o, contact_area, mu):
87+
mu_lambda = np.array([0.0] * len(x))
88+
for i in range(0, len(x)):
89+
d = n.dot(x[i] - o)
90+
if d < dhat:
91+
s = d / dhat
92+
mu_lambda[i] = mu * -contact_area[i] * dhat * (kappa / 2 * (math.log(s) / dhat + (s - 1) / d))
93+
return mu_lambda

self_contact/FrictionEnergy.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import numpy as np
2+
import utils
3+
4+
epsv = 1e-3
5+
6+
def f0(vbarnorm, epsv, hhat):
7+
if vbarnorm >= epsv:
8+
return vbarnorm * hhat
9+
else:
10+
vbarnormhhat = vbarnorm * hhat
11+
epsvhhat = epsv * hhat
12+
return vbarnormhhat * vbarnormhhat * (-vbarnormhhat / 3.0 + epsvhhat) / (epsvhhat * epsvhhat) + epsvhhat / 3.0
13+
14+
def f1_div_vbarnorm(vbarnorm, epsv):
15+
if vbarnorm >= epsv:
16+
return 1.0 / vbarnorm
17+
else:
18+
return (-vbarnorm + 2.0 * epsv) / (epsv * epsv)
19+
20+
def f_hess_term(vbarnorm, epsv):
21+
if vbarnorm >= epsv:
22+
return -1.0 / (vbarnorm * vbarnorm)
23+
else:
24+
return -1.0 / (epsv * epsv)
25+
26+
def val(v, mu_lambda, hhat, n):
27+
sum = 0.0
28+
T = np.identity(2) - np.outer(n, n) # tangent of slope is constant
29+
for i in range(0, len(v)):
30+
if mu_lambda[i] > 0:
31+
vbar = np.transpose(T).dot(v[i])
32+
sum += mu_lambda[i] * f0(np.linalg.norm(vbar), epsv, hhat)
33+
return sum
34+
35+
def grad(v, mu_lambda, hhat, n):
36+
g = np.array([[0.0, 0.0]] * len(v))
37+
T = np.identity(2) - np.outer(n, n) # tangent of slope is constant
38+
for i in range(0, len(v)):
39+
if mu_lambda[i] > 0:
40+
vbar = np.transpose(T).dot(v[i])
41+
g[i] = mu_lambda[i] * f1_div_vbarnorm(np.linalg.norm(vbar), epsv) * T.dot(vbar)
42+
return g
43+
44+
def hess(v, mu_lambda, hhat, n):
45+
IJV = [[0] * 0, [0] * 0, np.array([0.0] * 0)]
46+
T = np.identity(2) - np.outer(n, n) # tangent of slope is constant
47+
for i in range(0, len(v)):
48+
if mu_lambda[i] > 0:
49+
vbar = np.transpose(T).dot(v[i])
50+
vbarnorm = np.linalg.norm(vbar)
51+
inner_term = f1_div_vbarnorm(vbarnorm, epsv) * np.identity(2)
52+
if vbarnorm != 0:
53+
inner_term += f_hess_term(vbarnorm, epsv) / vbarnorm * np.outer(vbar, vbar)
54+
local_hess = mu_lambda[i] * T.dot(utils.make_PD(inner_term)).dot(np.transpose(T)) / hhat
55+
for c in range(0, 2):
56+
for r in range(0, 2):
57+
IJV[0].append(i * 2 + r)
58+
IJV[1].append(i * 2 + c)
59+
IJV[2] = np.append(IJV[2], local_hess[r, c])
60+
return IJV

self_contact/GravityEnergy.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import numpy as np
2+
3+
gravity = [0.0, -9.81]
4+
5+
def val(x, m):
6+
sum = 0.0
7+
for i in range(0, len(x)):
8+
sum += -m[i] * x[i].dot(gravity)
9+
return sum
10+
11+
def grad(x, m):
12+
g = np.array([gravity] * len(x))
13+
for i in range(0, len(x)):
14+
g[i] *= -m[i]
15+
return g
16+
17+
# Hessian is 0

self_contact/InertiaEnergy.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import numpy as np
2+
3+
def val(x, x_tilde, m):
4+
sum = 0.0
5+
for i in range(0, len(x)):
6+
diff = x[i] - x_tilde[i]
7+
sum += 0.5 * m[i] * diff.dot(diff)
8+
return sum
9+
10+
def grad(x, x_tilde, m):
11+
g = np.array([[0.0, 0.0]] * len(x))
12+
for i in range(0, len(x)):
13+
g[i] = m[i] * (x[i] - x_tilde[i])
14+
return g
15+
16+
def hess(x, x_tilde, m):
17+
IJV = [[0] * (len(x) * 2), [0] * (len(x) * 2), np.array([0.0] * (len(x) * 2))]
18+
for i in range(0, len(x)):
19+
for d in range(0, 2):
20+
IJV[0][i * 2 + d] = i * 2 + d
21+
IJV[1][i * 2 + d] = i * 2 + d
22+
IJV[2][i * 2 + d] = m[i]
23+
return IJV

self_contact/NeoHookeanEnergy.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import utils
2+
import numpy as np
3+
import math
4+
5+
def polar_svd(F):
6+
[U, s, VT] = np.linalg.svd(F)
7+
if np.linalg.det(U) < 0:
8+
U[:, 1] = -U[:, 1]
9+
s[1] = -s[1]
10+
if np.linalg.det(VT) < 0:
11+
VT[1, :] = -VT[1, :]
12+
s[1] = -s[1]
13+
return [U, s, VT]
14+
15+
def dPsi_div_dsigma(s, mu, lam):
16+
ln_sigma_prod = math.log(s[0] * s[1])
17+
inv0 = 1.0 / s[0]
18+
dPsi_dsigma_0 = mu * (s[0] - inv0) + lam * inv0 * ln_sigma_prod
19+
inv1 = 1.0 / s[1]
20+
dPsi_dsigma_1 = mu * (s[1] - inv1) + lam * inv1 * ln_sigma_prod
21+
return [dPsi_dsigma_0, dPsi_dsigma_1]
22+
23+
def d2Psi_div_dsigma2(s, mu, lam):
24+
ln_sigma_prod = math.log(s[0] * s[1])
25+
inv2_0 = 1 / (s[0] * s[0])
26+
d2Psi_dsigma2_00 = mu * (1 + inv2_0) - lam * inv2_0 * (ln_sigma_prod - 1)
27+
inv2_1 = 1 / (s[1] * s[1])
28+
d2Psi_dsigma2_11 = mu * (1 + inv2_1) - lam * inv2_1 * (ln_sigma_prod - 1)
29+
d2Psi_dsigma2_01 = lam / (s[0] * s[1])
30+
return [[d2Psi_dsigma2_00, d2Psi_dsigma2_01], [d2Psi_dsigma2_01, d2Psi_dsigma2_11]]
31+
32+
def B_left_coef(s, mu, lam):
33+
sigma_prod = s[0] * s[1]
34+
return (mu + (mu - lam * math.log(sigma_prod)) / sigma_prod) / 2
35+
36+
def Psi(F, mu, lam):
37+
J = np.linalg.det(F)
38+
lnJ = math.log(J)
39+
return mu / 2 * (np.trace(np.transpose(F).dot(F)) - 2) - mu * lnJ + lam / 2 * lnJ * lnJ
40+
41+
def dPsi_div_dF(F, mu, lam):
42+
FinvT = np.transpose(np.linalg.inv(F))
43+
return mu * (F - FinvT) + lam * math.log(np.linalg.det(F)) * FinvT
44+
45+
def d2Psi_div_dF2(F, mu, lam):
46+
[U, sigma, VT] = polar_svd(F)
47+
48+
Psi_sigma_sigma = utils.make_PD(d2Psi_div_dsigma2(sigma, mu, lam))
49+
50+
B_left = B_left_coef(sigma, mu, lam)
51+
Psi_sigma = dPsi_div_dsigma(sigma, mu, lam)
52+
B_right = (Psi_sigma[0] + Psi_sigma[1]) / (2 * max(sigma[0] + sigma[1], 1e-6))
53+
B = utils.make_PD([[B_left + B_right, B_left - B_right], [B_left - B_right, B_left + B_right]])
54+
55+
M = np.array([[0, 0, 0, 0]] * 4)
56+
M[0, 0] = Psi_sigma_sigma[0, 0]
57+
M[0, 3] = Psi_sigma_sigma[0, 1]
58+
M[1, 1] = B[0, 0]
59+
M[1, 2] = B[0, 1]
60+
M[2, 1] = B[1, 0]
61+
M[2, 2] = B[1, 1]
62+
M[3, 0] = Psi_sigma_sigma[1, 0]
63+
M[3, 3] = Psi_sigma_sigma[1, 1]
64+
65+
dP_div_dF = np.array([[0, 0, 0, 0]] * 4)
66+
for j in range(0, 2):
67+
for i in range(0, 2):
68+
ij = j * 2 + i
69+
for s in range(0, 2):
70+
for r in range(0, 2):
71+
rs = s * 2 + r
72+
dP_div_dF[ij, rs] = M[0, 0] * U[i, 0] * VT[0, j] * U[r, 0] * VT[0, s] \
73+
+ M[0, 3] * U[i, 0] * VT[0, j] * U[r, 1] * VT[1, s] \
74+
+ M[1, 1] * U[i, 0] * VT[1, j] * U[r, 0] * VT[1, s] \
75+
+ M[1, 2] * U[i, 0] * VT[1, j] * U[r, 1] * VT[0, s] \
76+
+ M[2, 1] * U[i, 1] * VT[0, j] * U[r, 0] * VT[1, s] \
77+
+ M[2, 2] * U[i, 1] * VT[0, j] * U[r, 1] * VT[0, s] \
78+
+ M[3, 0] * U[i, 1] * VT[1, j] * U[r, 0] * VT[0, s] \
79+
+ M[3, 3] * U[i, 1] * VT[1, j] * U[r, 1] * VT[1, s]
80+
return dP_div_dF
81+
82+
def deformation_grad(x, elemVInd, IB):
83+
F = [x[elemVInd[1]] - x[elemVInd[0]], x[elemVInd[2]] - x[elemVInd[0]]]
84+
return np.transpose(F).dot(IB)
85+
86+
def dPsi_div_dx(P, IB): # applying chain-rule, dPsi_div_dx = dPsi_div_dF * dF_div_dx
87+
dPsi_dx_2 = P[0, 0] * IB[0, 0] + P[0, 1] * IB[0, 1]
88+
dPsi_dx_3 = P[1, 0] * IB[0, 0] + P[1, 1] * IB[0, 1]
89+
dPsi_dx_4 = P[0, 0] * IB[1, 0] + P[0, 1] * IB[1, 1]
90+
dPsi_dx_5 = P[1, 0] * IB[1, 0] + P[1, 1] * IB[1, 1]
91+
return [np.array([-dPsi_dx_2 - dPsi_dx_4, -dPsi_dx_3 - dPsi_dx_5]), np.array([dPsi_dx_2, dPsi_dx_3]), np.array([dPsi_dx_4, dPsi_dx_5])]
92+
93+
def d2Psi_div_dx2(dP_div_dF, IB): # applying chain-rule, d2Psi_div_dx2 = dF_div_dx^T * d2Psi_div_dF2 * dF_div_dx (note that d2F_div_dx2 = 0)
94+
intermediate = np.array([[0.0, 0.0, 0.0, 0.0]] * 6)
95+
for colI in range(0, 4):
96+
_000 = dP_div_dF[0, colI] * IB[0, 0]
97+
_010 = dP_div_dF[0, colI] * IB[1, 0]
98+
_101 = dP_div_dF[2, colI] * IB[0, 1]
99+
_111 = dP_div_dF[2, colI] * IB[1, 1]
100+
_200 = dP_div_dF[1, colI] * IB[0, 0]
101+
_210 = dP_div_dF[1, colI] * IB[1, 0]
102+
_301 = dP_div_dF[3, colI] * IB[0, 1]
103+
_311 = dP_div_dF[3, colI] * IB[1, 1]
104+
intermediate[2, colI] = _000 + _101
105+
intermediate[3, colI] = _200 + _301
106+
intermediate[4, colI] = _010 + _111
107+
intermediate[5, colI] = _210 + _311
108+
intermediate[0, colI] = -intermediate[2, colI] - intermediate[4, colI]
109+
intermediate[1, colI] = -intermediate[3, colI] - intermediate[5, colI]
110+
result = np.array([[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] * 6)
111+
for colI in range(0, 6):
112+
_000 = intermediate[colI, 0] * IB[0, 0]
113+
_010 = intermediate[colI, 0] * IB[1, 0]
114+
_101 = intermediate[colI, 2] * IB[0, 1]
115+
_111 = intermediate[colI, 2] * IB[1, 1]
116+
_200 = intermediate[colI, 1] * IB[0, 0]
117+
_210 = intermediate[colI, 1] * IB[1, 0]
118+
_301 = intermediate[colI, 3] * IB[0, 1]
119+
_311 = intermediate[colI, 3] * IB[1, 1]
120+
result[2, colI] = _000 + _101
121+
result[3, colI] = _200 + _301
122+
result[4, colI] = _010 + _111
123+
result[5, colI] = _210 + _311
124+
result[0, colI] = -_000 - _101 - _010 - _111
125+
result[1, colI] = -_200 - _301 - _210 - _311
126+
return result
127+
128+
def val(x, e, vol, IB, mu, lam):
129+
sum = 0.0
130+
for i in range(0, len(e)):
131+
F = deformation_grad(x, e[i], IB[i])
132+
sum += vol[i] * Psi(F, mu[i], lam[i])
133+
return sum
134+
135+
def grad(x, e, vol, IB, mu, lam):
136+
g = np.array([[0.0, 0.0]] * len(x))
137+
for i in range(0, len(e)):
138+
F = deformation_grad(x, e[i], IB[i])
139+
P = vol[i] * dPsi_div_dF(F, mu[i], lam[i])
140+
g_local = dPsi_div_dx(P, IB[i])
141+
for j in range(0, 3):
142+
g[e[i][j]] += g_local[j]
143+
return g
144+
145+
def hess(x, e, vol, IB, mu, lam):
146+
IJV = [[0] * (len(e) * 36), [0] * (len(e) * 36), np.array([0.0] * (len(e) * 36))]
147+
for i in range(0, len(e)):
148+
F = deformation_grad(x, e[i], IB[i])
149+
dP_div_dF = vol[i] * d2Psi_div_dF2(F, mu[i], lam[i])
150+
local_hess = d2Psi_div_dx2(dP_div_dF, IB[i])
151+
for xI in range(0, 3):
152+
for xJ in range(0, 3):
153+
for dI in range(0, 2):
154+
for dJ in range(0, 2):
155+
ind = i * 36 + (xI * 3 + xJ) * 4 + dI * 2 + dJ
156+
IJV[0][ind] = e[i][xI] * 2 + dI
157+
IJV[1][ind] = e[i][xJ] * 2 + dJ
158+
IJV[2][ind] = local_hess[xI * 2 + dI, xJ * 2 + dJ]
159+
return IJV
160+
161+
def init_step_size(x, e, p):
162+
alpha = 1
163+
for i in range(0, len(e)):
164+
x21 = x[e[i][1]] - x[e[i][0]]
165+
x31 = x[e[i][2]] - x[e[i][0]]
166+
p21 = p[e[i][1]] - p[e[i][0]]
167+
p31 = p[e[i][2]] - p[e[i][0]]
168+
detT = np.linalg.det(np.transpose([x21, x31]))
169+
a = np.linalg.det(np.transpose([p21, p31])) / detT
170+
b = (np.linalg.det(np.transpose([x21, p31])) + np.linalg.det(np.transpose([p21, x31]))) / detT
171+
c = 0.9 # solve for alpha that first brings the new volume to 0.1x the old volume for slackness
172+
critical_alpha = utils.smallest_positive_real_root_quad(a, b, c)
173+
if critical_alpha > 0:
174+
alpha = min(alpha, critical_alpha)
175+
return alpha

0 commit comments

Comments
 (0)