def run_vector_test(mesh, V, degree): """Projection into H(div/curl) spaces""" u, v = ufl.TrialFunction(V), ufl.TestFunction(V) a = inner(u, v) * dx # Source term x = SpatialCoordinate(mesh) u_exact = x[0]**degree L = inner(u_exact, v[0]) * dx with common.Timer("Assemble vector"): b = assemble_vector(L) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) with common.Timer("Assemble matrix"): A = assemble_matrix(a) A.assemble() with common.Timer("Solve"): # Create LU linear solver (Note: need to use a solver that # re-orders to handle pivots, e.g. not the PETSc built-in LU solver) solver = PETSc.KSP().create(MPI.COMM_WORLD) solver.setType("preonly") solver.getPC().setType('lu') solver.setOperators(A) # 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"): # Calculate error M = (u_exact - uh[0])**2 * dx for i in range(1, mesh.topology.dim): M += uh[i]**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
def test_context_manager_named(): """Test that named Timer works as context manager""" task = get_random_task_name() # Execute task in the context manager t = common.Timer(task) sleep(0.05) assert t.elapsed()[0] >= 0.05 del t # Check timing t = common.timing(task) assert t[0] == 1 assert t[1] >= 0.05
def mesh_3D_dolfin(theta: float = 0, ct: _mesh.CellType = _mesh.CellType.tetrahedron, ext: str = "tetrahedron", res: float = 0.1): timer = _common.Timer("~~Contact: Create mesh") def find_plane_function(p0, p1, p2): """ Find plane function given three points: http://www.nabla.hr/CG-LinesPlanesIn3DA3.htm """ v1 = np.array(p1) - np.array(p0) v2 = np.array(p2) - np.array(p0) n = np.cross(v1, v2) D = -(n[0] * p0[0] + n[1] * p0[1] + n[2] * p0[2]) return lambda x: np.isclose(0, np.dot(n, x) + D) def over_plane(p0, p1, p2): """ Returns function that checks if a point is over a plane defined by the points p0, p1 and p2. """ v1 = np.array(p1) - np.array(p0) v2 = np.array(p2) - np.array(p0) n = np.cross(v1, v2) D = -(n[0] * p0[0] + n[1] * p0[1] + n[2] * p0[2]) return lambda x: n[0] * x[0] + n[1] * x[1] + D > -n[2] * x[2] N = int(1 / res) if MPI.COMM_WORLD.rank == 0: mesh0 = _mesh.create_unit_cube(MPI.COMM_SELF, N, N, N, ct) mesh1 = _mesh.create_unit_cube(MPI.COMM_SELF, 2 * N, 2 * N, 2 * N, ct) mesh0.geometry.x[:, 2] += 1 # Stack the two meshes in one mesh r_matrix = _utils.rotation_matrix([1 / np.sqrt(2), 1 / np.sqrt(2), 0], -theta) points = np.vstack([mesh0.geometry.x, mesh1.geometry.x]) points = np.dot(r_matrix, points.T).T # Transform topology info into geometry info tdim0 = mesh0.topology.dim num_cells0 = mesh0.topology.index_map(tdim0).size_local cells0 = _cpp.mesh.entities_to_geometry( mesh0, tdim0, np.arange(num_cells0, dtype=np.int32).reshape((-1, 1)), False) tdim1 = mesh1.topology.dim num_cells1 = mesh1.topology.index_map(tdim1).size_local cells1 = _cpp.mesh.entities_to_geometry( mesh1, tdim1, np.arange(num_cells1, dtype=np.int32).reshape((-1, 1)), False) cells1 += mesh0.geometry.x.shape[0] cells = np.vstack([cells0, cells1]) cell = ufl.Cell(ext, geometric_dimension=points.shape[1]) domain = ufl.Mesh(ufl.VectorElement("Lagrange", cell, 1)) mesh = _mesh.create_mesh(MPI.COMM_SELF, cells, points, domain) tdim = mesh.topology.dim fdim = tdim - 1 # Find information about facets to be used in meshtags bottom_points = np.dot( r_matrix, np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0]]).T) bottom = find_plane_function(bottom_points[:, 0], bottom_points[:, 1], bottom_points[:, 2]) bottom_facets = _mesh.locate_entities_boundary(mesh, fdim, bottom) top_points = np.dot( r_matrix, np.array([[0, 0, 2], [1, 0, 2], [0, 1, 2], [1, 1, 2]]).T) top = find_plane_function(top_points[:, 0], top_points[:, 1], top_points[:, 2]) top_facets = _mesh.locate_entities_boundary(mesh, fdim, top) # left_side = find_line_function(top_points[:, 0], top_points[:, 3]) # left_facets = _mesh.locate_entities_boundary( # mesh, fdim, left_side) # right_side = find_line_function(top_points[:, 1], top_points[:, 2]) # right_facets = _mesh.locate_entities_boundary( # mesh, fdim, right_side) if_points = np.dot( r_matrix, np.array([[0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]]).T) interface = find_plane_function(if_points[:, 0], if_points[:, 1], if_points[:, 2]) i_facets = _mesh.locate_entities_boundary(mesh, fdim, interface) mesh.topology.create_connectivity(fdim, tdim) top_interface = [] bottom_interface = [] facet_to_cell = mesh.topology.connectivity(fdim, tdim) num_cells = mesh.topology.index_map(tdim).size_local cell_midpoints = _cpp.mesh.compute_midpoints(mesh, tdim, range(num_cells)) top_cube = over_plane(if_points[:, 0], if_points[:, 1], if_points[:, 2]) for facet in i_facets: i_cells = facet_to_cell.links(facet) assert (len(i_cells == 1)) i_cell = i_cells[0] if top_cube(cell_midpoints[i_cell]): top_interface.append(facet) else: bottom_interface.append(facet) num_cells = mesh.topology.index_map(tdim).size_local cell_midpoints = _cpp.mesh.compute_midpoints(mesh, tdim, range(num_cells)) top_cube_marker = 2 indices = [] values = [] for cell_index in range(num_cells): if top_cube(cell_midpoints[cell_index]): indices.append(cell_index) values.append(top_cube_marker) ct = _mesh.meshtags(mesh, tdim, np.array(indices, dtype=np.int32), np.array(values, dtype=np.int32)) # Create meshtags for facet data markers: Dict[int, np.ndarray] = { 3: top_facets, 4: np.hstack(bottom_interface), 9: np.hstack(top_interface), 5: bottom_facets } # , 6: left_facets, 7: right_facets} all_indices = [] all_values = [] for key in markers.keys(): all_indices.append(np.asarray(markers[key], dtype=np.int32)) all_values.append(np.full(len(markers[key]), key, dtype=np.int32)) arg_sort = np.argsort(np.hstack(all_indices)) sorted_vals = np.asarray(np.hstack(all_values)[arg_sort], dtype=np.int32) sorted_indices = np.asarray(np.hstack(all_indices)[arg_sort], dtype=np.int32) mt = _mesh.meshtags(mesh, fdim, sorted_indices, sorted_vals) mt.name = "facet_tags" # type: ignore fname = f"meshes/mesh_{ext}_{theta:.2f}.xdmf" with _io.XDMFFile(MPI.COMM_SELF, fname, "w") as o_f: o_f.write_mesh(mesh) o_f.write_meshtags(ct) o_f.write_meshtags(mt) timer.stop()
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
def run_dg_test(mesh, V, degree): """ Manufactured Poisson problem, solving u = x[component]**n, where n is the degree of the Lagrange function space. """ u, v = TrialFunction(V), TestFunction(V) # Exact solution x = SpatialCoordinate(mesh) u_exact = x[1]**degree # Coefficient k = Function(V) k.vector.set(2.0) k.vector.ghostUpdate(addv=PETSc.InsertMode.INSERT, mode=PETSc.ScatterMode.FORWARD) # Source term f = -div(k * grad(u_exact)) # Mesh normals and element size n = FacetNormal(mesh) h = CellDiameter(mesh) h_avg = (h("+") + h("-")) / 2.0 # Penalty parameter alpha = 32 dx_ = dx(metadata={"quadrature_degree": -1}) ds_ = ds(metadata={"quadrature_degree": -1}) dS_ = dS(metadata={"quadrature_degree": -1}) with common.Timer("Compile forms"): a = inner(k * grad(u), grad(v)) * dx_ \ - k("+") * inner(avg(grad(u)), jump(v, n)) * dS_ \ - k("+") * inner(jump(u, n), avg(grad(v))) * dS_ \ + k("+") * (alpha / h_avg) * inner(jump(u, n), jump(v, n)) * dS_ \ - inner(k * grad(u), v * n) * ds_ \ - inner(u * n, k * grad(v)) * ds_ \ + (alpha / h) * inner(k * u, v) * ds_ L = inner(f, v) * dx_ - inner(k * u_exact * n, grad(v)) * ds_ \ + (alpha / h) * inner(k * u_exact, v) * ds_ for integral in a.integrals(): integral.metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree( a) for integral in L.integrals(): integral.metadata( )["quadrature_degree"] = ufl.algorithms.estimate_total_polynomial_degree( L) with common.Timer("Assemble vector"): b = assemble_vector(L) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) with common.Timer("Assemble matrix"): A = assemble_matrix(a, []) A.assemble() with common.Timer("Solve"): # 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 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"): # Calculate error 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
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_context_manager_anonymous(): """Test that anonymous Timer works as context manager""" with common.Timer() as t: sleep(0.05) assert t.elapsed()[0] >= 0.05
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