def build_nullspace(V): """Function to build PETSc nullspace for 3D elasticity""" # Create list of vectors for null space index_map = V.dofmap.index_map bs = V.dofmap.index_map_bs ns = [la.create_petsc_vector(index_map, bs) for i in range(6)] with ExitStack() as stack: vec_local = [stack.enter_context(x.localForm()) for x in ns] basis = [np.asarray(x) for x in vec_local] # Get dof indices for each subspace (x, y and z dofs) dofs = [V.sub(i).dofmap.list.array for i in range(3)] # Build translational nullspace basis for i in range(3): basis[i][dofs[i]] = 1.0 # Build rotational nullspace basis x = V.tabulate_dof_coordinates() dofs_block = V.dofmap.list.array x0, x1, x2 = x[dofs_block, 0], x[dofs_block, 1], x[dofs_block, 2] basis[3][dofs[0]] = -x1 basis[3][dofs[1]] = x0 basis[4][dofs[0]] = x2 basis[4][dofs[2]] = -x0 basis[5][dofs[2]] = x1 basis[5][dofs[1]] = -x2 la.orthonormalize(ns) assert la.is_orthonormal(ns) return PETSc.NullSpace().create(vectors=ns)
def build_broken_elastic_nullspace(V): """Function to build incorrect null space for 2D elasticity""" # Create list of vectors for null space ns = [la.create_petsc_vector(V.dofmap.index_map, V.dofmap.index_map_bs) for i in range(4)] with ExitStack() as stack: vec_local = [stack.enter_context(x.localForm()) for x in ns] basis = [np.asarray(x) for x in vec_local] dofs = [V.sub(i).dofmap.list.array for i in range(2)] basis[0][dofs[0]] = 1.0 basis[1][dofs[1]] = 1.0 # Build rotational null space basis x = V.tabulate_dof_coordinates() dofs_block = V.dofmap.list.array x0, x1 = x[dofs_block, 0], x[dofs_block, 1] basis[2][dofs[0]] = -x1 basis[2][dofs[1]] = x0 # Add vector that is not in nullspace basis[3][dofs[1]] = x1 return ns
def create_vector(L: FormMetaClass) -> PETSc.Vec: """Create a PETSc vector that is compaible with a linear form. Args: L: A linear form. Returns: A PETSc vector with a layout that is compatible with `L`. """ dofmap = L.function_spaces[0].dofmap return la.create_petsc_vector(dofmap.index_map, dofmap.index_map_bs)
def assemble_vector( L: FormMetaClass, coeffs=Coefficients(None, None)) -> PETSc.Vec: """Assemble linear form into a new PETSc vector. The returned vector is not finalised, i.e. ghost values are not accumulated on the owning processes. """ b = la.create_petsc_vector(L.function_spaces[0].dofmap.index_map, L.function_spaces[0].dofmap.index_map_bs) c = (coeffs[0] if coeffs[0] is not None else pack_constants(L), coeffs[1] if coeffs[1] is not None else pack_coefficients(L)) with b.localForm() as b_local: b_local.set(0.0) _cpp.fem.assemble_vector(b_local.array_w, L, c[0], c[1]) return b
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 build_elastic_nullspace(V): """Function to build nullspace for 2D/3D elasticity""" # Get geometric dim gdim = V.mesh.geometry.dim assert gdim == 2 or gdim == 3 # Set dimension of nullspace dim = 3 if gdim == 2 else 6 # Create list of vectors for null space ns = [ la.create_petsc_vector(V.dofmap.index_map, V.dofmap.index_map_bs) for i in range(dim) ] with ExitStack() as stack: vec_local = [stack.enter_context(x.localForm()) for x in ns] basis = [np.asarray(x) for x in vec_local] dofs = [V.sub(i).dofmap.list.array for i in range(gdim)] # Build translational null space basis for i in range(gdim): basis[i][dofs[i]] = 1.0 # Build rotational null space basis x = V.tabulate_dof_coordinates() dofs_block = V.dofmap.list.array x0, x1, x2 = x[dofs_block, 0], x[dofs_block, 1], x[dofs_block, 2] if gdim == 2: basis[2][dofs[0]] = -x1 basis[2][dofs[1]] = x0 elif gdim == 3: basis[3][dofs[0]] = -x1 basis[3][dofs[1]] = x0 basis[4][dofs[0]] = x2 basis[4][dofs[2]] = -x0 basis[5][dofs[2]] = x1 basis[5][dofs[1]] = -x2 return ns
def assemble_vector(L: FormMetaClass, constants=None, coeffs=None) -> PETSc.Vec: """Assemble linear form into a new PETSc vector. Note: The returned vector is not finalised, i.e. ghost values are not accumulated on the owning processes. Args: L: A linear form. Returns: An assembled vector. """ b = la.create_petsc_vector(L.function_spaces[0].dofmap.index_map, L.function_spaces[0].dofmap.index_map_bs) with b.localForm() as b_local: assemble.assemble_vector(b_local.array_w, L, constants, coeffs) return b
def create_vector(L: FormMetaClass) -> PETSc.Vec: dofmap = L.function_spaces[0].dofmap return la.create_petsc_vector(dofmap.index_map, dofmap.index_map_bs)