def test_piecewise_lagrange_tetrahedron(order): reference = symfem.create_reference("tetrahedron") mid = tuple( sympy.Rational(sum(i), len(i)) for i in zip(*reference.vertices)) sub_tets = [(reference.vertices[0], reference.vertices[1], reference.vertices[2], mid), (reference.vertices[0], reference.vertices[1], reference.vertices[3], mid), (reference.vertices[0], reference.vertices[2], reference.vertices[3], mid), (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid)] N = 5 fs = make_piecewise_lagrange(sub_tets, "tetrahedron", order, zero_on_boundary=True) for f_n in range(reference.sub_entity_count(2)): face = reference.sub_entity(2, f_n) for i in range(N + 1): for j in range(N + 1 - i): point = face.get_point( (sympy.Rational(i, N), sympy.Rational(j, N))) for f in fs: assert subs(f, x, point) == (0, 0, 0) fs = make_piecewise_lagrange(sub_tets, "tetrahedron", order, zero_at_centre=True) for f in fs: assert subs(f, x, mid) == (0, 0, 0)
def test_piecewise_lagrange_triangle(order): reference = symfem.create_reference("triangle") mid = tuple( sympy.Rational(sum(i), len(i)) for i in zip(*reference.vertices)) sub_tris = [(reference.vertices[0], reference.vertices[1], mid), (reference.vertices[0], reference.vertices[2], mid), (reference.vertices[1], reference.vertices[2], mid)] N = 5 fs = make_piecewise_lagrange(sub_tris, "triangle", order, zero_on_boundary=True) for e_n in range(reference.sub_entity_count(1)): edge = reference.sub_entity(1, e_n) for i in range(N + 1): point = edge.get_point((sympy.Rational(i, N), )) for f in fs: assert subs(f, x, point) == (0, 0) fs = make_piecewise_lagrange(sub_tris, "triangle", order, zero_at_centre=True) for f in fs: assert subs(f, x, mid) == (0, 0)
def test_dual_elements(elements_to_test, cells_to_test, n_tri, order): if elements_to_test != "ALL" and "dual" not in elements_to_test: pytest.skip() if cells_to_test != "ALL" and "dual polygon" not in cells_to_test: pytest.skip() space = create_element(f"dual polygon({n_tri})", "dual", order) sub_e = create_element("triangle", space.fine_space, space.order) for f, coeff_list in zip(space.get_basis_functions(), space.dual_coefficients): for piece, coeffs in zip(f.pieces, coeff_list): map = sub_e.reference.get_map_to(piece[0]) for dof, value in zip(sub_e.dofs, coeffs): point = subs(map, x, dof.point) assert symequal(value, subs(piece[1], x, point))
def test_guzman_neilan_tetrahedron(order): e = symfem.create_element("tetrahedron", "Guzman-Neilan", order) mid = tuple( sympy.Rational(sum(i), len(i)) for i in zip(*e.reference.vertices)) for p in e._basis[-4:]: for piece in p.pieces: print(div(piece[1]).expand()) float(div(piece[1]).expand()) assert subs(p, x, mid) == (0, 0, 0)
def test_BDFM_space(reference, order): e = create_element(reference, "BDFM", order) polynomials = e.get_polynomial_basis() tdim = e.reference.tdim for p in polynomials: for vs in e.reference.sub_entities(tdim - 1): sub_ref = create_reference(e.reference.sub_entity_types[tdim - 1], [e.reference.vertices[i] for i in vs]) if tdim == 2: p_edge = subs(p, x, [ i + t[0] * j for i, j in zip(sub_ref.origin, sub_ref.axes[0]) ]) else: p_edge = subs(p, x, [ i + t[0] * j + t[1] * k for i, j, k in zip( sub_ref.origin, sub_ref.axes[0], sub_ref.axes[1]) ]) poly = vdot(p_edge, sub_ref.normal()).expand().simplify() assert poly.is_real or sympy.Poly(poly).degree() <= order - 1
def test_hct(): e = symfem.create_element("triangle", "HCT", 3) for f in e.get_polynomial_basis(): # edge from (1,0) to (1/3,1/3) f1 = f.get_piece((half, 0)) f2 = f.get_piece((half, half)) line = ((1 - 2 * t[0], t[0])) f1 = subs(f1, x[:2], line) f2 = subs(f2, x[:2], line) assert symequal(f1, f2) assert symequal(grad(f1, 2), grad(f2, 2)) # edge from (0,1) to (1/3,1/3) f1 = f.get_piece((half, half)) f2 = f.get_piece((0, half)) line = ((t[0], 1 - 2 * t[0])) f1 = subs(f1, x[:2], line) f2 = subs(f2, x[:2], line) assert symequal(f1, f2) assert symequal(grad(f1, 2), grad(f2, 2)) # edge from (0,0) to (1/3,1/3) f1 = f.get_piece((0, half)) f2 = f.get_piece((half, 0)) line = ((t[0], t[0])) f1 = subs(f1, x[:2], line) f2 = subs(f2, x[:2], line) assert symequal(f1, f2) assert symequal(grad(f1, 2), grad(f2, 2))
def test_MTW_space(reference): e = create_element(reference, "MTW", 3) polynomials = e.get_polynomial_basis() for p in polynomials: assert div(p).is_real for vs in e.reference.sub_entities(1): sub_ref = create_reference(e.reference.sub_entity_types[1], [e.reference.vertices[i] for i in vs]) p_edge = subs(p, x, [ i + t[0] * j for i, j in zip(sub_ref.origin, sub_ref.axes[0]) ]) poly = vdot(p_edge, sub_ref.normal()).expand().simplify() assert poly.is_real or sympy.Poly(poly).degree() <= 1
def test_orthogonal(cell): if cell == "pyramid": pytest.xfail("Legendre polynomials not implemented for pyramids yet.") if cell == "interval": e = create_element(cell, "Lagrange", 5) else: e = create_element(cell, "Lagrange", 2) basis = legendre.get_legendre_basis(e._basis, e.reference) for i, f in enumerate(basis): for g in basis[:i]: assert e.reference.integral(subs(f * g, x, t)) == 0
def test_legendre(cell, order): if cell == "pyramid": pytest.xfail("Legendre polynomials not implemented for pyramids yet.") e = create_element(cell, "Lagrange", order) points = make_lattice(cell) basis = legendre.get_legendre_basis(e._basis, e.reference) values = legendre.evaluate_legendre_basis(points, e._basis, e.reference) for b in basis: assert b != 0 values2 = np.array([[to_float(subs(b, x, p)) for b in basis] for p in points]) assert np.allclose(values, values2)
def test_rhct(): e = symfem.create_element("triangle", "rHCT", 3) for f in e.get_polynomial_basis(): # edge from (1,0) to (1/3,1/3) f1 = f.get_piece((half, 0)) f2 = f.get_piece((half, half)) line = ((1 - 2 * t[0], t[0])) f1 = subs(f1, x[:2], line) f2 = subs(f2, x[:2], line) assert symequal(f1, f2) assert symequal(grad(f1, 2), grad(f2, 2)) # edge from (0,1) to (1/3,1/3) f1 = f.get_piece((half, half)) f2 = f.get_piece((0, half)) line = ((t[0], 1 - 2 * t[0])) f1 = subs(f1, x[:2], line) f2 = subs(f2, x[:2], line) assert symequal(f1, f2) assert symequal(grad(f1, 2), grad(f2, 2)) # edge from (0,0) to (1/3,1/3) f1 = f.get_piece((0, half)) f2 = f.get_piece((half, 0)) line = ((t[0], t[0])) f1 = subs(f1, x[:2], line) f2 = subs(f2, x[:2], line) assert symequal(f1, f2) assert symequal(grad(f1, 2), grad(f2, 2)) # Check that normal derivatives are linear f1 = diff(f.get_piece((half, 0)), x[1]).subs(x[1], 0) f2 = f.get_piece((half, half)) f2 = (diff(f2, x[0]) + diff(f2, x[1])).subs(x[1], 1 - x[0]) f3 = diff(f.get_piece((0, half)), x[0]).subs(x[0], 0) assert diff(f1, x[0], x[0]) == 0 assert diff(f2, x[0], x[0]) == 0 assert diff(f3, x[1], x[1]) == 0
# In this demo, we use the function vdot to compute the dot product of two vectors. # The module symfem.vectors contains many functions that can be used to manipulate # vectors. from symfem.vectors import vdot element = symfem.create_element("triangle", "Nedelec1", 4) polys = element.get_polynomial_basis() # Check that the first 20 polynomials in the polynomial basis are # the polynomials of degree 3 p3 = polynomial_set(2, 2, 3) assert len(p3) == 20 for i, j in zip(p3, polys[:20]): assert i == j # Check that the rest of the polynomials in the polynomial basis # satisfy p DOT x = 0 for p in polys[20:]: assert vdot(p, x) == 0 # Get the basis functions associated with the interior of the triangle basis = element.get_basis_functions() functions = [basis[d] for d in element.entity_dofs(2, 0)] # Check that these functions have 0 tangential component on each edge # symequal will simplify the expressions then check that they are equal for f in functions: assert symequal(vdot(subs(f, x[0], 1 - x[1]), (1, -1)), 0) assert symequal(vdot(subs(f, x[0], 0), (0, 1)), 0) assert symequal(vdot(subs(f, x[1], 0), (1, 0)), 0)
(reference.vertices[0], reference.vertices[2], reference.vertices[3], mid), (reference.vertices[1], reference.vertices[2], reference.vertices[3], mid)] xx, yy, zz = [i.to_sympy() for i in x] terms = [1, xx, yy, zz, xx**2, yy**2, zz**2, xx * yy, xx * zz, yy * zz] sub_basis = make_piecewise_lagrange(sub_cells, ref, br.reference.tdim, True) filename = os.path.join(folder, f"_guzman_neilan_{ref}.py") output = "import sympy\n\ncoeffs = [\n" for f in fs: output += " [\n" fun = (div(f) - reference.integral(subs(div(f), x, t)) / reference.volume()).as_coefficients_dict() for term in fun: assert term in terms aim = [fun[term] if term in fun else 0 for term in terms] * (br.reference.tdim + 1) mat = [[] for t in terms for p in sub_basis[0].pieces] for b in sub_basis: i = 0 for _, p in b.pieces: d = div(p).expand().as_coefficients_dict() for term in d: assert term == 0 or term in terms for term in terms:
""" import symfem import sympy from symfem.symbolic import x, subs, symequal element = symfem.create_element("triangle", "Lagrange", 5) edge_element = symfem.create_element("interval", "Lagrange", 5) # Define a parameter that will go from 0 to 1 on the chosen edge a = sympy.Symbol("a") # Get the basis functions of the Lagrange space and substitute the parameter into the # functions on the edge basis = element.get_basis_functions() edge_basis = [subs(f, x[0], a) for f in edge_element.get_basis_functions()] # Get the DOFs on edge 0 (from vertex 1 (1,0) to vertex 2 (0,1)) # (1 - a, a) is a parametrisation of this edge dofs = element.entity_dofs(0, 1) + element.entity_dofs( 0, 2) + element.entity_dofs(1, 0) # Check that the basis functions on this edge are equal for d, edge_f in zip(dofs, edge_basis): # symequal will simplify the expressions then check that they are equal assert symequal(subs(basis[d], x[:2], (1 - a, a)), edge_f) # Get the DOFs on edge 1 (from vertex 0 (0,0) to vertex 2 (0,1), parametrised (0, a)) dofs = element.entity_dofs(0, 0) + element.entity_dofs( 0, 2) + element.entity_dofs(1, 1) for d, edge_f in zip(dofs, edge_basis): assert symequal(subs(basis[d], x[:2], (0, a)), edge_f)