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