def test_nonlinear_pde(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = create_unit_square(MPI.COMM_WORLD, 12, 5) V = FunctionSpace(mesh, ("Lagrange", 1)) u = Function(V) v = TestFunction(V) F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx bc = dirichletbc( PETSc.ScalarType(1.0), locate_dofs_geometrical( V, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))), V) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve u.x.array[:] = 0.9 solver = _cpp.nls.petsc.NewtonSolver(MPI.COMM_WORLD) solver.setF(problem.F, problem.vector()) solver.setJ(problem.J, problem.matrix()) solver.set_form(problem.form) n, converged = solver.solve(u.vector) assert converged assert n < 6 # Modify boundary condition and solve again bc.g.value[...] = 0.5 n, converged = solver.solve(u.vector) assert converged assert n > 0 and n < 6
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 test_linear_pde(): """Test Newton solver for a linear PDE""" # Create mesh and function space mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 12, 12) V = function.FunctionSpace(mesh, ("Lagrange", 1)) u = function.Function(V) v = TestFunction(V) F = inner(10.0, v) * dx - inner(grad(u), grad(v)) * dx def boundary(x): """Define Dirichlet boundary (x = 0 or x = 1).""" return np.logical_or(x[0] < 1.0e-8, x[0] > 1.0 - 1.0e-8) u_bc = function.Function(V) u_bc.vector.set(1.0) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bc = fem.DirichletBC(u_bc, fem.locate_dofs_geometrical(V, boundary)) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve solver = dolfinx.cpp.nls.NewtonSolver(MPI.COMM_WORLD) n, converged = solver.solve(problem, u.vector) assert converged assert n == 1 # Increment boundary condition and solve again u_bc.vector.set(2.0) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) n, converged = solver.solve(problem, u.vector) assert converged assert n == 1
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_linear_pde(): """Test Newton solver for a linear PDE""" # Create mesh and function space mesh = create_unit_square(MPI.COMM_WORLD, 12, 12) V = FunctionSpace(mesh, ("Lagrange", 1)) u = Function(V) v = TestFunction(V) F = inner(10.0, v) * dx - inner(grad(u), grad(v)) * dx bc = dirichletbc(PETSc.ScalarType(1.0), locate_dofs_geometrical(V, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))), V) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve solver = _cpp.nls.NewtonSolver(MPI.COMM_WORLD) solver.setF(problem.F, problem.vector()) solver.setJ(problem.J, problem.matrix()) solver.set_form(problem.form) n, converged = solver.solve(u.vector) assert converged assert n == 1 # Increment boundary condition and solve again bc.g.value[...] = PETSc.ScalarType(2.0) n, converged = solver.solve(u.vector) assert converged assert n == 1
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 test_nonlinear_pde_snes(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 12, 15) V = function.FunctionSpace(mesh, ("Lagrange", 1)) u = function.Function(V) v = TestFunction(V) F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx def boundary(x): """Define Dirichlet boundary (x = 0 or x = 1).""" return np.logical_or(x[0] < 1.0e-8, x[0] > 1.0 - 1.0e-8) u_bc = function.Function(V) u_bc.vector.set(1.0) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bc = fem.DirichletBC(u_bc, fem.locate_dofs_geometrical(V, boundary)) # Create nonlinear problem problem = NonlinearPDE_SNESProblem(F, u, bc) u.vector.set(0.9) u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) b = dolfinx.cpp.la.create_vector(V.dofmap.index_map) J = dolfinx.cpp.fem.create_matrix(problem.a_comp._cpp_object) # Create Newton solver and solve snes = PETSc.SNES().create() snes.setFunction(problem.F, b) snes.setJacobian(problem.J, J) snes.setTolerances(rtol=1.0e-9, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().setTolerances(rtol=1.0e-9) snes.getKSP().getPC().setType("lu") snes.getKSP().getPC().setFactorSolverType("superlu_dist") snes.solve(None, u.vector) assert snes.getConvergedReason() > 0 assert snes.getIterationNumber() < 6 # Modify boundary condition and solve again u_bc.vector.set(0.5) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) snes.solve(None, u.vector) assert snes.getConvergedReason() > 0 assert snes.getIterationNumber() < 6
def test_locate_dofs_geometrical(): """Test that locate_dofs_geometrical, when passed two function spaces, returns the correct degrees of freedom in each space""" 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) W = FunctionSpace(mesh, P0 * P1) V = W.sub(0).collapse()[0] with pytest.raises(RuntimeError): locate_dofs_geometrical( W, lambda x: np.isclose(x.T, [0, 0, 0]).all(axis=1)) dofs = locate_dofs_geometrical( (W.sub(0), V), lambda x: np.isclose(x.T, [0, 0, 0]).all(axis=1)) # Collect dofs (global indices) from all processes dofs0_global = W.sub(0).dofmap.index_map.local_to_global(dofs[0]) dofs1_global = V.dofmap.index_map.local_to_global(dofs[1]) all_dofs0 = set(np.concatenate(MPI.COMM_WORLD.allgather(dofs0_global))) all_dofs1 = set(np.concatenate(MPI.COMM_WORLD.allgather(dofs1_global))) # Check only one dof pair is found globally assert len(all_dofs0) == 1 assert len(all_dofs1) == 1 # On process with the dof pair if len(dofs) == 1: # Check correct dof returned in W coords_W = W.tabulate_dof_coordinates() assert np.isclose(coords_W[dofs[0][0]], [0, 0, 0]).all() # Check correct dof returned in V coords_V = V.tabulate_dof_coordinates() assert np.isclose(coords_V[dofs[0][1]], [0, 0, 0]).all()
def test_newton_solver_inheritance_override_methods(): import functools called_methods = {} def check_is_called(method): @functools.wraps(method) def wrapper(*args, **kwargs): called_methods[method.__name__] = True return method(*args, **kwargs) return wrapper class CustomNewtonSolver(dolfinx.cpp.nls.NewtonSolver): def __init__(self, comm): super().__init__(comm) @check_is_called def update_solution(self, x, dx, relaxation, problem, it): return super().update_solution(x, dx, relaxation, problem, it) @check_is_called def converged(self, r, problem, it): return super().converged(r, problem, it) mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 12, 12) V = function.FunctionSpace(mesh, ("Lagrange", 1)) u = function.Function(V) v = TestFunction(V) F = inner(10.0, v) * dx - inner(grad(u), grad(v)) * dx def boundary(x): """Define Dirichlet boundary (x = 0 or x = 1).""" return np.logical_or(x[0] < 1.0e-8, x[0] > 1.0 - 1.0e-8) u_bc = function.Function(V) bc = fem.DirichletBC(u_bc, fem.locate_dofs_geometrical(V, boundary)) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve solver = CustomNewtonSolver(MPI.COMM_WORLD) n, converged = solver.solve(problem, u.vector) assert called_methods[CustomNewtonSolver.converged.__name__] assert called_methods[CustomNewtonSolver.update_solution.__name__]
def test_nonlinear_pde_snes(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = create_unit_square(MPI.COMM_WORLD, 12, 15) V = FunctionSpace(mesh, ("Lagrange", 1)) u = Function(V) v = TestFunction(V) F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx u_bc = Function(V) u_bc.x.array[:] = 1.0 bc = dirichletbc( u_bc, locate_dofs_geometrical( V, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0)))) # Create nonlinear problem problem = NonlinearPDE_SNESProblem(F, u, bc) u.x.array[:] = 0.9 b = la.create_petsc_vector(V.dofmap.index_map, V.dofmap.index_map_bs) J = create_matrix(problem.a) # Create Newton solver and solve snes = PETSc.SNES().create() snes.setFunction(problem.F, b) snes.setJacobian(problem.J, J) snes.setTolerances(rtol=1.0e-9, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().setTolerances(rtol=1.0e-9) snes.getKSP().getPC().setType("lu") snes.solve(None, u.vector) assert snes.getConvergedReason() > 0 assert snes.getIterationNumber() < 6 # Modify boundary condition and solve again u_bc.x.array[:] = 0.6 snes.solve(None, u.vector) assert snes.getConvergedReason() > 0 assert snes.getIterationNumber() < 6
def test_nonlinear_pde(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = dolfinx.generation.UnitSquareMesh(dolfinx.MPI.comm_world, 12, 5) V = function.FunctionSpace(mesh, ("Lagrange", 1)) u = dolfinx.function.Function(V) v = TestFunction(V) F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx def boundary(x): """Define Dirichlet boundary (x = 0 or x = 1).""" return np.logical_or(x[0] < 1.0e-8, x[0] > 1.0 - 1.0e-8) u_bc = function.Function(V) u_bc.vector.set(1.0) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bc = fem.DirichletBC(u_bc, fem.locate_dofs_geometrical(V, boundary)) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve u.vector.set(0.9) u.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) solver = dolfinx.cpp.nls.NewtonSolver(dolfinx.MPI.comm_world) n, converged = solver.solve(problem, u.vector) assert converged assert n < 6 # Modify boundary condition and solve again u_bc.vector.set(0.5) u_bc.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) n, converged = solver.solve(problem, u.vector) assert converged assert n < 6
def test_nonlinear_pde(): """Test Newton solver for a simple nonlinear PDE""" # Create mesh and function space mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 12, 5) V = fem.FunctionSpace(mesh, ("Lagrange", 1)) u = dolfinx.fem.Function(V) v = TestFunction(V) F = inner(5.0, v) * dx - ufl.sqrt(u * u) * inner( grad(u), grad(v)) * dx - inner(u, v) * dx def boundary(x): """Define Dirichlet boundary (x = 0 or x = 1).""" return np.logical_or(x[0] < 1.0e-8, x[0] > 1.0 - 1.0e-8) u_bc = fem.Function(V) with u_bc.vector.localForm() as u_local: u_local.set(1.0) bc = fem.DirichletBC(u_bc, fem.locate_dofs_geometrical(V, boundary)) # Create nonlinear problem problem = NonlinearPDEProblem(F, u, bc) # Create Newton solver and solve with u.vector.localForm() as u_local: u_local.set(0.9) solver = dolfinx.cpp.nls.NewtonSolver(MPI.COMM_WORLD) solver.setF(problem.F, problem.vector()) solver.setJ(problem.J, problem.matrix()) solver.set_form(problem.form) n, converged = solver.solve(u.vector) assert converged assert n < 6 # Modify boundary condition and solve again with u_bc.vector.localForm() as u_local: u_local.set(0.5) n, converged = solver.solve(u.vector) assert converged assert n < 6
# Driving velocity condition u = (1, 0) on top boundary (y = 1) lid_velocity = Function(W0) lid_velocity.interpolate(lid_velocity_expression) facets = locate_entities_boundary(mesh, 1, lid) dofs = locate_dofs_topological((W.sub(0), V), 1, facets) bc1 = DirichletBC(lid_velocity, dofs, W.sub(0)) # 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)
def test_lifting(get_assemblers): # noqa: F811 """ Test MPC lifting operation on a single cell """ assemble_matrix, assemble_vector = get_assemblers # Create mesh and function space mesh = create_unit_square(MPI.COMM_WORLD, 1, 1, CellType.quadrilateral) V = fem.FunctionSpace(mesh, ("Lagrange", 1)) # Solve Problem without MPC for reference u = ufl.TrialFunction(V) v = ufl.TestFunction(V) x = ufl.SpatialCoordinate(mesh) f = x[1] * ufl.sin(2 * ufl.pi * x[0]) a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx rhs = ufl.inner(f, v) * ufl.dx bilinear_form = fem.form(a) linear_form = fem.form(rhs) # Create Dirichlet boundary condition u_bc = fem.Function(V) with u_bc.vector.localForm() as u_local: u_local.set(2.3) def dirichletboundary(x): return np.isclose(x[0], 1) mesh.topology.create_connectivity(2, 1) geometrical_dofs = fem.locate_dofs_geometrical(V, dirichletboundary) bc = fem.dirichletbc(u_bc, geometrical_dofs) bcs = [bc] # Generate reference matrices A_org = fem.petsc.assemble_matrix(bilinear_form, bcs=bcs) A_org.assemble() L_org = fem.petsc.assemble_vector(linear_form) fem.petsc.apply_lifting(L_org, [bilinear_form], [bcs]) L_org.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) fem.petsc.set_bc(L_org, bcs) # Create multipoint constraint def l2b(li): return np.array(li, dtype=np.float64).tobytes() s_m_c = {l2b([0, 0]): {l2b([0, 1]): 1}} mpc = dolfinx_mpc.MultiPointConstraint(V) mpc.create_general_constraint(s_m_c) mpc.finalize() A = assemble_matrix(bilinear_form, mpc, bcs=bcs) b = assemble_vector(linear_form, mpc) dolfinx_mpc.apply_lifting(b, [bilinear_form], [bcs], mpc) b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) fem.petsc.set_bc(b, bcs) solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A) # Solve uh = b.copy() uh.set(0) solver.solve(b, uh) uh.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) mpc.backsubstitution(uh) V_mpc = mpc.function_space u_out = fem.Function(V_mpc) u_out.vector.array[:] = uh.array root = 0 comm = mesh.comm with Timer("~TEST: Compare"): dolfinx_mpc.utils.compare_mpc_lhs(A_org, A, mpc, root=root) dolfinx_mpc.utils.compare_mpc_rhs(L_org, b, mpc, root=root) # Gather LHS, RHS and solution on one process A_csr = dolfinx_mpc.utils.gather_PETScMatrix(A_org, root=root) K = dolfinx_mpc.utils.gather_transformation_matrix(mpc, root=root) L_np = dolfinx_mpc.utils.gather_PETScVector(L_org, root=root) u_mpc = dolfinx_mpc.utils.gather_PETScVector(uh, root=root) # constants = dolfinx_mpc.utils.gather_contants(mpc, root=root) if MPI.COMM_WORLD.rank == root: KTAK = K.T * A_csr * K reduced_L = K.T @ (L_np) # - constants) # Solve linear system d = scipy.sparse.linalg.spsolve(KTAK, reduced_L) # Back substitution to full solution vecto uh_numpy = K @ (d) # + constants) assert np.allclose(uh_numpy, u_mpc) list_timings(comm, [TimingType.wall])
# Create function space V = VectorFunctionSpace(mesh, ("Lagrange", 1)) # Define variational problem u = TrialFunction(V) v = TestFunction(V) 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)
def demo_periodic3D(celltype: CellType): # Create mesh and finite element if celltype == CellType.tetrahedron: # Tet setup N = 10 mesh = create_unit_cube(MPI.COMM_WORLD, N, N, N) V = fem.VectorFunctionSpace(mesh, ("CG", 1)) else: # Hex setup N = 10 mesh = create_unit_cube(MPI.COMM_WORLD, N, N, N, CellType.hexahedron) V = fem.VectorFunctionSpace(mesh, ("CG", 2)) def dirichletboundary(x: NDArray[np.float64]) -> NDArray[np.bool_]: return np.logical_or( np.logical_or(np.isclose(x[1], 0), np.isclose(x[1], 1)), np.logical_or(np.isclose(x[2], 0), np.isclose(x[2], 1))) # Create Dirichlet boundary condition zero = PETSc.ScalarType([0, 0, 0]) geometrical_dofs = fem.locate_dofs_geometrical(V, dirichletboundary) bc = fem.dirichletbc(zero, geometrical_dofs, V) bcs = [bc] def PeriodicBoundary(x): return np.isclose(x[0], 1) facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, PeriodicBoundary) arg_sort = np.argsort(facets) mt = meshtags(mesh, mesh.topology.dim - 1, facets[arg_sort], np.full(len(facets), 2, dtype=np.int32)) def periodic_relation(x): out_x = np.zeros(x.shape) out_x[0] = 1 - x[0] out_x[1] = x[1] out_x[2] = x[2] return out_x with Timer("~~Periodic: Compute mpc condition"): mpc = dolfinx_mpc.MultiPointConstraint(V) mpc.create_periodic_constraint_topological(V.sub(0), mt, 2, periodic_relation, bcs, 1) mpc.finalize() # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = inner(grad(u), grad(v)) * dx x = SpatialCoordinate(mesh) dx_ = x[0] - 0.9 dy_ = x[1] - 0.5 dz_ = x[2] - 0.1 f = as_vector((x[0] * sin(5.0 * pi * x[1]) + 1.0 * exp(-(dx_ * dx_ + dy_ * dy_ + dz_ * dz_) / 0.02), 0.1 * dx_ * dz_, 0.1 * dx_ * dy_)) rhs = inner(f, v) * dx petsc_options: Dict[str, Union[str, float, int]] if complex_mode: rtol = 1e-16 petsc_options = {"ksp_type": "preonly", "pc_type": "lu"} else: rtol = 1e-8 petsc_options = { "ksp_type": "cg", "ksp_rtol": rtol, "pc_type": "hypre", "pc_hypre_typ": "boomeramg", "pc_hypre_boomeramg_max_iter": 1, "pc_hypre_boomeramg_cycle_type": "v", "pc_hypre_boomeramg_print_statistics": 1 } problem = LinearProblem(a, rhs, mpc, bcs, petsc_options=petsc_options) u_h = problem.solve() # --------------------VERIFICATION------------------------- print("----Verification----") u_ = fem.Function(V) u_.x.array[:] = 0 org_problem = fem.petsc.LinearProblem(a, rhs, u=u_, bcs=bcs, petsc_options=petsc_options) with Timer("~Periodic: Unconstrained solve"): org_problem.solve() it = org_problem.solver.getIterationNumber() print(f"Unconstrained solver iterations: {it}") # Write solutions to file ext = "tet" if celltype == CellType.tetrahedron else "hex" u_.name = "u_" + ext + "_unconstrained" # NOTE: Workaround as tabulate dof coordinates does not like extra ghosts u_out = fem.Function(V) old_local = u_out.x.map.size_local * u_out.x.bs old_ghosts = u_out.x.map.num_ghosts * u_out.x.bs mpc_local = u_h.x.map.size_local * u_h.x.bs assert (old_local == mpc_local) u_out.x.array[:old_local + old_ghosts] = u_h.x.array[:mpc_local + old_ghosts] u_out.name = "u_" + ext fname = f"results/demo_periodic3d_{ext}.bp" out_periodic = VTXWriter(MPI.COMM_WORLD, fname, u_out) out_periodic.write(0) out_periodic.close() root = 0 with Timer("~Demo: Verification"): dolfinx_mpc.utils.compare_mpc_lhs(org_problem.A, problem.A, mpc, root=root) dolfinx_mpc.utils.compare_mpc_rhs(org_problem.b, problem.b, mpc, root=root) # Gather LHS, RHS and solution on one process A_csr = dolfinx_mpc.utils.gather_PETScMatrix(org_problem.A, root=root) K = dolfinx_mpc.utils.gather_transformation_matrix(mpc, root=root) L_np = dolfinx_mpc.utils.gather_PETScVector(org_problem.b, root=root) u_mpc = dolfinx_mpc.utils.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)
#FOR IMPOSED DISPLACEMENT : if parameters['loading']['type'] == 'ID': def ID_points(x): return np.logical_and( np.equal(x[1], h), np.logical_and(np.greater_equal(x[0], -1 * n), np.less_equal(x[0], n))) ID_entities = dolfinx.mesh.locate_entities_boundary(mesh, 0, ID_points) ID_dofs = dolfinx.fem.locate_dofs_topological(V_u, 0, ID_entities) u_imposed.interpolate(lambda x: (np.zeros_like(x[0]), -1 * parameters[ 'loading']['max'] * np.ones_like(x[1]))) bcs_u = [dirichletbc(u_, BC_dofs), dirichletbc(u_imposed, ID_dofs)] dofs_alpha_left = locate_dofs_geometrical(V_alpha, lambda x: np.isclose(x[0], -L / 2)) dofs_alpha_right = locate_dofs_geometrical(V_alpha, lambda x: np.isclose(x[0], L / 2)) BC_dofs_alpha = dolfinx.fem.locate_dofs_topological(V_alpha, 0, BC_entities) if parameters['loading']['type'] == 'IF': bcs_alpha = [ dirichletbc(np.array(0., dtype=PETSc.ScalarType), BC_dofs_alpha, V_alpha) ] if parameters['loading']['type'] == 'ID': ID_dofs_alpha = dolfinx.fem.locate_dofs_topological( V_alpha, 0, ID_entities) bcs_alpha = [ dirichletbc( np.array(0., dtype=PETSc.ScalarType), np.concatenate([
def test_vector_possion(Nx, Ny, slave_space, master_space, get_assemblers): # noqa: F811 assemble_matrix, assemble_vector = get_assemblers # Create mesh and function space mesh = create_unit_square(MPI.COMM_WORLD, Nx, Ny) V = fem.VectorFunctionSpace(mesh, ("Lagrange", 1)) def boundary(x): return np.isclose(x.T, [0, 0, 0]).all(axis=1) # Define boundary conditions (HAS TO BE NON-MASTER NODES) u_bc = fem.Function(V) with u_bc.vector.localForm() as u_local: u_local.set(0.0) bdofsV = fem.locate_dofs_geometrical(V, boundary) bc = fem.dirichletbc(u_bc, bdofsV) bcs = [bc] # Define variational problem u = ufl.TrialFunction(V) v = ufl.TestFunction(V) x = ufl.SpatialCoordinate(mesh) f = ufl.as_vector((-5 * x[1], 7 * x[0])) a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx rhs = ufl.inner(f, v) * ufl.dx bilinear_form = fem.form(a) linear_form = fem.form(rhs) # Setup LU solver solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) # Create multipoint constraint def l2b(li): return np.array(li, dtype=np.float64).tobytes() s_m_c = {l2b([1, 0]): {l2b([1, 1]): 0.1, l2b([0.5, 1]): 0.3}} mpc = dolfinx_mpc.MultiPointConstraint(V) mpc.create_general_constraint(s_m_c, slave_space, master_space) mpc.finalize() with Timer("~TEST: Assemble matrix"): A = assemble_matrix(bilinear_form, mpc, bcs=bcs) with Timer("~TEST: Assemble vector"): b = dolfinx_mpc.assemble_vector(linear_form, mpc) dolfinx_mpc.apply_lifting(b, [bilinear_form], [bcs], mpc) b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) fem.petsc.set_bc(b, bcs) solver.setOperators(A) uh = b.copy() uh.set(0) solver.solve(b, uh) uh.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) mpc.backsubstitution(uh) # Generate reference matrices for unconstrained problem A_org = fem.petsc.assemble_matrix(bilinear_form, bcs) A_org.assemble() L_org = fem.petsc.assemble_vector(linear_form) fem.petsc.apply_lifting(L_org, [bilinear_form], [bcs]) L_org.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) fem.petsc.set_bc(L_org, bcs) root = 0 comm = mesh.comm with Timer("~TEST: Compare"): dolfinx_mpc.utils.compare_mpc_lhs(A_org, A, mpc, root=root) dolfinx_mpc.utils.compare_mpc_rhs(L_org, b, mpc, root=root) # Gather LHS, RHS and solution on one process A_csr = dolfinx_mpc.utils.gather_PETScMatrix(A_org, root=root) K = dolfinx_mpc.utils.gather_transformation_matrix(mpc, root=root) L_np = dolfinx_mpc.utils.gather_PETScVector(L_org, root=root) u_mpc = dolfinx_mpc.utils.gather_PETScVector(uh, 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) list_timings(comm, [TimingType.wall])
def test_eigen_assembly(tempdir): # noqa: F811 """Compare assembly into scipy.CSR matrix with PETSc assembly""" def compile_eigen_csr_assembler_module(): dolfinx_pc = dolfinx.pkgconfig.parse("dolfinx") eigen_dir = dolfinx.pkgconfig.parse("eigen3")["include_dirs"] cpp_code_header = f""" <% setup_pybind11(cfg) cfg['include_dirs'] = {dolfinx_pc["include_dirs"] + [petsc4py.get_include()] + [pybind11.get_include()] + [str(pybind_inc())] + eigen_dir} cfg['compiler_args'] = ["-std=c++17", "-Wno-comment"] cfg['libraries'] = {dolfinx_pc["libraries"]} cfg['library_dirs'] = {dolfinx_pc["library_dirs"]} %> """ cpp_code = """ #include <pybind11/pybind11.h> #include <pybind11/eigen.h> #include <pybind11/stl.h> #include <vector> #include <Eigen/Sparse> #include <petscsys.h> #include <dolfinx/fem/assembler.h> #include <dolfinx/fem/DirichletBC.h> #include <dolfinx/fem/Form.h> template<typename T> Eigen::SparseMatrix<T, Eigen::RowMajor> assemble_csr(const dolfinx::fem::Form<T>& a, const std::vector<std::shared_ptr<const dolfinx::fem::DirichletBC<T>>>& bcs) { std::vector<Eigen::Triplet<T>> triplets; auto mat_add = [&triplets](const xtl::span<const std::int32_t>& rows, const xtl::span<const std::int32_t>& cols, const xtl::span<const T>& v) { for (std::size_t i = 0; i < rows.size(); ++i) for (std::size_t j = 0; j < cols.size(); ++j) triplets.emplace_back(rows[i], cols[j], v[i * cols.size() + j]); return 0; }; dolfinx::fem::assemble_matrix(mat_add, a, bcs); auto map0 = a.function_spaces().at(0)->dofmap()->index_map; int bs0 = a.function_spaces().at(0)->dofmap()->index_map_bs(); auto map1 = a.function_spaces().at(1)->dofmap()->index_map; int bs1 = a.function_spaces().at(1)->dofmap()->index_map_bs(); Eigen::SparseMatrix<T, Eigen::RowMajor> mat( bs0 * (map0->size_local() + map0->num_ghosts()), bs1 * (map1->size_local() + map1->num_ghosts())); mat.setFromTriplets(triplets.begin(), triplets.end()); return mat; } PYBIND11_MODULE(eigen_csr, m) { m.def("assemble_matrix", &assemble_csr<PetscScalar>); } """ path = pathlib.Path(tempdir) open(pathlib.Path(tempdir, "eigen_csr.cpp"), "w").write(cpp_code + cpp_code_header) rel_path = path.relative_to(pathlib.Path(__file__).parent) p = str(rel_path).replace("/", ".") + ".eigen_csr" return cppimport.imp(p) def assemble_csr_matrix(a, bcs): """Assemble bilinear form into an SciPy CSR matrix, in serial.""" module = compile_eigen_csr_assembler_module() A = module.assemble_matrix(a, bcs) if a.function_spaces[0] is a.function_spaces[1]: for bc in bcs: if a.function_spaces[0].contains(bc.function_space): bc_dofs, _ = bc.dof_indices() # See https://github.com/numpy/numpy/issues/14132 # for why we copy bc_dofs as a work-around dofs = bc_dofs.copy() A[dofs, dofs] = 1.0 return A mesh = create_unit_square(MPI.COMM_SELF, 12, 12) Q = FunctionSpace(mesh, ("Lagrange", 1)) u = ufl.TrialFunction(Q) v = ufl.TestFunction(Q) a = form(ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx) bdofsQ = locate_dofs_geometrical( Q, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0))) bc = dirichletbc(PETSc.ScalarType(1), bdofsQ, Q) A1 = assemble_matrix(a, [bc]) A1.assemble() A2 = assemble_csr_matrix(a, [bc]) assert np.isclose(A1.norm(), scipy.sparse.linalg.norm(A2))
def create_dictionary_constraint( V: fem.FunctionSpace, slave_master_dict: typing.Dict[bytes, typing.Dict[bytes, float]], subspace_slave: int = None, subspace_master: int = None): """ Returns a multi point constraint for a given function space and dictionary constraint. Parameters ---------- V The function space slave_master_dict The dictionary. subspace_slave If using mixed or vector space, and only want to use dofs from a sub space as slave add index here. subspace_master Subspace index for mixed or vector spaces Example ------- If the dof D located at [d0,d1] should be constrained to the dofs E and F at [e0,e1] and [f0,f1] as D = alpha E + beta F the dictionary should be: {np.array([d0, d1], dtype=numpy.float64).tobytes(): {numpy.array([e0, e1], dtype=numpy.float64).tobytes(): alpha, numpy.array([f0, f1], dtype=numpy.float64).tobytes(): beta}} """ dfloat = np.float64 comm = V.mesh.comm bs = V.dofmap.index_map_bs local_size = V.dofmap.index_map.size_local * bs index_map = V.dofmap.index_map owned_entities = {} ghosted_entities = {} non_local_entities = {} slaves_local = {} slaves_ghost = {} slave_point_nd = np.zeros((3, 1), dtype=dfloat) for i, slave_point in enumerate(slave_master_dict.keys()): num_masters = len(list(slave_master_dict[slave_point].keys())) # Status for current slave, -1 if not on proc, 0 if ghost, 1 if owned slave_status = -1 # Wrap slave point as numpy array sp = np.frombuffer(slave_point, dtype=dfloat) for j, coord in enumerate(sp): slave_point_nd[j] = coord slave_point_nd[len(sp):] = 0 if subspace_slave is None: slave_dofs = fem.locate_dofs_geometrical(V, close_to(slave_point_nd)) else: Vsub = V.sub(subspace_slave).collapse()[0] slave_dofs = fem.locate_dofs_geometrical( (V.sub(subspace_slave), Vsub), close_to(slave_point_nd))[0] if len(slave_dofs) == 1: # Decide if slave is ghost or not if slave_dofs[0] < local_size: slaves_local[i] = slave_dofs[0] owned_entities[i] = { "masters": np.full(num_masters, -1, dtype=np.int64), "coeffs": np.full(num_masters, -1, dtype=np.float64), "owners": np.full(num_masters, -1, dtype=np.int32), "master_count": 0, "local_index": [] } slave_status = 1 else: slaves_ghost[i] = slave_dofs[0] ghosted_entities[i] = { "masters": np.full(num_masters, -1, dtype=np.int64), "coeffs": np.full(num_masters, -1, dtype=np.float64), "owners": np.full(num_masters, -1, dtype=np.int32), "master_count": 0, "local_index": [] } slave_status = 0 elif len(slave_dofs) > 1: raise RuntimeError("Multiple slaves found at same point. " + "You should use sub-space locators.") # Wrap as list to ensure order later master_points = list(slave_master_dict[slave_point].keys()) master_points_nd = np.zeros((3, len(master_points)), dtype=dfloat) for (j, master_point) in enumerate(master_points): # Wrap bytes as numpy array for k, coord in enumerate(np.frombuffer(master_point, dtype=dfloat)): master_points_nd[k, j] = coord if subspace_master is None: master_dofs = fem.locate_dofs_geometrical( V, close_to(master_points_nd[:, j:j + 1])) else: Vsub = V.sub(subspace_master).collapse()[0] master_dofs = fem.locate_dofs_geometrical( (V.sub(subspace_master), Vsub), close_to(master_points_nd[:, j:j + 1]))[0] # Only add masters owned by this processor master_dofs = master_dofs[master_dofs < local_size] if len(master_dofs) == 1: master_block = master_dofs[0] // bs master_rem = master_dofs % bs glob_master = index_map.local_to_global([master_block])[0] if slave_status == -1: if i in non_local_entities.keys(): non_local_entities[i]["masters"].append(glob_master * bs + master_rem) non_local_entities[i]["coeffs"].append( slave_master_dict[slave_point][master_point]) non_local_entities[i]["owners"].append(comm.rank), non_local_entities[i]["local_index"].append(j) else: non_local_entities[i] = { "masters": [glob_master * bs + master_rem], "coeffs": [slave_master_dict[slave_point][master_point]], "owners": [comm.rank], "local_index": [j] } elif slave_status == 0: ghosted_entities[i]["masters"][ j] = glob_master * bs + master_rem ghosted_entities[i]["owners"][j] = comm.rank ghosted_entities[i]["coeffs"][j] = slave_master_dict[ slave_point][master_point] ghosted_entities[i]["local_index"].append(j) elif slave_status == 1: owned_entities[i]["masters"][ j] = glob_master * bs + master_rem owned_entities[i]["owners"][j] = comm.rank owned_entities[i]["coeffs"][j] = slave_master_dict[ slave_point][master_point] owned_entities[i]["local_index"].append(j) else: raise RuntimeError( "Invalid slave status: {0:d} (-1,0,1 are valid options)" .format(slave_status)) elif len(master_dofs) > 1: raise RuntimeError( "Multiple masters found at same point. You should use sub-space locators." ) # Send the ghost and owned entities to processor 0 to gather them data_to_send = [owned_entities, ghosted_entities, non_local_entities] if comm.rank != 0: comm.send(data_to_send, dest=0, tag=1) del owned_entities, ghosted_entities, non_local_entities # Gather all info on proc 0 and sort data owned_slaves, ghosted_slaves = None, None if comm.rank == 0: recv = {0: data_to_send} for proc in range(1, comm.size): recv[proc] = comm.recv(source=proc, tag=1) for proc in range(comm.size): # Loop through all masters other_procs = np.arange(comm.size) other_procs = other_procs[other_procs != proc] # Loop through all owned slaves and ghosts, and update # the master entries for pair in [[0, 1], [1, 0]]: i, j = pair for slave in recv[proc][i].keys(): for o_proc in other_procs: # If slave is ghost on other proc add local masters if slave in recv[o_proc][j].keys(): # Update master with possible entries from ghost o_masters = recv[o_proc][j][slave]["local_index"] for o_master in o_masters: recv[proc][i][slave]["masters"][ o_master] = recv[o_proc][j][slave][ "masters"][o_master] recv[proc][i][slave]["coeffs"][ o_master] = recv[o_proc][j][slave][ "coeffs"][o_master] recv[proc][i][slave]["owners"][ o_master] = recv[o_proc][j][slave][ "owners"][o_master] # If proc only has master, but not the slave if slave in recv[o_proc][2].keys(): o_masters = recv[o_proc][2][slave]["local_index"] # As non owned indices only store non-zero entries for k, o_master in enumerate(o_masters): recv[proc][i][slave]["masters"][ o_master] = recv[o_proc][2][slave][ "masters"][k] recv[proc][i][slave]["coeffs"][ o_master] = recv[o_proc][2][slave][ "coeffs"][k] recv[proc][i][slave]["owners"][ o_master] = recv[o_proc][2][slave][ "owners"][k] if proc == comm.rank: owned_slaves = recv[proc][0] ghosted_slaves = recv[proc][1] else: # If no owned masters, do not send masters if len(recv[proc][0].keys()) > 0: comm.send(recv[proc][0], dest=proc, tag=55) if len(recv[proc][1].keys()) > 0: comm.send(recv[proc][1], dest=proc, tag=66) else: if len(slaves_local.keys()) > 0: owned_slaves = comm.recv(source=0, tag=55) if len(slaves_ghost.keys()) > 0: ghosted_slaves = comm.recv(source=0, tag=66) # Flatten slaves (local) slaves, masters, coeffs, owners, offsets = [], [], [], [], [0] for slave_index in slaves_local.keys(): slaves.append(slaves_local[slave_index]) masters.extend(owned_slaves[slave_index]["masters"]) # type: ignore owners.extend(owned_slaves[slave_index]["owners"]) # type: ignore coeffs.extend(owned_slaves[slave_index]["coeffs"]) # type: ignore offsets.append(len(masters)) for slave_index in slaves_ghost.keys(): slaves.append(slaves_ghost[slave_index]) masters.extend(ghosted_slaves[slave_index]["masters"]) # type: ignore owners.extend(ghosted_slaves[slave_index]["owners"]) # type: ignore coeffs.extend(ghosted_slaves[slave_index]["coeffs"]) # type: ignore offsets.append(len(masters)) return (np.asarray(slaves, dtype=np.int32), np.asarray(masters, dtype=np.int64), np.asarray(coeffs, dtype=PETSc.ScalarType), np.asarray(owners, dtype=np.int32), np.asarray(offsets, dtype=np.int32))
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) dx = Measure("dx", domain=mesh, subdomain_data=ct) a = inner(sigma(u), grad(v)) * dx x = SpatialCoordinate(mesh) rhs = inner(Constant(mesh, PETSc.ScalarType((0, 0))), v) * dx # Set boundary conditions u_push = np.array([0.1, 0], dtype=PETSc.ScalarType) dofs = locate_dofs_geometrical(V, lambda x: np.isclose(x[0], 0)) bc_push = dirichletbc(u_push, dofs, V) u_fix = np.array([0, 0], dtype=PETSc.ScalarType) bc_fix = dirichletbc( u_fix, locate_dofs_geometrical(V, lambda x: np.isclose(x[0], 2.1)), V) bcs = [bc_push, bc_fix] def gather_dof_coordinates(V: FunctionSpace, dofs: np.ndarray): """ Distributes the dof coordinates of this subset of dofs to all processors """ x = V.tabulate_dof_coordinates() local_dofs = dofs[dofs < V.dofmap.index_map.size_local * V.dofmap.index_map_bs] coords = x[local_dofs]
u_ = Function(V_u, name="Boundary Displacement") zero_u = Function(V_u, name=" Boundary Displacement") alpha = Function(V_alpha, name="Damage") zero_alpha = Function(V_alpha, name="Damage Boundary Field") state = {"u": u, "alpha": alpha} # need upper/lower bound for the damage field alpha_lb = Function(V_alpha, name="Lower bound") alpha_ub = Function(V_alpha, name="Upper bound") # Measures dx = ufl.Measure("dx", domain=mesh) ds = ufl.Measure("ds", domain=mesh) dofs_alpha_left = locate_dofs_geometrical(V_alpha, lambda x: np.isclose(x[0], 0.0)) dofs_alpha_right = locate_dofs_geometrical(V_alpha, lambda x: np.isclose(x[0], Lx)) dofs_u_left = locate_dofs_geometrical(V_u, lambda x: np.isclose(x[0], 0.0)) dofs_u_right = locate_dofs_geometrical(V_u, lambda x: np.isclose(x[0], Lx)) # Set Bcs Function zero_u.interpolate(lambda x: (np.zeros_like(x[0]), np.zeros_like(x[1]))) zero_alpha.interpolate((lambda x: np.zeros_like(x[0]))) u_.interpolate(lambda x: (np.ones_like(x[0]), 0 * np.ones_like(x[1]))) alpha_lb.interpolate(lambda x: np.zeros_like(x[0])) alpha_ub.interpolate(lambda x: np.ones_like(x[0])) for f in [zero_u, zero_alpha, u_, alpha_lb, alpha_ub]: f.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD)
def reference_periodic(tetra: bool, r_lvl: int = 0, out_hdf5: h5py.File = None, xdmf: bool = False, boomeramg: bool = False, kspview: bool = False, degree: int = 1): # Create mesh and finite element if tetra: # Tet setup N = 3 mesh = create_unit_cube(MPI.COMM_WORLD, N, N, N) for i in range(r_lvl): mesh.topology.create_entities(mesh.topology.dim - 2) mesh = refine(mesh, redistribute=True) N *= 2 else: # Hex setup N = 3 for i in range(r_lvl): N *= 2 mesh = create_unit_cube(MPI.COMM_WORLD, N, N, N, CellType.hexahedron) V = FunctionSpace(mesh, ("CG", degree)) # Create Dirichlet boundary condition def dirichletboundary(x): return np.logical_or( np.logical_or(np.isclose(x[1], 0), np.isclose(x[1], 1)), np.logical_or(np.isclose(x[2], 0), np.isclose(x[2], 1))) mesh.topology.create_connectivity(2, 1) geometrical_dofs = locate_dofs_geometrical(V, dirichletboundary) bc = dirichletbc(PETSc.ScalarType(0), geometrical_dofs, V) bcs = [bc] # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = inner(grad(u), grad(v)) * dx x = SpatialCoordinate(mesh) dx_ = x[0] - 0.9 dy_ = x[1] - 0.5 dz_ = x[2] - 0.1 f = x[0] * sin(5.0 * pi * x[1]) + 1.0 * exp( -(dx_ * dx_ + dy_ * dy_ + dz_ * dz_) / 0.02) rhs = inner(f, v) * dx # Assemble rhs, RHS and apply lifting bilinear_form = form(a) linear_form = form(rhs) A_org = assemble_matrix(bilinear_form, bcs) A_org.assemble() L_org = assemble_vector(linear_form) apply_lifting(L_org, [bilinear_form], [bcs]) L_org.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) set_bc(L_org, bcs) # Create PETSc nullspace nullspace = PETSc.NullSpace().create(constant=True) PETSc.Mat.setNearNullSpace(A_org, nullspace) # Set PETSc options opts = PETSc.Options() if boomeramg: opts["ksp_type"] = "cg" opts["ksp_rtol"] = 1.0e-5 opts["pc_type"] = "hypre" opts['pc_hypre_type'] = 'boomeramg' opts["pc_hypre_boomeramg_max_iter"] = 1 opts["pc_hypre_boomeramg_cycle_type"] = "v" # opts["pc_hypre_boomeramg_print_statistics"] = 1 else: opts["ksp_type"] = "cg" opts["ksp_rtol"] = 1.0e-12 opts["pc_type"] = "gamg" opts["pc_gamg_type"] = "agg" opts["pc_gamg_sym_graph"] = True # Use Chebyshev smoothing for multigrid opts["mg_levels_ksp_type"] = "richardson" opts["mg_levels_pc_type"] = "sor" # opts["help"] = None # List all available options # opts["ksp_view"] = None # List progress of solver # Initialize PETSc solver, set options and operator solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setFromOptions() solver.setOperators(A_org) # Solve linear problem u_ = Function(V) start = perf_counter() with Timer("Solve"): solver.solve(L_org, u_.vector) end = perf_counter() u_.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) if kspview: solver.view() it = solver.getIterationNumber() num_dofs = V.dofmap.index_map.size_global * V.dofmap.index_map_bs if out_hdf5 is not None: d_set = out_hdf5.get("its") d_set[r_lvl] = it d_set = out_hdf5.get("num_dofs") d_set[r_lvl] = num_dofs d_set = out_hdf5.get("solve_time") d_set[r_lvl, MPI.COMM_WORLD.rank] = end - start if MPI.COMM_WORLD.rank == 0: print("Rlvl {0:d}, Iterations {1:d}".format(r_lvl, it)) # Output solution to XDMF if xdmf: ext = "tet" if tetra else "hex" fname = "results/reference_periodic_{0:d}_{1:s}.xdmf".format( r_lvl, ext) u_.name = "u_" + ext + "_unconstrained" with XDMFFile(MPI.COMM_WORLD, fname, "w") as out_periodic: out_periodic.write_mesh(mesh) out_periodic.write_function( u_, 0.0, "Xdmf/Domain/" + "Grid[@Name='{0:s}'][1]".format(mesh.name))
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)
# Create function space V = VectorFunctionSpace(mesh, ("Lagrange", 1)) # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = form(inner(sigma(u), grad(v)) * dx) L = form(inner(f, v) * dx) # Set up boundary condition on inner surface bc = dirichletbc( 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])
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) # 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) bdofs = locate_dofs_geometrical(V, lambda x: np.full(x.shape[1], True)) 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