def create_cg1_function_space(mesh, sh): r = len(sh) if r == 0: V = fem.FunctionSpace(mesh, ("CG", 1)) elif r == 1: V = fem.VectorFunctionSpace(mesh, ("CG", 1), dim=sh[0]) else: V = fem.TensorFunctionSpace(mesh, ("CG", 1), shape=sh) return V
def test_assembly_solve_taylor_hood(mesh): """Assemble Stokes problem with Taylor-Hood elements and solve.""" P2 = fem.VectorFunctionSpace(mesh, ("Lagrange", 2)) P1 = fem.FunctionSpace(mesh, ("Lagrange", 1)) def boundary0(x): """Define boundary x = 0""" return x[0] < 10 * numpy.finfo(float).eps def boundary1(x): """Define boundary x = 1""" return x[0] > (1.0 - 10 * numpy.finfo(float).eps) # Locate facets on boundaries facetdim = mesh.topology.dim - 1 bndry_facets0 = dolfinx.mesh.locate_entities_boundary( mesh, facetdim, boundary0) bndry_facets1 = dolfinx.mesh.locate_entities_boundary( mesh, facetdim, boundary1) bdofs0 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets0) bdofs1 = dolfinx.fem.locate_dofs_topological(P2, facetdim, bndry_facets1) u0 = dolfinx.Function(P2) u0.vector.set(1.0) u0.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) bc0 = dolfinx.DirichletBC(u0, bdofs0) bc1 = dolfinx.DirichletBC(u0, bdofs1) u, p = ufl.TrialFunction(P2), ufl.TrialFunction(P1) v, q = ufl.TestFunction(P2), ufl.TestFunction(P1) a00 = inner(ufl.grad(u), ufl.grad(v)) * dx a01 = ufl.inner(p, ufl.div(v)) * dx a10 = ufl.inner(ufl.div(u), q) * dx a11 = None p00 = a00 p01, p10 = None, None p11 = inner(p, q) * dx # FIXME # We need zero function for the 'zero' part of L p_zero = dolfinx.Function(P1) f = dolfinx.Function(P2) L0 = ufl.inner(f, v) * dx L1 = ufl.inner(p_zero, q) * dx def nested_solve(): """Nested solver""" A = dolfinx.fem.assemble_matrix_nest([[a00, a01], [a10, a11]], [bc0, bc1], [["baij", "aij"], ["aij", ""]]) A.assemble() P = dolfinx.fem.assemble_matrix_nest([[p00, p01], [p10, p11]], [bc0, bc1], [["aij", "aij"], ["aij", ""]]) P.assemble() b = dolfinx.fem.assemble_vector_nest([L0, L1]) dolfinx.fem.apply_lifting_nest(b, [[a00, a01], [a10, a11]], [bc0, bc1]) for b_sub in b.getNestSubVecs(): b_sub.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) bcs = dolfinx.cpp.fem.bcs_rows( dolfinx.fem.assemble._create_cpp_form([L0, L1]), [bc0, bc1]) dolfinx.fem.set_bc_nest(b, bcs) b.assemble() ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setOperators(A, P) nested_IS = P.getNestISs() ksp.setType("minres") pc = ksp.getPC() pc.setType("fieldsplit") pc.setFieldSplitIS(["u", nested_IS[0][0]], ["p", nested_IS[1][1]]) ksp_u, ksp_p = pc.getFieldSplitSubKSP() ksp_u.setType("preonly") ksp_u.getPC().setType('lu') ksp_p.setType("preonly") def monitor(ksp, its, rnorm): # print("Num it, rnorm:", its, rnorm) pass ksp.setTolerances(rtol=1.0e-8, max_it=50) ksp.setMonitor(monitor) ksp.setFromOptions() x = b.copy() ksp.solve(b, x) assert ksp.getConvergedReason() > 0 return b.norm(), x.norm(), nest_matrix_norm(A), nest_matrix_norm(P) def blocked_solve(): """Blocked (monolithic) solver""" A = dolfinx.fem.assemble_matrix_block([[a00, a01], [a10, a11]], [bc0, bc1]) A.assemble() P = dolfinx.fem.assemble_matrix_block([[p00, p01], [p10, p11]], [bc0, bc1]) P.assemble() b = dolfinx.fem.assemble_vector_block([L0, L1], [[a00, a01], [a10, a11]], [bc0, bc1]) ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setOperators(A, P) ksp.setType("minres") pc = ksp.getPC() pc.setType('lu') ksp.setTolerances(rtol=1.0e-8, max_it=50) 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 (interleaved) solver""" P2_el = ufl.VectorElement("Lagrange", mesh.ufl_cell(), 2) P1_el = ufl.FiniteElement("Lagrange", mesh.ufl_cell(), 1) TH = P2_el * P1_el W = dolfinx.FunctionSpace(mesh, TH) (u, p) = ufl.TrialFunctions(W) (v, q) = ufl.TestFunctions(W) a00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * dx a01 = ufl.inner(p, ufl.div(v)) * dx a10 = ufl.inner(ufl.div(u), q) * dx a = a00 + a01 + a10 p00 = ufl.inner(ufl.grad(u), ufl.grad(v)) * dx p11 = ufl.inner(p, q) * dx p_form = p00 + p11 f = dolfinx.Function(W.sub(0).collapse()) p_zero = dolfinx.Function(W.sub(1).collapse()) L0 = inner(f, v) * dx L1 = inner(p_zero, q) * dx L = L0 + L1 bdofsW0_P2_0 = dolfinx.fem.locate_dofs_topological( (W.sub(0), P2), facetdim, bndry_facets0) bdofsW0_P2_1 = dolfinx.fem.locate_dofs_topological( (W.sub(0), P2), facetdim, bndry_facets1) bc0 = dolfinx.DirichletBC(u0, bdofsW0_P2_0, W.sub(0)) bc1 = dolfinx.DirichletBC(u0, bdofsW0_P2_1, W.sub(0)) A = dolfinx.fem.assemble_matrix(a, [bc0, bc1]) A.assemble() P = dolfinx.fem.assemble_matrix(p_form, [bc0, bc1]) P.assemble() b = dolfinx.fem.assemble_vector(L) dolfinx.fem.apply_lifting(b, [a], [[bc0, bc1]]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) dolfinx.fem.set_bc(b, [bc0, bc1]) ksp = PETSc.KSP() ksp.create(mesh.mpi_comm()) ksp.setOperators(A, P) ksp.setType("minres") pc = ksp.getPC() pc.setType('lu') def monitor(ksp, its, rnorm): # print("Num it, rnorm:", its, rnorm) pass ksp.setTolerances(rtol=1.0e-8, max_it=50) ksp.setMonitor(monitor) ksp.setFromOptions() x = A.createVecRight() ksp.solve(b, x) assert ksp.getConvergedReason() > 0 return b.norm(), x.norm(), A.norm(), P.norm() bnorm0, xnorm0, Anorm0, Pnorm0 = nested_solve() bnorm1, xnorm1, Anorm1, Pnorm1 = blocked_solve() bnorm2, xnorm2, Anorm2, Pnorm2 = monolithic_solve() assert bnorm1 == pytest.approx(bnorm0, 1.0e-12) assert xnorm1 == pytest.approx(xnorm0, 1.0e-8) assert Anorm1 == pytest.approx(Anorm0, 1.0e-12) assert Pnorm1 == pytest.approx(Pnorm0, 1.0e-12) assert bnorm2 == pytest.approx(bnorm0, 1.0e-12) assert xnorm2 == pytest.approx(xnorm0, 1.0e-8) assert Anorm2 == pytest.approx(Anorm0, 1.0e-12) assert Pnorm2 == pytest.approx(Pnorm0, 1.0e-12)
def calculate_norm_tensor_function(C): norm: float = 0.0 for i in range(3): for j in range(3): norm += fem.assemble_scalar(fem.form(C[i, j] * C[i, j] * dx)) return np.sqrt(norm) # mesh and inputs comm = MPI.COMM_WORLD mesh = create_unit_cube(comm, 2, 2, 2, ctype.tetrahedron) mesh.topology.create_connectivity(2, 3) V = fem.VectorFunctionSpace(mesh, ("CR", 1)) Vp = fem.VectorFunctionSpace(mesh, ("CG", 1)) u = fem.Function(V, name="u") Cn = fem.Function(V, name="Cn") uplot = fem.Function(Vp, name="disp") v = TestFunction(V) du = TrialFunction(V) WCv = TensorElement( "Quadrature", mesh.ufl_cell(), degree=metadata["quadrature_degree"], quad_scheme="default", ) Ws = TensorElement( "Quadrature",
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)
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]))
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_surface_integrals(get_assemblers): # noqa: F811 assemble_matrix, assemble_vector = get_assemblers N = 4 mesh = create_unit_square(MPI.COMM_WORLD, N, N) V = fem.VectorFunctionSpace(mesh, ("Lagrange", 1)) # Fixed Dirichlet BC on the left wall def left_wall(x): return np.isclose(x[0], np.finfo(float).eps) fdim = mesh.topology.dim - 1 left_facets = locate_entities_boundary(mesh, fdim, left_wall) bc_dofs = fem.locate_dofs_topological(V, 1, left_facets) u_bc = fem.Function(V) with u_bc.vector.localForm() as u_local: u_local.set(0.0) bc = fem.dirichletbc(u_bc, bc_dofs) bcs = [bc] # Traction on top of domain def top(x): return np.isclose(x[1], 1) top_facets = locate_entities_boundary(mesh, 1, top) arg_sort = np.argsort(top_facets) mt = meshtags(mesh, fdim, top_facets[arg_sort], np.full(len(top_facets), 3, dtype=np.int32)) ds = ufl.Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=3) g = fem.Constant(mesh, PETSc.ScalarType((0, -9.81e2))) # 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 * ufl.sym(ufl.grad(v)) + lmbda * ufl.tr(ufl.sym(ufl.grad(v))) * ufl.Identity(len(v))) # Define variational problem u = ufl.TrialFunction(V) v = ufl.TestFunction(V) a = ufl.inner(sigma(u), ufl.grad(v)) * ufl.dx rhs = ufl.inner(fem.Constant(mesh, PETSc.ScalarType((0, 0))), v) * ufl.dx\ + ufl.inner(g, v) * ds 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) # Setup multipointconstraint def l2b(li): return np.array(li, dtype=np.float64).tobytes() s_m_c = {} for i in range(1, N): s_m_c[l2b([1, i / N])] = {l2b([1, 1]): 0.8} mpc = dolfinx_mpc.MultiPointConstraint(V) mpc.create_general_constraint(s_m_c, 1, 1) mpc.finalize() with Timer("~TEST: Assemble matrix old"): A = assemble_matrix(bilinear_form, mpc, bcs=bcs) with Timer("~TEST: Assemble vector"): 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.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) # Write solution to file # u_h = dolfinx.Function(mpc.function_space) # u_h.vector.setArray(uh.array) # u_h.name = "u_mpc" # outfile = dolfinx.io.XDMFFile(MPI.COMM_WORLD, "output/uh.xdmf", "w") # outfile.write_mesh(mesh) # outfile.write_function(u_h) # outfile.close() # Solve the MPC problem using a global transformation matrix # and numpy solvers to get reference values # Generate reference matrices and unconstrained solution 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_surface_integral_dependency(get_assemblers): # noqa: F811 assemble_matrix, assemble_vector = get_assemblers N = 10 mesh = create_unit_square(MPI.COMM_WORLD, N, N) V = fem.VectorFunctionSpace(mesh, ("Lagrange", 1)) def top(x): return np.isclose(x[1], 1) fdim = mesh.topology.dim - 1 top_facets = locate_entities_boundary(mesh, fdim, top) indices = np.array([], dtype=np.intc) values = np.array([], dtype=np.intc) markers = {3: top_facets} for key in markers.keys(): indices = np.append(indices, markers[key]) values = np.append(values, np.full(len(markers[key]), key, dtype=np.intc)) sort = np.argsort(indices) mt = meshtags(mesh, mesh.topology.dim - 1, np.array(indices[sort], dtype=np.intc), np.array(values[sort], dtype=np.intc)) ds = ufl.Measure("ds", domain=mesh, subdomain_data=mt) g = fem.Constant(mesh, PETSc.ScalarType((2, 1))) h = fem.Constant(mesh, PETSc.ScalarType((3, 2))) # Define variational problem u = ufl.TrialFunction(V) v = ufl.TestFunction(V) a = ufl.inner(u, v) * ds(3) + ufl.inner(ufl.grad(u), ufl.grad(v)) * ds rhs = ufl.inner(g, v) * ds + ufl.inner(h, v) * ds(3) bilinear_form = fem.form(a) linear_form = fem.form(rhs) # Create multipoint constraint and assemble system def l2b(li): return np.array(li, dtype=np.float64).tobytes() s_m_c = {} for i in range(1, N): s_m_c[l2b([1, i / N])] = {l2b([1, 1]): 0.3} mpc = dolfinx_mpc.MultiPointConstraint(V) mpc.create_general_constraint(s_m_c, 1, 1) mpc.finalize() with Timer("~TEST: Assemble matrix"): A = assemble_matrix(bilinear_form, mpc) with Timer("~TEST: Assemble vector"): b = assemble_vector(linear_form, mpc) b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) # Solve the MPC problem using a global transformation matrix # and numpy solvers to get reference values # Generate reference matrices and unconstrained solution A_org = fem.petsc.assemble_matrix(bilinear_form) A_org.assemble() L_org = fem.petsc.assemble_vector(linear_form) L_org.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE) 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) list_timings(comm, [TimingType.wall])
def test_cube_contact(generate_hex_boxes, nonslip, get_assemblers): # noqa: F811 assemble_matrix, assemble_vector = get_assemblers comm = MPI.COMM_WORLD root = 0 # Generate mesh mesh_data = generate_hex_boxes mesh, mt = mesh_data fdim = mesh.topology.dim - 1 # Create functionspaces V = fem.VectorFunctionSpace(mesh, ("Lagrange", 1)) # Helper for orienting traction # Bottom boundary is fixed in all directions u_bc = fem.Function(V) with u_bc.vector.localForm() as u_local: u_local.set(0.0) bottom_dofs = fem.locate_dofs_topological(V, fdim, mt.find(5)) bc_bottom = fem.dirichletbc(u_bc, bottom_dofs) g_vec = [0, 0, -4.25e-1] if not nonslip: # Helper for orienting traction r_matrix = dolfinx_mpc.utils.rotation_matrix( [1 / np.sqrt(2), 1 / np.sqrt(2), 0], -theta) # Top boundary has a given deformation normal to the interface g_vec = np.dot(r_matrix, [0, 0, -4.25e-1]) # Top boundary has a given deformation normal to the interface def top_v(x): values = np.empty((3, x.shape[1])) values[0] = g_vec[0] values[1] = g_vec[1] values[2] = g_vec[2] return values u_top = fem.Function(V) u_top.interpolate(top_v) top_dofs = fem.locate_dofs_topological(V, fdim, mt.find(3)) bc_top = fem.dirichletbc(u_top, top_dofs) bcs = [bc_bottom, bc_top] # Elasticity parameters E = PETSc.ScalarType(1.0e3) nu = 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 * ufl.sym(ufl.grad(v)) + lmbda * ufl.tr(ufl.sym(ufl.grad(v))) * ufl.Identity(len(v))) # Define variational problem u = ufl.TrialFunction(V) v = ufl.TestFunction(V) a = ufl.inner(sigma(u), ufl.grad(v)) * ufl.dx rhs = ufl.inner(fem.Constant(mesh, PETSc.ScalarType( (0, 0, 0))), v) * ufl.dx bilinear_form = fem.form(a) linear_form = fem.form(rhs) # Create LU solver solver = PETSc.KSP().create(comm) solver.setType("preonly") solver.setTolerances(rtol=1.0e-14) solver.getPC().setType("lu") # Create MPC contact condition and assemble matrices mpc = dolfinx_mpc.MultiPointConstraint(V) if nonslip: with Timer("~Contact: Create non-elastic constraint"): mpc.create_contact_inelastic_condition(mt, 4, 9) else: with Timer("~Contact: Create contact constraint"): nh = dolfinx_mpc.utils.create_normal_approximation(V, mt, 4) mpc.create_contact_slip_condition(mt, 4, 9, nh) mpc.finalize() with Timer("~TEST: Assemble bilinear form"): A = assemble_matrix(bilinear_form, mpc, bcs=bcs) with Timer("~TEST: Assemble vector"): 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) with Timer("~MPC: Solve"): 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) # Write solution to file # u_h = fem.Function(mpc.function_space) # u_h.vector.setArray(uh.array) # u_h.x.scatter_forward() # u_h.name = "u_{0:.2f}".format(theta) # import dolfinx.io as io # with io.XDMFFile(comm, "output/rotated_cube3D.xdmf", "w") as outfile: # 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 dolfinx_mpc.utils.log_info( "Solving reference problem with global matrix (using numpy)") with Timer("~TEST: Assemble bilinear form (unconstrained)"): 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) 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 demo_stacked_cubes(outfile: XDMFFile, theta: float, gmsh: bool = False, ct: CellType = CellType.tetrahedron, compare: bool = True, res: float = 0.1, noslip: bool = False): celltype = "hexahedron" if ct == CellType.hexahedron else "tetrahedron" type_ext = "no_slip" if noslip else "slip" mesh_ext = "_gmsh_" if gmsh else "_" log_info(f"Run theta:{theta:.2f}, Cell: {celltype}, GMSH {gmsh}, Noslip: {noslip}") # Read in mesh if gmsh: mesh, mt = gmsh_3D_stacked(celltype, theta, res) tdim = mesh.topology.dim fdim = tdim - 1 mesh.topology.create_connectivity(tdim, tdim) mesh.topology.create_connectivity(fdim, tdim) else: mesh_3D_dolfin(theta, ct, celltype, res) MPI.COMM_WORLD.barrier() with XDMFFile(MPI.COMM_WORLD, f"meshes/mesh_{celltype}_{theta:.2f}.xdmf", "r") as xdmf: mesh = xdmf.read_mesh(name="mesh") tdim = mesh.topology.dim fdim = tdim - 1 mesh.topology.create_connectivity(tdim, tdim) mesh.topology.create_connectivity(fdim, tdim) mt = xdmf.read_meshtags(mesh, "facet_tags") mesh.name = f"mesh_{celltype}_{theta:.2f}{type_ext}{mesh_ext}" # Create functionspaces V = fem.VectorFunctionSpace(mesh, ("Lagrange", 1)) # Define boundary conditions # Bottom boundary is fixed in all directions bottom_dofs = fem.locate_dofs_topological(V, fdim, mt.find(5)) # type: ignore u_bc = np.array((0, ) * mesh.geometry.dim, dtype=PETSc.ScalarType) bc_bottom = fem.dirichletbc(u_bc, bottom_dofs, V) g_vec = np.array([0, 0, -4.25e-1], dtype=PETSc.ScalarType) if not noslip: # Helper for orienting traction r_matrix = rotation_matrix([1 / np.sqrt(2), 1 / np.sqrt(2), 0], -theta) # Top boundary has a given deformation normal to the interface g_vec = np.dot(r_matrix, g_vec) top_dofs = fem.locate_dofs_topological(V, fdim, mt.find(3)) # type: ignore bc_top = fem.dirichletbc(g_vec, top_dofs, V) bcs = [bc_bottom, bc_top] # Elasticity parameters E = PETSc.ScalarType(1.0e3) nu = 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))) # Define variational problem u = TrialFunction(V) v = TestFunction(V) a = inner(sigma(u), grad(v)) * dx # NOTE: Traction deactivated until we have a way of fixing nullspace # g = fem.Constant(mesh, PETSc.ScalarType(g_vec)) # ds = Measure("ds", domain=mesh, subdomain_data=mt, subdomain_id=3) rhs = inner(fem.Constant(mesh, PETSc.ScalarType((0, 0, 0))), v) * dx # + inner(g, v) * ds bilinear_form = fem.form(a) linear_form = fem.form(rhs) mpc = MultiPointConstraint(V) if noslip: with Timer("~~Contact: Create non-elastic constraint"): mpc.create_contact_inelastic_condition(mt, 4, 9) else: 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 data and finialize MPC"): mpc.finalize() # Create null-space null_space = rigid_motions_nullspace(mpc.function_space) num_dofs = V.dofmap.index_map.size_global * V.dofmap.index_map_bs with Timer(f"~~Contact: Assemble matrix ({num_dofs})"): A = assemble_matrix(bilinear_form, mpc, bcs=bcs) with Timer(f"~~Contact: Assemble vector ({num_dofs})"): b = assemble_vector(linear_form, 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) # Solve Linear problem opts = PETSc.Options() opts["ksp_rtol"] = 1.0e-8 opts["pc_type"] = "gamg" opts["pc_gamg_type"] = "agg" opts["pc_gamg_coarse_eq_limit"] = 1000 opts["pc_gamg_sym_graph"] = True opts["mg_levels_ksp_type"] = "chebyshev" opts["mg_levels_pc_type"] = "jacobi" opts["mg_levels_esteig_ksp_type"] = "cg" opts["matptap_via"] = "scalable" # opts["pc_gamg_square_graph"] = 2 # opts["pc_gamg_threshold"] = 1e-2 # opts["help"] = None # List all available options # opts["ksp_view"] = None # List progress of solver # Create functionspace and build near nullspace A.setNearNullSpace(null_space) solver = PETSc.KSP().create(mesh.comm) solver.setOperators(A) solver.setFromOptions() u_h = fem.Function(mpc.function_space) with Timer("~~Contact: Solve"): solver.solve(b, u_h.vector) u_h.x.scatter_forward() with Timer("~~Contact: Backsubstitution"): mpc.backsubstitution(u_h.vector) it = solver.getIterationNumber() unorm = u_h.vector.norm() num_slaves = MPI.COMM_WORLD.allreduce(mpc.num_local_slaves, op=MPI.SUM) if mesh.comm.rank == 0: num_dofs = V.dofmap.index_map.size_global * V.dofmap.index_map_bs print(f"Number of dofs: {num_dofs}") print(f"Number of slaves: {num_slaves}") print(f"Number of iterations: {it}") print(f"Norm of u {unorm:.5e}") # Write solution to file u_h.name = f"u_{celltype}_{theta:.2f}{mesh_ext}{type_ext}".format(celltype, theta, type_ext, mesh_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 scipy)") with Timer("~~Contact: Reference 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 with Timer("~~Contact: Compare LHS, RHS and solution"): compare_mpc_lhs(A_org, A, mpc, root=root) compare_mpc_rhs(L_org, 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) list_timings(mesh.comm, [TimingType.wall])
def nitsche_ufl(mesh: dmesh.Mesh, mesh_data: Tuple[_cpp.mesh.MeshTags_int32, int, int], physical_parameters: dict = {}, nitsche_parameters: Dict[str, float] = {}, plane_loc: float = 0.0, vertical_displacement: float = -0.1, nitsche_bc: bool = True, quadrature_degree: int = 5, form_compiler_params: Dict = {}, jit_params: Dict = {}, petsc_options: Dict = {}, newton_options: Dict = {}) -> _fem.Function: """ Use UFL to compute the one sided contact problem with a mesh coming into contact with a rigid surface (not meshed). Parameters ========== mesh The input mesh mesh_data A triplet with a mesh tag for facets and values v0, v1. v0 should be the value in the mesh tags for facets to apply a Dirichlet condition on. v1 is the value for facets which should have applied a contact condition on physical_parameters Optional dictionary with information about the linear elasticity problem. Valid (key, value) tuples are: ('E': float), ('nu', float), ('strain', bool) nitsche_parameters Optional dictionary with information about the Nitsche configuration. Valid (keu, value) tuples are: ('gamma', float), ('theta', float) where theta can be -1, 0 or 1 for skew-symmetric, penalty like or symmetric enforcement of Nitsche conditions plane_loc The location of the plane in y-coordinate (2D) and z-coordinate (3D) vertical_displacement The amount of verticial displacment enforced on Dirichlet boundary nitsche_bc Use Nitche's method to enforce Dirichlet boundary conditions quadrature_degree The quadrature degree to use for the custom contact kernels 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. jit_params Parameters used in CFFI JIT compilation of C code generated by FFCX. See https://github.com/FEniCS/dolfinx/blob/main/python/dolfinx/jit.py for all available parameters. Takes priority over all other parameter values. petsc_options Parameters that is passed to the linear algebra backend PETSc. For available choices for the 'petsc_options' kwarg, see the `PETSc-documentation <https://petsc4py.readthedocs.io/en/stable/manual/ksp/>` newton_options Dictionary with Newton-solver options. Valid (key, item) tuples are: ("atol", float), ("rtol", float), ("convergence_criterion", "str"), ("max_it", int), ("error_on_nonconvergence", bool), ("relaxation_parameter", float) """ # Compute lame parameters plane_strain = physical_parameters.get("strain", False) E = physical_parameters.get("E", 1e3) nu = physical_parameters.get("nu", 0.1) mu_func, lambda_func = lame_parameters(plane_strain) mu = mu_func(E, nu) lmbda = lambda_func(E, nu) sigma = sigma_func(mu, lmbda) # Nitche parameters and variables theta = nitsche_parameters.get("theta", 1) gamma = nitsche_parameters.get("gamma", 1) (facet_marker, top_value, bottom_value) = mesh_data assert(facet_marker.dim == mesh.topology.dim - 1) # Normal vector pointing into plane (but outward of the body coming into contact) # Similar to computing the normal by finding the gap vector between two meshes n_vec = np.zeros(mesh.geometry.dim) n_vec[mesh.geometry.dim - 1] = -1 n_2 = ufl.as_vector(n_vec) # Normal of plane (projection onto other body) # Scaled Nitsche parameter h = ufl.CellDiameter(mesh) gamma_scaled = gamma * E / h # Mimicking the plane y=-plane_loc x = ufl.SpatialCoordinate(mesh) gap = x[mesh.geometry.dim - 1] + plane_loc g_vec = [i for i in range(mesh.geometry.dim)] g_vec[mesh.geometry.dim - 1] = gap V = _fem.VectorFunctionSpace(mesh, ("CG", 1)) u = _fem.Function(V) v = ufl.TestFunction(V) metadata = {"quadrature_degree": quadrature_degree} dx = ufl.Measure("dx", domain=mesh) ds = ufl.Measure("ds", domain=mesh, metadata=metadata, subdomain_data=facet_marker) a = ufl.inner(sigma(u), epsilon(v)) * dx zero = np.asarray([0, ] * mesh.geometry.dim, dtype=_PETSc.ScalarType) L = ufl.inner(_fem.Constant(mesh, zero), v) * dx # Derivation of one sided Nitsche with gap function n = ufl.FacetNormal(mesh) def sigma_n(v): # NOTE: Different normals, see summary paper return ufl.dot(sigma(v) * n, n_2) F = a - theta / gamma_scaled * sigma_n(u) * sigma_n(v) * ds(bottom_value) - L F += 1 / gamma_scaled * R_minus(sigma_n(u) + gamma_scaled * (gap - ufl.dot(u, n_2))) * \ (theta * sigma_n(v) - gamma_scaled * ufl.dot(v, n_2)) * ds(bottom_value) # Compute corresponding Jacobian du = ufl.TrialFunction(V) q = sigma_n(u) + gamma_scaled * (gap - ufl.dot(u, n_2)) J = ufl.inner(sigma(du), epsilon(v)) * ufl.dx - theta / gamma_scaled * sigma_n(du) * sigma_n(v) * ds(bottom_value) J += 1 / gamma_scaled * 0.5 * (1 - ufl.sign(q)) * (sigma_n(du) - gamma_scaled * ufl.dot(du, n_2)) * \ (theta * sigma_n(v) - gamma_scaled * ufl.dot(v, n_2)) * ds(bottom_value) # Nitsche for Dirichlet, another theta-scheme. # https://doi.org/10.1016/j.cma.2018.05.024 if nitsche_bc: disp_vec = np.zeros(mesh.geometry.dim) disp_vec[mesh.geometry.dim - 1] = vertical_displacement u_D = ufl.as_vector(disp_vec) F += - ufl.inner(sigma(u) * n, v) * ds(top_value)\ - theta * ufl.inner(sigma(v) * n, u - u_D) * \ ds(top_value) + gamma_scaled / h * ufl.inner(u - u_D, v) * ds(top_value) bcs = [] J += - ufl.inner(sigma(du) * n, v) * ds(top_value)\ - theta * ufl.inner(sigma(v) * n, du) * \ ds(top_value) + gamma_scaled / h * ufl.inner(du, v) * ds(top_value) else: # strong Dirichlet boundary conditions def _u_D(x): values = np.zeros((mesh.geometry.dim, x.shape[1])) values[mesh.geometry.dim - 1] = vertical_displacement return values u_D = _fem.Function(V) u_D.interpolate(_u_D) u_D.name = "u_D" u_D.x.scatter_forward() tdim = mesh.topology.dim dirichlet_dofs = _fem.locate_dofs_topological(V, tdim - 1, facet_marker.find(top_value)) bc = _fem.dirichletbc(u_D, dirichlet_dofs) bcs = [bc] # DEBUG: Write each step of Newton iterations # Create nonlinear problem and Newton solver # def form(self, x: _PETSc.Vec): # x.ghostUpdate(addv=_PETSc.InsertMode.INSERT, mode=_PETSc.ScatterMode.FORWARD) # self.i += 1 # xdmf.write_function(u, self.i) # setattr(_fem.petsc.NonlinearProblem, "form", form) problem = _fem.petsc.NonlinearProblem(F, u, bcs, J=J, jit_params=jit_params, form_compiler_params=form_compiler_params) # DEBUG: Write each step of Newton iterations # problem.i = 0 # xdmf = _io.XDMFFile(mesh.comm, "results/tmp_sol.xdmf", "w") # xdmf.write_mesh(mesh) solver = _nls.petsc.NewtonSolver(mesh.comm, problem) null_space = rigid_motions_nullspace(V) solver.A.setNearNullSpace(null_space) # Set Newton solver options solver.atol = newton_options.get("atol", 1e-9) solver.rtol = newton_options.get("rtol", 1e-9) solver.convergence_criterion = newton_options.get("convergence_criterion", "incremental") solver.max_it = newton_options.get("max_it", 50) solver.error_on_nonconvergence = newton_options.get("error_on_nonconvergence", True) solver.relaxation_parameter = newton_options.get("relaxation_parameter", 0.8) def _u_initial(x): values = np.zeros((mesh.geometry.dim, x.shape[1])) values[-1] = -0.01 - plane_loc return values # Set initial_condition: u.interpolate(_u_initial) # Define solver and options ksp = solver.krylov_solver opts = _PETSc.Options() option_prefix = ksp.getOptionsPrefix() # Set PETSc options opts = _PETSc.Options() opts.prefixPush(option_prefix) for k, v in petsc_options.items(): opts[k] = v opts.prefixPop() ksp.setFromOptions() # Solve non-linear problem _log.set_log_level(_log.LogLevel.INFO) num_dofs_global = V.dofmap.index_map_bs * V.dofmap.index_map.size_global with _common.Timer(f"{num_dofs_global} Solve Nitsche"): n, converged = solver.solve(u) u.x.scatter_forward() if solver.error_on_nonconvergence: assert(converged) print(f"{num_dofs_global}, Number of interations: {n:d}") return u