def test_assembly_bcs(mode): mesh = create_unit_square(MPI.COMM_WORLD, 12, 12, ghost_mode=mode) V = FunctionSpace(mesh, ("Lagrange", 1)) u, v = ufl.TrialFunction(V), ufl.TestFunction(V) a = inner(u, v) * dx + inner(u, v) * ds L = inner(1.0, v) * dx a, L = form(a), form(L) bdofsV = locate_dofs_geometrical(V, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))) bc = dirichletbc(PETSc.ScalarType(1), bdofsV, V) # Assemble and apply 'global' lifting of bcs A = assemble_matrix(a) A.assemble() b = assemble_vector(L) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) g = b.duplicate() with g.localForm() as g_local: g_local.set(0.0) set_bc(g, [bc]) f = b - A * g set_bc(f, [bc]) # Assemble vector and apply lifting of bcs during assembly b_bc = assemble_vector(L) apply_lifting(b_bc, [a], [[bc]]) b_bc.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b_bc, [bc]) assert (f - b_bc).norm() == pytest.approx(0.0, rel=1e-12, abs=1e-12)
def test_assemble_manifold(): """Test assembly of poisson problem on a mesh with topological dimension 1 but embedded in 2D (gdim=2)""" points = np.array([[0.0, 0.0], [0.2, 0.0], [0.4, 0.0], [0.6, 0.0], [0.8, 0.0], [1.0, 0.0]], dtype=np.float64) cells = np.array([[0, 1], [1, 2], [2, 3], [3, 4], [4, 5]], dtype=np.int32) cell = ufl.Cell("interval", geometric_dimension=points.shape[1]) domain = ufl.Mesh(ufl.VectorElement("Lagrange", cell, 1)) mesh = create_mesh(MPI.COMM_WORLD, cells, points, domain) assert mesh.geometry.dim == 2 assert mesh.topology.dim == 1 U = FunctionSpace(mesh, ("P", 1)) u, v = ufl.TrialFunction(U), ufl.TestFunction(U) a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx(mesh) L = ufl.inner(1.0, v) * ufl.dx(mesh) a, L = form(a), form(L) bcdofs = locate_dofs_geometrical(U, lambda x: np.isclose(x[0], 0.0)) bcs = [dirichletbc(PETSc.ScalarType(0), bcdofs, U)] A = assemble_matrix(a, bcs=bcs) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], bcs=[bcs]) set_bc(b, bcs) assert np.isclose(b.norm(), 0.41231) assert np.isclose(A.norm(), 25.0199)
def solve(dtype=np.float32): """Solve the variational problem""" # Process forms. This will compile the forms for the requested type. a0 = fem.form(a, dtype=dtype) if np.issubdtype(dtype, np.complexfloating): L0 = fem.form(L, dtype=dtype) else: L0 = fem.form(ufl.replace(L, {fc: 0, gc: 0}), dtype=dtype) # Create a Dirichlet boundary condition bc = fem.dirichletbc(value=dtype(0), dofs=dofs, V=V) # Assemble forms A = fem.assemble_matrix(a0, [bc]) A.finalize() b = fem.assemble_vector(L0) fem.apply_lifting(b.array, [a0], bcs=[[bc]]) b.scatter_reverse(common.ScatterMode.add) fem.set_bc(b.array, [bc]) # Create a Scipy sparse matrix that shares data with A As = scipy.sparse.csr_matrix((A.data, A.indices, A.indptr)) # Solve the variational problem and return the solution uh = fem.Function(V, dtype=dtype) uh.x.array[:] = scipy.sparse.linalg.spsolve(As, b.array) return uh
def solve(self) -> fem.Function: """Solve the problem.""" # Assemble lhs self._A.zeroEntries() fem.assemble_matrix(self._A, self._a, bcs=self.bcs) self._A.assemble() # Assemble rhs with self._b.localForm() as b_loc: b_loc.set(0) fem.assemble_vector(self._b, self._L) # Apply boundary conditions to the rhs fem.apply_lifting(self._b, [self._a], [self.bcs]) self._b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) fem.set_bc(self._b, self.bcs) # Solve linear system and update ghost values in the solution self._solver.solve(self._b, self.u.vector) self.u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) return self.u
def F_nest(self, snes, x, F): assert x.getType() == "nest" and F.getType() == "nest" # Update solution x = x.getNestSubVecs() for x_sub, var_sub in zip(x, self.soln_vars): x_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) with x_sub.localForm() as _x: var_sub.x.array[:] = _x.array_r # Assemble bcs1 = bcs_by_block(extract_function_spaces(self.a, 1), self.bcs) for L, F_sub, a in zip(self.L, F.getNestSubVecs(), self.a): with F_sub.localForm() as F_sub_local: F_sub_local.set(0.0) assemble_vector(F_sub, L) apply_lifting(F_sub, a, bcs=bcs1, x0=x, scale=-1.0) F_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) # Set bc value in RHS bcs0 = bcs_by_block(extract_function_spaces(self.L), self.bcs) for F_sub, bc, x_sub in zip(F.getNestSubVecs(), bcs0, x): set_bc(F_sub, bc, x_sub, -1.0) # Must assemble F here in the case of nest matrices F.assemble()
def F(self, snes: PETSc.SNES, x: PETSc.Vec, b: PETSc.Vec): """Assemble the residual F into the vector b. Parameters ========== snes: the snes object x: Vector containing the latest solution. b: Vector to assemble the residual into. """ # We need to assign the vector to the function x.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) x.copy(self.u.vector) self.u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # Zero the residual vector # import pdb; pdb.set_trace() with b.localForm() as b_local: b_local.set(0.0) assemble_vector(b, self.F_form) # Apply boundary conditions apply_lifting(b, [self.J_form], [self.bcs], [x], -1.0) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, self.bcs, x, -1.0)
def run_scalar_test(mesh, V, degree): """ Manufactured Poisson problem, solving u = x[1]**p, where p is the degree of the Lagrange function space. """ u, v = TrialFunction(V), TestFunction(V) a = inner(grad(u), grad(v)) * dx # Get quadrature degree for bilinear form integrand (ignores effect of non-affine map) a = inner(grad(u), grad(v)) * dx(metadata={"quadrature_degree": -1}) a.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(a) a = form(a) # Source term x = SpatialCoordinate(mesh) u_exact = x[1]**degree f = -div(grad(u_exact)) # Set quadrature degree for linear form integrand (ignores effect of non-affine map) L = inner(f, v) * dx(metadata={"quadrature_degree": -1}) L.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(L) L = form(L) u_bc = Function(V) u_bc.interpolate(lambda x: x[1]**degree) # Create Dirichlet boundary condition facetdim = mesh.topology.dim - 1 mesh.topology.create_connectivity(facetdim, mesh.topology.dim) bndry_facets = np.where( np.array(compute_boundary_facets(mesh.topology)) == 1)[0] bdofs = locate_dofs_topological(V, facetdim, bndry_facets) bc = dirichletbc(u_bc, bdofs) b = assemble_vector(L) apply_lifting(b, [a], bcs=[[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) a = form(a) A = assemble_matrix(a, bcs=[bc]) A.assemble() # Create LU linear solver solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A) uh = Function(V) solver.solve(b, uh.vector) uh.x.scatter_forward() M = (u_exact - uh)**2 * dx M = form(M) error = mesh.comm.allreduce(assemble_scalar(M), op=MPI.SUM) assert np.absolute(error) < 1.0e-14
def monolithic_solve(): """Monolithic (interleaved) solver""" P2_el = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2) P1_el = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) TH = P2_el * P1_el W = FunctionSpace(mesh, TH) (u, p) = ufl.TrialFunctions(W) (v, q) = ufl.TestFunctions(W) a00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * dx a01 = ufl.inner(p, ufl.div(v)) * dx a10 = ufl.inner(ufl.div(u), q) * dx a = a00 + a01 + a10 p00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * dx p11 = ufl.inner(p, q) * dx p_form = p00 + p11 f = Function(W.sub(0).collapse()[0]) p_zero = Function(W.sub(1).collapse()[0]) L0 = inner(f, v) * dx L1 = inner(p_zero, q) * dx L = L0 + L1 a, p_form, L = form(a), form(p_form), form(L) bdofsW0_P2_0 = locate_dofs_topological(W.sub(0), facetdim, bndry_facets0) bdofsW0_P2_1 = locate_dofs_topological(W.sub(0), facetdim, bndry_facets1) bc0 = dirichletbc(bc_value, bdofsW0_P2_0, W.sub(0)) bc1 = dirichletbc(bc_value, bdofsW0_P2_1, W.sub(0)) A = assemble_matrix(a, bcs=[bc0, bc1]) A.assemble() P = assemble_matrix(p_form, bcs=[bc0, bc1]) P.assemble() b = assemble_vector(L) apply_lifting(b, [a], bcs=[[bc0, bc1]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc0, bc1]) ksp = PETSc.KSP() ksp.create(mesh.comm) ksp.setOperators(A, P) ksp.setType("minres") pc = ksp.getPC() pc.setType('lu') def monitor(ksp, its, rnorm): # print("Num it, rnorm:", its, rnorm) pass ksp.setTolerances(rtol=1.0e-8, max_it=50) ksp.setMonitor(monitor) ksp.setFromOptions() x = A.createVecRight() ksp.solve(b, x) assert ksp.getConvergedReason() > 0 return b.norm(), x.norm(), A.norm(), P.norm()
def F(self, x, b): """Assemble residual vector.""" with b.localForm() as b_local: b_local.set(0.0) assemble_vector(b, self.L) apply_lifting(b, [self.a], bcs=[[self.bc]], x0=[x], scale=-1.0) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [self.bc], x, -1.0)
def amg_solve(N, method): # Elasticity parameters E = 1.0e9 nu = 0.3 mu = E / (2.0 * (1.0 + nu)) lmbda = E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu)) # Stress computation def sigma(v): return 2.0 * mu * sym(grad(v)) + lmbda * tr(sym( grad(v))) * Identity(2) # Define problem mesh = create_unit_square(MPI.COMM_WORLD, N, N) V = VectorFunctionSpace(mesh, 'Lagrange', 1) u = TrialFunction(V) v = TestFunction(V) facetdim = mesh.topology.dim - 1 bndry_facets = locate_entities_boundary(mesh, facetdim, lambda x: np.full(x.shape[1], True)) bdofs = locate_dofs_topological(V.sub(0), V, facetdim, bndry_facets) bc = dirichletbc(PETSc.ScalarType(0), bdofs, V.sub(0)) # Forms a, L = inner(sigma(u), grad(v)) * dx, dot(ufl.as_vector((1.0, 1.0)), v) * dx # Assemble linear algebra objects A = assemble_matrix(a, [bc]) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) # Create solution function u = Function(V) # Create near null space basis and orthonormalize null_space = build_nullspace(V, u.vector) # Attached near-null space to matrix A.set_near_nullspace(null_space) # Test that basis is orthonormal assert null_space.is_orthonormal() # Create PETSC smoothed aggregation AMG preconditioner, and # create CG solver solver = PETSc.KSP().create(mesh.comm) solver.setType("cg") # Set matrix operator solver.setOperators(A) # Compute solution and return number of iterations return solver.solve(b, u.vector)
def test_assembly_dx_domains(mode): mesh = create_unit_square(MPI.COMM_WORLD, 10, 10, ghost_mode=mode) V = FunctionSpace(mesh, ("Lagrange", 1)) u, v = ufl.TrialFunction(V), ufl.TestFunction(V) # Prepare a marking structures # indices cover all cells # values are [1, 2, 3, 3, ...] cell_map = mesh.topology.index_map(mesh.topology.dim) num_cells = cell_map.size_local + cell_map.num_ghosts indices = np.arange(0, num_cells) values = np.full(indices.shape, 3, dtype=np.intc) values[0] = 1 values[1] = 2 marker = MeshTags(mesh, mesh.topology.dim, indices, values) dx = ufl.Measure('dx', subdomain_data=marker, domain=mesh) w = Function(V) w.x.array[:] = 0.5 # Assemble matrix a = form(w * ufl.inner(u, v) * (dx(1) + dx(2) + dx(3))) A = assemble_matrix(a) A.assemble() a2 = form(w * ufl.inner(u, v) * dx) A2 = assemble_matrix(a2) A2.assemble() assert (A - A2).norm() < 1.0e-12 bc = dirichletbc(Function(V), range(30)) # Assemble vector L = form(ufl.inner(w, v) * (dx(1) + dx(2) + dx(3))) b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) L2 = form(ufl.inner(w, v) * dx) b2 = assemble_vector(L2) apply_lifting(b2, [a], [[bc]]) b2.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) set_bc(b2, [bc]) assert (b - b2).norm() < 1.0e-12 # Assemble scalar L = form(w * (dx(1) + dx(2) + dx(3))) s = assemble_scalar(L) s = mesh.comm.allreduce(s, op=MPI.SUM) assert s == pytest.approx(0.5, 1.0e-12) L2 = form(w * dx) s2 = assemble_scalar(L2) s2 = mesh.comm.allreduce(s2, op=MPI.SUM) assert s == pytest.approx(s2, 1.0e-12)
def F(self, snes, x, F): """Assemble residual vector.""" x.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) x.copy(self.u.vector) self.u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) with F.localForm() as f_local: f_local.set(0.0) fem.assemble_vector(F, self.L) fem.apply_lifting(F, [self.a], [[self.bc]], [x], -1.0) F.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) fem.set_bc(F, [self.bc], x, -1.0)
def F_mono(self, snes, x, F): x.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) with x.localForm() as _x: self.soln_vars.x.array[:] = _x.array_r with F.localForm() as f_local: f_local.set(0.0) assemble_vector(F, self.L) apply_lifting(F, [self.a], bcs=[self.bcs], x0=[x], scale=-1.0) F.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(F, self.bcs, x, -1.0)
def F(self, x: PETSc.Vec, b: PETSc.Vec): """Assemble the residual F into the vector b. Parameters ---------- x The vector containing the latest solution b Vector to assemble the residual into """ # Reset the residual vector with b.localForm() as b_local: b_local.set(0.0) fem.assemble_vector(b, self._L) # Apply boundary condition fem.apply_lifting(b, [self._a], [self.bcs], [x], -1.0) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) fem.set_bc(b, self.bcs, x, -1.0)
def test_overlapping_bcs(): """Test that, when boundaries condition overlap, the last provided boundary condition is applied""" n = 23 mesh = create_unit_square(MPI.COMM_WORLD, n, n) V = FunctionSpace(mesh, ("Lagrange", 1)) u, v = ufl.TrialFunction(V), ufl.TestFunction(V) a = form(inner(u, v) * dx) L = form(inner(1, v) * dx) dofs_left = locate_dofs_geometrical(V, lambda x: x[0] < 1.0 / (2.0 * n)) dofs_top = locate_dofs_geometrical(V, lambda x: x[1] > 1.0 - 1.0 / (2.0 * n)) dof_corner = np.array(list(set(dofs_left).intersection(set(dofs_top))), dtype=np.int64) # Check only one dof pair is found globally assert len(set(np.concatenate(MPI.COMM_WORLD.allgather(dof_corner)))) == 1 bcs = [ dirichletbc(PETSc.ScalarType(0), dofs_left, V), dirichletbc(PETSc.ScalarType(123.456), dofs_top, V) ] A, b = create_matrix(a), create_vector(L) assemble_matrix(A, a, bcs=bcs) A.assemble() # Check the diagonal (only on the rank that owns the row) d = A.getDiagonal() if len(dof_corner) > 0 and dof_corner[0] < V.dofmap.index_map.size_local: assert np.isclose(d.array_r[dof_corner[0]], 1.0) with b.localForm() as b_loc: b_loc.set(0) assemble_vector(b, L) apply_lifting(b, [a], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, bcs) b.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) if len(dof_corner) > 0: with b.localForm() as b_loc: assert b_loc[dof_corner[0]] == 123.456
def project(v, target_func, bcs=[]): # Ensure we have a mesh and attach to measure V = target_func.function_space dx = ufl.dx(V.mesh) # Define variational problem for projection w = ufl.TestFunction(V) Pv = ufl.TrialFunction(V) a = ufl.inner(Pv, w) * dx L = ufl.inner(v, w) * dx # Assemble linear system A = assemble_matrix(a, bcs) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, bcs) solve(A, target_func.vector, b)
def _solve_varproblem(*args, **kwargs): "Solve variational problem a == L or F == 0" # Extract arguments eq, u, bcs, J, tol, M, form_compiler_parameters, petsc_options \ = _extract_args(*args, **kwargs) # Solve linear variational problem if isinstance(eq.lhs, ufl.Form) and isinstance(eq.rhs, ufl.Form): a = fem.Form(eq.lhs, form_compiler_parameters=form_compiler_parameters) L = fem.Form(eq.rhs, form_compiler_parameters=form_compiler_parameters) b = fem.assemble_vector(L._cpp_object) fem.apply_lifting(b, [a._cpp_object], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) fem.set_bc(b, bcs) A = fem.assemble_matrix(a._cpp_object, bcs) A.assemble() comm = L._cpp_object.mesh().mpi_comm() ksp = PETSc.KSP().create(comm) ksp.setOperators(A) ksp.setOptionsPrefix("dolfin_solve_") opts = PETSc.Options() opts.prefixPush("dolfin_solve_") for k, v in petsc_options.items(): opts[k] = v opts.prefixPop() ksp.setFromOptions() ksp.solve(b, u.vector) # Solve nonlinear variational problem else: raise RuntimeError("Not implemented")
def project(v, target_func, degree, bcs=[]): # Ensure we have a mesh and attach to measure V = target_func.function_space dx = ufl.dx(V.mesh, degree=degree) # Define variational problem for projection w = ufl.TestFunction(V) Pv = ufl.TrialFunction(V) a = fem.form(ufl.inner(Pv, w) * dx) L = fem.form(ufl.inner(v, w) * dx) # Assemble linear system A = fem.assemble_matrix(a, bcs) A.assemble() b = fem.assemble_vector(L) fem.apply_lifting(b, [a], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) fem.set_bc(b, bcs) # Solve linear system solver = PETSc.KSP().create(A.getComm()) solver.setOperators(A) solver.solve(b, target_func.vector)
np.array([0, 0, 0], dtype=PETSc.ScalarType), locate_dofs_geometrical( V, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[1], 1.0))), V) # Assembly and solve # ------------------ # :: # Assemble system, applying boundary conditions A = assemble_matrix(a, bcs=[bc]) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], bcs=[[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) # Create solution function u = Function(V) # Create near null space basis (required for smoothed aggregation AMG) null_space = build_nullspace(V) # Attach near nullspace to matrix A.setNearNullSpace(null_space) # Set solver options opts = PETSc.Options() opts["ksp_type"] = "cg"
def test_matrix_assembly_block_nl(): """Test assembly of block matrices and vectors into (a) monolithic blocked structures, PETSc Nest structures, and monolithic structures in the nonlinear setting """ mesh = create_unit_square(MPI.COMM_WORLD, 4, 8) p0, p1 = 1, 2 P0 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p0) P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p1) V0 = FunctionSpace(mesh, P0) V1 = FunctionSpace(mesh, P1) def initial_guess_u(x): return np.sin(x[0]) * np.sin(x[1]) def initial_guess_p(x): return -x[0]**2 - x[1]**3 def bc_value(x): return np.cos(x[0]) * np.cos(x[1]) facetdim = mesh.topology.dim - 1 bndry_facets = locate_entities_boundary( mesh, facetdim, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))) u_bc = Function(V1) u_bc.interpolate(bc_value) bdofs = locate_dofs_topological(V1, facetdim, bndry_facets) bc = dirichletbc(u_bc, bdofs) # Define variational problem du, dp = ufl.TrialFunction(V0), ufl.TrialFunction(V1) u, p = Function(V0), Function(V1) v, q = ufl.TestFunction(V0), ufl.TestFunction(V1) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) f = 1.0 g = -3.0 F0 = inner(u, v) * dx + inner(p, v) * dx - inner(f, v) * dx F1 = inner(u, q) * dx + inner(p, q) * dx - inner(g, q) * dx a_block = form([[derivative(F0, u, du), derivative(F0, p, dp)], [derivative(F1, u, du), derivative(F1, p, dp)]]) L_block = form([F0, F1]) # Monolithic blocked x0 = create_vector_block(L_block) scatter_local_vectors(x0, [u.vector.array_r, p.vector.array_r], [(u.function_space.dofmap.index_map, u.function_space.dofmap.index_map_bs), (p.function_space.dofmap.index_map, p.function_space.dofmap.index_map_bs)]) x0.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # Ghosts are updated inside assemble_vector_block A0 = assemble_matrix_block(a_block, bcs=[bc]) b0 = assemble_vector_block(L_block, a_block, bcs=[bc], x0=x0, scale=-1.0) A0.assemble() assert A0.getType() != "nest" Anorm0 = A0.norm() bnorm0 = b0.norm() # Nested (MatNest) x1 = create_vector_nest(L_block) for x1_soln_pair in zip(x1.getNestSubVecs(), (u, p)): x1_sub, soln_sub = x1_soln_pair soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) soln_sub.vector.copy(result=x1_sub) x1_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) A1 = assemble_matrix_nest(a_block, bcs=[bc]) b1 = assemble_vector_nest(L_block) apply_lifting_nest(b1, a_block, bcs=[bc], x0=x1, scale=-1.0) for b_sub in b1.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = bcs_by_block([L.function_spaces[0] for L in L_block], [bc]) set_bc_nest(b1, bcs0, x1, scale=-1.0) A1.assemble() assert A1.getType() == "nest" assert nest_matrix_norm(A1) == pytest.approx(Anorm0, 1.0e-12) assert b1.norm() == pytest.approx(bnorm0, 1.0e-12) # Monolithic version E = P0 * P1 W = FunctionSpace(mesh, E) dU = ufl.TrialFunction(W) U = Function(W) u0, u1 = ufl.split(U) v0, v1 = ufl.TestFunctions(W) U.sub(0).interpolate(initial_guess_u) U.sub(1).interpolate(initial_guess_p) F = inner(u0, v0) * dx + inner(u1, v0) * dx + inner(u0, v1) * dx + inner(u1, v1) * dx \ - inner(f, v0) * ufl.dx - inner(g, v1) * dx J = derivative(F, U, dU) F, J = form(F), form(J) bdofsW_V1 = locate_dofs_topological((W.sub(1), V1), facetdim, bndry_facets) bc = dirichletbc(u_bc, bdofsW_V1, W.sub(1)) A2 = assemble_matrix(J, bcs=[bc]) A2.assemble() b2 = assemble_vector(F) apply_lifting(b2, [J], bcs=[[bc]], x0=[U.vector], scale=-1.0) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b2, [bc], x0=U.vector, scale=-1.0) assert A2.getType() != "nest" assert A2.norm() == pytest.approx(Anorm0, 1.0e-12) assert b2.norm() == pytest.approx(bnorm0, 1.0e-12)
def test_manufactured_poisson(degree, filename, datadir): """ Manufactured Poisson problem, solving u = x[1]**p, where p is the degree of the Lagrange function space. """ with XDMFFile(MPI.comm_world, os.path.join(datadir, filename)) as xdmf: mesh = xdmf.read_mesh(GhostMode.none) V = FunctionSpace(mesh, ("Lagrange", degree)) u, v = TrialFunction(V), TestFunction(V) a = inner(grad(u), grad(v)) * dx # Get quadrature degree for bilinear form integrand (ignores effect # of non-affine map) a = inner(grad(u), grad(v)) * dx(metadata={"quadrature_degree": -1}) a.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(a) # Source term x = SpatialCoordinate(mesh) u_exact = x[1]**degree f = -div(grad(u_exact)) # Set quadrature degree for linear form integrand (ignores effect of # non-affine map) L = inner(f, v) * dx(metadata={"quadrature_degree": -1}) L.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(L) t0 = time.time() L = fem.Form(L) t1 = time.time() print("Linear form compile time:", t1 - t0) u_bc = Function(V) u_bc.interpolate(lambda x: x[1]**degree) # Create Dirichlet boundary condition mesh.create_connectivity_all() facetdim = mesh.topology.dim - 1 bndry_facets = np.where( np.array(mesh.topology.on_boundary(facetdim)) == 1)[0] bdofs = locate_dofs_topological(V, facetdim, bndry_facets) assert (len(bdofs) < V.dim()) bc = DirichletBC(u_bc, bdofs) t0 = time.time() b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) t1 = time.time() print("Vector assembly time:", t1 - t0) t0 = time.time() a = fem.Form(a) t1 = time.time() print("Bilinear form compile time:", t1 - t0) t0 = time.time() A = assemble_matrix(a, [bc]) A.assemble() t1 = time.time() print("Matrix assembly time:", t1 - t0) # Create LU linear solver solver = PETSc.KSP().create(MPI.comm_world) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A) # Solve t0 = time.time() uh = Function(V) solver.solve(b, uh.vector) uh.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) t1 = time.time() print("Linear solver time:", t1 - t0) M = (u_exact - uh)**2 * dx t0 = time.time() M = fem.Form(M) t1 = time.time() print("Error functional compile time:", t1 - t0) t0 = time.time() error = assemble_scalar(M) error = MPI.sum(mesh.mpi_comm(), error) t1 = time.time() print("Error assembly time:", t1 - t0) assert np.absolute(error) < 1.0e-14
a = inner(sigma(u), grad(v)) * dx L = inner(f, v) * dx u0 = Function(V) with u0.vector.localForm() as bc_local: bc_local.set(0.0) # Set up boundary condition on inner surface bc = DirichletBC(u0, locate_dofs_geometrical(V, boundary)) # Assemble system, applying boundary conditions and preserving symmetry) A = assemble_matrix(a, [bc]) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) # Create solution function u = Function(V) # Create near null space basis (required for smoothed aggregation AMG). null_space = build_nullspace(V) # Attach near nullspace to matrix A.setNearNullSpace(null_space) # Set solver options opts = PETSc.Options() opts["ksp_type"] = "cg"
def run_scalar_test(mesh, V, degree): """ Manufactured Poisson problem, solving u = x[1]**p, where p is the degree of the Lagrange function space. """ u, v = TrialFunction(V), TestFunction(V) a = inner(grad(u), grad(v)) * dx # Get quadrature degree for bilinear form integrand (ignores effect of non-affine map) a = inner(grad(u), grad(v)) * dx(metadata={"quadrature_degree": -1}) a.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(a) # Source term x = SpatialCoordinate(mesh) u_exact = x[1]**degree f = -div(grad(u_exact)) # Set quadrature degree for linear form integrand (ignores effect of non-affine map) L = inner(f, v) * dx(metadata={"quadrature_degree": -1}) L.integrals()[0].metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree(L) with common.Timer("Linear form compile"): L = fem.Form(L) with common.Timer("Function interpolation"): u_bc = Function(V) u_bc.interpolate(lambda x: x[1]**degree) # Create Dirichlet boundary condition mesh.topology.create_connectivity_all() facetdim = mesh.topology.dim - 1 bndry_facets = np.where( np.array(cpp.mesh.compute_boundary_facets(mesh.topology)) == 1)[0] bdofs = locate_dofs_topological(V, facetdim, bndry_facets) bc = DirichletBC(u_bc, bdofs) with common.Timer("Vector assembly"): b = assemble_vector(L) apply_lifting(b, [a], [[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) with common.Timer("Bilinear form compile"): a = fem.Form(a) with common.Timer("Matrix assembly"): A = assemble_matrix(a, [bc]) A.assemble() # Create LU linear solver solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A) with common.Timer("Solve"): uh = Function(V) solver.solve(b, uh.vector) uh.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) with common.Timer("Error functional compile"): M = (u_exact - uh)**2 * dx M = fem.Form(M) with common.Timer("Error assembly"): error = mesh.mpi_comm().allreduce(assemble_scalar(M), op=MPI.SUM) common.list_timings(MPI.COMM_WORLD, [common.TimingType.wall]) assert np.absolute(error) < 1.0e-14
L = form(ufl.inner(f_exact, v) * dx) zero_u = Function(V_1) zero_u.x.array[:] = 0.0 boundary_facets = dolfinx.mesh.locate_entities_boundary( mesh, mesh.topology.dim - 1, lambda x: np.full(x.shape[1], True, dtype=bool)) boundary_dofs = dolfinx.fem.locate_dofs_topological( (V.sub(1), V_1), mesh.topology.dim - 1, boundary_facets) bcs = [dirichletbc(zero_u, boundary_dofs, V.sub(1))] A = assemble_matrix(a, bcs=bcs) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], bcs=[bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, bcs) # Solve solver = PETSc.KSP().create(MPI.COMM_WORLD) PETSc.Options()["ksp_type"] = "preonly" PETSc.Options()["pc_type"] = "lu" PETSc.Options()["pc_factor_mat_solver_type"] = "mumps" solver.setFromOptions() solver.setOperators(A) x_h = Function(V) solver.solve(b, x_h.vector) x_h.x.scatter_forward() sigma_h = S(ufl.as_tensor([[x_h[0], x_h[1]], [x_h[2], x_h[3]]]))
# A = - A10 * A00^{-1} * A01 A[:, :] = - A10 @ np.linalg.solve(A00, A01) # Prepare a Form with a condensed tabulation kernel Form = Form_float64 if PETSc.ScalarType == np.float64 else Form_complex128 integrals = {IntegralType.cell: ([(-1, tabulate_condensed_tensor_A.address)], None)} a_cond = Form([U._cpp_object, U._cpp_object], integrals, [], [], False, None) A_cond = assemble_matrix(a_cond, bcs=[bc]) A_cond.assemble() b = assemble_vector(b1) apply_lifting(b, [a_cond], bcs=[[bc]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, [bc]) uc = Function(U) solver = PETSc.KSP().create(A_cond.getComm()) solver.setOperators(A_cond) solver.solve(b, uc.vector) # Pure displacement based formulation a = form(- ufl.inner(sigma_u(u), ufl.grad(v)) * ufl.dx) A = assemble_matrix(a, bcs=[bc]) A.assemble() # Create bounding box for function evaluation bb_tree = geometry.BoundingBoxTree(mesh, 2)
def solve_displ_system(J, F, intern_var0, intern_var1, expr_compiled, w0, w1, bcs, df, dt, t, k, io_callback=None, refinement_callback=None, rtol=1.e-6, atol=1.e-16, max_its=20): """Solve system for displacement""" rank = MPI.COMM_WORLD.rank t0 = time() A = create_matrix(J) if rank == 0: logger.info(f"[Timer] Preallocation matrix time {time() - t0:.3f}") t0 = time() t0 = time() b = create_vector(F) if rank == 0: logger.info(f"[Timer] Preallocation vector time {time() - t0:.3f}") with b.localForm() as local: local.set(0.0) assemble_vector(b, F) apply_lifting(b, [J], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, bcs) resid_norm0 = b.norm() if rank == 0: print(f"Initial DISPL residual: {resid_norm0:.2e}") iter = 0 # Total number of NR iterations including refinement attempts iter_total = 0 converged = False refine = 0 scale = 1.0 rel_resid_norm = 1.0 bcs_init = [] for bc in bcs: bcs_init += [bc.value.vector.duplicate()] with bcs_init[-1].localForm() as loc0, bc.value.vector.localForm( ) as loc1: loc1.copy(loc0) df0 = np.copy(df.value) while converged is False: if iter > max_its or rel_resid_norm > 1.e+2: refine += 1 iter = 0 if rank == 0: logger.info(79 * "!") logger.info( f"Restarting NR with rel. stepsize: {1.0 / (2 ** refine)}") with w0["displ"].vector.localForm( ) as w_local, w1["displ"].vector.localForm() as w1_local: w_local.copy(w1_local) df.value = df0 scale = 1.0 / 2**refine if refine > 10: raise ConvergenceError( "Inner adaptivity reqiures > 10 refinements.") # Reset and scale boundary condition for i, bc in enumerate(bcs_init): with bcs[i].value.vector.localForm( ) as bcsi_local, bc.localForm() as bc_local: bc_local.copy(bcsi_local) bcsi_local.scale(scale) df.value *= scale dt.value *= scale if refinement_callback is not None: refinement_callback(scale) if rank == 0: logger.info("Newton iteration for displ {}".format(iter)) if iter > 0: for bc in bcs: with bc.value.vector.localForm() as locvec: locvec.set(0.0) A.zeroEntries() with b.localForm() as local: local.set(0.0) size = A.getSize()[0] local_size = A.getLocalSize()[0] t0 = time() assemble_matrix(A, J, bcs) A.assemble() _time = time() - t0 Anorm = A.norm() if rank == 0: logger.info(f"[Timer] A0 size: {size}, local size: {local_size}") logger.info(f"[Timer] A0 assembly: {_time:.4f}") logger.info(f"[Timer] A0 assembly dofs/s: {size / _time:.1f}") logger.info(f"A0 norm: {Anorm:.4f}") t0 = time() assemble_vector(b, F) apply_lifting(b, [J], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, bcs) bnorm = b.norm() if rank == 0: logger.info(f"[Timer] b0 assembly {time() - t0:.4f}") logger.info(f"b norm: {bnorm:.4f}") nsp = build_nullspace(w1["displ"].function_space) ksp = PETSc.KSP() ksp.create(MPI.COMM_WORLD) ksp.setOptionsPrefix("disp") opts = PETSc.Options() A.setNearNullSpace(nsp) A.setBlockSize(3) ksp.setOperators(A) x = A.createVecRight() ksp.setFromOptions() t0 = time() ksp.solve(b, x) t1 = time() - t0 opts.view() xnorm = x.norm() if rank == 0: its = ksp.its t1 = time() - t0 dofsps = int(size / t1) logger.info(f"[Timer] A0 converged in: {its}") logger.info(f"[Timer] A0 solve: {t1:.4f}") logger.info(f"[Timer] A0 solve dofs/s: {dofsps:.1f}") logger.info(f"Increment norm: {xnorm}") # TODO: Local axpy segfaults, could ghostUpdate be avoided? w1["displ"].vector.axpy(1.0, x) w1["displ"].vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # # Evaluate true residual and check # with b.localForm() as local: local.set(0.0) assemble_vector(b, F) apply_lifting(b, [J], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, bcs) norm = b.norm() rel_resid_norm = norm / resid_norm0 rel_dx_norm = x.norm() / w1["displ"].vector.norm() if rank == 0: logger.info("---") logger.info(f"Abs. resid norm: {norm:.2e}") logger.info(f"Rel. dx norm: {rel_dx_norm:.2e}") logger.info(f"Rel. resid norm: {rel_resid_norm:.2e}") logger.info("---") iter += 1 iter_total += 1 if rel_resid_norm < rtol or norm < atol: if rank == 0: logger.info( f"Newton converged in: {iter}, total: {iter_total}") if io_callback is not None: io_callback(intern_var1, w1, t.value + dt.value) return scale
def test_biharmonic(): """Manufactured biharmonic problem. Solved using rotated Regge mixed finite element method. This is equivalent to the Hellan-Herrmann-Johnson (HHJ) finite element method in two-dimensions.""" mesh = RectangleMesh(MPI.COMM_WORLD, [np.array([0.0, 0.0, 0.0]), np.array([1.0, 1.0, 0.0])], [32, 32], CellType.triangle) element = ufl.MixedElement([ufl.FiniteElement("Regge", ufl.triangle, 1), ufl.FiniteElement("Lagrange", ufl.triangle, 2)]) V = FunctionSpace(mesh, element) sigma, u = ufl.TrialFunctions(V) tau, v = ufl.TestFunctions(V) x = ufl.SpatialCoordinate(mesh) u_exact = ufl.sin(ufl.pi * x[0]) * ufl.sin(ufl.pi * x[0]) * ufl.sin(ufl.pi * x[1]) * ufl.sin(ufl.pi * x[1]) f_exact = div(grad(div(grad(u_exact)))) sigma_exact = grad(grad(u_exact)) # sigma and tau are tangential-tangential continuous according to the # H(curl curl) continuity of the Regge space. However, for the biharmonic # problem we require normal-normal continuity H (div div). Theorem 4.2 of # Lizao Li's PhD thesis shows that the latter space can be constructed by # the former through the action of the operator S: def S(tau): return tau - ufl.Identity(2) * ufl.tr(tau) sigma_S = S(sigma) tau_S = S(tau) # Discrete duality inner product eq. 4.5 Lizao Li's PhD thesis def b(tau_S, v): n = FacetNormal(mesh) return inner(tau_S, grad(grad(v))) * dx \ - ufl.dot(ufl.dot(tau_S('+'), n('+')), n('+')) * jump(grad(v), n) * dS \ - ufl.dot(ufl.dot(tau_S, n), n) * ufl.dot(grad(v), n) * ds # Non-symmetric formulation a = inner(sigma_S, tau_S) * dx - b(tau_S, u) + b(sigma_S, v) L = inner(f_exact, v) * dx V_1 = V.sub(1).collapse() zero_u = Function(V_1) with zero_u.vector.localForm() as zero_u_local: zero_u_local.set(0.0) # Strong (Dirichlet) boundary condition boundary_facets = locate_entities_boundary( mesh, mesh.topology.dim - 1, lambda x: np.full(x.shape[1], True, dtype=bool)) boundary_dofs = locate_dofs_topological((V.sub(1), V_1), mesh.topology.dim - 1, boundary_facets) bcs = [DirichletBC(zero_u, boundary_dofs, V.sub(1))] A = assemble_matrix(a, bcs=bcs) A.assemble() b = assemble_vector(L) apply_lifting(b, [a], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) # Solve solver = PETSc.KSP().create(MPI.COMM_WORLD) PETSc.Options()["ksp_type"] = "preonly" PETSc.Options()["pc_type"] = "lu" # PETSc.Options()["pc_factor_mat_solver_type"] = "mumps" solver.setFromOptions() solver.setOperators(A) x_h = Function(V) solver.solve(b, x_h.vector) x_h.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # Recall that x_h has flattened indices. u_error_numerator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar( inner(u_exact - x_h[4], u_exact - x_h[4]) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM)) u_error_denominator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar( inner(u_exact, u_exact) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM)) assert(np.absolute(u_error_numerator / u_error_denominator) < 0.05) # Reconstruct tensor from flattened indices. # Apply inverse transform. In 2D we have S^{-1} = S. sigma_h = S(ufl.as_tensor([[x_h[0], x_h[1]], [x_h[2], x_h[3]]])) sigma_error_numerator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar( inner(sigma_exact - sigma_h, sigma_exact - sigma_h) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM)) sigma_error_denominator = np.sqrt(mesh.mpi_comm().allreduce(assemble_scalar( inner(sigma_exact, sigma_exact) * dx(mesh, metadata={"quadrature_degree": 5})), op=MPI.SUM)) assert(np.absolute(sigma_error_numerator / sigma_error_denominator) < 0.005)
def demo_stacked_cubes(outfile: XDMFFile, theta: float, gmsh: bool = True, quad: bool = False, compare: bool = False, res: float = 0.1): log_info( f"Run theta:{theta:.2f}, Quad: {quad}, Gmsh {gmsh}, Res {res:.2e}") celltype = "quadrilateral" if quad else "triangle" if gmsh: mesh, mt = gmsh_2D_stacked(celltype, theta) mesh.name = f"mesh_{celltype}_{theta:.2f}_gmsh" else: mesh_name = "mesh" filename = f"meshes/mesh_{celltype}_{theta:.2f}.xdmf" mesh_2D_dolfin(celltype, theta) with XDMFFile(MPI.COMM_WORLD, filename, "r") as xdmf: mesh = xdmf.read_mesh(name=mesh_name) mesh.name = f"mesh_{celltype}_{theta:.2f}" tdim = mesh.topology.dim fdim = tdim - 1 mesh.topology.create_connectivity(tdim, tdim) mesh.topology.create_connectivity(fdim, tdim) mt = xdmf.read_meshtags(mesh, name="facet_tags") # Helper until meshtags can be read in from xdmf V = VectorFunctionSpace(mesh, ("Lagrange", 1)) r_matrix = rotation_matrix([0, 0, 1], theta) g_vec = np.dot(r_matrix, [0, -1.25e2, 0]) g = Constant(mesh, PETSc.ScalarType(g_vec[:2])) def bottom_corner(x): return np.isclose(x, [[0], [0], [0]]).all(axis=0) # Fix bottom corner bc_value = np.array((0, ) * mesh.geometry.dim, dtype=PETSc.ScalarType) bottom_dofs = locate_dofs_geometrical(V, bottom_corner) bc_bottom = dirichletbc(bc_value, bottom_dofs, V) bcs = [bc_bottom] # Elasticity parameters E = PETSc.ScalarType(1.0e3) nu = 0 mu = Constant(mesh, E / (2.0 * (1.0 + nu))) lmbda = Constant(mesh, E * nu / ((1.0 + nu) * (1.0 - 2.0 * nu))) # Stress computation def sigma(v): return (2.0 * mu * sym(grad(v)) + lmbda * tr(sym(grad(v))) * Identity(len(v))) # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = inner(sigma(u), grad(v)) * dx ds = Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=3) rhs = inner(Constant(mesh, PETSc.ScalarType( (0, 0))), v) * dx + inner(g, v) * ds def left_corner(x): return np.isclose(x.T, np.dot(r_matrix, [0, 2, 0])).all(axis=1) # Create multi point constraint mpc = MultiPointConstraint(V) with Timer("~Contact: Create contact constraint"): nh = create_normal_approximation(V, mt, 4) mpc.create_contact_slip_condition(mt, 4, 9, nh) with Timer("~Contact: Add non-slip condition at bottom interface"): bottom_normal = facet_normal_approximation(V, mt, 5) mpc.create_slip_constraint(V, (mt, 5), bottom_normal, bcs=bcs) with Timer("~Contact: Add tangential constraint at one point"): vertex = locate_entities_boundary(mesh, 0, left_corner) tangent = facet_normal_approximation(V, mt, 3, tangent=True) mtv = meshtags(mesh, 0, vertex, np.full(len(vertex), 6, dtype=np.int32)) mpc.create_slip_constraint(V, (mtv, 6), tangent, bcs=bcs) mpc.finalize() rtol = 1e-9 petsc_options = { "ksp_rtol": 1e-9, "pc_type": "gamg", "pc_gamg_type": "agg", "pc_gamg_square_graph": 2, "pc_gamg_threshold": 0.02, "pc_gamg_coarse_eq_limit": 1000, "pc_gamg_sym_graph": True, "mg_levels_ksp_type": "chebyshev", "mg_levels_pc_type": "jacobi", "mg_levels_esteig_ksp_type": "cg" # , "help": None, "ksp_view": None } # Solve Linear problem problem = LinearProblem(a, rhs, mpc, bcs=bcs, petsc_options=petsc_options) # Build near nullspace null_space = rigid_motions_nullspace(mpc.function_space) problem.A.setNearNullSpace(null_space) u_h = problem.solve() it = problem.solver.getIterationNumber() if MPI.COMM_WORLD.rank == 0: print("Number of iterations: {0:d}".format(it)) unorm = u_h.vector.norm() if MPI.COMM_WORLD.rank == 0: print(f"Norm of u: {unorm}") # Write solution to file ext = "_gmsh" if gmsh else "" u_h.name = "u_mpc_{0:s}_{1:.2f}{2:s}".format(celltype, theta, ext) outfile.write_mesh(mesh) outfile.write_function(u_h, 0.0, f"Xdmf/Domain/Grid[@Name='{mesh.name}'][1]") # Solve the MPC problem using a global transformation matrix # and numpy solvers to get reference values if not compare: return log_info("Solving reference problem with global matrix (using numpy)") with Timer("~MPC: Reference problem"): # Generate reference matrices and unconstrained solution A_org = assemble_matrix(form(a), bcs) A_org.assemble() L_org = assemble_vector(form(rhs)) apply_lifting(L_org, [form(a)], [bcs]) L_org.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) set_bc(L_org, bcs) root = 0 with Timer("~MPC: Verification"): compare_mpc_lhs(A_org, problem.A, mpc, root=root) compare_mpc_rhs(L_org, problem.b, mpc, root=root) # Gather LHS, RHS and solution on one process A_csr = gather_PETScMatrix(A_org, root=root) K = gather_transformation_matrix(mpc, root=root) L_np = gather_PETScVector(L_org, root=root) u_mpc = gather_PETScVector(u_h.vector, root=root) if MPI.COMM_WORLD.rank == root: KTAK = K.T * A_csr * K reduced_L = K.T @ L_np # Solve linear system d = scipy.sparse.linalg.spsolve(KTAK, reduced_L) # Back substitution to full solution vector uh_numpy = K @ d assert np.allclose(uh_numpy, u_mpc, rtol=rtol)
def test_matrix_assembly_block(mode): """Test assembly of block matrices and vectors into (a) monolithic blocked structures, PETSc Nest structures, and monolithic structures""" mesh = create_unit_square(MPI.COMM_WORLD, 4, 8, ghost_mode=mode) p0, p1 = 1, 2 P0 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p0) P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p1) V0 = FunctionSpace(mesh, P0) V1 = FunctionSpace(mesh, P1) # Locate facets on boundary facetdim = mesh.topology.dim - 1 bndry_facets = locate_entities_boundary(mesh, facetdim, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))) bdofsV1 = locate_dofs_topological(V1, facetdim, bndry_facets) u_bc = PETSc.ScalarType(50.0) bc = dirichletbc(u_bc, bdofsV1, V1) # Define variational problem u, p = ufl.TrialFunction(V0), ufl.TrialFunction(V1) v, q = ufl.TestFunction(V0), ufl.TestFunction(V1) f = 1.0 g = -3.0 zero = Function(V0) a00 = inner(u, v) * dx a01 = inner(p, v) * dx a10 = inner(u, q) * dx a11 = inner(p, q) * dx L0 = zero * inner(f, v) * dx L1 = inner(g, q) * dx a_block = form([[a00, a01], [a10, a11]]) L_block = form([L0, L1]) # Monolithic blocked A0 = assemble_matrix_block(a_block, bcs=[bc]) A0.assemble() b0 = assemble_vector_block(L_block, a_block, bcs=[bc]) assert A0.getType() != "nest" Anorm0 = A0.norm() bnorm0 = b0.norm() # Nested (MatNest) A1 = assemble_matrix_nest(a_block, bcs=[bc], mat_types=[["baij", "aij"], ["aij", ""]]) A1.assemble() Anorm1 = nest_matrix_norm(A1) assert Anorm0 == pytest.approx(Anorm1, 1.0e-12) b1 = assemble_vector_nest(L_block) apply_lifting_nest(b1, a_block, bcs=[bc]) for b_sub in b1.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = bcs_by_block([L.function_spaces[0] for L in L_block], [bc]) set_bc_nest(b1, bcs0) b1.assemble() bnorm1 = math.sqrt(sum([x.norm()**2 for x in b1.getNestSubVecs()])) assert bnorm0 == pytest.approx(bnorm1, 1.0e-12) # Monolithic version E = P0 * P1 W = FunctionSpace(mesh, E) u0, u1 = ufl.TrialFunctions(W) v0, v1 = ufl.TestFunctions(W) a = inner(u0, v0) * dx + inner(u1, v1) * dx + inner(u0, v1) * dx + inner( u1, v0) * dx L = zero * inner(f, v0) * ufl.dx + inner(g, v1) * dx a, L = form(a), form(L) bdofsW_V1 = locate_dofs_topological(W.sub(1), mesh.topology.dim - 1, bndry_facets) bc = dirichletbc(u_bc, bdofsW_V1, W.sub(1)) A2 = assemble_matrix(a, bcs=[bc]) A2.assemble() b2 = assemble_vector(L) apply_lifting(b2, [a], bcs=[[bc]]) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b2, [bc]) assert A2.getType() != "nest" assert A2.norm() == pytest.approx(Anorm0, 1.0e-9) assert b2.norm() == pytest.approx(bnorm0, 1.0e-9)
def test_assembly_solve_block(mode): """Solve a two-field mass-matrix like problem with block matrix approaches and test that solution is the same""" mesh = create_unit_square(MPI.COMM_WORLD, 32, 31, ghost_mode=mode) P = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) V0 = FunctionSpace(mesh, P) V1 = V0.clone() # Locate facets on boundary facetdim = mesh.topology.dim - 1 bndry_facets = locate_entities_boundary(mesh, facetdim, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))) bdofsV0 = locate_dofs_topological(V0, facetdim, bndry_facets) bdofsV1 = locate_dofs_topological(V1, facetdim, bndry_facets) u0_bc = PETSc.ScalarType(50.0) u1_bc = PETSc.ScalarType(20.0) bcs = [dirichletbc(u0_bc, bdofsV0, V0), dirichletbc(u1_bc, bdofsV1, V1)] # Variational problem u, p = ufl.TrialFunction(V0), ufl.TrialFunction(V1) v, q = ufl.TestFunction(V0), ufl.TestFunction(V1) f = 1.0 g = -3.0 zero = Function(V0) a00 = form(inner(u, v) * dx) a01 = form(zero * inner(p, v) * dx) a10 = form(zero * inner(u, q) * dx) a11 = form(inner(p, q) * dx) L0 = form(inner(f, v) * dx) L1 = form(inner(g, q) * dx) def monitor(ksp, its, rnorm): pass # print("Norm:", its, rnorm) A0 = assemble_matrix_block([[a00, a01], [a10, a11]], bcs=bcs) b0 = assemble_vector_block([L0, L1], [[a00, a01], [a10, a11]], bcs=bcs) A0.assemble() A0norm = A0.norm() b0norm = b0.norm() x0 = A0.createVecLeft() ksp = PETSc.KSP() ksp.create(mesh.comm) ksp.setOperators(A0) ksp.setMonitor(monitor) ksp.setType('cg') ksp.setTolerances(rtol=1.0e-14) ksp.setFromOptions() ksp.solve(b0, x0) x0norm = x0.norm() # Nested (MatNest) A1 = assemble_matrix_nest([[a00, a01], [a10, a11]], bcs=bcs, diagonal=1.0) A1.assemble() b1 = assemble_vector_nest([L0, L1]) apply_lifting_nest(b1, [[a00, a01], [a10, a11]], bcs=bcs) for b_sub in b1.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = bcs_by_block([L0.function_spaces[0], L1.function_spaces[0]], bcs) set_bc_nest(b1, bcs0) b1.assemble() b1norm = b1.norm() assert b1norm == pytest.approx(b0norm, 1.0e-12) A1norm = nest_matrix_norm(A1) assert A0norm == pytest.approx(A1norm, 1.0e-12) x1 = b1.copy() ksp = PETSc.KSP() ksp.create(mesh.comm) ksp.setMonitor(monitor) ksp.setOperators(A1) ksp.setType('cg') ksp.setTolerances(rtol=1.0e-12) ksp.setFromOptions() ksp.solve(b1, x1) x1norm = x1.norm() assert x1norm == pytest.approx(x0norm, rel=1.0e-12) # Monolithic version E = P * P W = FunctionSpace(mesh, E) u0, u1 = ufl.TrialFunctions(W) v0, v1 = ufl.TestFunctions(W) a = inner(u0, v0) * dx + inner(u1, v1) * dx L = inner(f, v0) * ufl.dx + inner(g, v1) * dx a, L = form(a), form(L) bdofsW0_V0 = locate_dofs_topological(W.sub(0), facetdim, bndry_facets) bdofsW1_V1 = locate_dofs_topological(W.sub(1), facetdim, bndry_facets) bcs = [dirichletbc(u0_bc, bdofsW0_V0, W.sub(0)), dirichletbc(u1_bc, bdofsW1_V1, W.sub(1))] A2 = assemble_matrix(a, bcs=bcs) A2.assemble() b2 = assemble_vector(L) apply_lifting(b2, [a], [bcs]) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b2, bcs) A2norm = A2.norm() b2norm = b2.norm() assert A2norm == pytest.approx(A0norm, 1.0e-12) assert b2norm == pytest.approx(b0norm, 1.0e-12) x2 = b2.copy() ksp = PETSc.KSP() ksp.create(mesh.comm) ksp.setMonitor(monitor) ksp.setOperators(A2) ksp.setType('cg') ksp.getPC().setType('jacobi') ksp.setTolerances(rtol=1.0e-12) ksp.setFromOptions() ksp.solve(b2, x2) x2norm = x2.norm() assert x2norm == pytest.approx(x0norm, 1.0e-10)