def test_vector_constant_bc(mesh_factory): """Test that setting a dirichletbc with a vector valued constant yields the same result as setting it with a function""" func, args = mesh_factory mesh = func(*args) tdim = mesh.topology.dim V = VectorFunctionSpace(mesh, ("Lagrange", 1)) assert V.num_sub_spaces == mesh.geometry.dim c = np.arange(1, mesh.geometry.dim + 1, dtype=PETSc.ScalarType) boundary_facets = locate_entities_boundary( mesh, tdim - 1, lambda x: np.ones(x.shape[1], dtype=bool)) # Set using sub-functions Vs = [V.sub(i).collapse()[0] for i in range(V.num_sub_spaces)] boundary_dofs = [ locate_dofs_topological((V.sub(i), Vs[i]), tdim - 1, boundary_facets) for i in range(len(Vs)) ] u_bcs = [Function(Vs[i]) for i in range(len(Vs))] bcs_f = [] for i, u in enumerate(u_bcs): u_bcs[i].x.array[:] = c[i] bcs_f.append(dirichletbc(u_bcs[i], boundary_dofs[i], V.sub(i))) u_f = Function(V) set_bc(u_f.vector, bcs_f) # Set using constant boundary_dofs = locate_dofs_topological(V, tdim - 1, boundary_facets) bc_c = dirichletbc(c, boundary_dofs, V) u_c = Function(V) u_c.x.array[:] = 0.0 set_bc(u_c.vector, [bc_c]) assert np.allclose(u_f.x.array, u_c.x.array)
def test_mixed_constant_bc(mesh_factory): """Test that setting a dirichletbc with on a component of a mixed function yields the same result as setting it with a function""" func, args = mesh_factory mesh = func(*args) tdim, gdim = mesh.topology.dim, mesh.geometry.dim boundary_facets = locate_entities_boundary( mesh, tdim - 1, lambda x: np.ones(x.shape[1], dtype=bool)) TH = ufl.MixedElement([ ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2), ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) ]) W = FunctionSpace(mesh, TH) U = Function(W) # Apply BC to component of a mixed space using a Constant c = Constant(mesh, (PETSc.ScalarType(2), PETSc.ScalarType(2))) dofs0 = locate_dofs_topological(W.sub(0), tdim - 1, boundary_facets) bc0 = dirichletbc(c, dofs0, W.sub(0)) u = U.sub(0) set_bc(u.vector, [bc0]) # Apply BC to component of a mixed space using a Function ubc1 = u.collapse() ubc1.interpolate(lambda x: np.full((gdim, x.shape[1]), 2.0)) dofs1 = locate_dofs_topological((W.sub(0), ubc1.function_space), tdim - 1, boundary_facets) bc1 = dirichletbc(ubc1, dofs1, W.sub(0)) U1 = Function(W) u1 = U1.sub(0) set_bc(u1.vector, [bc1]) # Check that both approaches yield the same vector assert np.allclose(u.x.array, u1.x.array)
def test_sub_constant_bc(mesh_factory): """Test that setting a dirichletbc with on a component of a vector valued function yields the same result as setting it with a function""" func, args = mesh_factory mesh = func(*args) tdim = mesh.topology.dim V = VectorFunctionSpace(mesh, ("Lagrange", 1)) c = Constant(mesh, PETSc.ScalarType(3.14)) boundary_facets = locate_entities_boundary( mesh, tdim - 1, lambda x: np.ones(x.shape[1], dtype=bool)) for i in range(V.num_sub_spaces): Vi = V.sub(i).collapse()[0] u_bci = Function(Vi) u_bci.x.array[:] = PETSc.ScalarType(c.value) boundary_dofsi = locate_dofs_topological((V.sub(i), Vi), tdim - 1, boundary_facets) bc_fi = dirichletbc(u_bci, boundary_dofsi, V.sub(i)) boundary_dofs = locate_dofs_topological(V.sub(i), tdim - 1, boundary_facets) bc_c = dirichletbc(c, boundary_dofs, V.sub(i)) u_f = Function(V) set_bc(u_f.vector, [bc_fi]) u_c = Function(V) set_bc(u_c.vector, [bc_c]) assert np.allclose(u_f.vector.array, u_c.vector.array)
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 monolithic_solve(): """Monolithic version""" E = P * P W = FunctionSpace(mesh, E) U = Function(W) dU = ufl.TrialFunction(W) u0, u1 = ufl.split(U) v0, v1 = ufl.TestFunctions(W) F = inner((u0**2 + 1) * ufl.grad(u0), ufl.grad(v0)) * dx \ + inner((u1**2 + 1) * ufl.grad(u1), ufl.grad(v1)) * dx \ - inner(f, v0) * ufl.dx - inner(g, v1) * dx J = derivative(F, U, dU) F, J = form(F), form(J) u0_bc = Function(V0) u0_bc.interpolate(bc_val_0) u1_bc = Function(V1) u1_bc.interpolate(bc_val_1) bdofsW0_V0 = locate_dofs_topological((W.sub(0), V0), facetdim, bndry_facets) bdofsW1_V1 = locate_dofs_topological((W.sub(1), V1), facetdim, bndry_facets) bcs = [ dirichletbc(u0_bc, bdofsW0_V0, W.sub(0)), dirichletbc(u1_bc, bdofsW1_V1, W.sub(1)) ] Jmat = create_matrix(J) Fvec = create_vector(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, U, bcs) snes.setFunction(problem.F_mono, Fvec) snes.setJacobian(problem.J_mono, J=Jmat, P=None) U.sub(0).interpolate(initial_guess_u) U.sub(1).interpolate(initial_guess_p) x = create_vector(F) x.array = U.vector.array_r snes.solve(None, x) assert snes.getKSP().getConvergedReason() > 0 assert snes.getConvergedReason() > 0 return x.norm()
def test_constant_bc(mesh_factory): """Test that setting a dirichletbc with a constant yields the same result as setting it with a function""" func, args = mesh_factory mesh = func(*args) V = FunctionSpace(mesh, ("Lagrange", 1)) c = PETSc.ScalarType(2) tdim = mesh.topology.dim boundary_facets = locate_entities_boundary( mesh, tdim - 1, lambda x: np.ones(x.shape[1], dtype=bool)) boundary_dofs = locate_dofs_topological(V, tdim - 1, boundary_facets) u_bc = Function(V) u_bc.x.array[:] = c bc_f = dirichletbc(u_bc, boundary_dofs) bc_c = dirichletbc(c, boundary_dofs, V) u_f = Function(V) set_bc(u_f.vector, [bc_f]) u_c = Function(V) set_bc(u_c.vector, [bc_c]) assert np.allclose(u_f.vector.array, u_c.vector.array)
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 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 solve_system(N): fenics_mesh = dolfinx.UnitCubeMesh(fenicsx_comm, N, N, N) fenics_space = dolfinx.FunctionSpace(fenics_mesh, ("CG", 1)) u = ufl.TrialFunction(fenics_space) v = ufl.TestFunction(fenics_space) k = 2 # print(u*v*ufl.ds) form = (ufl.inner(ufl.grad(u), ufl.grad(v)) - k**2 * ufl.inner(u, v)) * ufl.dx # locate facets on the cube boundary facets = locate_entities_boundary( fenics_mesh, 2, lambda x: np.logical_or( np.logical_or( np.logical_or(np.isclose(x[2], 0.0), np.isclose(x[2], 1.0)), np.logical_or(np.isclose(x[1], 0.0), np.isclose(x[1], 1.0))), np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0)))) facets.sort() # alternative - more general approach boundary = entities_to_geometry( fenics_mesh, fenics_mesh.topology.dim - 1, exterior_facet_indices(fenics_mesh), True, ) # print(len(facets) assert len(facets) == len(exterior_facet_indices(fenics_mesh)) u0 = fem.Function(fenics_space) with u0.vector.localForm() as u0_loc: u0_loc.set(0) # solution vector bc = DirichletBC(u0, locate_dofs_topological(fenics_space, 2, facets)) A = 1 + 1j f = Function(fenics_space) f.interpolate(lambda x: A * k**2 * np.cos(k * x[0]) * np.cos(k * x[1])) L = ufl.inner(f, v) * ufl.dx u0.name = "u" problem = fem.LinearProblem(form, L, u=u0, petsc_options={ "ksp_type": "preonly", "pc_type": "lu" }) # problem = fem.LinearProblem(form, L, bcs=[bc], u=u0, petsc_options={"ksp_type": "preonly", "pc_type": "lu"}) start_time = time.time() soln = problem.solve() if world_rank == 0: print("--- fenics solve done in %s seconds ---" % (time.time() - start_time))
def test_add_diagonal(): """Test adding entries to diagonal of sparsity pattern""" mesh = create_unit_square(MPI.COMM_WORLD, 10, 10) V = VectorFunctionSpace(mesh, ("Lagrange", 1)) pattern = SparsityPattern(mesh.comm, [V.dofmap.index_map, V.dofmap.index_map], [V.dofmap.index_map_bs, V.dofmap.index_map_bs]) mesh.topology.create_connectivity(mesh.topology.dim - 1, mesh.topology.dim) facets = compute_boundary_facets(mesh.topology) blocks = locate_dofs_topological(V, mesh.topology.dim - 1, facets) pattern.insert_diagonal(blocks) pattern.assemble() assert len(blocks) == pattern.num_nonzeros
def locate_dofs_topological(V, meshtags, value): """Identifes the degrees of freedom of a given function space associated with a given meshtags value. Parameters ---------- V: FunctionSpace meshtags: MeshTags object value: mesh tag value Returns ------- The system dof indices. """ from dolfinx import fem from numpy import where return fem.locate_dofs_topological( V, meshtags.dim, meshtags.indices[where(meshtags.values == value)[0]])
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
P2 = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2) P1 = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) V, Q = FunctionSpace(mesh, P2), FunctionSpace(mesh, P1) # We can define boundary conditions:: # No-slip boundary condition for velocity field (`V`) on boundaries # where x = 0, x = 1, and y = 0 noslip = Function(V) with noslip.vector.localForm() as bc_local: bc_local.set(0.0) facets = locate_entities_boundary(mesh, 1, noslip_boundary) bc0 = DirichletBC(noslip, locate_dofs_topological(V, 1, facets)) # Driving velocity condition u = (1, 0) on top boundary (y = 1) lid_velocity = Function(V) lid_velocity.interpolate(lid_velocity_expression) facets = locate_entities_boundary(mesh, 1, lid) bc1 = DirichletBC(lid_velocity, locate_dofs_topological(V, 1, facets)) # Collect Dirichlet boundary conditions bcs = [bc0, bc1] # We now define the bilinear and linear forms corresponding to the weak # mixed formulation of the Stokes equations in a blocked structure:: # Define variational problem (u, p) = ufl.TrialFunction(V), ufl.TrialFunction(Q)
# freedom in the function space to which we apply the boundary conditions. # A method ``locate_dofs_geometrical`` is provided to extract the boundary # degrees of freedom using a geometrical criterium. # In our example, the function space is ``V``, # the value of the boundary condition (0.0) can represented using a # :py:class:`Function <dolfinx.functions.Function>` and the Dirichlet # boundary is defined immediately above. The definition of the Dirichlet # boundary condition then looks as follows: :: # Define boundary condition on x = 0 or x = 1 u0 = Function(V) u0.vector.set(0.0) facets = locate_entities_boundary( mesh, 1, lambda x: np.logical_or(x[0] < np.finfo(float).eps, x[0] > 1.0 - np.finfo(float).eps)) bc = DirichletBC(u0, locate_dofs_topological(V, 1, facets)) # Next, we want to express the variational problem. First, we need to # specify the trial function :math:`u` and the test function :math:`v`, # both living in the function space :math:`V`. We do this by defining a # :py:class:`TrialFunction <dolfinx.functions.function.TrialFunction>` # and a :py:class:`TestFunction # <dolfinx.functions.function.TrialFunction>` on the previously defined # :py:class:`FunctionSpace <dolfinx.functions.FunctionSpace>` ``V``. # # Further, the source :math:`f` and the boundary normal derivative # :math:`g` are involved in the variational forms, and hence we must # specify these. # # With these ingredients, we can write down the bilinear form ``a`` and # the linear form ``L`` (using UFL operators). In summary, this reads ::
# We do this using using {py:func}`locate_entities_boundary # <dolfinx.mesh.locate_entities_boundary>` and providing a marker # function that returns `True` for points `x` on the boundary and # `False` otherwise. facets = mesh.locate_entities_boundary( msh, dim=1, marker=lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose( x[0], 2.0))) # We now find the degrees-of-freedom that are associated with the # boundary facets using {py:func}`locate_dofs_topological # <dolfinx.fem.locate_dofs_topological>` dofs = fem.locate_dofs_topological(V=V, entity_dim=1, entities=facets) # and use {py:func}`dirichletbc <dolfinx.fem.dirichletbc>` to create a # {py:class}`DirichletBCMetaClass <dolfinx.fem.DirichletBCMetaClass>` # class that represents the boundary condition bc = fem.dirichletbc(value=ScalarType(0), dofs=dofs, V=V) # Next, we express the variational problem using UFL. # + u = ufl.TrialFunction(V) v = ufl.TestFunction(V) x = ufl.SpatialCoordinate(msh) f = 10 * ufl.exp(-((x[0] - 0.5)**2 + (x[1] - 0.5)**2) / 0.02) g = ufl.sin(5 * x[0])
# To identify the degrees of freedom, we first find the facets (entities # of dimension 1) that likes on the boundary of the mesh, and satisfies # our criteria for `\Gamma_D`. Then, we use the function # ``locate_dofs_topological`` to identify all degrees of freedom that is # located on the facet (including the vertices). In our example, the # function space is ``V``, the value of the boundary condition (0.0) can # represented using a :py:class:`Constant # <dolfinx.fem.function.Constant>` and the Dirichlet boundary is defined # immediately above. The definition of the Dirichlet boundary condition # then looks as follows: :: # Define boundary condition on x = 0 or x = 1 facets = locate_entities_boundary( mesh, 1, lambda x: np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 2.0))) bc = dirichletbc(ScalarType(0), locate_dofs_topological(V, 1, facets), V) # Next, we want to express the variational problem. First, we need to # specify the trial function :math:`u` and the test function :math:`v`, # both living in the function space :math:`V`. We do this by defining a # :py:class:`TrialFunction <dolfinx.functions.fem.TrialFunction>` and a # :py:class:`TestFunction <dolfinx.functions.fem.TrialFunction>` on the # previously defined :py:class:`FunctionSpace # <dolfinx.fem.FunctionSpace>` ``V``. # # Further, the source :math:`f` and the boundary normal derivative # :math:`g` are involved in the variational forms, and hence we must # specify these. # # With these ingredients, we can write down the bilinear form ``a`` and # the linear form ``L`` (using UFL operators). In summary, this reads ::
def test_assembly_solve_taylor_hood_nl(mesh): """Assemble Stokes problem with Taylor-Hood elements and solve.""" gdim = mesh.geometry.dim P2 = VectorFunctionSpace(mesh, ("Lagrange", 2)) P1 = FunctionSpace(mesh, ("Lagrange", 1)) def boundary0(x): """Define boundary x = 0""" return np.isclose(x[0], 0.0) def boundary1(x): """Define boundary x = 1""" return np.isclose(x[0], 1.0) def initial_guess_u(x): u_init = np.row_stack( (np.sin(x[0]) * np.sin(x[1]), np.cos(x[0]) * np.cos(x[1]))) if gdim == 3: u_init = np.row_stack((u_init, np.cos(x[2]))) return u_init def initial_guess_p(x): return -x[0]**2 - x[1]**3 u_bc_0 = Function(P2) u_bc_0.interpolate( lambda x: np.row_stack(tuple(x[j] + float(j) for j in range(gdim)))) u_bc_1 = Function(P2) u_bc_1.interpolate( lambda x: np.row_stack(tuple(np.sin(x[j]) for j in range(gdim)))) facetdim = mesh.topology.dim - 1 bndry_facets0 = locate_entities_boundary(mesh, facetdim, boundary0) bndry_facets1 = locate_entities_boundary(mesh, facetdim, boundary1) bdofs0 = locate_dofs_topological(P2, facetdim, bndry_facets0) bdofs1 = locate_dofs_topological(P2, facetdim, bndry_facets1) bcs = [dirichletbc(u_bc_0, bdofs0), dirichletbc(u_bc_1, bdofs1)] u, p = Function(P2), Function(P1) du, dp = ufl.TrialFunction(P2), ufl.TrialFunction(P1) v, q = ufl.TestFunction(P2), ufl.TestFunction(P1) F = [ inner(ufl.grad(u), ufl.grad(v)) * dx + inner(p, ufl.div(v)) * dx, inner(ufl.div(u), q) * dx ] J = [[derivative(F[0], u, du), derivative(F[0], p, dp)], [derivative(F[1], u, du), derivative(F[1], p, dp)]] P = [[J[0][0], None], [None, inner(dp, q) * dx]] F, J, P = form(F), form(J), form(P) # -- Blocked and monolithic Jmat0 = create_matrix_block(J) Pmat0 = create_matrix_block(P) Fvec0 = create_vector_block(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("minres") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs, P=P) snes.setFunction(problem.F_block, Fvec0) snes.setJacobian(problem.J_block, J=Jmat0, P=Pmat0) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x0 = create_vector_block(F) with u.vector.localForm() as _u, p.vector.localForm() as _p: scatter_local_vectors(x0, [_u.array_r, _p.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) snes.solve(None, x0) assert snes.getConvergedReason() > 0 # -- Blocked and nested Jmat1 = create_matrix_nest(J) Pmat1 = create_matrix_nest(P) Fvec1 = create_vector_nest(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) nested_IS = Jmat1.getNestISs() snes.getKSP().setType("minres") snes.getKSP().setTolerances(rtol=1e-12) snes.getKSP().getPC().setType("fieldsplit") snes.getKSP().getPC().setFieldSplitIS(["u", nested_IS[0][0]], ["p", nested_IS[1][1]]) ksp_u, ksp_p = snes.getKSP().getPC().getFieldSplitSubKSP() ksp_u.setType("preonly") ksp_u.getPC().setType('lu') ksp_p.setType("preonly") ksp_p.getPC().setType('lu') problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs, P=P) snes.setFunction(problem.F_nest, Fvec1) snes.setJacobian(problem.J_nest, J=Jmat1, P=Pmat1) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x1 = create_vector_nest(F) 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) x1.set(0.0) snes.solve(None, x1) assert snes.getConvergedReason() > 0 assert nest_matrix_norm(Jmat1) == pytest.approx(Jmat0.norm(), 1.0e-12) assert Fvec1.norm() == pytest.approx(Fvec0.norm(), 1.0e-12) assert x1.norm() == pytest.approx(x0.norm(), 1.0e-12) # -- 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 = FunctionSpace(mesh, TH) U = Function(W) dU = ufl.TrialFunction(W) u, p = ufl.split(U) du, dp = ufl.split(dU) v, q = ufl.TestFunctions(W) F = inner(ufl.grad(u), ufl.grad(v)) * dx + inner(p, ufl.div(v)) * dx \ + inner(ufl.div(u), q) * dx J = derivative(F, U, dU) P = inner(ufl.grad(du), ufl.grad(v)) * dx + inner(dp, q) * dx F, J, P = form(F), form(J), form(P) bdofsW0_P2_0 = locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets0) bdofsW0_P2_1 = locate_dofs_topological((W.sub(0), P2), facetdim, bndry_facets1) bcs = [ dirichletbc(u_bc_0, bdofsW0_P2_0, W.sub(0)), dirichletbc(u_bc_1, bdofsW0_P2_1, W.sub(0)) ] Jmat2 = create_matrix(J) Pmat2 = create_matrix(P) Fvec2 = create_vector(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("minres") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, U, bcs, P=P) snes.setFunction(problem.F_mono, Fvec2) snes.setJacobian(problem.J_mono, J=Jmat2, P=Pmat2) U.sub(0).interpolate(initial_guess_u) U.sub(1).interpolate(initial_guess_p) x2 = create_vector(F) x2.array = U.vector.array_r snes.solve(None, x2) assert snes.getConvergedReason() > 0 assert Jmat2.norm() == pytest.approx(Jmat0.norm(), 1.0e-12) assert Fvec2.norm() == pytest.approx(Fvec0.norm(), 1.0e-12) assert x2.norm() == pytest.approx(x0.norm(), 1.0e-12)
logger.info(f"Simulation times: {times}") plt.plot(times, range(len(times)), marker="o") plt.savefig("simulation_times.pdf") W0sub1c = w0["displ"].function_space.sub(1).collapse() W0sub2c = w0["displ"].function_space.sub(2).collapse() displ_bc_y = Function(W0sub1c) displ_bc_z = Function(W0sub2c) displ_bc_top = Function(W0sub1c) dt = Constant(mesh, 0.0) t = Constant(mesh, 0.0) bottom = locate_entities_boundary(mesh, 2, lambda x: np.isclose(x[1], 0.0)) bottomW0 = locate_dofs_topological(w0["displ"].function_space, 2, bottom) top_dofsW0 = locate_dofs_topological( (w0["displ"].function_space.sub(1), W0sub1c), 2, top_load_facets) boundaryf = locate_entities_boundary(mesh, 2, lambda x: [True] * x.shape[1]) boundaryf_dofsW1 = locate_dofs_topological(w0["temp"].function_space, 2, boundaryf) boundaryf_dofsW2 = locate_dofs_topological(w0["phi"].function_space, 2, boundaryf) boundaryf_dofsW3 = locate_dofs_topological(w0["co2"].function_space, 2, boundaryf) left = locate_entities_boundary(mesh, 2, lambda x: np.isclose(x[2], 0.0)) leftW2 = locate_dofs_topological(w0["phi"].function_space, 2, left) leftW1 = locate_dofs_topological(w0["temp"].function_space, 2, left)
def facet_normal_approximation(V, mt: _cpp.mesh.MeshTags_int32, mt_id: int, tangent=False, jit_params: dict = {}, form_compiler_params: dict = {}): """ Approximate the facet normal by projecting it into the function space for a set of facets Parameters ---------- V The function space to project into mt The `dolfinx.mesh.MeshTagsMetaClass` containing facet markers mt_id The id for the facets in `mt` we want to represent the normal at tangent To approximate the tangent to the facet set this flag to `True` jit_params Parameters used in CFFI JIT compilation of C code generated by FFCx. See `DOLFINx-documentation <https://github.com/FEniCS/dolfinx/blob/main/python/dolfinx/jit.py#L22-L37>` for all available parameters. Takes priority over all other parameter values. form_compiler_params Parameters used in FFCx compilation of this form. Run `ffcx - -help` at the commandline to see all available options. Takes priority over all other parameter values, except for `scalar_type` which is determined by DOLFINx. """ timer = _common.Timer("~MPC: Facet normal projection") comm = V.mesh.comm n = ufl.FacetNormal(V.mesh) nh = _fem.Function(V) u, v = ufl.TrialFunction(V), ufl.TestFunction(V) ds = ufl.ds(domain=V.mesh, subdomain_data=mt, subdomain_id=mt_id) if tangent: if V.mesh.geometry.dim == 1: raise ValueError("Tangent not defined for 1D problem") elif V.mesh.geometry.dim == 2: a = ufl.inner(u, v) * ds L = ufl.inner(ufl.as_vector([-n[1], n[0]]), v) * ds else: def tangential_proj(u, n): """ See for instance: https://link.springer.com/content/pdf/10.1023/A:1022235512626.pdf """ return (ufl.Identity(u.ufl_shape[0]) - ufl.outer(n, n)) * u c = _fem.Constant(V.mesh, [1, 1, 1]) a = ufl.inner(u, v) * ds L = ufl.inner(tangential_proj(c, n), v) * ds else: a = (ufl.inner(u, v) * ds) L = ufl.inner(n, v) * ds # Find all dofs that are not boundary dofs imap = V.dofmap.index_map all_blocks = np.arange(imap.size_local, dtype=np.int32) top_blocks = _fem.locate_dofs_topological(V, V.mesh.topology.dim - 1, mt.find(mt_id)) deac_blocks = all_blocks[np.isin(all_blocks, top_blocks, invert=True)] # Note there should be a better way to do this # Create sparsity pattern only for constraint + bc bilinear_form = _fem.form(a, jit_params=jit_params, form_compiler_params=form_compiler_params) pattern = _fem.create_sparsity_pattern(bilinear_form) pattern.insert_diagonal(deac_blocks) pattern.assemble() u_0 = _fem.Function(V) u_0.vector.set(0) bc_deac = _fem.dirichletbc(u_0, deac_blocks) A = _cpp.la.petsc.create_matrix(comm, pattern) A.zeroEntries() # Assemble the matrix with all entries form_coeffs = _cpp.fem.pack_coefficients(bilinear_form) form_consts = _cpp.fem.pack_constants(bilinear_form) _cpp.fem.petsc.assemble_matrix(A, bilinear_form, form_consts, form_coeffs, [bc_deac]) if bilinear_form.function_spaces[0] is bilinear_form.function_spaces[1]: A.assemblyBegin(PETSc.Mat.AssemblyType.FLUSH) A.assemblyEnd(PETSc.Mat.AssemblyType.FLUSH) _cpp.fem.petsc.insert_diagonal(A, bilinear_form.function_spaces[0], [bc_deac], 1.0) A.assemble() linear_form = _fem.form(L, jit_params=jit_params, form_compiler_params=form_compiler_params) b = _fem.petsc.assemble_vector(linear_form) _fem.petsc.apply_lifting(b, [bilinear_form], [[bc_deac]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) _fem.petsc.set_bc(b, [bc_deac]) # Solve Linear problem solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType("cg") solver.rtol = 1e-8 solver.setOperators(A) solver.solve(b, nh.vector) nh.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) timer.stop() return nh
def test_curl_curl_eigenvalue(family, order): """curl curl eigenvalue problem. Solved using H(curl)-conforming finite element method. See https://www-users.cse.umn.edu/~arnold/papers/icm2002.pdf for details. """ slepc4py = pytest.importorskip("slepc4py") # noqa: F841 from slepc4py import SLEPc mesh = create_rectangle( MPI.COMM_WORLD, [np.array([0.0, 0.0]), np.array([np.pi, np.pi])], [24, 24], CellType.triangle) element = ufl.FiniteElement(family, ufl.triangle, order) V = FunctionSpace(mesh, element) u = ufl.TrialFunction(V) v = ufl.TestFunction(V) a = inner(ufl.curl(u), ufl.curl(v)) * dx b = inner(u, v) * dx 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, mesh.topology.dim - 1, boundary_facets) zero_u = Function(V) zero_u.x.array[:] = 0.0 bcs = [dirichletbc(zero_u, boundary_dofs)] a, b = form(a), form(b) A = assemble_matrix(a, bcs=bcs) A.assemble() B = assemble_matrix(b, bcs=bcs, diagonal=0.01) B.assemble() eps = SLEPc.EPS().create() eps.setOperators(A, B) PETSc.Options()["eps_type"] = "krylovschur" PETSc.Options()["eps_gen_hermitian"] = "" PETSc.Options()["eps_target_magnitude"] = "" PETSc.Options()["eps_target"] = 5.0 PETSc.Options()["eps_view"] = "" PETSc.Options()["eps_nev"] = 12 eps.setFromOptions() eps.solve() num_converged = eps.getConverged() eigenvalues_unsorted = np.zeros(num_converged, dtype=np.complex128) for i in range(0, num_converged): eigenvalues_unsorted[i] = eps.getEigenvalue(i) assert np.isclose(np.imag(eigenvalues_unsorted), 0.0).all() eigenvalues_sorted = np.sort(np.real(eigenvalues_unsorted))[:-1] eigenvalues_sorted = eigenvalues_sorted[np.logical_not( eigenvalues_sorted < 1E-8)] eigenvalues_exact = np.array([1.0, 1.0, 2.0, 4.0, 4.0, 5.0, 5.0, 8.0, 9.0]) assert np.isclose(eigenvalues_sorted[0:eigenvalues_exact.shape[0]], eigenvalues_exact, rtol=1E-2).all()
def dirichlet_bcs(self, V): for d in self.bc_dict['dirichlet']: func, func_old = Function(V), Function(V) if 'curve' in d.keys(): load = expression.template() load.val = self.ti.timecurves(d['curve'][0])(self.ti.t_init) func.interpolate(load.evaluate), func_old.interpolate( load.evaluate) self.ti.funcs_to_update.append( {func: self.ti.timecurves(d['curve'][0])}) self.ti.funcs_to_update_old.append( {func_old: self.ti.timecurves(d['curve'][0])}) else: func.vector.set(d['val']) if d['dir'] == 'all': for i in range(len(d['id'])): self.dbcs.append( DirichletBC( func, locate_dofs_topological( V, self.io.mesh.topology.dim - 1, self.io.mt_b1.indices[self.io.mt_b1.values == d['id'][i]]))) elif d['dir'] == 'x': for i in range(len(d['id'])): self.dbcs.append( DirichletBC( func, locate_dofs_topological( (V.sub(0), V.sub(0).collapse()), self.io.mesh.topology.dim - 1, self.io.mt_b1.indices[self.io.mt_b1.values == d['id'][i]]), V.sub(0))) elif d['dir'] == 'y': for i in range(len(d['id'])): self.dbcs.append( DirichletBC( func, locate_dofs_topological( (V.sub(1), V.sub(1).collapse()), self.io.mesh.topology.dim - 1, self.io.mt_b1.indices[self.io.mt_b1.values == d['id'][i]]), V.sub(1))) elif d['dir'] == 'z': for i in range(len(d['id'])): self.dbcs.append( DirichletBC( func, locate_dofs_topological( (V.sub(2), V.sub(2).collapse()), self.io.mesh.topology.dim - 1, self.io.mt_b1.indices[self.io.mt_b1.values == d['id'][i]]), V.sub(2))) elif d['dir'] == '2dimX': self.dbcs.append( DirichletBC( func, locate_dofs_topological( (V.sub(0), V.sub(0).collapse()), self.io.mesh.topology.dim - 1, locate_entities_boundary( self.io.mesh, self.io.mesh.topology.dim - 1, self.twodimX)), V.sub(0))) elif d['dir'] == '2dimY': self.dbcs.append( DirichletBC( func, locate_dofs_topological( (V.sub(1), V.sub(1).collapse()), self.io.mesh.topology.dim - 1, locate_entities_boundary( self.io.mesh, self.io.mesh.topology.dim - 1, self.twodimY)), V.sub(1))) elif d['dir'] == '2dimZ': self.dbcs.append( DirichletBC( func, locate_dofs_topological( (V.sub(2), V.sub(2).collapse()), self.io.mesh.topology.dim - 1, locate_entities_boundary( self.io.mesh, self.io.mesh.topology.dim - 1, self.twodimZ)), V.sub(2))) else: raise NameError("Unknown dir option for Dirichlet BC!")
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)
times = np.hstack(([ t0, ], times)) dtimes = np.diff(times) if rank == 0: logger.info("Simulation times: {}".format(times)) plt.plot(times, range(len(times)), marker="o") plt.savefig("simulation_times.pdf") dt = Constant(mesh, 0.0) t = Constant(mesh, 0.0) mesh.topology.create_connectivity_all() leftW0 = locate_dofs_topological(w0["displ"].function_space, 2, left_side_facets) comm = MPI.COMM_WORLD filename = "cantilever" with XDMFFile(comm, f"{filename}.xdmf", "w") as ofile: ofile.write_mesh(mesh) force = Constant(mesh, 0.0) dforce = Constant(mesh, 0.0) f = {} body_force = Constant(mesh, 1.0) g = { dx: body_force * 1.0E-6 * fecoda.mech.rho_c * ufl.as_vector((0, 0, 9.81)), }
dl_interp(my_identity, Cvn) dl_interp(my_identity, C_quart) dl_interp(my_identity, C_thr_quart) dl_interp(my_identity, C_half) a_uv = (derivative(freeEnergy(CC, CCv), u, v) * dx + qvals / h_avg * dot(jump(u), jump(v)) * dS) jac = derivative(a_uv, u, du) # assign DirichletBC left_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, left) right_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, right) bottom_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, bottom) back_facets = locate_entities_boundary(mesh, mesh.topology.dim - 1, back) left_dofs = fem.locate_dofs_topological(V.sub(0), mesh.topology.dim - 1, left_facets) right_dofs = fem.locate_dofs_topological(V.sub(0), mesh.topology.dim - 1, right_facets) back_dofs = fem.locate_dofs_topological(V.sub(2), mesh.topology.dim - 1, back_facets) bottom_dofs = fem.locate_dofs_topological(V.sub(1), mesh.topology.dim - 1, bottom_facets) right_disp = fem.Constant(mesh, 0.0) ul = fem.dirichletbc(st(0), left_dofs, V.sub(0)) ub = fem.dirichletbc(st(0), bottom_dofs, V.sub(1)) ubak = fem.dirichletbc(st(0), back_dofs, V.sub(2)) ur = fem.dirichletbc(right_disp, right_dofs, V.sub(0)) bcs = [ul, ub, ubak, ur] problem = fem.NonlinearProblem(a_uv, u, bcs=bcs, J=jac)
def test_assembly_solve_block_nl(): """Solve a two-field nonlinear diffusion like problem with block matrix approaches and test that solution is the same. """ mesh = create_unit_square(MPI.COMM_WORLD, 12, 11) p = 1 P = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), p) V0 = FunctionSpace(mesh, P) V1 = V0.clone() def bc_val_0(x): return x[0]**2 + x[1]**2 def bc_val_1(x): return np.sin(x[0]) * np.cos(x[1]) 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 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_bc0 = Function(V0) u_bc0.interpolate(bc_val_0) u_bc1 = Function(V1) u_bc1.interpolate(bc_val_1) bdofs0 = locate_dofs_topological(V0, facetdim, bndry_facets) bdofs1 = locate_dofs_topological(V1, facetdim, bndry_facets) bcs = [dirichletbc(u_bc0, bdofs0), dirichletbc(u_bc1, bdofs1)] # Block and Nest variational problem u, p = Function(V0), Function(V1) du, dp = ufl.TrialFunction(V0), ufl.TrialFunction(V1) v, q = ufl.TestFunction(V0), ufl.TestFunction(V1) f = 1.0 g = -3.0 F = [ inner((u**2 + 1) * ufl.grad(u), ufl.grad(v)) * dx - inner(f, v) * dx, inner((p**2 + 1) * ufl.grad(p), ufl.grad(q)) * dx - inner(g, q) * dx ] J = [[derivative(F[0], u, du), derivative(F[0], p, dp)], [derivative(F[1], u, du), derivative(F[1], p, dp)]] F, J = form(F), form(J) def blocked_solve(): """Blocked version""" Jmat = create_matrix_block(J) Fvec = create_vector_block(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs) snes.setFunction(problem.F_block, Fvec) snes.setJacobian(problem.J_block, J=Jmat, P=None) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x = create_vector_block(F) scatter_local_vectors(x, [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)]) x.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) snes.solve(None, x) assert snes.getKSP().getConvergedReason() > 0 assert snes.getConvergedReason() > 0 return x.norm() def nested_solve(): """Nested version""" Jmat = create_matrix_nest(J) assert Jmat.getType() == "nest" Fvec = create_vector_nest(F) assert Fvec.getType() == "nest" snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) nested_IS = Jmat.getNestISs() snes.getKSP().setType("gmres") snes.getKSP().setTolerances(rtol=1e-12) snes.getKSP().getPC().setType("fieldsplit") snes.getKSP().getPC().setFieldSplitIS(["u", nested_IS[0][0]], ["p", nested_IS[1][1]]) ksp_u, ksp_p = snes.getKSP().getPC().getFieldSplitSubKSP() ksp_u.setType("preonly") ksp_u.getPC().setType('lu') ksp_p.setType("preonly") ksp_p.getPC().setType('lu') problem = NonlinearPDE_SNESProblem(F, J, [u, p], bcs) snes.setFunction(problem.F_nest, Fvec) snes.setJacobian(problem.J_nest, J=Jmat, P=None) u.interpolate(initial_guess_u) p.interpolate(initial_guess_p) x = create_vector_nest(F) assert x.getType() == "nest" for x_soln_pair in zip(x.getNestSubVecs(), (u, p)): x_sub, soln_sub = x_soln_pair soln_sub.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) soln_sub.vector.copy(result=x_sub) x_sub.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) snes.solve(None, x) assert snes.getKSP().getConvergedReason() > 0 assert snes.getConvergedReason() > 0 return x.norm() def monolithic_solve(): """Monolithic version""" E = P * P W = FunctionSpace(mesh, E) U = Function(W) dU = ufl.TrialFunction(W) u0, u1 = ufl.split(U) v0, v1 = ufl.TestFunctions(W) F = inner((u0**2 + 1) * ufl.grad(u0), ufl.grad(v0)) * dx \ + inner((u1**2 + 1) * ufl.grad(u1), ufl.grad(v1)) * dx \ - inner(f, v0) * ufl.dx - inner(g, v1) * dx J = derivative(F, U, dU) F, J = form(F), form(J) u0_bc = Function(V0) u0_bc.interpolate(bc_val_0) u1_bc = Function(V1) u1_bc.interpolate(bc_val_1) bdofsW0_V0 = locate_dofs_topological((W.sub(0), V0), facetdim, bndry_facets) bdofsW1_V1 = locate_dofs_topological((W.sub(1), V1), facetdim, bndry_facets) bcs = [ dirichletbc(u0_bc, bdofsW0_V0, W.sub(0)), dirichletbc(u1_bc, bdofsW1_V1, W.sub(1)) ] Jmat = create_matrix(J) Fvec = create_vector(F) snes = PETSc.SNES().create(MPI.COMM_WORLD) snes.setTolerances(rtol=1.0e-15, max_it=10) snes.getKSP().setType("preonly") snes.getKSP().getPC().setType("lu") problem = NonlinearPDE_SNESProblem(F, J, U, bcs) snes.setFunction(problem.F_mono, Fvec) snes.setJacobian(problem.J_mono, J=Jmat, P=None) U.sub(0).interpolate(initial_guess_u) U.sub(1).interpolate(initial_guess_p) x = create_vector(F) x.array = U.vector.array_r snes.solve(None, x) assert snes.getKSP().getConvergedReason() > 0 assert snes.getConvergedReason() > 0 return x.norm() norm0 = blocked_solve() norm1 = nested_solve() norm2 = monolithic_solve() assert norm1 == pytest.approx(norm0, 1.0e-12) assert norm2 == pytest.approx(norm0, 1.0e-12)
t = 0.1 tp = args.tp inital_times = numpy.linspace(t, tp, 5) afterload_times = tp + numpy.logspace(numpy.log10(fecoda.mps.t_begin), numpy.log10(tp + args.end), args.steps) times = numpy.hstack((inital_times, afterload_times)) dtimes = numpy.diff(times) dt = Constant(mesh, 0.0) t = Constant(mesh, 0.0) bottom_facets = mt_facet.indices[numpy.where(mt_facet.values == 1)[0]] bottom_dofs = locate_dofs_topological( (w0["displ"].function_space.sub(2), displ_bc2.function_space), 2, bottom_facets) comm = MPI.COMM_WORLD filename = f"{meshname}_{args.out}" with XDMFFile(comm, f"{filename}.xdmf", "w") as ofile: ofile.write_mesh(mesh) ksp = [None] * 4 log = {"compl": [], "times": []} force = Constant(mesh, 0.0) df = Constant(mesh, 0.0) f = {ds(2): (force + df) * ufl.as_vector((0, 0, -1.0))} gravity_force = Constant(mesh, 1.0)
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 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
# Locate all facets at the free end and assign them value 1 free_end_facets = locate_entities_boundary(mesh, 1, free_end) mt = dolfinx.mesh.MeshTags(mesh, 1, free_end_facets, 1) ds = ufl.Measure("ds", subdomain_data=mt) # Homogeneous boundary condition in displacement u_bc = dolfinx.Function(U) with u_bc.vector.localForm() as loc: loc.set(0.0) # Displacement BC is applied to the left side left_facets = locate_entities_boundary(mesh, 1, left) bdofs = locate_dofs_topological(U, 1, left_facets) bc = dolfinx.fem.DirichletBC(u_bc, bdofs) # Elastic stiffness tensor and Poisson ratio E, nu = 1.0, 1.0 / 3.0 def sigma_u(u): """Consitutive relation for stress-strain. Assuming plane-stress in XY""" eps = 0.5 * (ufl.grad(u) + ufl.grad(u).T) sigma = E / (1. - nu**2) * ( (1. - nu) * eps + nu * ufl.Identity(2) * ufl.tr(eps)) return sigma a00 = ufl.inner(sigma, tau) * ufl.dx
def demo_elasticity(): mesh = create_unit_square(MPI.COMM_WORLD, 10, 10) V = fem.VectorFunctionSpace(mesh, ("Lagrange", 1)) # Generate Dirichlet BC on lower boundary (Fixed) def boundaries(x): return np.isclose(x[0], np.finfo(float).eps) facets = locate_entities_boundary(mesh, 1, boundaries) topological_dofs = fem.locate_dofs_topological(V, 1, facets) bc = fem.dirichletbc(np.array([0, 0], dtype=PETSc.ScalarType), topological_dofs, V) bcs = [bc] # Define variational problem u = TrialFunction(V) v = TestFunction(V) # Elasticity parameters E = PETSc.ScalarType(1.0e4) nu = 0.0 mu = fem.Constant(mesh, E / (2.0 * (1.0 + nu))) lmbda = fem.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))) x = SpatialCoordinate(mesh) # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = inner(sigma(u), grad(v)) * dx rhs = inner(as_vector((0, (x[0] - 0.5) * 10**4 * x[1])), v) * dx # Create MPC def l2b(li): return np.array(li, dtype=np.float64).tobytes() s_m_c = {l2b([1, 0]): {l2b([1, 1]): 0.9}} mpc = MultiPointConstraint(V) mpc.create_general_constraint(s_m_c, 1, 1) mpc.finalize() # Solve Linear problem petsc_options = {"ksp_type": "preonly", "pc_type": "lu"} problem = LinearProblem(a, rhs, mpc, bcs=bcs, petsc_options=petsc_options) u_h = problem.solve() u_h.name = "u_mpc" with XDMFFile(MPI.COMM_WORLD, "results/demo_elasticity.xdmf", "w") as outfile: outfile.write_mesh(mesh) outfile.write_function(u_h) # Solve the MPC problem using a global transformation matrix # and numpy solvers to get reference values bilinear_form = fem.form(a) A_org = fem.petsc.assemble_matrix(bilinear_form, bcs) A_org.assemble() linear_form = fem.form(rhs) 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) solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType(PETSc.KSP.Type.PREONLY) solver.getPC().setType(PETSc.PC.Type.LU) solver.setOperators(A_org) u_ = fem.Function(V) solver.solve(L_org, u_.vector) u_.x.scatter_forward() u_.name = "u_unconstrained" with XDMFFile(MPI.COMM_WORLD, "results/demo_elasticity.xdmf", "a") as outfile: outfile.write_function(u_) outfile.close() root = 0 with Timer("~Demo: Verification"): dolfinx_mpc.utils.compare_mpc_lhs(A_org, problem.A, mpc, root=root) dolfinx_mpc.utils.compare_mpc_rhs(L_org, problem.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(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) # Print out master-slave connectivity for the first slave master_owner = None master_data = None slave_owner = None if mpc.num_local_slaves > 0: slave_owner = MPI.COMM_WORLD.rank bs = mpc.function_space.dofmap.index_map_bs slave = mpc.slaves[0] print("Constrained: {0:.5e}\n Unconstrained: {1:.5e}".format( u_h.x.array[slave], u_.vector.array[slave])) master_owner = mpc._cpp_object.owners.links(slave)[0] _masters = mpc.masters master = _masters.links(slave)[0] glob_master = mpc.function_space.dofmap.index_map.local_to_global( [master // bs])[0] coeffs, offs = mpc.coefficients() master_data = [ glob_master * bs + master % bs, coeffs[offs[slave]:offs[slave + 1]][0] ] # If master not on proc send info to this processor if MPI.COMM_WORLD.rank != master_owner: MPI.COMM_WORLD.send(master_data, dest=master_owner, tag=1) else: print("Master*Coeff: {0:.5e}".format( coeffs[offs[slave]:offs[slave + 1]][0] * u_h.x.array[_masters.links(slave)[0]])) # As a processor with a master is not aware that it has a master, # Determine this so that it can receive the global dof and coefficient master_recv = MPI.COMM_WORLD.allgather(master_owner) for master in master_recv: if master is not None: master_owner = master break if slave_owner != master_owner and MPI.COMM_WORLD.rank == master_owner: dofmap = mpc.function_space.dofmap bs = dofmap.index_map_bs in_data = MPI.COMM_WORLD.recv(source=MPI.ANY_SOURCE, tag=1) num_local = dofmap.index_map.size_local + dofmap.index_map.num_ghosts l2g = dofmap.index_map.local_to_global( np.arange(num_local, dtype=np.int32)) l_index = np.flatnonzero(l2g == in_data[0] // bs)[0] print("Master*Coeff (on other proc): {0:.5e}".format( u_h.x.array[l_index * bs + in_data[0] % bs] * in_data[1]))