def test_assembly_into_quadrature_function(): """Test assembly into a Quadrature function. This test evaluates a UFL Expression into a Quadrature function space by evaluating the Expression on all cells of the mesh, and then inserting the evaluated values into a PETSc Vector constructed from a matching Quadrature function space. Concretely, we consider the evaluation of: e = B*(K(T)))**2 * grad(T) where K = 1/(A + B*T) where A and B are Constants and T is a Coefficient on a P2 finite element space with T = x + 2*y. The result is compared with interpolating the analytical expression of e directly into the Quadrature space. In parallel, each process evaluates the Expression on both local cells and ghost cells so that no parallel communication is required after insertion into the vector. """ mesh = dolfinx.UnitSquareMesh(MPI.COMM_WORLD, 3, 6) quadrature_degree = 2 quadrature_points = basix.make_quadrature(basix.CellType.triangle, quadrature_degree) Q_element = ufl.VectorElement("Quadrature", ufl.triangle, quadrature_degree, quad_scheme="default") Q = dolfinx.FunctionSpace(mesh, Q_element) def T_exact(x): return x[0] + 2.0 * x[1] P2 = dolfinx.FunctionSpace(mesh, ("P", 2)) T = dolfinx.Function(P2) T.interpolate(T_exact) A = dolfinx.Constant(mesh, 1.0) B = dolfinx.Constant(mesh, 2.0) K = 1.0 / (A + B * T) e = B * K**2 * ufl.grad(T) e_expr = dolfinx.Expression(e, quadrature_points) map_c = mesh.topology.index_map(mesh.topology.dim) num_cells = map_c.size_local + map_c.num_ghosts cells = np.arange(0, num_cells, dtype=np.int32) e_eval = e_expr.eval(cells) # Assemble into Function e_Q = dolfinx.Function(Q) with e_Q.vector.localForm() as e_Q_local: e_Q_local.setBlockSize(e_Q.function_space.dofmap.bs) e_Q_local.setValuesBlocked(Q.dofmap.list.array, e_eval, addv=PETSc.InsertMode.INSERT) def e_exact(x): T = x[0] + 2.0 * x[1] K = 1.0 / (A.value + B.value * T) grad_T = np.zeros((2, x.shape[1])) grad_T[0, :] = 1.0 grad_T[1, :] = 2.0 e = B.value * K**2 * grad_T return e e_exact_Q = dolfinx.Function(Q) e_exact_Q.interpolate(e_exact) assert np.isclose((e_exact_Q.vector - e_Q.vector).norm(), 0.0)
def test_assembly_into_quadrature_function(): """Test assembly into a Quadrature function. This test evaluates a UFL Expression into a Quadrature function space by evaluating the Expression on all cells of the mesh, and then inserting the evaluated values into a PETSc Vector constructed from a matching Quadrature function space. Concretely, we consider the evaluation of: e = B*(K(T)))**2 * grad(T) where K = 1/(A + B*T) where A and B are Constants and T is a Coefficient on a P2 finite element space with T = x + 2*y. The result is compared with interpolating the analytical expression of e directly into the Quadrature space. In parallel, each process evaluates the Expression on both local cells and ghost cells so that no parallel communication is required after insertion into the vector. """ mesh = dolfinx.UnitSquareMesh(MPI.COMM_WORLD, 3, 6) quadrature_degree = 2 quadrature_points, wts = basix.make_quadrature("default", basix.CellType.triangle, quadrature_degree) Q_element = ufl.VectorElement("Quadrature", ufl.triangle, quadrature_degree, quad_scheme="default") Q = dolfinx.FunctionSpace(mesh, Q_element) def T_exact(x): return x[0] + 2.0 * x[1] P2 = dolfinx.FunctionSpace(mesh, ("P", 2)) T = dolfinx.Function(P2) T.interpolate(T_exact) A = dolfinx.Constant(mesh, 1.0) B = dolfinx.Constant(mesh, 2.0) K = 1.0 / (A + B * T) e = B * K**2 * ufl.grad(T) e_expr = dolfinx.Expression(e, quadrature_points) map_c = mesh.topology.index_map(mesh.topology.dim) num_cells = map_c.size_local + map_c.num_ghosts cells = np.arange(0, num_cells, dtype=np.int32) e_eval = e_expr.eval(cells) # Assemble into Function e_Q = dolfinx.Function(Q) with e_Q.vector.localForm() as e_Q_local: e_Q_local.setBlockSize(e_Q.function_space.dofmap.bs) e_Q_local.setValuesBlocked(Q.dofmap.list.array, e_eval, addv=PETSc.InsertMode.INSERT) def e_exact(x): T = x[0] + 2.0 * x[1] K = 1.0 / (A.value + B.value * T) grad_T = np.zeros((2, x.shape[1])) grad_T[0, :] = 1.0 grad_T[1, :] = 2.0 e = B.value * K**2 * grad_T return e # FIXME: Below is only for testing purposes, # never to be used in user code! # # Replace when interpolation into Quadrature element works. coord_dofs = mesh.geometry.dofmap x_g = mesh.geometry.x tdim = mesh.topology.dim Q_dofs = Q.dofmap.list.array.reshape(num_cells, quadrature_points.shape[0]) bs = Q.dofmap.bs Q_dofs_unrolled = bs * np.repeat(Q_dofs, bs).reshape(-1, bs) + np.arange(bs) Q_dofs_unrolled = Q_dofs_unrolled.reshape( -1, bs * quadrature_points.shape[0]).astype(Q_dofs.dtype) with e_Q.vector.localForm() as local: e_exact_eval = np.zeros_like(local.array) for cell in range(num_cells): xg = x_g[coord_dofs.links(cell), :tdim] x = mesh.geometry.cmap.push_forward(quadrature_points, xg) e_exact_eval[Q_dofs_unrolled[cell]] = e_exact(x.T).T.flatten() assert np.allclose(local.array, e_exact_eval)
def test_simple_evaluation(): """Test evaluation of UFL Expression. This test evaluates a UFL Expression on cells of the mesh and compares the result with an analytical expression. For a function f(x, y) = 3*(x^2 + 2*y^2) the result is compared with the exact gradient: grad f(x, y) = 3*[2*x, 4*y]. (x^2 + 2*y^2) is first interpolated into a P2 finite element space. The scaling by a constant factor of 3 and the gradient is calculated using code generated by FFCX. The analytical solution is found by evaluating the spatial coordinates as an Expression using UFL/FFCX and passing the result to a numpy function that calculates the exact gradient. """ mesh = dolfinx.generation.UnitSquareMesh(MPI.COMM_WORLD, 3, 3) P2 = dolfinx.FunctionSpace(mesh, ("P", 2)) # NOTE: The scaling by a constant factor of 3.0 to get f(x, y) is # implemented within the UFL Expression. This is to check that the # Constants are being set up correctly. def exact_expr(x): return x[0] ** 2 + 2.0 * x[1] ** 2 # Unused, but remains for clarity. def f(x): return 3 * (x[0] ** 2 + 2.0 * x[1] ** 2) def exact_grad_f(x): values = np.zeros_like(x) values[:, 0::2] = 2 * x[:, 0::2] values[:, 1::2] = 4 * x[:, 1::2] values *= 3.0 return values expr = dolfinx.Function(P2) expr.interpolate(exact_expr) ufl_grad_f = dolfinx.Constant(mesh, 3.0) * ufl.grad(expr) points = np.array([[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) grad_f_expr = dolfinx.Expression(ufl_grad_f, points) assert grad_f_expr.num_points == points.shape[0] assert grad_f_expr.value_size == 2 # NOTE: Cell numbering is process local. map_c = mesh.topology.index_map(mesh.topology.dim) num_cells = map_c.size_local + map_c.num_ghosts cells = np.arange(0, num_cells, dtype=np.int32) grad_f_evaluated = grad_f_expr.eval(cells) assert grad_f_evaluated.shape[0] == cells.shape[0] assert grad_f_evaluated.shape[1] == grad_f_expr.value_size * grad_f_expr.num_points # Evaluate points in global space ufl_x = ufl.SpatialCoordinate(mesh) x_expr = dolfinx.Expression(ufl_x, points) assert x_expr.num_points == points.shape[0] assert x_expr.value_size == 2 x_evaluated = x_expr.eval(cells) assert x_evaluated.shape[0] == cells.shape[0] assert x_evaluated.shape[1] == x_expr.num_points * x_expr.value_size # Evaluate exact gradient using global points grad_f_exact = exact_grad_f(x_evaluated) assert np.allclose(grad_f_evaluated, grad_f_exact)
def test_expression(): """ Test UFL expression evaluation """ mesh = dolfinx.UnitSquareMesh(MPI.COMM_WORLD, 10, 10) V = dolfinx.FunctionSpace(mesh, ("CG", 2)) def f(x): return 2 * x[0]**2 + x[1]**2 def gradf(x): return numpy.asarray([4 * x[0], 2 * x[1]]) u = dolfinx.Function(V) u.interpolate(f) u.x.scatter_forward() grad_u = ufl.grad(u) points = numpy.array([[0.15, 0.3, 0], [0.953, 0.81, 0]]) gdim = mesh.geometry.dim tdim = mesh.topology.dim bb = dolfinx.geometry.BoundingBoxTree(mesh, mesh.topology.dim) # Find colliding cells on proc closest_cell = [] local_map = [] for i, p in enumerate(points): cells = dolfinx.geometry.compute_collisions_point(bb, p) if len(cells) > 0: actual_cells = dolfinx.geometry.select_colliding_cells( mesh, cells, p, 1) if len(actual_cells) > 0: local_map.append(i) closest_cell.append(actual_cells[0]) num_dofs_x = mesh.geometry.dofmap.links( 0).size # NOTE: Assumes same cell geometry in whole mesh t_imap = mesh.topology.index_map(tdim) num_cells = t_imap.size_local + t_imap.num_ghosts x = mesh.geometry.x x_dofs = mesh.geometry.dofmap.array.reshape(num_cells, num_dofs_x) cell_geometry = numpy.zeros((num_dofs_x, gdim), dtype=numpy.float64) points_ref = numpy.zeros((len(local_map), tdim)) # Map cells on process back to reference element for i, cell in enumerate(closest_cell): cell_geometry[:] = x[x_dofs[cell], :gdim] point_ref = mesh.geometry.cmap.pull_back( points[local_map[i]][:gdim].reshape(1, -1), cell_geometry) points_ref[i] = point_ref # Eval using Expression expr = dolfinx.Expression(grad_u, points_ref) grad_u_at_x = expr.eval(closest_cell).reshape(len(closest_cell), points_ref.shape[0], gdim) # Compare solutions for i, cell in enumerate(closest_cell): point = points[local_map[i]] assert numpy.allclose(grad_u_at_x[i, i], gradf(point))