Skip to content

Commit ab32c8d

Browse files
authored
Merge pull request #77 from firedrakeproject/pbrubeck/hct/hierarchical
HCT: ensure hierarchical edge basis functions
2 parents 11e3ea6 + b34e015 commit ab32c8d

File tree

2 files changed

+51
-33
lines changed

2 files changed

+51
-33
lines changed

FIAT/hct.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ def __init__(self, ref_complex, degree, reduced=False):
5353
entity_ids[1][e].extend(range(cur, len(nodes)))
5454
else:
5555
x = 2.0*qpts - 1
56-
phis = eval_jacobi_batch(2, 2, k, x)
57-
dphis = eval_jacobi_deriv_batch(2, 2, k, x)
56+
phis = eval_jacobi_batch(1, 1, k, x)
57+
dphis = eval_jacobi_deriv_batch(1, 1, k, x)
5858
for e in sorted(top[1]):
5959
Q_mapped = FacetQuadratureRule(ref_el, 1, e, Q)
6060
scale = 2 / Q_mapped.jacobian_determinant()

test/unit/test_argyris.py

+49-31
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
import numpy
33

4-
from FIAT import Argyris
4+
from FIAT import Argyris, HsiehCloughTocher
55
from FIAT.jacobi import eval_jacobi_batch, eval_jacobi_deriv_batch
66
from FIAT.quadrature import FacetQuadratureRule
77
from FIAT.quadrature_schemes import create_quadrature
@@ -13,60 +13,78 @@ def cell():
1313
return ufc_simplex(2)
1414

1515

16-
@pytest.mark.parametrize("degree", range(6, 10))
17-
def test_argyris_basis_functions(cell, degree):
18-
fe = Argyris(cell, degree)
16+
def directional_derivative(direction, tab):
17+
return sum(direction[alpha.index(1)] * tab[alpha]
18+
for alpha in tab if sum(alpha) == 1)
1919

20+
21+
def inner(u, v, wts):
22+
return numpy.dot(numpy.multiply(u, wts), v.T)
23+
24+
25+
@pytest.mark.parametrize("family, degree", [
26+
*((Argyris, k) for k in range(6, 10)),
27+
*((HsiehCloughTocher, k) for k in range(4, 8))])
28+
def test_argyris_basis_functions(cell, family, degree):
29+
fe = family(cell, degree)
30+
31+
ref_el = fe.get_reference_element()
32+
sd = ref_el.get_spatial_dimension()
33+
top = ref_el.get_topology()
2034
entity_ids = fe.entity_dofs()
21-
sd = cell.get_spatial_dimension()
22-
top = cell.get_topology()
35+
36+
degree = fe.degree()
37+
lowest_p = 5 if isinstance(fe, Argyris) else 3
38+
a = (lowest_p - 1) // 2
39+
q = degree - lowest_p
40+
2341
rline = ufc_simplex(1)
2442
Qref = create_quadrature(rline, 2*degree)
43+
xref = 2.0 * Qref.get_points() - 1
2544

26-
q = degree - 5
27-
xref = 2.0*Qref.get_points()-1
2845
ell_at_qpts = eval_jacobi_batch(0, 0, degree, xref)
29-
P_at_qpts = eval_jacobi_batch(2, 2, degree, xref)
30-
DP_at_qpts = eval_jacobi_deriv_batch(2, 2, degree, xref)
46+
P_at_qpts = eval_jacobi_batch(a, a, degree, xref)
47+
DP_at_qpts = eval_jacobi_deriv_batch(a, a, degree, xref)
3148

3249
for e in top[1]:
33-
n = cell.compute_normal(e)
34-
t = cell.compute_edge_tangent(e)
35-
Q = FacetQuadratureRule(cell, 1, e, Qref)
50+
n = ref_el.compute_normal(e)
51+
t = ref_el.compute_edge_tangent(e)
52+
Q = FacetQuadratureRule(ref_el, 1, e, Qref)
3653
qpts, qwts = Q.get_points(), Q.get_weights()
3754

3855
ids = entity_ids[1][e]
3956
ids1 = ids[1:-q]
4057
ids0 = ids[-q:]
4158

4259
tab = fe.tabulate(1, qpts)
43-
# Test that normal derivative moment bfs have vanishing trace
44-
assert numpy.allclose(tab[(0,) * sd][ids[:-q]], 0)
60+
phi = tab[(0,) * sd]
61+
phi_n = directional_derivative(n, tab)
62+
phi_t = directional_derivative(t, tab)
4563

46-
phi = tab[(0,) * sd][ids0]
47-
48-
phi_n = sum(n[alpha.index(1)] * tab[alpha][ids1]
49-
for alpha in tab if sum(alpha) == 1)
64+
# Test that normal derivative moment bfs have vanishing trace
65+
assert numpy.allclose(phi[ids[:-q]], 0)
5066

51-
phi_t = sum(t[alpha.index(1)] * tab[alpha][ids0]
52-
for alpha in tab if sum(alpha) == 1)
67+
# Test that trace moment bfs have vanishing normal derivative
68+
assert numpy.allclose(phi_n[ids0], 0)
5369

5470
# Test that facet bfs are hierarchical
55-
coeffs = numpy.dot(numpy.multiply(phi, qwts), ell_at_qpts[6:].T)
56-
assert numpy.allclose(numpy.triu(coeffs, k=1), 0)
71+
coeffs = inner(ell_at_qpts[1+lowest_p:], phi[ids0], qwts)
72+
assert numpy.allclose(coeffs, numpy.triu(coeffs))
73+
coeffs = inner(ell_at_qpts[1+lowest_p:], phi_n[ids1], qwts)
74+
assert numpy.allclose(coeffs, numpy.triu(coeffs))
5775

58-
# Test duality of normal derivarive moments
59-
coeffs = numpy.dot(numpy.multiply(phi_n, qwts), P_at_qpts[1:].T)
60-
assert numpy.allclose(coeffs[:, q:], 0)
61-
assert numpy.allclose(coeffs[:, :q], numpy.diag(numpy.diag(coeffs[:, :q])))
76+
# Test duality of normal derivative moments
77+
coeffs = inner(P_at_qpts[1:], phi_n[ids1], qwts)
78+
assert numpy.allclose(coeffs[q:], 0)
79+
assert numpy.allclose(coeffs[:q], numpy.diag(numpy.diag(coeffs[:q])))
6280

6381
# Test duality of trace moments
64-
coeffs = numpy.dot(numpy.multiply(phi, qwts), DP_at_qpts[1:].T)
65-
assert numpy.allclose(coeffs[:, q:], 0)
66-
assert numpy.allclose(coeffs[:, :q], numpy.diag(numpy.diag(coeffs[:, :q])))
82+
coeffs = inner(DP_at_qpts[1:], phi[ids0], qwts)
83+
assert numpy.allclose(coeffs[q:], 0)
84+
assert numpy.allclose(coeffs[:q], numpy.diag(numpy.diag(coeffs[:q])))
6785

6886
# Test the integration by parts property arising from the choice
6987
# of normal derivative and trace moments DOFs.
7088
# The normal derivative of the normal derviative bfs must be equal
7189
# to minus the tangential derivative of the trace moment bfs
72-
assert numpy.allclose(numpy.dot((phi_n + phi_t)**2, qwts), 0)
90+
assert numpy.allclose(numpy.dot((phi_n[ids1] + phi_t[ids0])**2, qwts), 0)

0 commit comments

Comments
 (0)