def test_lhs_rhs_simple(): """Test taking lhs/rhs of DOLFINX specific forms (constants without cell). """ mesh = RectangleMesh( MPI.COMM_WORLD, [numpy.array([0.0, 0.0, 0.0]), numpy.array([2.0, 1.0, 0.0])], [3, 5], CellType.triangle) V = FunctionSpace(mesh, "CG", 1) f = 2.0 g = 3.0 v = TestFunction(V) u = TrialFunction(V) F = inner(g * grad(f * v), grad(u)) * dx + f * v * dx a, L = system(F) Fl = lhs(F) Fr = rhs(F) assert (Fr) a0 = inner(grad(v), grad(u)) * dx n = assemble(a).norm("frobenius") # noqa nl = assemble(Fl).norm("frobenius") # noqa n0 = 6.0 * assemble(a0).norm("frobenius") # noqa assert round(n - n0, 7) == 0 assert round(n - nl, 7) == 0
def mesh2d(): """Create 2D mesh with one equilateral triangle""" mesh2d = RectangleMesh( MPI.comm_world, [np.array([0.0, 0.0, 0.0]), np.array([1., 1., 0.0])], [1, 1], CellType.triangle, cpp.mesh.GhostMode.none, 'left') mesh2d.geometry.points[3, :2] += 0.5 * (math.sqrt(3.0) - 1.0) return mesh2d
def mesh2d(): """Create 2D mesh with one equilateral triangle""" mesh2d = RectangleMesh( MPI.COMM_WORLD, [np.array([0.0, 0.0, 0.0]), np.array([1., 1., 0.0])], [1, 1], CellType.triangle, cpp.mesh.GhostMode.none, '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 index(): r"""Use simple flas application to serve dolfinX.""" RectangleMesh( MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([1, 1, 0])], [32, 32], CellType.triangle, dolfinx.cpp.mesh.GhostMode.none) d = {"numbers": list(range(10)), 'method': request.method} if request.method == 'POST': data = request.form name = data['name'] d = None if name == "numbers": d = {"numbers": list(range(10)), 'method': request.method} return jsonify(d)
def rectangle(): return RectangleMesh( MPI.COMM_WORLD, [np.array([0.0, 0.0, 0.0]), np.array([2.0, 2.0, 0.0])], [5, 5], CellType.triangle, cpp.mesh.GhostMode.none)
T = wx / c0 + 2.0 / f0 omega = 2 * np.pi * f0 degree = 2 # discretisation degree nPerLam = 4 # no. elements per wavelength CFL = 0.9 # CFL constant lam = 2 * np.pi * c0 / omega # wavelength h = lam / nPerLam # element dimension period = 1.0 / f0 # temporal period nx = np.int(np.round(wx / h)) ny = np.int(np.round(wy / h)) # Build mesh mesh = RectangleMesh(MPI.COMM_WORLD, [np.array([0, -wy/2, 0]), np.array([wx, wy/2, 0])], [nx, ny], CellType.quadrilateral, dolfinx.cpp.mesh.GhostMode.none) # Determine timestep for Runge-Kutta via CFL hmin = min(MPI.COMM_WORLD.allgather(mesh.hmin())) dt = CFL * hmin / (c0 * (2 * degree + 1)) # CFL condition # Adjust dt so that it exactly divides one period n_period = np.int(np.ceil(period / dt)) dt = period / n_period num_steps = np.int(np.ceil(T / dt)) if (MPI.COMM_WORLD.rank == 0): print("#time steps = ", num_steps) # Create model
import ufl from dolfinx import DirichletBC, Function, FunctionSpace, RectangleMesh from dolfinx.cpp.mesh import CellType from dolfinx.fem import locate_dofs_geometrical, locate_dofs_topological from dolfinx.io import XDMFFile from dolfinx.mesh import locate_entities_boundary from mpi4py import MPI from petsc4py import PETSc from ufl import div, dx, grad, inner # We create a Mesh and attach a coordinate map to the mesh:: # Create mesh mesh = RectangleMesh(MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([1, 1, 0])], [32, 32], CellType.triangle, dolfinx.cpp.mesh.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)
from dolfinx.cpp.mesh import CellType from dolfinx.fem import locate_dofs_topological from dolfinx.io import XDMFFile from dolfinx.mesh import locate_entities_boundary from ufl import ds, dx, grad, inner # 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:`UnitSquareMesh <dolfinx.cpp.UnitSquareMesh>`. 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 = RectangleMesh( MPI.COMM_WORLD, [np.array([0, 0, 0]), np.array([1, 1, 0])], [32, 32], CellType.triangle, dolfinx.cpp.mesh.GhostMode.none) V = FunctionSpace(mesh, ("Lagrange", 1)) # The second argument to :py:class:`FunctionSpace # <dolfinx.function.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
def test_div_grad_then_integrate_over_cells_and_boundary(): # Define 2D geometry n = 10 mesh = RectangleMesh( [numpy.array([0.0, 0.0, 0.0]), numpy.array([2.0, 3.0, 0.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
import matplotlib.pyplot as plt import numpy as np from mpi4py import MPI from petsc4py import PETSc import dolfinx import dolfinx.plotting import ufl from dolfinx import DirichletBC, Function, FunctionSpace, RectangleMesh, solve from dolfinx.cpp.mesh import CellType from dolfinx.fem import locate_dofs_topological from dolfinx.mesh import locate_entities_boundary from ufl import ds, dx, grad, inner mesh = RectangleMesh( MPI.COMM_WORLD, [np.array([0., 0., 0.]), np.array([1., 1., 0.])], [128, 128], CellType.triangle, dolfinx.cpp.mesh.GhostMode.none) V = FunctionSpace(mesh, ("Lagrange", 1)) u0 = Function(V) u0.vector.set(0.) facets = locate_entities_boundary(mesh, 1, lambda x: np.logical_or(x[0] < np.finfo(float).eps, x[0] > 1.-np.finfo(float).eps) ) bc = DirichletBC(u0, local_dofs_topological(V, 1, facets)) u = ufl.TrialFunction(V) v = ufl.TrialFunction(V) x = ufl.SpatialCoordinate(mesh) f = ufl.mathfunctions.ConstantValue(20.)
# The constant sigma_0 is chosen to achieve a specified "round-trip" reflection # of a wave that through the layer, reflects and returns back into the domain. # See Oskooi et al. (2008) for more details. RT = 1.0e-6 # round-trip reflection sigma0 = -(deg_absorb + 1) * np.log(RT) / (2.0 * d_absorb) ''' Meshing ''' # For this problem we use a square mesh with triangular elements. # The mesh element size is h_elem, and the #elements in one dimension is n_elem h_elem = wave_len / n_wave n_elem_x = int(np.round(dim_x / h_elem)) n_elem_y = int(np.round(dim_y / h_elem)) # Create mesh mesh = RectangleMesh(MPI.COMM_WORLD, [ np.array([-dim_x / 2, -dim_y / 2, 0]), np.array([dim_x / 2, dim_y / 2, 0]) ], [n_elem_x, n_elem_y], CellType.triangle, dolfinx.cpp.mesh.GhostMode.none) ''' Incident field, wavenumber and adiabatic absorber functions ''' def incident(x): # Plane wave travelling in positive x-direction return np.exp(1.0j * k0 * x[0]) def wavenumber(x): # Create sonic crystal structure x_start = -0.5 * (nx - 1) * (2 * rad_crys + gap) y_start = -0.5 * (ny - 1) * (2 * rad_crys + gap) for i in range(nx): for j in range(ny):
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)
from dolfinx.io import XDMFFile from dolfinx.mesh import compute_marked_boundary_entities from dolfinx.fem import locate_dofs_topological from dolfinx.specialfunctions import SpatialCoordinate from ufl import ds, dx, grad, inner # 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:`UnitSquareMesh <dolfinx.cpp.UnitSquareMesh>`. 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 = RectangleMesh( MPI.comm_world, [np.array([0, 0, 0]), np.array([1, 1, 0])], [32, 32], CellType.triangle, dolfinx.cpp.mesh.GhostMode.none) V = FunctionSpace(mesh, ("Lagrange", 1)) cmap = dolfinx.fem.create_coordinate_map(mesh.ufl_domain()) mesh.geometry.coord_mapping = cmap # The second argument to :py:class:`FunctionSpace # <dolfinx.function.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