def test_submesh_cell_assembly(d, n, k, space, ghost_mode): """Check that assembling a form over a unit square gives the same result as assembling over half of a 2x1 rectangle with the same triangulation.""" if d == 2: mesh_0 = create_unit_square( MPI.COMM_WORLD, n, n, ghost_mode=ghost_mode) mesh_1 = create_rectangle( MPI.COMM_WORLD, ((0.0, 0.0), (2.0, 1.0)), (2 * n, n), ghost_mode=ghost_mode) else: mesh_0 = create_unit_cube( MPI.COMM_WORLD, n, n, n, ghost_mode=ghost_mode) mesh_1 = create_box( MPI.COMM_WORLD, ((0.0, 0.0, 0.0), (2.0, 1.0, 1.0)), (2 * n, n, n), ghost_mode=ghost_mode) A_mesh_0 = assemble(mesh_0, space, k) edim = mesh_1.topology.dim entities = locate_entities(mesh_1, edim, lambda x: x[0] <= 1.0) submesh = create_submesh(mesh_1, edim, entities)[0] A_submesh = assemble(submesh, space, k) # FIXME Would probably be better to compare entries rather than just # norms assert(np.isclose(A_mesh_0.norm(), A_submesh.norm()))
def mesh2d(): """Create 2D mesh with one equilateral triangle""" mesh2d = create_rectangle( MPI.COMM_WORLD, [np.array([0.0, 0.0]), np.array([1., 1.])], [1, 1], CellType.triangle, GhostMode.none, create_cell_partitioner(), DiagonalType.left) i1 = np.where((mesh2d.geometry.x == (1, 1, 0)).all(axis=1))[0][0] mesh2d.geometry.x[i1, :2] += 0.5 * (math.sqrt(3.0) - 1.0) return mesh2d
def test_basic_interior_facet_assembly(): mesh = create_rectangle(MPI.COMM_WORLD, [np.array([0.0, 0.0]), np.array([1.0, 1.0])], [5, 5], cell_type=CellType.triangle, ghost_mode=GhostMode.shared_facet) V = FunctionSpace(mesh, ("DG", 1)) u, v = ufl.TrialFunction(V), ufl.TestFunction(V) a = ufl.inner(ufl.avg(u), ufl.avg(v)) * ufl.dS a = form(a) A = assemble_matrix(a) A.assemble() assert isinstance(A, PETSc.Mat) L = ufl.conj(ufl.avg(v)) * ufl.dS L = form(L) b = assemble_vector(L) b.assemble() assert isinstance(b, PETSc.Vec)
from ufl import ds, dx, grad, inner from mpi4py import MPI from petsc4py.PETSc import ScalarType # - # We begin by using {py:func}`create_rectangle # <dolfinx.mesh.create_rectangle>` to create a rectangular # {py:class}`Mesh <dolfinx.mesh.Mesh>` of the domain, and creating a # finite element {py:class}`FunctionSpace <dolfinx.fem.FunctionSpace>` # $V$ on the mesh. # + msh = mesh.create_rectangle( comm=MPI.COMM_WORLD, points=((0.0, 0.0), (2.0, 1.0)), n=(32, 16), cell_type=mesh.CellType.triangle, ) V = fem.FunctionSpace(msh, ("Lagrange", 1)) # - # The second argument to {py:class}`FunctionSpace # <dolfinx.fem.FunctionSpace>` is a tuple consisting of `(family, # degree)`, where `family` is the finite element family, and `degree` # specifies the polynomial degree. in this case `V` consists of # first-order, continuous Lagrange finite element functions. # # Next, we locate the mesh facets that lie on the boundary $\Gamma_D$. # 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
from dolfinx import common, fem, mesh, plot from mpi4py import MPI # - # SciPy solvers do no support MPI, so all computations are performed on # a single MPI rank # + comm = MPI.COMM_SELF # - # Create a mesh and function space msh = mesh.create_rectangle(comm=comm, points=((0.0, 0.0), (2.0, 1.0)), n=(32, 16), cell_type=mesh.CellType.triangle) V = fem.FunctionSpace(msh, ("Lagrange", 1)) # Define a variartional problem u, v = ufl.TrialFunction(V), ufl.TestFunction(V) x = ufl.SpatialCoordinate(msh) fr = 10 * ufl.exp(-((x[0] - 0.5) ** 2 + (x[1] - 0.5) ** 2) / 0.02) fc = ufl.sin(2 * np.pi * x[0]) + 10 * ufl.sin(4 * np.pi * x[1]) * 1j gr = ufl.sin(5 * x[0]) gc = ufl.sin(5 * x[0]) * 1j a = ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx L = ufl.inner(fr + fc, v) * ufl.dx + ufl.inner(gr + gc, v) * ufl.ds # In preparation for constructing Dirichlet boundary conditions, locate # facets on the constrained boundary and the corresponding
from dolfinx.mesh import CellType, create_rectangle, locate_entities_boundary from ufl import ds, dx, grad, inner from mpi4py import MPI from petsc4py.PETSc import ScalarType # We begin by defining a mesh of the domain and a finite element # function space :math:`V` relative to this mesh. As the unit square is # a very standard domain, we can use a built-in mesh provided by the # class :py:class:`create_unit_square_mesh # <dolfinx.mesh.create_unit_square_mesh>`. In order to create a mesh # consisting of 32 x 32 squares with each square divided into two # triangles, we do as follows :: # Create mesh and define function space mesh = create_rectangle(MPI.COMM_WORLD, ((0.0, 0.0), (2.0, 1.0)), (32, 16), CellType.triangle) V = FunctionSpace(mesh, ("Lagrange", 1)) # The second argument to :py:class:`FunctionSpace # <dolfinx.fem.FunctionSpace>` is the finite element family, while the # third argument specifies the polynomial degree. Thus, in this case, # our space ``V`` consists of first-order, continuous Lagrange finite # element functions (or in order words, continuous piecewise linear # polynomials). # # Next, we want to consider the Dirichlet boundary condition. A simple # Python function, returning a boolean, can be used to define the # boundary for the Dirichlet boundary condition (:math:`\Gamma_D`). The # function should return ``True`` for those points inside the boundary # and ``False`` for the points outside. In our case, we want to say that # the points :math:`(x, y)` such that :math:`x = 0` or :math:`x = 1` are
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 = create_rectangle( MPI.COMM_WORLD, [np.array([0.0, 0.0]), np.array([1.0, 1.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 = form(inner(sigma_S, tau_S) * dx - b(tau_S, u) + b(sigma_S, v)) L = form(inner(f_exact, v) * dx) V_1 = V.sub(1).collapse()[0] zero_u = Function(V_1) zero_u.x.array[:] = 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=[bcs]) b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE) set_bc(b, bcs) # 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.x.scatter_forward() # Recall that x_h has flattened indices. u_error_numerator = np.sqrt( mesh.comm.allreduce(assemble_scalar( form( 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.comm.allreduce(assemble_scalar( form( 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.comm.allreduce(assemble_scalar( form( inner(sigma_exact - sigma_h, sigma_exact - sigma_h) * dx(mesh, metadata={"quadrature_degree": 5}))), op=MPI.SUM)) sigma_error_denominator = np.sqrt( mesh.comm.allreduce(assemble_scalar( form( 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
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()
locate_dofs_geometrical, locate_dofs_topological) from dolfinx.io import XDMFFile from dolfinx.mesh import (CellType, GhostMode, create_rectangle, locate_entities_boundary) from ufl import div, dx, grad, inner from mpi4py import MPI from petsc4py import PETSc # - # We create a Mesh and attach a coordinate map to the mesh: # + # Create mesh msh = create_rectangle(MPI.COMM_WORLD, [np.array([0, 0]), np.array([1, 1])], [32, 32], CellType.triangle, GhostMode.none) # Function to mark x = 0, x = 1 and y = 0 def noslip_boundary(x): return np.logical_or( np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0)), np.isclose(x[1], 0.0)) # Function to mark the lid (y = 1) def lid(x): return np.isclose(x[1], 1.0)
def test_div_grad_then_integrate_over_cells_and_boundary(): # Define 2D geometry n = 10 mesh = create_rectangle( [np.array([0.0, 0.0]), np.array([2.0, 3.0])], 2 * n, 3 * n) x, y = SpatialCoordinate(mesh) xs = 0.1 + 0.8 * x / 2 # scaled to be within [0.1,0.9] # ys = 0.1 + 0.8 * y / 3 # scaled to be within [0.1,0.9] n = FacetNormal(mesh) # Define list of expressions to test, and configure accuracies # these expressions are known to pass with. The reason some # functions are less accurately integrated is likely that the # default choice of quadrature rule is not perfect F_list = [] def reg(exprs, acc=10): for expr in exprs: F_list.append((expr, acc)) # FIXME: 0*dx and 1*dx fails in the ufl-ffcx-jit framework somewhere # reg([Constant(0.0, cell=cell)]) # reg([Constant(1.0, cell=cell)]) monomial_list = [x**q for q in range(2, 6)] reg(monomial_list) reg([2.3 * p + 4.5 * q for p in monomial_list for q in monomial_list]) reg([xs**xs]) reg( [xs**(xs**2)], 8 ) # Note: Accuracies here are from 1D case, not checked against 2D results. reg([xs**(xs**3)], 6) reg([xs**(xs**4)], 2) # Special functions: reg([atan(xs)], 8) reg([sin(x), cos(x), exp(x)], 5) reg([ln(xs), pow(x, 2.7), pow(2.7, x)], 3) reg([asin(xs), acos(xs)], 1) reg([tan(xs)], 7) # To handle tensor algebra, make an x dependent input tensor # xx and square all expressions def reg2(exprs, acc=10): for expr in exprs: F_list.append((inner(expr, expr), acc)) xx = as_matrix([[2 * x**2, 3 * x**3], [11 * x**5, 7 * x**4]]) xxs = as_matrix([[2 * xs**2, 3 * xs**3], [11 * xs**5, 7 * xs**4]]) x3v = as_vector([3 * x**2, 5 * x**3, 7 * x**4]) cc = as_matrix([[2, 3], [4, 5]]) reg2( [xx] ) # TODO: Make unit test for UFL from this, results in listtensor with free indices reg2([x3v]) reg2([cross(3 * x3v, as_vector([-x3v[1], x3v[0], x3v[2]]))]) reg2([xx.T]) reg2([tr(xx)]) reg2([det(xx)]) reg2([dot(xx, 0.1 * xx)]) reg2([outer(xx, xx.T)]) reg2([dev(xx)]) reg2([sym(xx)]) reg2([skew(xx)]) reg2([elem_mult(7 * xx, cc)]) reg2([elem_div(7 * xx, xx + cc)]) reg2([elem_pow(1e-3 * xxs, 1e-3 * cc)]) reg2([elem_pow(1e-3 * cc, 1e-3 * xx)]) reg2([elem_op(lambda z: sin(z) + 2, 0.03 * xx)], 2) # pretty inaccurate... # FIXME: Add tests for all UFL operators: # These cause discontinuities and may be harder to test in the # above fashion: # 'inv', 'cofac', # 'eq', 'ne', 'le', 'ge', 'lt', 'gt', 'And', 'Or', 'Not', # 'conditional', 'sign', # 'jump', 'avg', # 'LiftingFunction', 'LiftingOperator', # FIXME: Test other derivatives: (but algorithms for operator # derivatives are the same!): # 'variable', 'diff', # 'Dx', 'grad', 'div', 'curl', 'rot', 'Dn', 'exterior_derivative', # Run through all operators defined above and compare integrals debug = 0 if debug: F_list = F_list[1:] for F, acc in F_list: if debug: print('\n', "F:", str(F)) # Integrate over domain and its boundary int_dx = assemble(div(grad(F)) * dx(mesh)) # noqa int_ds = assemble(dot(grad(F), n) * ds(mesh)) # noqa if debug: print(int_dx, int_ds) # Compare results. Using custom relative delta instead of # decimal digits here because some numbers are >> 1. delta = min(abs(int_dx), abs(int_ds)) * 10**-acc assert int_dx - int_ds <= delta
def rectangle(): return create_rectangle( MPI.COMM_WORLD, [np.array([0.0, 0.0]), np.array([2.0, 2.0])], [5, 5], CellType.triangle, GhostMode.none)