def test_different_element_interpolation(family1, args1, family2, args2, cell_type, order): lower_element = basix.create_element(family1, cell_type, order, *args1) higher_element = basix.create_element(family2, cell_type, order, *args2) run_test(lower_element, higher_element, order - 1, lower_element.value_size)
def test_different_variant_interpolation(cell_type, order, variant1, variant2): lower_element = basix.create_element(basix.ElementFamily.P, cell_type, order, variant1) higher_element = basix.create_element(basix.ElementFamily.P, cell_type, order, variant2) run_test(lower_element, higher_element, order, lower_element.value_size)
def test_blocked_interpolation(cell_type, order): """Test interpolation of Nedelec's componenets into a Lagrange space.""" nedelec = basix.create_element(basix.ElementFamily.N2E, cell_type, order) lagrange = basix.create_element(basix.ElementFamily.P, cell_type, order, basix.LagrangeVariant.gll_isaac) n_points = nedelec.points if nedelec.value_size == 2: n_eval = np.concatenate([n_points[:, 0]**order, n_points[:, 1]**order]) else: n_eval = np.concatenate( [n_points[:, 0]**order, 0 * n_points[:, 0], n_points[:, 1]**order]) n_coeffs = nedelec.interpolation_matrix @ n_eval l_points = lagrange.points if nedelec.value_size == 2: values = [l_points[:, 0]**order, l_points[:, 1]**order] else: values = [ l_points[:, 0]**order, 0 * l_points[:, 0], l_points[:, 1]**order ] l_coeffs = np.empty(lagrange.dim * nedelec.value_size) for i, v in enumerate(values): l_coeffs[i::nedelec.value_size] = v # Test interpolation from Nedelec to blocked Lagrange i_m = basix.compute_interpolation_operator(nedelec, lagrange) assert np.allclose(l_coeffs, i_m @ n_coeffs) # Test interpolation from blocked Lagrange to Nedelec i_m = basix.compute_interpolation_operator(lagrange, nedelec) assert np.allclose(n_coeffs, i_m @ l_coeffs)
def create_basix_element(ufl_element): # TODO: EnrichedElement # TODO: Short/alternative names for elements if isinstance(ufl_element, ufl.VectorElement): return BlockedElement( create_basix_element(ufl_element.sub_elements()[0]), ufl_element.num_sub_elements()) if isinstance(ufl_element, ufl.TensorElement): return BlockedElement( create_basix_element(ufl_element.sub_elements()[0]), ufl_element.num_sub_elements(), None) # TODO: block shape if isinstance(ufl_element, ufl.MixedElement): return MixedElement( [create_basix_element(e) for e in ufl_element.sub_elements()]) if ufl_element.family() in ufl_to_basix_names: return BasixElement( basix.create_element(ufl_to_basix_names[ufl_element.family()], ufl_element.cell().cellname(), ufl_element.degree())) return BasixElement( basix.create_element(ufl_element.family(), ufl_element.cell().cellname(), ufl_element.degree()))
def test_different_order_interpolation_lagrange(cell_type, orders): lower_element = basix.create_element(basix.ElementFamily.P, cell_type, orders[0], basix.LagrangeVariant.gll_warped) higher_element = basix.create_element(basix.ElementFamily.P, cell_type, orders[1], basix.LagrangeVariant.gll_warped) run_test(lower_element, higher_element, orders[0], lower_element.value_size)
def test_lagrange(celltype, degree): lagrange = basix.create_element(basix.ElementFamily.P, celltype[1], degree, basix.LagrangeVariant.equispaced) pts = basix.create_lattice(celltype[0], 6, basix.LatticeType.equispaced, True) w = lagrange.tabulate(0, pts)[0] assert (numpy.isclose(numpy.sum(w, axis=1), 1.0).all())
def test_cr2d(): cr2 = basix.create_element(basix.ElementFamily.CR, basix.CellType.triangle, 1) pts = basix.create_lattice(basix.CellType.triangle, 2, basix.LatticeType.equispaced, True) w = cr2.tabulate(0, pts)[0] print(w.shape)
def test_cr3d(): cr3 = basix.create_element(basix.ElementFamily.CR, basix.CellType.tetrahedron, 1) pts = basix.create_lattice(basix.CellType.tetrahedron, 2, basix.LatticeType.equispaced, True) w = cr3.tabulate(0, pts)[0] print(w.shape)
def test_regge_tri2(): # Second order regge = basix.create_element(basix.ElementFamily.Regge, basix.CellType.triangle, 2) # tabulate at origin pts = [[0.0, 0.0]] w = regge.tabulate(0, pts)[0].reshape(-1, 2, 2) ref = np.array([[[0., -0.5], [-0.5, 0.]], [[0., -0.5], [-0.5, -0.]], [[-0., -0.5], [-0.5, 0.]], [[-0., 1.5], [1.5, 3.]], [[0., -1.5], [-1.5, -3.]], [[-0., 0.5], [0.5, 1.]], [[3., 1.5], [1.5, -0.]], [[-3., -1.5], [-1.5, 0.]], [[1., 0.5], [0.5, -0.]], [[-0., -0.], [-0., -0.]], [[0., -0.], [-0., -0.]], [[0., -3.], [-3., 0.]], [[0., -0.], [-0., -0.]], [[-0., -0.], [-0., 0.]], [[-0., 2.], [2., -0.]], [[-0., 0.], [0., 0.]], [[0., 0.], [0., -0.]], [[0., 2.], [2., -0.]]]) assert(np.isclose(ref, w).all())
def test_regge_tet(): # Simplest element regge = basix.create_element(basix.ElementFamily.Regge, basix.CellType.tetrahedron, 1) # tabulate at origin pts = [[0.0, 0.0, 0.0]] w = regge.tabulate(0, pts)[0].reshape(-1, 3, 3) ref = np.array([[[0., 0., 0.], [0., 0., 0.5], [0., 0.5, -0.]], [[-0., 0., -0.], [0., -0., 0.5], [-0., 0.5, 0.]], [[0., -0., 0.5], [-0., 0., 0.], [0.5, 0., 0.]], [[-0., 0., 0.5], [0., -0., 0.], [0.5, 0., 0.]], [[-0., 0.5, 0.], [0.5, -0., -0.], [0., -0., 0.]], [[0., 0.5, 0.], [0.5, -0., 0.], [0., 0., 0.]], [[0., 0., 1.], [0., 0., 1.], [1., 1., 2.]], [[0., -0., -0.5], [-0., 0., -0.5], [-0.5, -0.5, -1.]], [[0., 1., -0.], [1., 2., 1.], [-0., 1., -0.]], [[-0., -0.5, -0.], [-0.5, -1., -0.5], [-0., -0.5, -0.]], [[2., 1., 1.], [1., -0., 0.], [1., 0., 0.]], [[-1., -0.5, -0.5], [-0.5, -0., 0.], [-0.5, 0., -0.]], [[-0., 0., -0.], [0., -0., -0.], [-0., -0., -0.]], [[-0., -0., -0.], [-0., -0., -0.], [-0., -0., -0.]], [[-0., 0., -0.], [0., 0., -0.], [-0., -0., -0.]], [[0., -0., 0.], [-0., -0., -0.], [0., -0., 0.]], [[-0., -0., -0.], [-0., -0., 0.], [-0., 0., 0.]], [[-0., -0., -0.], [-0., -0., -1.5], [-0., -1.5, -0.]], [[0., -0., 0.], [-0., 0., -0.], [0., -0., 0.]], [[0., -0., -0.], [-0., -0., -0.], [-0., -0., 0.]], [[-0., 0., -1.5], [0., -0., -0.], [-1.5, -0., -0.]], [[-0., -0., -0.], [-0., 0., -0.], [-0., -0., -0.]], [[0., -0., -0.], [-0., -0., -0.], [-0., -0., 0.]], [[-0., -1.5, -0.], [-1.5, 0., -0.], [-0., -0., -0.]]]) assert(np.isclose(ref, w).all())
def test_transformation_of_tabulated_data_quadrilateral(element_type, degree, element_args): e = basix.create_element(element_type, basix.CellType.quadrilateral, degree, *element_args) bt = e.base_transformations() N = 4 points = np.array([[i / N, j / N] for i in range(N + 1) for j in range(N + 1)]) values = e.tabulate(0, points)[0] start = sum(e.num_entity_dofs[0]) ndofs = e.num_entity_dofs[1][0] if ndofs != 0: # Check that the 0th transformation undoes the effect of reflecting edge 0 reflected_points = np.array([[1 - p[0], p[1]] for p in points]) reflected_values = e.tabulate(0, reflected_points)[0] _J = np.array([[-1, 0], [0, 1]]) J = np.array([_J for p in points]) detJ = np.array([np.linalg.det(_J) for p in points]) K = np.array([np.linalg.inv(_J) for p in points]) mapped_values = e.push_forward(reflected_values, J, detJ, K) for i, j in zip(values, mapped_values): for d in range(e.value_size): i_slice = i[:, d] j_slice = j[:, d] assert np.allclose((bt[0].dot(i_slice))[start: start + ndofs], j_slice[start: start + ndofs])
def test_interpolation(cell_type, n, element_type): element = basix.create_element(element_type, cell_type, n, basix.LagrangeVariant.gll_warped) assert element.interpolation_matrix.shape[0] == element.dim assert element.interpolation_matrix.shape[1] == element.points.shape[0] assert element.points.shape[1] == len(basix.topology( element.cell_type)) - 1
def test_tet(order): celltype = basix.CellType.tetrahedron g = sympy_rt(celltype, order) x = sympy.Symbol("x") y = sympy.Symbol("y") z = sympy.Symbol("z") rt = basix.create_element(basix.ElementFamily.RT, basix.CellType.tetrahedron, order) pts = basix.create_lattice(celltype, 5, basix.LatticeType.equispaced, True) nderiv = 1 wtab = rt.tabulate(nderiv, pts) for kx in range(nderiv + 1): for ky in range(nderiv + 1 - kx): for kz in range(nderiv + 1 - kx - ky): wsym = numpy.zeros_like(wtab[0]) for i, gi in enumerate(g): for j, gij in enumerate(gi): wd = sympy.diff(gij, x, kx, y, ky, z, kz) for k, p in enumerate(pts): wsym[k, i, j] = wd.subs([(x, p[0]), (y, p[1]), (z, p[2])]) assert (numpy.isclose(wtab[basix.index(kx, ky, kz)], wsym).all())
def test_dof_transformations_triangle(degree): lagrange = basix.create_element(basix.ElementFamily.P, basix.CellType.triangle, degree, basix.LagrangeVariant.equispaced) permuted = {} if degree == 3: # Reflect 2 DOFs on edges permuted[0] = {3: 4, 4: 3} permuted[1] = {5: 6, 6: 5} permuted[2] = {7: 8, 8: 7} elif degree == 4: # Reflect 3 DOFs on edges permuted[0] = {3: 5, 5: 3} permuted[1] = {6: 8, 8: 6} permuted[2] = {9: 11, 11: 9} base_transformations = lagrange.base_transformations() assert len(base_transformations) == 3 for i, t in enumerate(base_transformations): actual = numpy.zeros_like(t) for j, row in enumerate(t): if i in permuted and j in permuted[i]: actual[j, permuted[i][j]] = 1 else: actual[j, j] = 1 assert numpy.allclose(t, actual)
def test_if_identity(cell_type, element_type, degree, element_args): e = basix.create_element(element_type, cell_type, degree, *element_args) for t in e.base_transformations(): if not np.allclose(t, np.eye(t.shape[0])): assert not e.dof_transformations_are_identity return assert e.dof_transformations_are_identity
def test_mappings_2d_to_2d(element_type, element_args): e = basix.create_element(element_type, basix.CellType.triangle, 1, *element_args) J = np.array([[random.random() + 1, random.random()], [random.random(), random.random()]]) detJ = np.linalg.det(J) K = np.linalg.inv(J) run_map_test(e, J, detJ, K, e.value_size, e.value_size)
def __init__(self, family_type, cell_type, degree, variant_info, discontinuous): self.element = basix.create_element(family_type, cell_type, degree, *variant_info, discontinuous) self._family = family_type self._cell = cell_type self._variant_info = variant_info self._discontinuous = discontinuous
def test_quadrilateral_transformation_degrees(element_type, degree, element_args): e = basix.create_element(element_type, basix.CellType.quadrilateral, degree, *element_args) bt = e.base_transformations() assert len(bt) == 4 identity = np.identity(e.dim) for i, degree in enumerate([2, 2, 2, 2]): assert np.allclose( np.linalg.matrix_power(bt[i], degree), identity)
def test_if_permutations(cell_type, element_type, degree, element_args): e = basix.create_element(element_type, cell_type, degree, *element_args) for t in e.base_transformations(): for row in t: a = np.argmax(row) if not np.isclose(row[a], 1) or not np.allclose(row[:a], 0) or not np.allclose(row[a + 1:], 0): assert not e.dof_transformations_are_permutations return assert e.dof_transformations_are_permutations
def test_dof_transformations_to_transpose(cell, element, degree, block_size, element_args): try: import numba # noqa: F401 except ImportError: pytest.skip("Numba must be installed to run this test.") from basix import numba_helpers from numba.core import types from numba.typed import Dict transform_functions = { basix.CellType.triangle: numba_helpers.apply_dof_transformation_to_transpose_triangle, basix.CellType.quadrilateral: numba_helpers.apply_dof_transformation_to_transpose_quadrilateral, basix.CellType.tetrahedron: numba_helpers.apply_dof_transformation_to_transpose_tetrahedron, basix.CellType.hexahedron: numba_helpers.apply_dof_transformation_to_transpose_hexahedron, basix.CellType.prism: numba_helpers.apply_dof_transformation_to_transpose_prism, basix.CellType.pyramid: numba_helpers.apply_dof_transformation_to_transpose_pyramid } random.seed(1337) e = basix.create_element(element, cell, degree, *element_args) data = np.array(list(range(e.dim * block_size)), dtype=np.double) for i in range(10): cell_info = random.randrange(2**30) data1 = data.copy() data1 = e.apply_dof_transformation_to_transpose( data1, block_size, cell_info) # Numba function does not use blocked data data2 = data.copy().reshape(block_size, e.dim) # Mapping lists to numba dictionaries entity_transformations = Dict.empty(key_type=types.string, value_type=types.float64[:, :, :]) for i, transformation in e.entity_transformations().items(): entity_transformations[i] = transformation entity_dofs = Dict.empty(key_type=types.int64, value_type=types.int32[:]) for i, e_dofs in enumerate(e.num_entity_dofs): entity_dofs[i] = np.asarray(e_dofs, dtype=np.int32) transform_functions[cell](entity_transformations, entity_dofs, data2, cell_info) # Reshape numba output for comparison data2 = data2.reshape(-1) assert np.allclose(data1, data2)
def test_number_of_dofs(cell_type, degree, element_type, element_args): element = basix.create_element(element_type, cell_type, degree, *element_args) entity_dofs = element.entity_dofs num_entity_dofs = element.num_entity_dofs for entity_dofs, num_entity_dofs in zip(element.entity_dofs, element.num_entity_dofs): for dofs, ndofs in zip(entity_dofs, num_entity_dofs): assert len(dofs) == ndofs assert element.dim == sum(sum(i) for i in element.num_entity_dofs)
def test_tensor_product_factorisation_quadrilateral(degree): P = degree cell_type = basix.CellType.quadrilateral element = basix.create_element(basix.ElementFamily.P, cell_type, P, basix.LagrangeVariant.gll_warped) factors = element.get_tensor_product_representation()[0] # Quadrature degree Q = 2 * P + 2 points, w = basix.make_quadrature(basix.QuadratureType.Default, cell_type, Q) data = element.tabulate(1, points) dphi_x = data[1, :, :, 0] dphi_y = data[2, :, :, 0] assert points.shape[0] == (P + 2) * (P + 2) # FIXME: This test assumes all factors formed by a single element perm = factors[1] element0 = factors[0][0] cell1d = element0.cell_type points, w = basix.make_quadrature(basix.QuadratureType.Default, cell1d, Q) data = element0.tabulate(1, points) phi0 = data[0, :, :, 0] dphi0 = data[1, :, :, 0] # number of dofs in each direction Nd = P + 1 # number of quadrature points in each direction Nq = P + 2 # Compute derivative of basis function in the x direction dphi_tensor = numpy.zeros([Nq, Nq, Nd, Nd]) for q0 in range(Nq): for q1 in range(Nq): for i0 in range(Nd): for i1 in range(Nd): dphi_tensor[q0, q1, i0, i1] = dphi0[q0, i0] * phi0[q1, i1] dphi_tensor = dphi_tensor.reshape([Nq * Nq, Nd * Nd]) assert numpy.allclose(dphi_x[:, perm], dphi_tensor) # Compute derivative of basis function in the y direction dphi_tensor = numpy.zeros([Nq, Nq, Nd, Nd]) for q0 in range(Nq): for q1 in range(Nq): for i0 in range(Nd): for i1 in range(Nd): dphi_tensor[q0, q1, i0, i1] = phi0[q0, i0] * dphi0[q1, i1] dphi_tensor = dphi_tensor.reshape([Nq * Nq, Nd * Nd]) assert numpy.allclose(dphi_y[:, perm], dphi_tensor)
def test_mappings_2d_to_3d(element_type, element_args): e = basix.create_element(element_type, basix.CellType.triangle, 1, *element_args) # Map from (0,0)--(1,0)--(0,1) to (1,0,1)--(2,1,0)--(0,1,1) J = np.array([[1., -1.], [1., 1.], [-1., 0.]]) detJ = np.sqrt(6) K = np.array([[0.5, 0.5, 0.], [-0.5, 0.5, 0.]]) if e.value_size == 1: physical_vs = 1 elif e.value_size == 2: physical_vs = 3 elif e.value_size == 4: physical_vs = 9 run_map_test(e, J, detJ, K, e.value_size, physical_vs)
def test_closure_dofs(cell_type, degree, element_type, element_args): element = basix.create_element(element_type, cell_type, degree, *element_args) entity_dofs = element.entity_dofs entity_closure_dofs = element.entity_closure_dofs topology = basix.topology(cell_type) connectivity = basix.cell.sub_entity_connectivity(cell_type) for dim, t in enumerate(topology): for e, _ in enumerate(t): for dim2 in range(dim + 1): for e2 in connectivity[dim][e][dim2]: for dof in entity_dofs[dim2][e2]: assert dof in entity_closure_dofs[dim][e]
def test_line_without_variant(n): celltype = basix.CellType.interval g = sympy_lagrange(celltype, n) x = sympy.Symbol("x") lagrange = basix.create_element(basix.ElementFamily.P, basix.CellType.interval, n) pts = basix.create_lattice(celltype, 6, basix.LatticeType.equispaced, True) nderiv = n wtab = lagrange.tabulate(nderiv, pts) for k in range(nderiv + 1): wsym = numpy.zeros_like(wtab[k]) for i in range(n + 1): wd = sympy.diff(g[i], x, k) for j, p in enumerate(pts): wsym[j, i] = wd.subs(x, p[0]) assert numpy.allclose(wtab[k], wsym)
def test_ordering_of_dofs(cell_type, degree, element_type, element_args): """ Check that DOFs are numbered in ascending order by entity. This assumption is required for DOF transformations to work correctly. """ element = basix.create_element(element_type, cell_type, degree, *element_args) entity_dofs = element.entity_dofs dof = 0 print(element.entity_dofs) for entity_dofs in element.entity_dofs: for dofs in entity_dofs: for d in sorted(dofs): assert d == dof dof += 1
def test_regge_tri(): # Simplest element regge = basix.create_element(basix.ElementFamily.Regge, basix.CellType.triangle, 1) # tabulate at origin pts = [[0.0, 0.0]] w = regge.tabulate(0, pts)[0].reshape(-1, 2, 2) ref = np.array([[[-0., 0.5], [0.5, -0.]], [[0., 0.5], [0.5, -0.]], [[-0., 1.], [1., 2.]], [[-0., -0.5], [-0.5, -1.]], [[2., 1.], [1., -0.]], [[-1., -0.5], [-0.5, 0.]], [[-0., 0.], [0., 0.]], [[0., -0.], [-0., -0.]], [[-0., -1.5], [-1.5, 0.]]]) assert np.allclose(ref, w)
def test_dof_transformations_tetrahedron(degree): lagrange = basix.create_element(basix.ElementFamily.P, basix.CellType.tetrahedron, degree, basix.LagrangeVariant.equispaced) permuted = {} if degree == 3: # Reflect 2 DOFs on edges permuted[0] = {4: 5, 5: 4} permuted[1] = {6: 7, 7: 6} permuted[2] = {8: 9, 9: 8} permuted[3] = {10: 11, 11: 10} permuted[4] = {12: 13, 13: 12} permuted[5] = {14: 15, 15: 14} elif degree == 4: # Reflect 3 DOFs on edges permuted[0] = {4: 6, 6: 4} permuted[1] = {7: 9, 9: 7} permuted[2] = {10: 12, 12: 10} permuted[3] = {13: 15, 15: 13} permuted[4] = {16: 18, 18: 16} permuted[5] = {19: 21, 21: 19} # Rotate and reflect 3 DOFs on faces permuted[6] = {22: 24, 23: 22, 24: 23} permuted[7] = {23: 24, 24: 23} permuted[8] = {25: 27, 26: 25, 27: 26} permuted[9] = {26: 27, 27: 26} permuted[10] = {28: 30, 29: 28, 30: 29} permuted[11] = {29: 30, 30: 29} permuted[12] = {31: 33, 32: 31, 33: 32} permuted[13] = {32: 33, 33: 32} base_transformations = lagrange.base_transformations() assert len(base_transformations) == 14 for i, t in enumerate(base_transformations): actual = numpy.zeros_like(t) for j, row in enumerate(t): if i in permuted and j in permuted[i]: actual[j, permuted[i][j]] = 1 else: actual[j, j] = 1 assert numpy.allclose(t, actual)
def test_tensor_product_factorisation(cell_type, degree, element_type, element_args): element = basix.create_element(element_type, cell_type, degree, *element_args) tdim = len(basix.topology(cell_type)) - 1 # These elements should have a factorisation if cell_type in [basix.CellType.quadrilateral, basix.CellType.hexahedron] and element_type in [ basix.ElementFamily.P ] and basix.LagrangeVariant.equispaced in element_args: assert element.has_tensor_product_factorisation if not element.has_tensor_product_factorisation: with pytest.raises(RuntimeError): element.get_tensor_product_representation() pytest.skip() factors = element.get_tensor_product_representation() lattice = basix.create_lattice(cell_type, 1, basix.LatticeType.equispaced, True) tab1 = element.tabulate(1, lattice) for i, point in enumerate(lattice): for ds in product(range(2), repeat=tdim): if sum(ds) > 1: continue deriv = basix.index(*ds) values1 = tab1[deriv, i, :, :] values2 = np.empty((element.dim, 1)) for fs, perm in factors: evals = [ e.tabulate(d, [p])[d, 0, :, 0] for e, p, d in zip(fs, point, ds) ] tab2 = tensor_product(*evals) for a, b in enumerate(perm): if b != -1: values2[b, 0] = tab2[a] assert np.allclose(values1, values2)
def test_tri(order): celltype = basix.CellType.triangle g = sympy_nedelec(celltype, order) x = sympy.Symbol("x") y = sympy.Symbol("y") nedelec = basix.create_element(basix.ElementFamily.N1E, basix.CellType.triangle, order) pts = basix.create_lattice(celltype, 6, basix.LatticeType.equispaced, True) nderiv = 3 wtab = nedelec.tabulate(nderiv, pts) for kx in range(nderiv + 1): for ky in range(nderiv + 1 - kx): wsym = numpy.zeros_like(wtab[0]) for i, gi in enumerate(g): for j, gij in enumerate(gi): wd = sympy.diff(gij, x, kx, y, ky) for k, p in enumerate(pts): wsym[k, i, j] = wd.subs([(x, p[0]), (y, p[1])]) assert(numpy.isclose(wtab[basix.index(kx, ky)], wsym).all())