def test_mixed_element_vector_element_form(cell_type, sign, order): if cell_type == CellType.triangle or cell_type == CellType.quadrilateral: mesh = create_unit_square(MPI.COMM_WORLD, 2, 2, cell_type) else: mesh = create_unit_cube(MPI.COMM_WORLD, 2, 2, 2, cell_type) if cell_type == CellType.triangle: U_el = MixedElement([VectorElement("Lagrange", ufl.triangle, order), FiniteElement("N1curl", ufl.triangle, order)]) elif cell_type == CellType.quadrilateral: U_el = MixedElement([VectorElement("Lagrange", ufl.quadrilateral, order), FiniteElement("RTCE", ufl.quadrilateral, order)]) elif cell_type == CellType.tetrahedron: U_el = MixedElement([VectorElement("Lagrange", ufl.tetrahedron, order), FiniteElement("N1curl", ufl.tetrahedron, order)]) elif cell_type == CellType.hexahedron: U_el = MixedElement([VectorElement("Lagrange", ufl.hexahedron, order), FiniteElement("NCE", ufl.hexahedron, order)]) U = FunctionSpace(mesh, U_el) u, p = ufl.TrialFunctions(U) v, q = ufl.TestFunctions(U) f = form(inner(u, v) * ufl.dx + inner(p, q)(sign) * ufl.dS) A = dolfinx.fem.assemble_matrix(f) A.assemble() check_symmetry(A)
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 = dolfinx.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 = dolfinx.Function(W.sub(0).collapse()) p_zero = dolfinx.Function(W.sub(1).collapse()) L0 = inner(f, v) * dx L1 = inner(p_zero, q) * dx L = L0 + L1 bdofsW0_P2_0 = dolfinx.fem.locate_dofs_topological( (W.sub(0), P2), facetdim, bndry_facets0) bdofsW0_P2_1 = dolfinx.fem.locate_dofs_topological( (W.sub(0), P2), facetdim, bndry_facets1) bc0 = dolfinx.DirichletBC(u0, bdofsW0_P2_0, W.sub(0)) bc1 = dolfinx.DirichletBC(u0, bdofsW0_P2_1, W.sub(0)) A = dolfinx.fem.assemble_matrix(a, [bc0, bc1]) A.assemble() P = dolfinx.fem.assemble_matrix(p_form, [bc0, bc1]) P.assemble() b = dolfinx.fem.assemble_vector(L) dolfinx.fem.apply_lifting(b, [a], [[bc0, bc1]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.set_bc(b, [bc0, bc1]) ksp = PETSc.KSP() ksp.create(mesh.mpi_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 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 = UnitSquareMesh(MPI.COMM_WORLD, 32, 31, ghost_mode=mode) P = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) V0 = dolfinx.fem.FunctionSpace(mesh, P) V1 = V0.clone() def boundary(x): return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6) # Locate facets on boundary facetdim = mesh.topology.dim - 1 bndry_facets = dolfinx.mesh.locate_entities_boundary( mesh, facetdim, boundary) bdofsV0 = dolfinx.fem.locate_dofs_topological(V0, facetdim, bndry_facets) bdofsV1 = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets) u_bc0 = dolfinx.fem.Function(V0) u_bc0.vector.set(50.0) u_bc0.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) u_bc1 = dolfinx.fem.Function(V1) u_bc1.vector.set(20.0) u_bc1.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bcs = [ dolfinx.fem.dirichletbc.DirichletBC(u_bc0, bdofsV0), dolfinx.fem.dirichletbc.DirichletBC(u_bc1, bdofsV1) ] # 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 = dolfinx.Function(V0) a00 = inner(u, v) * dx a01 = zero * inner(p, v) * dx a10 = zero * inner(u, q) * dx a11 = inner(p, q) * dx L0 = inner(f, v) * dx L1 = inner(g, q) * dx def monitor(ksp, its, rnorm): pass # print("Norm:", its, rnorm) A0 = dolfinx.fem.assemble_matrix_block([[a00, a01], [a10, a11]], bcs) b0 = dolfinx.fem.assemble_vector_block([L0, L1], [[a00, a01], [a10, a11]], bcs) A0.assemble() A0norm = A0.norm() b0norm = b0.norm() x0 = A0.createVecLeft() ksp = PETSc.KSP() ksp.create(mesh.mpi_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 = dolfinx.fem.assemble_matrix_nest([[a00, a01], [a10, a11]], bcs, diagonal=1.0) A1.assemble() b1 = dolfinx.fem.assemble_vector_nest([L0, L1]) dolfinx.fem.apply_lifting_nest(b1, [[a00, a01], [a10, a11]], bcs) for b_sub in b1.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = dolfinx.cpp.fem.bcs_rows( dolfinx.fem.assemble._create_cpp_form([L0, L1]), bcs) dolfinx.fem.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.mpi_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 = dolfinx.fem.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 u0_bc = dolfinx.fem.Function(V0) u0_bc.vector.set(50.0) u0_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) u1_bc = dolfinx.fem.Function(V1) u1_bc.vector.set(20.0) u1_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bdofsW0_V0 = dolfinx.fem.locate_dofs_topological((W.sub(0), V0), facetdim, bndry_facets) bdofsW1_V1 = dolfinx.fem.locate_dofs_topological((W.sub(1), V1), facetdim, bndry_facets) bcs = [ dolfinx.fem.dirichletbc.DirichletBC(u0_bc, bdofsW0_V0, W.sub(0)), dolfinx.fem.dirichletbc.DirichletBC(u1_bc, bdofsW1_V1, W.sub(1)) ] A2 = dolfinx.fem.assemble_matrix(a, bcs) A2.assemble() b2 = dolfinx.fem.assemble_vector(L) dolfinx.fem.apply_lifting(b2, [a], [bcs]) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.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.mpi_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)
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 = UnitSquareMesh(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 = dolfinx.fem.FunctionSpace(mesh, P0) V1 = dolfinx.fem.FunctionSpace(mesh, P1) def boundary(x): return numpy.logical_or(x[0] < 1.0e-6, x[0] > 1.0 - 1.0e-6) # Locate facets on boundary facetdim = mesh.topology.dim - 1 bndry_facets = dolfinx.mesh.locate_entities_boundary( mesh, facetdim, boundary) bdofsV1 = dolfinx.fem.locate_dofs_topological(V1, facetdim, bndry_facets) u_bc = dolfinx.fem.Function(V1) with u_bc.vector.localForm() as u_local: u_local.set(50.0) bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofsV1) # 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 = dolfinx.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 = [[a00, a01], [a10, a11]] L_block = [L0, L1] # Monolithic blocked A0 = dolfinx.fem.assemble_matrix_block(a_block, [bc]) A0.assemble() b0 = dolfinx.fem.assemble_vector_block(L_block, a_block, [bc]) assert A0.getType() != "nest" Anorm0 = A0.norm() bnorm0 = b0.norm() # Nested (MatNest) A1 = dolfinx.fem.assemble_matrix_nest(a_block, [bc], [["baij", "aij"], ["aij", ""]]) A1.assemble() Anorm1 = nest_matrix_norm(A1) assert Anorm0 == pytest.approx(Anorm1, 1.0e-12) b1 = dolfinx.fem.assemble_vector_nest(L_block) dolfinx.fem.apply_lifting_nest(b1, a_block, [bc]) for b_sub in b1.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = dolfinx.cpp.fem.bcs_rows( dolfinx.fem.assemble._create_cpp_form(L_block), [bc]) dolfinx.fem.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 = dolfinx.fem.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 bdofsW_V1 = dolfinx.fem.locate_dofs_topological( (W.sub(1), V1), mesh.topology.dim - 1, bndry_facets) bc = dolfinx.fem.dirichletbc.DirichletBC(u_bc, bdofsW_V1, W.sub(1)) A2 = dolfinx.fem.assemble_matrix(a, [bc]) A2.assemble() b2 = dolfinx.fem.assemble_vector(L) dolfinx.fem.apply_lifting(b2, [a], [[bc]]) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.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)
# Since for this problem the pressure is only determined up to a # constant, we pin the pressure at the point (0, 0) zero = Function(Q) with zero.vector.localForm() as zero_local: zero_local.set(0.0) dofs = locate_dofs_geometrical((W.sub(1), Q), lambda x: np.isclose(x.T, [0, 0, 0]).all(axis=1)) bc2 = DirichletBC(zero, dofs, W.sub(1)) # Collect Dirichlet boundary conditions bcs = [bc0, bc1, bc2] # Define variational problem (u, p) = ufl.TrialFunctions(W) (v, q) = ufl.TestFunctions(W) f = Function(W0) zero = dolfinx.Constant(mesh, 0.0) a = (inner(grad(u), grad(v)) + inner(p, div(v)) + inner(div(u), q)) * dx L = inner(f, v) * dx # Assemble LHS matrix and RHS vector A = dolfinx.fem.assemble_matrix(a, bcs) A.assemble() b = dolfinx.fem.assemble.assemble_vector(L) dolfinx.fem.assemble.apply_lifting(b, [a], [bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) # Set Dirichlet boundary condition values in the RHS
def test_assembly_solve_taylor_hood(mesh): """Assemble Stokes problem with Taylor-Hood elements and solve.""" P2 = function.VectorFunctionSpace(mesh, ("Lagrange", 2)) P1 = function.FunctionSpace(mesh, ("Lagrange", 1)) def boundary0(x): """Define boundary x = 0""" return x[0] < 10 * numpy.finfo(float).eps def boundary1(x): """Define boundary x = 1""" return x[0] > (1.0 - 10 * numpy.finfo(float).eps) facetdim = mesh.topology.dim - 1 mf = dolfinx.MeshFunction("size_t", mesh, facetdim, 0) mf.mark(boundary0, 1) mf.mark(boundary1, 2) bndry_facets0 = numpy.where(mf.values == 1)[0] bndry_facets1 = numpy.where(mf.values == 2)[0] bdofs0 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets0) bdofs1 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets1) u0 = dolfinx.Function(P2) u0.vector.set(1.0) u0.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bc0 = dolfinx.DirichletBC(u0, bdofs0) bc1 = dolfinx.DirichletBC(u0, bdofs1) u, p = ufl.TrialFunction(P2), ufl.TrialFunction(P1) v, q = ufl.TestFunction(P2), ufl.TestFunction(P1) a00 = inner(ufl.grad(u), ufl.grad(v)) * dx a01 = ufl.inner(p, ufl.div(v)) * dx a10 = ufl.inner(ufl.div(u), q) * dx a11 = None p00 = a00 p01, p10 = None, None p11 = inner(p, q) * dx # FIXME # We need zero function for the 'zero' part of L p_zero = dolfinx.Function(P1) f = dolfinx.Function(P2) L0 = ufl.inner(f, v) * dx L1 = ufl.inner(p_zero, q) * dx # -- Blocked (nested) A0 = dolfinx.fem.assemble_matrix_nest([[a00, a01], [a10, a11]], [bc0, bc1]) A0.assemble() A0norm = nest_matrix_norm(A0) P0 = dolfinx.fem.assemble_matrix_nest([[p00, p01], [p10, p11]], [bc0, bc1]) P0.assemble() P0norm = nest_matrix_norm(P0) b0 = dolfinx.fem.assemble_vector_nest([L0, L1]) dolfinx.fem.apply_lifting_nest(b0, [[a00, a01], [a10, a11]], [bc0, bc1]) for b_sub in b0.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = dolfinx.cpp.fem.bcs_rows( dolfinx.fem.assemble._create_cpp_form([L0, L1]), [bc0, bc1]) dolfinx.fem.set_bc_nest(b0, bcs0) b0.assemble() b0norm = b0.norm() ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setOperators(A0, P0) nested_IS = P0.getNestISs() ksp.setType("minres") pc = ksp.getPC() pc.setType("fieldsplit") pc.setFieldSplitIS(["u", nested_IS[0][0]], ["p", nested_IS[1][1]]) ksp_u, ksp_p = pc.getFieldSplitSubKSP() ksp_u.setType("preonly") ksp_u.getPC().setType('lu') ksp_u.getPC().setFactorSolverType('mumps') ksp_p.setType("preonly") 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() x0 = b0.copy() ksp.solve(b0, x0) assert ksp.getConvergedReason() > 0 # -- Blocked (monolithic) A1 = dolfinx.fem.assemble_matrix_block([[a00, a01], [a10, a11]], [bc0, bc1]) A1.assemble() assert A1.norm() == pytest.approx(A0norm, 1.0e-12) P1 = dolfinx.fem.assemble_matrix_block([[p00, p01], [p10, p11]], [bc0, bc1]) P1.assemble() assert P1.norm() == pytest.approx(P0norm, 1.0e-12) b1 = dolfinx.fem.assemble_vector_block([L0, L1], [[a00, a01], [a10, a11]], [bc0, bc1]) assert b1.norm() == pytest.approx(b0norm, 1.0e-12) ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setOperators(A1, P1) ksp.setType("minres") pc = ksp.getPC() pc.setType('lu') pc.setFactorSolverType('mumps') ksp.setTolerances(rtol=1.0e-8, max_it=50) ksp.setFromOptions() x1 = A1.createVecRight() ksp.solve(b1, x1) assert ksp.getConvergedReason() > 0 assert x1.norm() == pytest.approx(x0.norm(), 1e-8) # -- Monolithic P2_el = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2) P1_el = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) TH = P2_el * P1_el W = dolfinx.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 = dolfinx.Function(W.sub(0).collapse()) p_zero = dolfinx.Function(W.sub(1).collapse()) L0 = inner(f, v) * dx L1 = inner(p_zero, q) * dx L = L0 + L1 bdofsW0_P2_0 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets0) bdofsW0_P2_1 = dolfinx.fem.locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets1) bc0 = dolfinx.DirichletBC(u0, bdofsW0_P2_0, W.sub(0)) bc1 = dolfinx.DirichletBC(u0, bdofsW0_P2_1, W.sub(0)) A2 = dolfinx.fem.assemble_matrix(a, [bc0, bc1]) A2.assemble() assert A2.norm() == pytest.approx(A0norm, 1.0e-12) P2 = dolfinx.fem.assemble_matrix(p_form, [bc0, bc1]) P2.assemble() assert P2.norm() == pytest.approx(P0norm, 1.0e-12) b2 = dolfinx.fem.assemble_vector(L) dolfinx.fem.apply_lifting(b2, [a], [[bc0, bc1]]) b2.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.set_bc(b2, [bc0, bc1]) b2norm = b2.norm() assert b2norm == pytest.approx(b0norm, 1.0e-12) ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setOperators(A2, P2) ksp.setType("minres") pc = ksp.getPC() pc.setType('lu') pc.setFactorSolverType('mumps') 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() x2 = A2.createVecRight() ksp.solve(b2, x2) assert ksp.getConvergedReason() > 0 assert x0.norm() == pytest.approx(x2.norm(), 1e-8)
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 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)
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)
# r=3 # x_extents = mesh_bounding_box(mesh, 0) # y_extents = mesh_bounding_box(mesh, 1) # Measures dx = ufl.Measure("dx", domain=mesh) ds = ufl.Measure("ds", domain=mesh) element = ufl.MixedElement([ufl.FiniteElement("Regge", ufl.triangle, r), ufl.FiniteElement("Lagrange", ufl.triangle, r+1)]) V = FunctionSpace(mesh, element) V_1 = V.sub(1).collapse() sigma, u = ufl.TrialFunctions(V) tau, v = ufl.TestFunctions(V) def S(tau): return tau - ufl.Identity(2) * ufl.tr(tau) # Discrete duality inner product # cf. 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 sigma_S = S(sigma) tau_S = S(tau)
def test_mixed_element(cell_type, ghost_mode): N = 4 mesh = dolfinx.mesh.create_unit_square( MPI.COMM_WORLD, N, N, cell_type=cell_type, ghost_mode=ghost_mode) # Inlet velocity Dirichlet BC bc_facets = dolfinx.mesh.locate_entities_boundary( mesh, mesh.topology.dim - 1, lambda x: np.isclose(x[0], 0.0)) other_facets = dolfinx.mesh.locate_entities_boundary( mesh, mesh.topology.dim - 1, lambda x: np.isclose(x[0], 1.0)) arg_sort = np.argsort(other_facets) mt = dolfinx.mesh.meshtags(mesh, mesh.topology.dim - 1, other_facets[arg_sort], np.full_like(other_facets, 1)) # Rotate the mesh to induce more interesting slip BCs th = np.pi / 4.0 rot = np.array([[np.cos(th), -np.sin(th)], [np.sin(th), np.cos(th)]]) gdim = mesh.geometry.dim mesh.geometry.x[:, :gdim] = (rot @ mesh.geometry.x[:, :gdim].T).T # Create the function space Ve = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2) Qe = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) V = dolfinx.fem.FunctionSpace(mesh, Ve) Q = dolfinx.fem.FunctionSpace(mesh, Qe) W = dolfinx.fem.FunctionSpace(mesh, Ve * Qe) inlet_velocity = dolfinx.fem.Function(V) inlet_velocity.interpolate( lambda x: np.zeros((mesh.geometry.dim, x[0].shape[0]), dtype=np.double)) inlet_velocity.x.scatter_forward() # -- Nested assembly dofs = dolfinx.fem.locate_dofs_topological(V, 1, bc_facets) bc1 = dolfinx.fem.dirichletbc(inlet_velocity, dofs) # Collect Dirichlet boundary conditions bcs = [bc1] mpc_v = dolfinx_mpc.MultiPointConstraint(V) n_approx = dolfinx_mpc.utils.create_normal_approximation(V, mt, 1) mpc_v.create_slip_constraint(V, (mt, 1), n_approx, bcs=bcs) mpc_v.finalize() mpc_q = dolfinx_mpc.MultiPointConstraint(Q) mpc_q.finalize() f = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0))) (u, p) = ufl.TrialFunction(V), ufl.TrialFunction(Q) (v, q) = ufl.TestFunction(V), ufl.TestFunction(Q) a00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx a01 = - ufl.inner(p, ufl.div(v)) * ufl.dx a10 = - ufl.inner(ufl.div(u), q) * ufl.dx a11 = None L0 = ufl.inner(f, v) * ufl.dx L1 = ufl.inner( dolfinx.fem.Constant(mesh, PETSc.ScalarType(0.0)), q) * ufl.dx n = ufl.FacetNormal(mesh) g_tau = ufl.as_vector((0.0, 0.0)) ds = ufl.Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=1) a00 -= ufl.inner(ufl.outer(n, n) * ufl.dot(ufl.grad(u), n), v) * ds a01 -= ufl.inner(ufl.outer(n, n) * ufl.dot( - p * ufl.Identity(u.ufl_shape[0]), n), v) * ds L0 += ufl.inner(g_tau, v) * ds a_nest = dolfinx.fem.form(((a00, a01), (a10, a11))) L_nest = dolfinx.fem.form((L0, L1)) # Assemble MPC nest matrix A_nest = dolfinx_mpc.create_matrix_nest(a_nest, [mpc_v, mpc_q]) dolfinx_mpc.assemble_matrix_nest(A_nest, a_nest, [mpc_v, mpc_q], bcs) A_nest.assemble() # Assemble original nest matrix A_org_nest = dolfinx.fem.petsc.assemble_matrix_nest(a_nest, bcs) A_org_nest.assemble() # MPC nested rhs b_nest = dolfinx_mpc.create_vector_nest(L_nest, [mpc_v, mpc_q]) dolfinx_mpc.assemble_vector_nest(b_nest, L_nest, [mpc_v, mpc_q]) dolfinx.fem.petsc.apply_lifting_nest(b_nest, a_nest, bcs) for b_sub in b_nest.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs0 = dolfinx.fem.bcs_by_block( dolfinx.fem.extract_function_spaces(L_nest), bcs) dolfinx.fem.petsc.set_bc_nest(b_nest, bcs0) # Original dolfinx rhs b_org_nest = dolfinx.fem.petsc.assemble_vector_nest(L_nest) dolfinx.fem.petsc.apply_lifting_nest(b_org_nest, a_nest, bcs) for b_sub in b_org_nest.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.petsc.set_bc_nest(b_org_nest, bcs0) # -- Monolithic assembly dofs = dolfinx.fem.locate_dofs_topological((W.sub(0), V), 1, bc_facets) bc1 = dolfinx.fem.dirichletbc(inlet_velocity, dofs, W.sub(0)) bcs = [bc1] V, V_to_W = W.sub(0).collapse() mpc_vq = dolfinx_mpc.MultiPointConstraint(W) n_approx = dolfinx_mpc.utils.create_normal_approximation(V, mt, 1) mpc_vq.create_slip_constraint(W.sub(0), (mt, 1), n_approx, bcs=bcs) mpc_vq.finalize() f = dolfinx.fem.Constant(mesh, PETSc.ScalarType((0, 0))) (u, p) = ufl.TrialFunctions(W) (v, q) = ufl.TestFunctions(W) a = ( ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx - ufl.inner(p, ufl.div(v)) * ufl.dx - ufl.inner(ufl.div(u), q) * ufl.dx ) L = ufl.inner(f, v) * ufl.dx + ufl.inner( dolfinx.fem.Constant(mesh, PETSc.ScalarType(0.0)), q) * ufl.dx # No prescribed shear stress n = ufl.FacetNormal(mesh) g_tau = ufl.as_vector((0.0, 0.0)) ds = ufl.Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=1) # Terms due to slip condition # Explained in for instance: https://arxiv.org/pdf/2001.10639.pdf a -= ufl.inner(ufl.outer(n, n) * ufl.dot(ufl.grad(u), n), v) * ds a -= ufl.inner(ufl.outer(n, n) * ufl.dot( - p * ufl.Identity(u.ufl_shape[0]), n), v) * ds L += ufl.inner(g_tau, v) * ds a, L = dolfinx.fem.form(a), dolfinx.fem.form(L) # Assemble LHS matrix and RHS vector A = dolfinx_mpc.assemble_matrix(a, mpc_vq, bcs) A.assemble() A_org = dolfinx.fem.petsc.assemble_matrix(a, bcs) A_org.assemble() b = dolfinx_mpc.assemble_vector(L, mpc_vq) b_org = dolfinx.fem.petsc.assemble_vector(L) # Set Dirichlet boundary condition values in the RHS dolfinx_mpc.apply_lifting(b, [a], [bcs], mpc_vq) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.petsc.set_bc(b, bcs) dolfinx.fem.petsc.apply_lifting(b_org, [a], [bcs]) b_org.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.petsc.set_bc(b_org, bcs) # -- Verification def nest_matrix_norm(A): assert A.getType() == "nest" nrows, ncols = A.getNestSize() sub_A = [A.getNestSubMatrix(row, col) for row in range(nrows) for col in range(ncols)] return sum(map(lambda A_: A_.norm()**2 if A_ else 0.0, sub_A))**0.5 # -- Ensure monolithic and nest matrices are the same assert np.isclose(nest_matrix_norm(A_nest), A.norm())