Skip to content

Commit 1370398

Browse files
authored
Add TNT elements (#139)
* Add TNTdiv space * Add TNT file * TNTdiv * TNTcurl * start adding scalar TNT element * docs * TNT scalar element * CHANGELOG * fewer TNT tests
1 parent a081558 commit 1370398

File tree

6 files changed

+314
-7
lines changed

6 files changed

+314
-7
lines changed

CHANGELOG_SINCE_LAST_VERSION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
- Removed discontinuous elements.
22
- Added pictures of reference cells to readme.
33
- Added trimmed serendipity Hdiv and Hcurl elements.
4+
- Added TNT scalar, Hdiv and Hcurl elements.

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ The reference quadrilateral has vertices (0, 0), (1, 0), (0, 1), and (1, 1). Its
208208
- serendipity (alternative names: S)
209209
- serendipity Hcurl (alternative names: Scurl, BDMCE, AAE)
210210
- serendipity Hdiv (alternative names: Sdiv, BDMCF, AAF)
211+
- tiniest tensor (alternative names: TNT)
212+
- tiniest tensor Hcurl (alternative names: TNTcurl)
213+
- tiniest tensor Hdiv (alternative names: TNTdiv)
211214
- trimmed serendipity Hcurl (alternative names: TScurl)
212215
- trimmed serendipity Hdiv (alternative names: TSdiv)
213216
- vector dPc
@@ -258,6 +261,9 @@ The reference hexahedron has vertices (0, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0)
258261
- serendipity (alternative names: S)
259262
- serendipity Hcurl (alternative names: Scurl, BDMCE, AAE)
260263
- serendipity Hdiv (alternative names: Sdiv, BDMCF, AAF)
264+
- tiniest tensor (alternative names: TNT)
265+
- tiniest tensor Hcurl (alternative names: TNTcurl)
266+
- tiniest tensor Hdiv (alternative names: TNTdiv)
261267
- trimmed serendipity Hcurl (alternative names: TScurl)
262268
- trimmed serendipity Hdiv (alternative names: TSdiv)
263269
- vector dPc

symfem/create.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,10 @@ def create_element(cell_type, element_type, order, **kwargs):
162162
Guzman-Neilan,
163163
nonconforming Arnold-Winther, nonconforming AW,
164164
TScurl, trimmed serendipity Hcurl,
165-
TSdiv, trimmed serendipity Hdiv
165+
TSdiv, trimmed serendipity Hdiv,
166+
TNT, tiniest tensor,
167+
TNTcurl, tiniest tensor Hcurl,
168+
TNTdiv, tiniest tensor Hdiv
166169
order : int
167170
The order of the element.
168171
"""

symfem/elements/tnt.py

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
"""TiNiest Tensor product (TNT) elements.
2+
3+
These elements' definitions appear in https://doi.org/10.1090/S0025-5718-2013-02729-9
4+
(Cockburn, Qiu, 2013)
5+
"""
6+
7+
from itertools import product
8+
from ..finite_element import CiarletElement
9+
from ..polynomials import quolynomial_set
10+
from ..functionals import (TangentIntegralMoment, IntegralAgainst,
11+
NormalIntegralMoment, PointEvaluation,
12+
DerivativeIntegralMoment)
13+
from ..symbolic import x, t, subs
14+
from ..calculus import grad, curl
15+
from ..moments import make_integral_moment_dofs
16+
from ..vectors import vcross
17+
from ..legendre import get_legendre_basis
18+
from ..references import Interval
19+
from .q import Q
20+
21+
22+
def P(k, v):
23+
"""Return the kth Legendre polynomial."""
24+
return subs(
25+
get_legendre_basis([x[0] ** i for i in range(k + 1)], Interval())[-1],
26+
x[0], v)
27+
28+
29+
def B(k, v):
30+
"""
31+
Return the function B_k.
32+
33+
This function is defined on page 4 (606) of
34+
https://doi.org/10.1090/S0025-5718-2013-02729-9 (Cockburn, Qiu, 2013).
35+
"""
36+
if k == 1:
37+
return 0
38+
return (P(k, v) - P(k - 2, v)) / (4 + k - 2)
39+
40+
41+
class TNT(CiarletElement):
42+
"""TiNiest Tensor scalar finite element."""
43+
44+
def __init__(self, reference, order, variant="equispaced"):
45+
poly = quolynomial_set(reference.tdim, 1, order)
46+
if reference.tdim == 2:
47+
for i in range(2):
48+
vars = [x[j] for j in range(2) if j != i]
49+
for f in [1 - vars[0], vars[0]]:
50+
poly.append(f * B(order + 1, x[i]))
51+
52+
elif reference.tdim == 3:
53+
for i in range(3):
54+
vars = [x[j] for j in range(3) if j != i]
55+
for f0 in [1 - vars[0], vars[0]]:
56+
for f1 in [1 - vars[1], vars[1]]:
57+
poly.append(f0 * f1 * B(order + 1, x[i]))
58+
59+
dofs = []
60+
for i, v in enumerate(reference.vertices):
61+
dofs.append(PointEvaluation(v, entity=(0, i)))
62+
63+
for i in range(1, order + 1):
64+
f = i * t[0] ** (i - 1)
65+
for edge_n in range(reference.sub_entity_count(1)):
66+
edge = reference.sub_entity(1, edge_n)
67+
dofs.append(IntegralAgainst(
68+
edge, f, entity=(1, edge_n), mapping="identity"))
69+
70+
for i in range(1, order):
71+
for j in range(1, order):
72+
f = t[0] ** i * (t[0] - 1) * t[1] ** j * (t[1] - 1)
73+
delta_f = (f.diff(t[0]).diff(t[0]) + f.diff(t[1]).diff(t[1])).expand()
74+
for face_n in range(reference.sub_entity_count(2)):
75+
face = reference.sub_entity(2, face_n)
76+
dofs.append(IntegralAgainst(
77+
face, delta_f, entity=(2, face_n), mapping="identity"))
78+
79+
if reference.tdim == 3:
80+
for i in product(range(1, order), repeat=3):
81+
f = 1
82+
for j, k in zip(i, x):
83+
f *= k ** j * (k - 1)
84+
grad_f = tuple(j.expand() for j in grad(f, 3))
85+
dofs.append(DerivativeIntegralMoment(
86+
reference, 1, grad_f, None, entity=(3, 0), mapping="identity"))
87+
88+
super().__init__(
89+
reference, order, poly, dofs, reference.tdim, 1
90+
)
91+
self.variant = variant
92+
93+
def init_kwargs(self):
94+
"""Return the kwargs used to create this element."""
95+
return {"variant": self.variant}
96+
97+
names = ["tiniest tensor", "TNT"]
98+
references = ["quadrilateral", "hexahedron"]
99+
min_order = 1
100+
continuity = "C0"
101+
102+
103+
class TNTcurl(CiarletElement):
104+
"""TiNiest Tensor Hcurl finite element."""
105+
106+
def __init__(self, reference, order, variant="equispaced"):
107+
poly = quolynomial_set(reference.tdim, reference.tdim, order)
108+
if reference.tdim == 2:
109+
for i in product([0, 1], repeat=2):
110+
if sum(i) != 0:
111+
poly.append([j.expand() for j in [
112+
P(order, i[0] * x[0]) * B(order + 1, i[1] * x[1]),
113+
-B(order + 1, i[0] * x[0]) * P(order, i[1] * x[1]),
114+
]])
115+
else:
116+
face_poly = []
117+
for i in product([0, 1], repeat=2):
118+
if sum(i) != 0:
119+
face_poly.append([j.expand() for j in [
120+
B(order + 1, i[0] * t[0]) * P(order, i[1] * t[1]),
121+
P(order, i[0] * t[0]) * B(order + 1, i[1] * t[1]),
122+
]])
123+
for lamb_n in [(x[0], 0, 0), (1 - x[0], 0, 0),
124+
(0, x[1], 0), (0, 1 - x[1], 0),
125+
(0, 0, x[2]), (0, 0, 1 - x[2])]:
126+
vars = tuple(i for i, j in enumerate(lamb_n) if j == 0)
127+
poly += [vcross(lamb_n, [
128+
subs(p[0], t[:2], [x[j] for j in vars]) if i == vars[0]
129+
else (subs(p[1], t[:2], [x[j] for j in vars]) if i == vars[1] else 0)
130+
for i in range(3)
131+
]) for p in face_poly]
132+
133+
dofs = []
134+
dofs += make_integral_moment_dofs(
135+
reference,
136+
edges=(TangentIntegralMoment, Q, order, {"variant": variant}),
137+
)
138+
139+
# Face moments
140+
face_moments = []
141+
for i in product(range(order + 1), repeat=2):
142+
if sum(i) > 0:
143+
f = x[0] ** i[0] * x[1] ** i[1]
144+
grad_f = grad(f, 2)
145+
grad_f = subs((grad_f[1], -grad_f[0]), x[:2], t[:2])
146+
face_moments.append(grad_f)
147+
148+
for i in range(2, order + 1):
149+
for j in range(2, order + 1):
150+
face_moments.append((
151+
t[1] ** (j - 1) * (1 - t[1]) * t[0] ** (i - 2) * (i * t[0] - i + 1),
152+
-t[0] ** (i - 1) * (1 - t[0]) * t[1] ** (j - 2) * (j - 1 - j * t[1])))
153+
if reference.tdim == 2:
154+
for f in face_moments:
155+
dofs.append(IntegralAgainst(
156+
reference, f, entity=(2, 0), mapping="contravariant"))
157+
elif reference.tdim == 3:
158+
for face_n in range(6):
159+
face = reference.sub_entity(2, face_n)
160+
for f in face_moments:
161+
dofs.append(IntegralAgainst(
162+
face, f, entity=(2, face_n), mapping="contravariant"))
163+
164+
# Interior Moments
165+
if reference.tdim == 3:
166+
for i in range(1, order):
167+
for j in range(1, order):
168+
for k in range(order + 1):
169+
f = (x[0] ** k * x[1] ** i * (1 - x[1]) * x[2] ** j * (1 - x[2]), 0, 0)
170+
dofs.append(IntegralAgainst(
171+
reference, curl(curl(f)), entity=(3, 0), mapping="covariant"))
172+
173+
f = (0, x[1] ** k * x[0] ** i * (1 - x[0]) * x[2] ** j * (1 - x[2]), 0, 0)
174+
dofs.append(IntegralAgainst(
175+
reference, curl(curl(f)), entity=(3, 0), mapping="covariant"))
176+
177+
if k in [0, 2]:
178+
f = (0, 0, x[2] ** k * x[0] ** i * (1 - x[0]) * x[1] ** j * (1 - x[1]))
179+
dofs.append(IntegralAgainst(
180+
reference, curl(curl(f)), entity=(3, 0), mapping="covariant"))
181+
182+
for i in range(2, order + 1):
183+
for j in range(2, order + 1):
184+
for k in range(2, order + 1):
185+
f = x[0] ** (i - 1) * x[0] ** i
186+
f *= x[1] ** (j - 1) * x[1] ** j
187+
f *= x[2] ** (k - 1) * x[2] ** k
188+
grad_f = grad(f, 3)
189+
dofs.append(IntegralAgainst(
190+
reference, grad_f, entity=(3, 0), mapping="contravariant"))
191+
192+
super().__init__(
193+
reference, order, poly, dofs, reference.tdim, reference.tdim
194+
)
195+
self.variant = variant
196+
197+
def init_kwargs(self):
198+
"""Return the kwargs used to create this element."""
199+
return {"variant": self.variant}
200+
201+
names = ["tiniest tensor Hcurl", "TNTcurl"]
202+
references = ["quadrilateral", "hexahedron"]
203+
min_order = 1
204+
continuity = "H(curl)"
205+
206+
207+
class TNTdiv(CiarletElement):
208+
"""TiNiest Tensor Hdiv finite element."""
209+
210+
def __init__(self, reference, order, variant="equispaced"):
211+
poly = quolynomial_set(reference.tdim, reference.tdim, order)
212+
if reference.tdim == 2:
213+
for i in product([0, 1], repeat=2):
214+
if sum(i) != 0:
215+
poly.append([j.expand() for j in [
216+
B(order + 1, i[0] * x[0]) * P(order, i[1] * x[1]),
217+
P(order, i[0] * x[0]) * B(order + 1, i[1] * x[1]),
218+
]])
219+
else:
220+
for i in product([0, 1], repeat=3):
221+
if sum(i) != 0:
222+
poly.append([
223+
B(order + 1, i[0] * x[0]) * P(order, i[1] * x[1]) * P(order, i[2] * x[2]),
224+
P(order, i[0] * x[0]) * B(order + 1, i[1] * x[1]) * P(order, i[2] * x[2]),
225+
P(order, i[0] * x[0]) * P(order, i[1] * x[1]) * B(order + 1, i[2] * x[2]),
226+
])
227+
228+
dofs = []
229+
dofs += make_integral_moment_dofs(
230+
reference,
231+
facets=(NormalIntegralMoment, Q, order, {"variant": variant}),
232+
)
233+
234+
for i in product(range(order + 1), repeat=reference.tdim):
235+
if sum(i) > 0:
236+
if reference.tdim == 2:
237+
f = x[0] ** i[0] * x[1] ** i[1]
238+
else:
239+
f = x[0] ** i[0] * x[1] ** i[1] * x[2] ** i[2]
240+
grad_f = grad(f, reference.tdim)
241+
dofs.append(IntegralAgainst(reference, grad_f, entity=(reference.tdim, 0),
242+
mapping="covariant"))
243+
244+
if reference.tdim == 2:
245+
for i in range(2, order + 1):
246+
for j in range(2, order + 1):
247+
f = (x[0] ** (i - 1) * (1 - x[0]) * x[1] ** (j - 2) * (j - 1 - j * x[1]),
248+
x[1] ** (j - 1) * (1 - x[1]) * x[0] ** (i - 2) * (i * x[0] - i + 1))
249+
dofs.append(IntegralAgainst(
250+
reference, f, entity=(reference.tdim, 0), mapping="covariant"))
251+
if reference.tdim == 3:
252+
for i in range(2, order + 1):
253+
for j in range(2, order + 1):
254+
for k in range(order + 1):
255+
f = (
256+
x[2] ** k * x[0] ** (i - 1) * (1 - x[0]) * x[2] ** (j - 2) * (
257+
j - 1 - j * x[1]),
258+
x[2] ** k * x[1] ** (j - 1) * (1 - x[1]) * x[0] ** (i - 2) * (
259+
i * x[0] - i + 1),
260+
0
261+
)
262+
dofs.append(IntegralAgainst(
263+
reference, f, entity=(reference.tdim, 0), mapping="covariant"))
264+
f = (
265+
x[1] ** k * x[0] ** (i - 1) * (1 - x[0]) * x[2] ** (j - 2) * (
266+
j - 1 - j * x[2]),
267+
0,
268+
x[1] ** k * x[2] ** (j - 1) * (1 - x[2]) * x[0] ** (i - 2) * (
269+
i * x[0] - i + 1)
270+
)
271+
dofs.append(IntegralAgainst(
272+
reference, f, entity=(reference.tdim, 0), mapping="covariant"))
273+
if k in [0, 2]:
274+
f = (
275+
0,
276+
x[0] ** k * x[1] ** (i - 1) * (1 - x[1]) * x[2] ** (j - 2) * (
277+
j - 1 - j * x[2]),
278+
x[0] ** k * x[2] ** (j - 1) * (1 - x[2]) * x[1] ** (i - 2) * (
279+
i * x[1] - i + 1)
280+
)
281+
dofs.append(IntegralAgainst(
282+
reference, f, entity=(reference.tdim, 0), mapping="covariant"))
283+
284+
super().__init__(
285+
reference, order, poly, dofs, reference.tdim, reference.tdim
286+
)
287+
self.variant = variant
288+
289+
def init_kwargs(self):
290+
"""Return the kwargs used to create this element."""
291+
return {"variant": self.variant}
292+
293+
names = ["tiniest tensor Hdiv", "TNTdiv"]
294+
references = ["quadrilateral", "hexahedron"]
295+
min_order = 1
296+
continuity = "H(div)"

symfem/elements/trimmed_serendipity.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
from ..polynomials import polynomial_set
1010
from ..functionals import (IntegralMoment, TangentIntegralMoment, IntegralAgainst,
1111
NormalIntegralMoment)
12-
from ..quadrature import get_quadrature
1312
from ..symbolic import x, t, subs
1413
from ..calculus import grad, curl
1514
from ..vectors import vcross
@@ -58,8 +57,6 @@ def __init__(self, reference, order, variant="equispaced"):
5857
p = x[2] * x[0] ** i * x[1] ** (order - 1 - i)
5958
poly.append((-x[1] * p, x[0] * p, 0))
6059

61-
points, _ = get_quadrature(variant, order)
62-
6360
dofs = []
6461
dofs += make_integral_moment_dofs(
6562
reference,
@@ -120,8 +117,6 @@ def __init__(self, reference, order, variant="equispaced"):
120117
p = x[2] * x[0] ** i * x[1] ** (order - 1 - i)
121118
poly.append(curl((-x[1] * p, x[0] * p, 0)))
122119

123-
points, _ = get_quadrature(variant, order)
124-
125120
dofs = []
126121
dofs += make_integral_moment_dofs(
127122
reference,

test/utils.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@
118118
"BDFM": [[{"variant": "equispaced"}, range(1, 4)]],
119119
"TScurl": [[{"variant": "equispaced"}, range(1, 5)]],
120120
"TSdiv": [[{"variant": "equispaced"}, range(1, 5)]],
121+
"TNT": [[{"variant": "equispaced"}, range(1, 4)]],
122+
"TNTcurl": [[{"variant": "equispaced"}, range(1, 4)]],
123+
"TNTdiv": [[{"variant": "equispaced"}, range(1, 4)]],
121124
},
122125
"hexahedron": {
123126
"bubble": [[{"variant": "equispaced"}, range(2, 4)], [{"variant": "lobatto"}, range(2, 4)]],
@@ -134,7 +137,10 @@
134137
"BDFM": [[{"variant": "equispaced"}, range(1, 3)]],
135138
"BDDF": [[{"variant": "equispaced"}, range(1, 3)]],
136139
"TScurl": [[{"variant": "equispaced"}, range(1, 4)]],
137-
"TSdiv": [[{"variant": "equispaced"}, range(1, 4)]]
140+
"TSdiv": [[{"variant": "equispaced"}, range(1, 4)]],
141+
"TNT": [[{"variant": "equispaced"}, range(1, 2)]],
142+
"TNTcurl": [[{"variant": "equispaced"}, range(1, 2)]],
143+
"TNTdiv": [[{"variant": "equispaced"}, range(1, 2)]],
138144
},
139145
"prism": {
140146
"Lagrange": [[{"variant": "equispaced"}, range(4)]],

0 commit comments

Comments
 (0)