@@ -16,16 +16,6 @@ def min_max_eigs(A, n=10):
16
16
return lmin , lmax
17
17
18
18
19
- def rest_pose_hessian (mesh , Y , nu ):
20
- x = mesh .X .reshape (math .prod (mesh .X .shape ), order = 'f' )
21
- energy = pbat .fem .HyperElasticEnergy .StableNeoHookean
22
- hep , detJeU , GNeU = pbat .fem .hyper_elastic_potential (
23
- mesh , Y , nu , energy = energy )
24
- hep .compute_element_elasticity (x )
25
- HU = hep .hessian ()
26
- return HU
27
-
28
-
29
19
def laplacian_energy (mesh , k = 1 , dims = 1 ):
30
20
L = - pbat .fem .laplacian (mesh , dims = dims )[0 ]
31
21
M = None if k == 1 else pbat .fem .mass_matrix (mesh , dims = 1 , lump = True )
@@ -43,8 +33,9 @@ def display_quadrature(mesh, Xg):
43
33
nelems = mesh .E .shape [1 ]
44
34
efree = np .setdiff1d (list (range (nelems )), np .unique (e ))
45
35
if len (efree ) > 0 :
46
- ps .register_volume_mesh (f"Invalid mesh nelems={
47
- nelems } " , V .T , C [:, efree ].T )
36
+ ps .register_volume_mesh (
37
+ f"Invalid mesh nelems={ nelems } " , V .T , C [:, efree ].T
38
+ )
48
39
49
40
50
41
def gradient_operator (mesh , Xg , dims = 1 ):
@@ -103,9 +94,10 @@ def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, H):
103
94
MS (pbat.fem.Mesh): Source mesh
104
95
MT (pbat.fem.Mesh): Target mesh
105
96
"""
97
+ nelems = MD .E .shape [1 ]
106
98
quadrature_order = 2 * max (MS .order , MT .order )
107
99
Xg = MD .quadrature_points (quadrature_order )
108
- wg = np .tile (MD .quadrature_weights (quadrature_order ), MD . E . shape [ 1 ] )
100
+ wg = np .tile (MD .quadrature_weights (quadrature_order ), nelems )
109
101
from scipy .sparse import kron , eye , diags
110
102
Ig = diags (wg )
111
103
Ig = kron (Ig , eye (MT .dims ))
@@ -128,70 +120,111 @@ def __matmul__(self, X):
128
120
pass
129
121
130
122
123
+ class NonLinearElasticRestrictionEnergy :
124
+ def __init__ (self , hep , rho , Ig , NT , y , xr , hxreg ):
125
+ self .hep = hep
126
+ self .rho = rho
127
+ self .Ig = Ig
128
+ self .NT = NT
129
+ self .y = y
130
+ self .xr = xr
131
+ self .hxreg = hxreg
132
+
133
+ def __call__ (self , u ):
134
+ x = self .xr + u
135
+ self .hep .compute_element_elasticity (x , grad = False , hessian = False )
136
+ duku = self .NT @ u - self .y
137
+ E = 0.5 * duku .T @ (self .rho * self .Ig ) @ duku + \
138
+ self .hxreg * self .hep .eval ()
139
+ return E
140
+
141
+
131
142
class CholFemFunctionTransferOperator (BaseFemFunctionTransferOperator ):
132
- def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh , H , lreg = 5 , hreg = 1 , greg = 1 , Y = 1e6 , nu = 0.45 , hxreg = 1e-4 , rho = 1e3 ):
143
+ def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh , H , lreg = 5 , hreg = 1 , greg = 1 , hxreg = 1e-4 , Y = 1e6 , nu = 0.45 , rho = 1e3 ):
133
144
super ().__init__ (MD , MS , MT , H )
134
- n = self .A .shape [0 ]
145
+ self .greg = greg
146
+ self .M = (self .NT .T @ (rho * self .Ig ) @ self .NT ).asformat ('csc' )
147
+ self .P = (self .NT .T @ (rho * self .Ig ) @ self .NS ).asformat ('csc' )
148
+ self .rho = rho
135
149
energy = pbat .fem .HyperElasticEnergy .StableNeoHookean
136
150
self .hep , self .detJeU , self .GNeU = pbat .fem .hyper_elastic_potential (
137
151
MT , Y , nu , energy = energy )
138
- self .MT = MT
139
- self .rho = rho
140
- self .M = self .NT .T @ (rho * self .Ig ) @ self .NT # + lreg*self.U + hreg*self.H + greg * self.GT.T @ self.IG @ self.GT
141
- self .P = self .NT .T @ (rho * self .Ig ) @ self .NS
142
152
self .hxreg = hxreg
153
+ self .X = MT .X
154
+ self .u = np .zeros (math .prod (self .X .shape ), order = "f" )
155
+ # n = self.A.shape[0]
156
+ # A = self.M + lreg*self.U + hreg*self.H
143
157
# lmin, lmax = min_max_eigs(A, n=1)
144
158
# tau = 0.
145
159
# if lmin[0] <= 0:
146
160
# # Regularize A (due to positive semi-definiteness)
147
- # print("WARNING: A is semi-definite")
148
161
# tau = abs(lmin[0]) + 1e-10
149
162
# tau = sp.sparse.diags(np.full(n, tau))
150
163
# AR = A + tau
151
164
# solver = pbat.math.linalg.SolverBackend.Eigen
152
165
# self.Ainv = pbat.math.linalg.chol(AR, solver=solver)
153
166
# self.Ainv.compute(AR)
154
- self .uk = np .zeros (math .prod (self .MT .X .shape ))
155
167
156
168
def __matmul__ (self , u ):
157
- self .uk = np .zeros (math .prod (self .MT .X .shape ))
158
- xr = self .MT .X .reshape (math .prod (self .MT .X .shape ), order = 'f' )
159
- for k in range (1 ):
160
- x = xr + self .uk
169
+ xr = self .X .reshape (math .prod (self .X .shape ), order = 'f' )
170
+ uk = self .u # np.zeros_like(xr)
171
+ y = self .P @ u
172
+ f = NonLinearElasticRestrictionEnergy (
173
+ self .hep , self .rho , self .Ig , self .NT , self .NS @ u , xr , self .hxreg )
174
+ for k in range (5 ):
175
+ x = xr + uk
161
176
self .hep .compute_element_elasticity (x )
162
- duku = self .NT @ self .uk - self .NS @ u
163
- E = 0.5 * duku .T @ (self .rho * self .Ig ) @ duku + self .hxreg * self .hep .eval ()
164
- H = self .M + self .hxreg * self .hep .hessian ()
177
+ K = self .hep .hessian ()
178
+ H = self .M + self .hxreg * K
165
179
solver = pbat .math .linalg .SolverBackend .Eigen
166
180
Hinv = pbat .math .linalg .ldlt (H , solver = solver )
167
181
Hinv .compute (H )
168
- # B = self.P @ u + self.GT.T @ self.IG @ self.GS @ u
169
- ng = self .P @ u - self .hxreg * self .hep .gradient ()
170
- r = ng .T @ ng
171
- if r < 1e-3 :
172
- break
173
- # X = self.Ainv.solve(B)
174
- du = Hinv .solve (ng ).squeeze ()
175
- self .uk += du
176
- return self .uk
182
+ gk = (self .M @ uk - y ) + self .hxreg * self .hep .gradient ()
183
+ du = Hinv .solve (- gk ).squeeze ()
184
+ # Line search
185
+ alpha = 1.
186
+ Dfk = gk .dot (du )
187
+ fk = f (uk )
188
+ maxiters = 20
189
+ tau = 0.5
190
+ for j in range (maxiters ):
191
+ fx = f (uk + alpha * du )
192
+ flinear = fk + alpha * c * Dfk
193
+ if fx <= flinear :
194
+ break
195
+ alpha = tau * alpha
196
+ uk += alpha * du
197
+ self .u = uk
198
+ return uk
177
199
178
200
179
201
class RankKApproximateFemFunctionTransferOperator (BaseFemFunctionTransferOperator ):
180
202
def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh , H , lreg = 5 , hreg = 1 , greg = 1 , modes = 30 ):
181
203
super ().__init__ (MD , MS , MT , H )
204
+ self .greg = greg
182
205
A = self .A + lreg * self .U + hreg * self .H + greg * self .GT .T @ self .IG @ self .GT
183
206
l , V = sp .sparse .linalg .eigsh (A , k = modes , sigma = 1e-5 , which = 'LM' )
184
207
keep = np .nonzero (l > 1e-5 )[0 ]
185
208
self .l , self .V = l [keep ], V [:, keep ]
186
209
187
210
def __matmul__ (self , B ):
188
- B = self .P @ B + self .GT .T @ self .IG @ self .GS @ B
211
+ B = self .P @ B + self .greg * self . GT .T @ self .IG @ self .GS @ B
189
212
B = self .V .T @ B
190
213
B = B @ sp .sparse .diags (1 / self .l )
191
214
X = self .V @ B
192
215
return X
193
216
194
217
218
+ def rest_pose_hessian (mesh , Y , nu ):
219
+ x = mesh .X .reshape (math .prod (mesh .X .shape ), order = 'f' )
220
+ energy = pbat .fem .HyperElasticEnergy .StableNeoHookean
221
+ hep , detJeU , GNeU = pbat .fem .hyper_elastic_potential (
222
+ mesh , Y , nu , energy = energy )
223
+ hep .compute_element_elasticity (x )
224
+ HU = hep .hessian ()
225
+ return HU
226
+
227
+
195
228
def linear_elastic_deformation_modes (mesh , rho , Y , nu , modes = 30 , sigma = - 1e-5 ):
196
229
M , detJeM = pbat .fem .mass_matrix (mesh , rho = rho )
197
230
HU = rest_pose_hessian (mesh , Y , nu )
@@ -248,20 +281,24 @@ def signal(w: float, v: np.ndarray, t: float, c: float, k: float):
248
281
w , L = linear_elastic_deformation_modes (
249
282
mesh , args .rho , args .Y , args .nu , args .modes )
250
283
HC = rest_pose_hessian (cmesh , args .Y , args .nu )
251
- lreg , hreg , greg , hxreg = 0 , 0 , 0 , 1e-4
284
+ lreg , hreg , greg , hxreg = 0 , 1e-2 , 0 , 1e-4
252
285
Fldl = CholFemFunctionTransferOperator (
253
- mesh , mesh , cmesh , HC , lreg = lreg , hreg = hreg , greg = greg , hxreg = hxreg , rho = args . rho )
254
- # Krestrict = 30
255
- # Frank = RankKApproximateFemFunctionTransferOperator(
256
- # mesh, mesh, cmesh, HC, lreg=lreg, hreg=hreg, greg=greg, modes=Krestrict)
286
+ mesh , mesh , cmesh , HC , lreg = lreg , hreg = hreg , greg = greg , hxreg = hxreg )
287
+ Krestrict = 30
288
+ Frank = RankKApproximateFemFunctionTransferOperator (
289
+ mesh , mesh , cmesh , HC , lreg = lreg , hreg = hreg , greg = greg , modes = Krestrict )
257
290
258
291
ps .set_up_dir ("z_up" )
259
292
ps .set_front_dir ("neg_y_front" )
260
293
ps .set_ground_plane_mode ("shadow_only" )
261
294
ps .init ()
262
295
vm = ps .register_surface_mesh ("model" , V , F )
263
296
ldlvm = ps .register_surface_mesh ("LDL cage" , CV , CF )
264
- # rankvm = ps.register_surface_mesh("Rank K cage", CV, CF)
297
+ ldlvm .set_transparency (0.5 )
298
+ ldlvm .set_edge_width (1 )
299
+ rankvm = ps .register_surface_mesh ("Rank K cage" , CV , CF )
300
+ rankvm .set_transparency (0.5 )
301
+ rankvm .set_edge_width (1 )
265
302
mode = 6
266
303
t0 = time .time ()
267
304
t = 0
@@ -275,7 +312,6 @@ def signal(w: float, v: np.ndarray, t: float, c: float, k: float):
275
312
def callback ():
276
313
global mode , c , k , theta , dtheta
277
314
global animate , step
278
-
279
315
changed , mode = imgui .InputInt ("Mode" , mode )
280
316
changed , c = imgui .InputFloat ("Wave amplitude" , c )
281
317
changed , k = imgui .InputFloat ("Wave frequency" , k )
@@ -293,10 +329,10 @@ def callback():
293
329
ut = 1
294
330
u = uf + ur + ut
295
331
XCldl = CV + (Fldl @ u ).reshape (CV .shape )
296
- # XCrank = CV + (Frank @ u).reshape(CV.shape)
332
+ XCrank = CV + (Frank @ u ).reshape (CV .shape )
297
333
vm .update_vertex_positions (V + (uf + ur ).reshape (V .shape ))
298
334
ldlvm .update_vertex_positions (XCldl )
299
- # rankvm.update_vertex_positions(XCrank)
335
+ rankvm .update_vertex_positions (XCrank )
300
336
301
337
theta += dtheta
302
338
if theta > 2 * np .pi :
0 commit comments