@@ -16,7 +16,16 @@ def min_max_eigs(A, n=10):
16
16
return lmin , lmax
17
17
18
18
19
- def shape_function_matrix (mesh , Xg ):
19
+ def laplacian_energy (mesh , k = 1 , dims = 1 ):
20
+ L = - pbat .fem .laplacian (mesh , dims = dims )[0 ]
21
+ M = None if k == 1 else pbat .fem .mass_matrix (mesh , dims = 1 , lump = True )
22
+ U = L
23
+ for i in range (k - 1 ):
24
+ U = U @ M @ L
25
+ return U
26
+
27
+
28
+ def shape_function_matrix (mesh , Xg , dims = 1 ):
20
29
nevalpts = Xg .shape [1 ]
21
30
# Unfortunately, we have to perform the copy here...
22
31
# mesh.X and mesh.E are defined via def_property in pybind11, and
@@ -35,11 +44,13 @@ def shape_function_matrix(mesh, Xg):
35
44
cols = mesh .E [:, e ].flatten (order = 'F' )
36
45
nnodes = mesh .X .shape [1 ]
37
46
N = sp .sparse .coo_matrix ((data , (rows , cols )), shape = (nevalpts , nnodes ))
47
+ if dims > 1 :
48
+ N = sp .sparse .kron (N , sp .sparse .eye (dims ))
38
49
return N .asformat ('csc' )
39
50
40
51
41
52
class BaseFemFunctionTransferOperator ():
42
- def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh ):
53
+ def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh , H ):
43
54
"""Operator for transferring FEM discretized functions from a source
44
55
mesh MS to a target mesh MT, given the domain MD.
45
56
@@ -48,36 +59,40 @@ def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh):
48
59
MS (pbat.fem.Mesh): Source mesh
49
60
MT (pbat.fem.Mesh): Target mesh
50
61
"""
51
- nelems = MT .E .shape [1 ]
62
+ nelems = MD .E .shape [1 ]
52
63
quadrature_order = 2 * max (MS .order , MT .order )
53
- Xg = MT .quadrature_points (quadrature_order )
54
- wg = np .tile (MT .quadrature_weights (quadrature_order ), nelems )
64
+ Xg = MD .quadrature_points (quadrature_order )
65
+ wg = np .tile (MD .quadrature_weights (quadrature_order ), nelems )
55
66
Ig = sp .sparse .diags (wg )
56
- NS = shape_function_matrix (MS , Xg )
57
- NT = shape_function_matrix (MT , Xg )
67
+ Ig = sp .sparse .kron (Ig , sp .sparse .eye (MT .dims ))
68
+ NS = shape_function_matrix (MS , Xg , dims = MT .dims )
69
+ NT = shape_function_matrix (MT , Xg , dims = MT .dims )
58
70
A = (NT .T @ Ig @ NT ).asformat ('csc' )
59
71
P = NT .T @ Ig @ NS
60
72
self .Ig = Ig
61
73
self .NS = NS
62
74
self .NT = NT
63
75
self .A = A
64
76
self .P = P
77
+ self .U = laplacian_energy (MT , dims = MT .dims )
78
+ self .H = H
65
79
66
80
def __matmul__ (self , X ):
67
81
pass
68
82
69
83
70
84
class CholFemFunctionTransferOperator (BaseFemFunctionTransferOperator ):
71
- def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh ):
72
- super ().__init__ (MD , MS , MT )
85
+ def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh , H , lreg = 5 , hreg = 1 ):
86
+ super ().__init__ (MD , MS , MT , H )
73
87
n = self .A .shape [0 ]
74
- lmin , lmax = min_max_eigs (self .A , n = 1 )
88
+ A = self .A + lreg * self .U + hreg * self .H
89
+ lmin , lmax = min_max_eigs (A , n = 1 )
75
90
tau = 0.
76
91
if lmin [0 ] <= 0 :
77
92
# Regularize A (due to positive semi-definiteness)
78
93
tau = abs (lmin [0 ]) + 1e-10
79
94
tau = sp .sparse .diags (np .full (n , tau ))
80
- AR = self . A + tau
95
+ AR = A + tau
81
96
solver = pbat .math .linalg .SolverBackend .Eigen
82
97
self .Ainv = pbat .math .linalg .chol (AR , solver = solver )
83
98
self .Ainv .compute (AR )
@@ -89,36 +104,34 @@ def __matmul__(self, B):
89
104
90
105
91
106
class RankKApproximateFemFunctionTransferOperator (BaseFemFunctionTransferOperator ):
92
- def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh , modes = 30 ):
93
- super ().__init__ (MD , MS , MT )
94
- modes = modes / 2
95
- Ulo , sigmalo , VTlo = sp .sparse .linalg .svds (
96
- self .Ig @ self .NT , k = modes , which = 'SM' )
97
- Uhi , sigmahi , VThi = sp .sparse .linalg .svds (
98
- self .Ig @ self .NT , k = modes , which = 'LM' )
99
- self .U , self .sigma , self .VT = np .hstack ((Ulo , Uhi )), np .hstack (
100
- (sigmalo , sigmahi )), np .vstack ((VTlo , VThi ))
101
- keep = np .nonzero (self .sigma > 1e-5 )[0 ]
102
- self .U = self .U [:, keep ]
103
- self .sigma = self .sigma [keep ]
104
- self .VT = self .VT [keep , :]
107
+ def __init__ (self , MD : pbat .fem .Mesh , MS : pbat .fem .Mesh , MT : pbat .fem .Mesh , H , lreg = 5 , hreg = 1 , modes = 30 ):
108
+ super ().__init__ (MD , MS , MT , H )
109
+ A = self .A + lreg * self .U + hreg * self .H
110
+ l , V = sp .sparse .linalg .eigsh (A , k = modes , sigma = 1e-5 , which = 'LM' )
111
+ keep = np .nonzero (l > 1e-5 )[0 ]
112
+ self .l , self .V = l [keep ], V [:, keep ]
105
113
106
114
def __matmul__ (self , B ):
107
- B = self .Ig @ self . NS @ B
108
- B = self .U .T @ B
109
- B = B / self .sigma [:, np . newaxis ]
110
- X = self .VT . T @ B
115
+ B = self .P @ B
116
+ B = self .V .T @ B
117
+ B = B @ sp . sparse . diags ( 1 / self .l )
118
+ X = self .V @ B
111
119
return X
112
120
113
121
114
- def linear_elastic_deformation_modes (mesh , rho , Y , nu , modes = 30 , sigma = - 1e-5 ):
122
+ def rest_pose_hessian (mesh , Y , nu ):
115
123
x = mesh .X .reshape (math .prod (mesh .X .shape ), order = 'f' )
116
- M , detJeM = pbat .fem .mass_matrix (mesh , rho = rho )
117
124
energy = pbat .fem .HyperElasticEnergy .StableNeoHookean
118
125
hep , detJeU , GNeU = pbat .fem .hyper_elastic_potential (
119
126
mesh , Y , nu , energy = energy )
120
127
hep .compute_element_elasticity (x )
121
128
HU = hep .hessian ()
129
+ return HU
130
+
131
+
132
+ def linear_elastic_deformation_modes (mesh , rho , Y , nu , modes = 30 , sigma = - 1e-5 ):
133
+ M , detJeM = pbat .fem .mass_matrix (mesh , rho = rho )
134
+ HU = rest_pose_hessian (mesh , Y , nu )
122
135
leigs , Veigs = sp .sparse .linalg .eigsh (
123
136
HU , k = modes , M = M , sigma = sigma , which = 'LM' )
124
137
Veigs = Veigs / sp .linalg .norm (Veigs , axis = 0 , keepdims = True )
@@ -171,10 +184,13 @@ def signal(w: float, v: np.ndarray, t: float, c: float, k: float):
171
184
# Precompute quantities
172
185
w , L = linear_elastic_deformation_modes (
173
186
mesh , args .rho , args .Y , args .nu , args .modes )
174
- Fldl = CholFemFunctionTransferOperator (mesh , mesh , cmesh )
187
+ HC = rest_pose_hessian (cmesh , args .Y , args .nu )
188
+ lreg , hreg = 0 , 1e-2
189
+ Fldl = CholFemFunctionTransferOperator (
190
+ mesh , mesh , cmesh , HC , lreg = lreg , hreg = hreg )
175
191
Krestrict = 30
176
192
Frank = RankKApproximateFemFunctionTransferOperator (
177
- mesh , mesh , cmesh , modes = Krestrict )
193
+ mesh , mesh , cmesh , HC , lreg = lreg , hreg = hreg , modes = Krestrict )
178
194
179
195
ps .set_up_dir ("z_up" )
180
196
ps .set_front_dir ("neg_y_front" )
@@ -188,21 +204,33 @@ def signal(w: float, v: np.ndarray, t: float, c: float, k: float):
188
204
t = 0
189
205
c = 0.15
190
206
k = 0.05
207
+ theta = 0
208
+ dtheta = np .pi / 120
191
209
192
210
def callback ():
193
- global mode , c , k
211
+ global mode , c , k , theta , dtheta
194
212
changed , mode = imgui .InputInt ("Mode" , mode )
195
213
changed , c = imgui .InputFloat ("Wave amplitude" , c )
196
214
changed , k = imgui .InputFloat ("Wave frequency" , k )
197
215
198
216
t = time .time () - t0
199
- X = V + signal (w [mode ], L [:, mode ],
200
- t , c , k ).reshape (V .shape [0 ], 3 )
201
- XCldl = CV + Fldl @ (X - V )
202
- XCrank = CV + Frank @ (X - V )
203
- vm .update_vertex_positions (X )
217
+
218
+ R = sp .spatial .transform .Rotation .from_quat (
219
+ [0 , np .sin (theta / 2 ), 0 , np .cos (theta / 4 )]).as_matrix ()
220
+ X = (V - V .mean (axis = 0 )) @ R .T + V .mean (axis = 0 )
221
+ uf = signal (w [mode ], L [:, mode ], t , c , k )
222
+ ur = (X - V ).flatten (order = "C" )
223
+ ut = 1
224
+ u = uf + ur + ut
225
+ XCldl = CV + (Fldl @ u ).reshape (CV .shape )
226
+ XCrank = CV + (Frank @ u ).reshape (CV .shape )
227
+ vm .update_vertex_positions (V + (uf + ur ).reshape (V .shape ))
204
228
ldlvm .update_vertex_positions (XCldl )
205
229
rankvm .update_vertex_positions (XCrank )
206
230
231
+ theta += dtheta
232
+ if theta > 2 * np .pi :
233
+ theta = 0
234
+
207
235
ps .set_user_callback (callback )
208
236
ps .show ()
0 commit comments