def save_nullspaces(): # points, cells = meshzoo.rectangle_tri((0.0, 0.0), (1.0, 1.0), 20) points, cells = meshzoo.disk(6, 20) @fem.BilinearForm def flux(u, v, w): return dot(w.n, u.grad) * v mesh = fem.MeshTri(points.T, cells.T) basis = fem.InteriorBasis(mesh, fem.ElementTriP1()) facet_basis = fem.FacetBasis(basis.mesh, basis.elem) lap = fem.asm(laplace, basis) boundary_terms = fem.asm(flux, facet_basis) A_dense = (lap - boundary_terms).toarray() # the right null space are the affine-linear functions rns = scipy.linalg.null_space(A_dense).T mesh.save("nullspace-right.vtk", point_data={ "k0": rns[0], "k1": rns[1], "k2": rns[2] }) # the left null space is a bit weird; something around the boundaries lns = scipy.linalg.null_space(A_dense.T).T mesh.save("nullspace-left.vtk", point_data={ "k0": lns[0], "k1": lns[1], "k2": lns[2] })
def _fit_skfem(x0, y0, points, cells, lmbda: float, degree: int = 1, solver: str = "lsqr"): import skfem from skfem.helpers import dot from skfem.models.poisson import laplace assert degree == 1 if cells.shape[1] == 2: mesh = skfem.MeshLine(np.ascontiguousarray(points.T), np.ascontiguousarray(cells.T)) element = skfem.ElementLineP1() elif cells.shape[1] == 3: mesh = skfem.MeshTri(np.ascontiguousarray(points.T), np.ascontiguousarray(cells.T)) element = skfem.ElementTriP1() else: assert cells.shape[1] == 4 mesh = skfem.MeshQuad(np.ascontiguousarray(points.T), np.ascontiguousarray(cells.T)) element = skfem.ElementQuad1() @skfem.BilinearForm def mass(u, v, _): return u * v @skfem.BilinearForm def flux(u, v, w): return dot(w.n, u.grad) * v basis = skfem.CellBasis(mesh, element) facet_basis = skfem.FacetBasis(basis.mesh, basis.elem) lap = skfem.asm(laplace, basis) boundary_terms = skfem.asm(flux, facet_basis) A = lap - boundary_terms A *= lmbda # get the evaluation matrix E = basis.probes(x0.T) # mass matrix M = skfem.asm(mass, basis) x = _solve(A, M, E, y0, solver) return basis, x
def setup(n): x0 = rng.random((n, 2)) - 0.5 # y0 = np.ones(n) # y0 = x0[:, 0] # y0 = x0[:, 0]**2 # y0 = np.cos(np.pi*x0.T[0]) # y0 = np.cos(np.pi*x0.T[0]) * np.cos(np.pi*x0.T[1]) y0 = np.cos(np.pi * np.sqrt(x0.T[0]**2 + x0.T[1]**2)) points, cells = meshzoo.rectangle_tri((-1.0, -1.0), (1.0, 1.0), n) mesh = skfem.MeshTri(points.T.copy(), cells.T.copy()) element = skfem.ElementTriP1() @skfem.BilinearForm def mass(u, v, _): return u * v @skfem.BilinearForm def flux(u, v, w): return dot(w.n, u.grad) * v basis = skfem.InteriorBasis(mesh, element) facet_basis = skfem.FacetBasis(basis.mesh, basis.elem) lap = skfem.asm(laplace, basis) boundary_terms = skfem.asm(flux, facet_basis) A = lap - boundary_terms # A *= lmbda # get the evaluation matrix E = basis.probes(x0.T) # mass matrix M = skfem.asm(mass, basis) # x = _solve(A, M, E, y0, solver) # # Neumann preconditioner # An = _assemble_eigen(dot(grad(u), grad(v)) * dx).sparray() # # Dirichlet preconditioner # Ad = _assemble_eigen(dot(grad(u), grad(v)) * dx) # bc = DirichletBC(V, 0.0, "on_boundary") # bc.apply(Ad) # # Ad = Ad.sparray() # Aq = _assemble_eigen( # dot(grad(u), grad(v)) * dx - dot(n, grad(u)) * v * ds - dot(n, grad(v)) * u * ds # ).sparray() # ml = pyamg.smoothed_aggregation_solver(A, coarse_solver="jacobi", max_coarse=100) # mln = pyamg.smoothed_aggregation_solver(An, coarse_solver="jacobi", max_coarse=100) # mld = pyamg.smoothed_aggregation_solver(Ad, coarse_solver="jacobi", max_coarse=100) # mlq = pyamg.smoothed_aggregation_solver(Aq, coarse_solver="jacobi", max_coarse=100) ml = pyamg.smoothed_aggregation_solver(A, coarse_solver="jacobi", symmetry="nonsymmetric", max_coarse=100) # mlT = pyamg.smoothed_aggregation_solver( # A.T, coarse_solver="jacobi", symmetry="nonsymmetric", max_coarse=100 # ) P = ml.aspreconditioner() # PT = mlT.aspreconditioner() # construct transpose -- dense, super expensive! I = np.eye(P.shape[0]) PT = (P @ I).T PT = scipy.sparse.csr_matrix(PT) # # make sure it's really the transpose # x = rng.random(A.shape[1]) # y = rng.random(A.shape[1]) # print(np.dot(x, P @ y)) # print(np.dot(PT @ x, y)) def matvec(x): return P @ x def rmatvec(y): return PT @ y precs = [ scipy.sparse.linalg.LinearOperator(A.shape, matvec=matvec, rmatvec=rmatvec) # not working well: # (ml.aspreconditioner(), mlT.aspreconditioner()) ] return A, E, M, precs, y0
rng = np.random.default_rng(0) tol = 1.0e-8 for n in range(5, 41, 5): # points, cells = meshzoo.rectangle_tri((0.0, 0.0), (1.0, 1.0), n) points, cells = meshzoo.disk(6, n) print(f"{n = }, {len(points) = }") @fem.BilinearForm def flux(u, v, w): return dot(w.n, u.grad) * v # copy to avoid warnings; better would be to get the transposed arrays from meshzoo # directly mesh = fem.MeshTri(points.T.copy(), cells.T.copy()) basis = fem.InteriorBasis(mesh, fem.ElementTriP1()) facet_basis = fem.FacetBasis(basis.mesh, basis.elem) lap = fem.asm(laplace, basis) boundary_terms = fem.asm(flux, facet_basis) A = lap - boundary_terms b = rng.random(A.shape[1]) # make the system consistent by removing A's left nullspace components from the # right-hand side lns = scipy.linalg.null_space(A.T.toarray()).T for n in lns: b -= np.dot(b, n) / np.dot(n, n) * n
from meshio.xdmf import TimeSeriesWriter @skfem.BilinearForm def vector_mass(u, v, w): return sum(v * u) @skfem.BilinearForm def port_pressure(u, v, w): return sum(v * (u * w.n)) p_inlet = 8.0 mesh = skfem.MeshTri() mesh.refine(3) boundary = { "inlet": mesh.facets_satisfying(lambda x: x[0] == 0), "outlet": mesh.facets_satisfying(lambda x: x[0] == 1), "wall": mesh.facets_satisfying(lambda x: np.logical_or(x[1] == 0, x[1] == 1)), } boundary["ports"] = np.concatenate([boundary["inlet"], boundary["outlet"]]) element = { "u": skfem.ElementVectorH1(skfem.ElementTriP2()), "p": skfem.ElementTriP1() } basis = {