Skip to content

Commit 16fa19b

Browse files
committed
Fix nested double quote issue
1 parent 8499723 commit 16fa19b

File tree

2 files changed

+115
-47
lines changed

2 files changed

+115
-47
lines changed

python/examples/multigrid/linear_restriction_prolongation.py

Lines changed: 114 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import meshio
99
import argparse
1010
import math
11+
import qpsolvers
1112

1213

1314
def min_max_eigs(A, n=10):
@@ -25,17 +26,43 @@ def laplacian_energy(mesh, k=1, dims=1):
2526
return U
2627

2728

28-
def display_quadrature(mesh, Xg):
29-
V = mesh.X
30-
C = mesh.E
31-
bvh = pbat.geometry.bvh(V, C, cell=pbat.geometry.Cell.Tetrahedron)
32-
e, d = bvh.nearest_primitives_to_points(Xg, parallelize=True)
33-
nelems = mesh.E.shape[1]
34-
efree = np.setdiff1d(list(range(nelems)), np.unique(e))
35-
if len(efree) > 0:
36-
ps.register_volume_mesh(
37-
f"Invalid mesh nelems={nelems}", V.T, C[:, efree].T
38-
)
29+
def transfer_quadrature(cmesh, wg2, Xg2, order=1):
30+
CV, CC = cmesh.X, cmesh.E
31+
Xg1 = cmesh.quadrature_points(order)
32+
e1 = np.linspace(0, cmesh.E.shape[1]-1,
33+
num=cmesh.E.shape[1], dtype=np.int64)
34+
e1 = np.repeat(e1, int(Xg1.shape[1] / cmesh.E.shape[1]))
35+
Xi1 = pbat.fem.reference_positions(cmesh, e1, Xg1)
36+
bvh1 = pbat.geometry.bvh(CV, CC, cell=pbat.geometry.Cell.Tetrahedron)
37+
e2 = np.array(bvh1.nearest_primitives_to_points(
38+
Xg2, parallelize=True)[0])
39+
Xi2 = pbat.fem.reference_positions(cmesh, e2, Xg2)
40+
wg1, err = pbat.math.transfer_quadrature(
41+
e1, Xi1, e2, Xi2, wg2, order=order, with_error=True, max_iters=50, precision=1e-10)
42+
return wg1, Xg1, e1, err
43+
44+
45+
def patch_quadrature(cmesh, wg1, Xg1, e1, err=1e-4, numerical_zero=1e-10):
46+
wtotal = np.zeros(cmesh.E.shape[1])
47+
np.add.at(wtotal, e1, wg1)
48+
ezero = np.argwhere(wtotal < numerical_zero).squeeze()
49+
ezeroinds = np.in1d(e1, ezero)
50+
e1zero = np.copy(e1[ezeroinds])
51+
Xg1zero = np.copy(Xg1[:, ezeroinds])
52+
MM, BM, PM = pbat.math.reference_moment_fitting_systems(
53+
e1zero, Xg1zero, e1zero, Xg1zero, np.zeros(Xg1zero.shape[1]))
54+
MM = pbat.math.block_diagonalize_moment_fitting(MM, PM)
55+
n = np.count_nonzero(ezeroinds)
56+
P = -sp.sparse.eye(n).asformat('csc')
57+
G = MM.asformat('csc')
58+
h = np.full(G.shape[0], err)
59+
lb = np.full(n, 0.)
60+
q = np.full(n, 0.)
61+
wzero = qpsolvers.solve_qp(P, q, G=G, h=h, lb=lb, initvals=np.zeros(
62+
n), solver="cvxopt")
63+
wg1p = np.copy(wg1)
64+
wg1p[ezeroinds] = wzero
65+
return wg1p, ezeroinds
3966

4067

4168
def gradient_operator(mesh, Xg, dims=1):
@@ -85,7 +112,7 @@ def shape_function_matrix(mesh, Xg, dims=1):
85112

86113

87114
class BaseFemFunctionTransferOperator():
88-
def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, H):
115+
def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, H=None):
89116
"""Operator for transferring FEM discretized functions from a source
90117
mesh MS to a target mesh MT, given the domain MD.
91118
@@ -141,9 +168,48 @@ def __call__(self, u):
141168

142169

143170
class CholFemFunctionTransferOperator(BaseFemFunctionTransferOperator):
144-
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):
171+
def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, H, lreg=5, hreg=1, greg=1):
172+
super().__init__(MD, MS, MT, H)
173+
n = self.A.shape[0]
174+
self.greg = greg
175+
A = self.A + lreg*self.U + hreg*self.H + greg * self.GT.T @ self.IG @ self.GT
176+
lmin, lmax = min_max_eigs(A, n=1)
177+
tau = 0.
178+
if lmin[0] <= 0:
179+
# Regularize A (due to positive semi-definiteness)
180+
tau = abs(lmin[0]) + 1e-10
181+
tau = sp.sparse.diags(np.full(n, tau))
182+
AR = A + tau
183+
solver = pbat.math.linalg.SolverBackend.Eigen
184+
self.Ainv = pbat.math.linalg.chol(AR, solver=solver)
185+
self.Ainv.compute(AR)
186+
187+
def __matmul__(self, u):
188+
b = self.P @ u + self.greg * self.GT.T @ self.IG @ self.GS @ u
189+
du = self.Ainv.solve(b).squeeze()
190+
return du
191+
192+
193+
class RankKApproximateFemFunctionTransferOperator(BaseFemFunctionTransferOperator):
194+
def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, H, lreg=5, hreg=1, greg=1, modes=30):
145195
super().__init__(MD, MS, MT, H)
146196
self.greg = greg
197+
A = self.A + lreg*self.U + hreg*self.H + greg * self.GT.T @ self.IG @ self.GT
198+
l, V = sp.sparse.linalg.eigsh(A, k=modes, sigma=1e-5, which='LM')
199+
keep = np.nonzero(l > 1e-5)[0]
200+
self.l, self.V = l[keep], V[:, keep]
201+
202+
def __matmul__(self, B):
203+
B = self.P @ B + self.greg * self.GT.T @ self.IG @ self.GS @ B
204+
B = self.V.T @ B
205+
B = B @ sp.sparse.diags(1 / self.l)
206+
X = self.V @ B
207+
return X
208+
209+
210+
class NewtonFunctionTransferOperator(BaseFemFunctionTransferOperator):
211+
def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, hxreg=1e-4, Y=1e6, nu=0.45, rho=1e3):
212+
super().__init__(MD, MS, MT)
147213
self.M = (self.NT.T @ (rho * self.Ig) @ self.NT).asformat('csc')
148214
self.P = (self.NT.T @ (rho * self.Ig) @ self.NS).asformat('csc')
149215
self.rho = rho
@@ -153,18 +219,6 @@ def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, H, l
153219
self.hxreg = hxreg
154220
self.X = MT.X
155221
self.u = np.zeros(math.prod(self.X.shape), order="f")
156-
# n = self.A.shape[0]
157-
# A = self.M + lreg*self.U + hreg*self.H
158-
# lmin, lmax = min_max_eigs(A, n=1)
159-
# tau = 0.
160-
# if lmin[0] <= 0:
161-
# # Regularize A (due to positive semi-definiteness)
162-
# tau = abs(lmin[0]) + 1e-10
163-
# tau = sp.sparse.diags(np.full(n, tau))
164-
# AR = A + tau
165-
# solver = pbat.math.linalg.SolverBackend.Eigen
166-
# self.Ainv = pbat.math.linalg.chol(AR, solver=solver)
167-
# self.Ainv.compute(AR)
168222

169223
def __matmul__(self, u):
170224
xr = self.X.reshape(math.prod(self.X.shape), order='f')
@@ -199,23 +253,6 @@ def __matmul__(self, u):
199253
return uk
200254

201255

202-
class RankKApproximateFemFunctionTransferOperator(BaseFemFunctionTransferOperator):
203-
def __init__(self, MD: pbat.fem.Mesh, MS: pbat.fem.Mesh, MT: pbat.fem.Mesh, H, lreg=5, hreg=1, greg=1, modes=30):
204-
super().__init__(MD, MS, MT, H)
205-
self.greg = greg
206-
A = self.A + lreg*self.U + hreg*self.H + greg * self.GT.T @ self.IG @ self.GT
207-
l, V = sp.sparse.linalg.eigsh(A, k=modes, sigma=1e-5, which='LM')
208-
keep = np.nonzero(l > 1e-5)[0]
209-
self.l, self.V = l[keep], V[:, keep]
210-
211-
def __matmul__(self, B):
212-
B = self.P @ B + self.greg * self.GT.T @ self.IG @ self.GS @ B
213-
B = self.V.T @ B
214-
B = B @ sp.sparse.diags(1 / self.l)
215-
X = self.V @ B
216-
return X
217-
218-
219256
def rest_pose_hessian(mesh, Y, nu):
220257
x = mesh.X.reshape(math.prod(mesh.X.shape), order='f')
221258
energy = pbat.fem.HyperElasticEnergy.StableNeoHookean
@@ -282,9 +319,11 @@ def signal(w: float, v: np.ndarray, t: float, c: float, k: float):
282319
w, L = linear_elastic_deformation_modes(
283320
mesh, args.rho, args.Y, args.nu, args.modes)
284321
HC = rest_pose_hessian(cmesh, args.Y, args.nu)
285-
lreg, hreg, greg, hxreg = 0, 1e-2, 1e-2, 1e-4
286-
Fldl = CholFemFunctionTransferOperator(
287-
mesh, mesh, cmesh, HC, lreg=lreg, hreg=hreg, greg=greg, hxreg=hxreg)
322+
lreg, hreg, greg, hxreg = 1e-2, 0, 1, 1e-4
323+
# Fldl = CholFemFunctionTransferOperator(
324+
# mesh, mesh, cmesh, HC, lreg=lreg, hreg=hreg, greg=greg)
325+
Fldl = NewtonFunctionTransferOperator(
326+
mesh, mesh, cmesh, hxreg=hxreg)
288327
Krestrict = 30
289328
Frank = RankKApproximateFemFunctionTransferOperator(
290329
mesh, mesh, cmesh, HC, lreg=lreg, hreg=hreg, greg=greg, modes=Krestrict)
@@ -309,14 +348,40 @@ def signal(w: float, v: np.ndarray, t: float, c: float, k: float):
309348
dtheta = np.pi/120
310349
animate = False
311350
step = False
351+
screenshot = False
352+
353+
wg2 = pbat.fem.inner_product_weights(mesh, 1).flatten(order="F")
354+
Xg2 = mesh.quadrature_points(1)
355+
356+
pg2 = ps.register_point_cloud("Fine quad", Xg2.T)
357+
pg2.add_scalar_quantity("weights", wg2, enabled=True)
358+
pg2.set_point_radius_quantity("weights")
359+
360+
wg1, Xg1, e1, err = transfer_quadrature(cmesh, wg2, Xg2, order=1)
361+
pg1 = ps.register_point_cloud("Cage quad", Xg1.T)
362+
pg1.add_scalar_quantity("weights", wg1, enabled=True)
363+
pg1.set_point_radius_quantity("weights")
364+
365+
wg1p, ezeroinds = patch_quadrature(cmesh, wg1, Xg1, e1, err=1e-4)
366+
ezero = np.unique(e1[ezeroinds])
367+
wgpatched = wg1p[ezeroinds]
368+
Xgpatched = Xg1[:, ezeroinds]
369+
pp = ps.register_point_cloud("Patched", Xgpatched.T)
370+
pp.add_scalar_quantity("weights", wgpatched, enabled=True)
371+
pp.set_point_radius_quantity("weights")
372+
ps.register_volume_mesh("Bad coarse", CV, CC[ezero, :])
373+
pg1p = ps.register_point_cloud("Cage quad patched", Xg1.T)
374+
pg1p.add_scalar_quantity("weights", wg1p, enabled=True)
375+
pg1p.set_point_radius_quantity("weights")
312376

313377
def callback():
314378
global mode, c, k, theta, dtheta
315-
global animate, step
379+
global animate, step, screenshot
316380
changed, mode = imgui.InputInt("Mode", mode)
317381
changed, c = imgui.InputFloat("Wave amplitude", c)
318382
changed, k = imgui.InputFloat("Wave frequency", k)
319383
changed, animate = imgui.Checkbox("animate", animate)
384+
changed, screenshot = imgui.Checkbox("screenshot", screenshot)
320385
step = imgui.Button("step")
321386

322387
if animate or step:
@@ -339,5 +404,8 @@ def callback():
339404
if theta > 2*np.pi:
340405
theta = 0
341406

407+
if screenshot:
408+
ps.screenshot()
409+
342410
ps.set_user_callback(callback)
343411
ps.show()

python/pbatoolkit/fem.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
setattr(
2020
__module,
2121
"__doc__",
22-
f"{getattr(__module, "__doc__")}\n\n{_strio.read()}"
22+
f"{getattr(__module, '__doc__')}\n\n{_strio.read()}"
2323
)
2424

2525

0 commit comments

Comments
 (0)