Skip to content

Commit 1187846

Browse files
authored
make elements use polynomial subdegree as canonical degree (#302)
* make elements use polynomial subdegree as canonical degree * fix tests * defelement branch * ruff * BC degree * remove assert degree == 1 * last_updated * change * remove print * remove assert * max order for test * nedelec degree in demo * only run coveralls on push to main (it takes >2 hours to run)
1 parent 36126b0 commit 1187846

33 files changed

+449
-393
lines changed

.github/workflows/coveralls.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name: 🥼 Coveralls
22

33
on:
4-
pull_request:
54
push:
65
branches:
76
- main

.github/workflows/defelement.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
with:
2828
path: ./defelement
2929
repository: DefElement/DefElement
30-
ref: main
30+
ref: mscroggs/remove-symfem-maps
3131
- name: Install requirements
3232
run: |
3333
cd defelement

CHANGELOG_SINCE_LAST_VERSION.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
- Implement component-wise integrals of VectorFunction and MatrixFunction
33
- Implement grad of VectorFunction
44
- Add Gopalakrishnan-Lederer-Schoberl element
5+
- Use polynomial subdegree to index elements wherever possible

demo/nedelec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from symfem.symbols import x
1616
from symfem.utils import allequal
1717

18-
element = symfem.create_element("triangle", "Nedelec1", 4)
18+
element = symfem.create_element("triangle", "Nedelec1", 3)
1919
polys = element.get_polynomial_basis()
2020

2121
# Check that the first 20 polynomials in the polynomial basis are

symfem/elements/abf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
4545
dofs: ListOfFunctionals = make_integral_moment_dofs(
4646
reference,
4747
edges=(NormalIntegralMoment, Lagrange, order, {"variant": variant}),
48-
faces=(IntegralMoment, Nedelec, order, {"variant": variant}),
48+
faces=(IntegralMoment, Nedelec, order - 1, {"variant": variant}),
4949
)
5050

5151
for i in range(order + 1):
@@ -98,4 +98,4 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
9898
min_order = 0
9999
continuity = "H(div)"
100100
value_type = "vector"
101-
last_updated = "2023.06"
101+
last_updated = "2025.03"

symfem/elements/aw.py

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,24 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
4040
self.variant = variant
4141
poly: typing.List[FunctionInput] = []
4242
poly += [
43-
((p[0], p[1]), (p[1], p[2]))
44-
for p in polynomial_set_vector(reference.tdim, 3, order - 1)
43+
((p[0], p[1]), (p[1], p[2])) for p in polynomial_set_vector(reference.tdim, 3, order)
4544
]
4645
poly += [
4746
(
4847
(
49-
(order - k + 1) * (order - k + 2) * x[0] ** k * x[1] ** (order - k),
50-
-k * (order - k + 2) * x[0] ** (k - 1) * x[1] ** (order - k + 1),
48+
(order - k + 2) * (order - k + 3) * x[0] ** k * x[1] ** (order - k + 1),
49+
-k * (order - k + 3) * x[0] ** (k - 1) * x[1] ** (order - k + 2),
5150
),
5251
(
53-
-k * (order - k + 2) * x[0] ** (k - 1) * x[1] ** (order - k + 1),
54-
k * (k - 1) * x[0] ** (k - 2) * x[1] ** (order - k + 2),
52+
-k * (order - k + 3) * x[0] ** (k - 1) * x[1] ** (order - k + 2),
53+
k * (k - 1) * x[0] ** (k - 2) * x[1] ** (order - k + 3),
5554
),
5655
)
57-
for k in range(order + 1)
56+
for k in range(order + 2)
5857
]
5958
poly += [
60-
((0, x[0] ** order), (x[0] ** order, -order * x[0] ** (order - 1) * x[1])),
61-
((0, 0), (0, x[0] ** order)),
59+
((0, x[0] ** (order + 1)), (x[0] ** (order + 1), -(order + 1) * x[0] ** order * x[1])),
60+
((0, 0), (0, x[0] ** (order + 1))),
6261
]
6362

6463
dofs: ListOfFunctionals = []
@@ -71,7 +70,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
7170
)
7271
for e_n in range(reference.sub_entity_count(1)):
7372
sub_ref = reference.sub_entity(1, e_n)
74-
sub_e = Lagrange(sub_ref.default_reference(), order - 2, variant)
73+
sub_e = Lagrange(sub_ref.default_reference(), order - 1, variant)
7574
for dof_n, dof in enumerate(sub_e.dofs):
7675
p = sub_e.get_basis_function(dof_n).get_function()
7776
for component in [sub_ref.normal(), sub_ref.tangent()]:
@@ -86,7 +85,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
8685
mapping="double_contravariant",
8786
)
8887
)
89-
sub_e = Lagrange(reference, order - 3, variant)
88+
sub_e = Lagrange(reference, order - 2, variant)
9089
for dof_n, dof in enumerate(sub_e.dofs):
9190
p = sub_e.get_basis_function(dof_n).get_function()
9291
for component22 in [((1, 0), (0, 0)), ((0, 1), (0, 0)), ((0, 0), (0, 1))]:
@@ -100,10 +99,10 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
10099
)
101100
)
102101

103-
if order >= 4:
104-
sub_e = Lagrange(reference, order - 4, variant)
102+
if order >= 3:
103+
sub_e = Lagrange(reference, order - 3, variant)
105104
for p, dof in zip(sub_e.get_basis_functions(), sub_e.dofs):
106-
if sympy.Poly(p.as_sympy(), x[:2]).degree() != order - 4:
105+
if sympy.Poly(p.as_sympy(), x[:2]).degree() != order - 3:
107106
continue
108107
f = p * x[0] ** 2 * x[1] ** 2 * (1 - x[0] - x[1]) ** 2
109108
J = [
@@ -134,26 +133,26 @@ def init_kwargs(self) -> typing.Dict[str, typing.Any]:
134133

135134
@property
136135
def lagrange_subdegree(self) -> int:
137-
return self.order - 1
136+
return self.order
138137

139138
@property
140139
def lagrange_superdegree(self) -> typing.Optional[int]:
141-
return self.order
140+
return self.order + 1
142141

143142
@property
144143
def polynomial_subdegree(self) -> int:
145-
return self.order - 1
144+
return self.order
146145

147146
@property
148147
def polynomial_superdegree(self) -> typing.Optional[int]:
149-
return self.order
148+
return self.order + 1
150149

151150
names = ["Arnold-Winther", "AW", "conforming Arnold-Winther"]
152151
references = ["triangle"]
153-
min_order = 3
152+
min_order = 2
154153
continuity = "integral inner H(div)"
155154
value_type = "symmetric matrix"
156-
last_updated = "2024.09"
155+
last_updated = "2025.03"
157156

158157

159158
class NonConformingArnoldWinther(CiarletElement):
@@ -171,8 +170,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
171170
self.variant = variant
172171
poly: typing.List[FunctionInput] = []
173172
poly += [
174-
((p[0], p[1]), (p[1], p[2]))
175-
for p in polynomial_set_vector(reference.tdim, 3, order - 1)
173+
((p[0], p[1]), (p[1], p[2])) for p in polynomial_set_vector(reference.tdim, 3, order)
176174
]
177175

178176
poly += [
@@ -235,24 +233,24 @@ def init_kwargs(self) -> typing.Dict[str, typing.Any]:
235233

236234
@property
237235
def lagrange_subdegree(self) -> int:
238-
return self.order - 1
236+
return self.order
239237

240238
@property
241239
def lagrange_superdegree(self) -> typing.Optional[int]:
242-
return self.order
240+
return self.order + 1
243241

244242
@property
245243
def polynomial_subdegree(self) -> int:
246-
return self.order - 1
244+
return self.order
247245

248246
@property
249247
def polynomial_superdegree(self) -> typing.Optional[int]:
250-
return self.order
248+
return self.order + 1
251249

252250
names = ["nonconforming Arnold-Winther", "nonconforming AW"]
253251
references = ["triangle"]
254-
min_order = 2
255-
max_order = 2
252+
min_order = 1
253+
max_order = 1
256254
continuity = "integral inner H(div)"
257255
value_type = "symmetric matrix"
258-
last_updated = "2024.10"
256+
last_updated = "2025.03"

symfem/elements/bdfm.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,38 +33,38 @@ def bdfm_polyset(reference: Reference, order: int) -> typing.List[FunctionInput]
3333
"""
3434
dim = reference.tdim
3535
pset: typing.List[FunctionInput] = []
36-
pset += polynomial_set_vector(dim, dim, order - 1)
36+
pset += polynomial_set_vector(dim, dim, order)
3737
if reference.name == "quadrilateral":
38-
for i in range(1, order + 1):
39-
j = order - i
38+
for i in range(1, order + 2):
39+
j = order + 1 - i
4040
pset.append((x[0] ** i * x[1] ** j, 0))
4141
pset.append((0, x[1] ** i * x[0] ** j))
4242
elif reference.name == "triangle":
43-
for i in range(order - 1):
44-
p = x[0] ** i * x[1] ** (order - 2 - i)
43+
for i in range(order):
44+
p = x[0] ** i * x[1] ** (order - 1 - i)
4545
pset.append((x[0] * (x[0] + x[1]) * p, 0))
4646
pset.append((0, x[1] * (x[0] + x[1]) * p))
47-
p = x[0] ** (order - 1)
47+
p = x[0] ** order
4848
pset.append((x[0] * p, x[1] * p))
4949
elif reference.name == "hexahedron":
50-
for i in range(1, order + 1):
51-
for j in range(order + 1 - i):
52-
k = order - i - j
50+
for i in range(1, order + 2):
51+
for j in range(order + 2 - i):
52+
k = order + 1 - i - j
5353
pset.append((x[0] ** i * x[1] ** j * x[2] ** k, 0, 0))
5454
pset.append((0, x[1] ** i * x[0] ** j * x[2] ** k, 0))
5555
pset.append((0, 0, x[2] ** i * x[0] ** j * x[1] ** k))
5656
elif reference.name == "tetrahedron":
57-
for i in range(order - 1):
58-
for j in range(order - i - 1):
59-
p = x[0] ** i * x[1] ** j * x[2] ** (order - 2 - i - j)
57+
for i in range(order):
58+
for j in range(order - i):
59+
p = x[0] ** i * x[1] ** j * x[2] ** (order - 1 - i - j)
6060
pset.append((x[0] * (x[0] + x[1] + x[2]) * p, 0, 0))
6161
pset.append((0, x[1] * (x[0] + x[1] + x[2]) * p, 0))
6262
pset.append((0, 0, x[2] * (x[0] + x[1] + x[2]) * p))
63-
for i in range(order):
64-
p = x[0] ** i * x[1] ** (order - 1 - i)
63+
for i in range(order + 1):
64+
p = x[0] ** i * x[1] ** (order - i)
6565
pset.append((x[0] * p, x[1] * p, x[2] * p))
66-
for i in range(1, order):
67-
p = x[0] ** i * x[1] ** (order - 1 - i)
66+
for i in range(1, order + 1):
67+
p = x[0] ** i * x[1] ** (order - i)
6868
pset.append((p * (x[0] + x[1]), 0, x[1] * p))
6969

7070
return pset
@@ -90,14 +90,14 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
9090
if reference.name in ["triangle", "tetrahedron"]:
9191
dofs = make_integral_moment_dofs(
9292
reference,
93-
facets=(NormalIntegralMoment, Lagrange, order - 1, {"variant": variant}),
93+
facets=(NormalIntegralMoment, Lagrange, order, {"variant": variant}),
9494
cells=(IntegralMoment, NedelecFirstKind, order - 1, {"variant": variant}),
9595
)
9696
else:
9797
dofs = make_integral_moment_dofs(
9898
reference,
99-
facets=(NormalIntegralMoment, DPC, order - 1, {"variant": variant}),
100-
cells=(IntegralMoment, VectorDPC, order - 2, {"variant": variant}),
99+
facets=(NormalIntegralMoment, DPC, order, {"variant": variant}),
100+
cells=(IntegralMoment, VectorDPC, order - 1, {"variant": variant}),
101101
)
102102
self.variant = variant
103103

@@ -114,25 +114,25 @@ def init_kwargs(self) -> typing.Dict[str, typing.Any]:
114114
@property
115115
def lagrange_subdegree(self) -> int:
116116
if self.reference.name in ["triangle", "tetrahedron"]:
117-
return self.order - 1
117+
return self.order
118118
else:
119-
return self.order // self.reference.tdim
119+
return (self.order + 1) // self.reference.tdim
120120

121121
@property
122122
def lagrange_superdegree(self) -> typing.Optional[int]:
123-
return self.order
123+
return self.order + 1
124124

125125
@property
126126
def polynomial_subdegree(self) -> int:
127-
return self.order - 1
127+
return self.order
128128

129129
@property
130130
def polynomial_superdegree(self) -> typing.Optional[int]:
131-
return self.order
131+
return self.order + 1
132132

133133
names = ["Brezzi-Douglas-Fortin-Marini", "BDFM"]
134134
references = ["triangle", "quadrilateral", "hexahedron", "tetrahedron"]
135-
min_order = 1
135+
min_order = 0
136136
continuity = "H(div)"
137137
value_type = "vector"
138-
last_updated = "2024.10"
138+
last_updated = "2025.03"

symfem/elements/bdm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
3838
dofs: ListOfFunctionals = make_integral_moment_dofs(
3939
reference,
4040
facets=(NormalIntegralMoment, Lagrange, order, {"variant": variant}),
41-
cells=(IntegralMoment, NedelecFirstKind, order - 1, {"variant": variant}),
41+
cells=(IntegralMoment, NedelecFirstKind, order - 2, {"variant": variant}),
4242
)
4343
self.variant = variant
4444

@@ -73,4 +73,4 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
7373
min_order = 1
7474
continuity = "H(div)"
7575
value_type = "vector"
76-
last_updated = "2023.06"
76+
last_updated = "2025.03"

symfem/elements/bell.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def __init__(self, reference: Reference, order: int, variant: str = "equispaced"
2727
variant: The variant of the element
2828
"""
2929
assert reference.name == "triangle"
30-
assert order == 5
3130
dofs: ListOfFunctionals = []
3231
for v_n, v in enumerate(reference.vertices):
3332
dofs.append(PointEvaluation(reference, v, entity=(0, v_n)))
@@ -72,8 +71,9 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
7271

7372
names = ["Bell"]
7473
references = ["triangle"]
75-
min_order = 5
76-
max_order = 5
74+
min_order = 4
75+
max_order = 4
7776
continuity = "C1"
7877
value_type = "scalar"
79-
last_updated = "2023.05"
78+
last_updated = "2025.03"
79+
_max_continuity_test_order = 3

symfem/elements/dual.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,6 @@ def get_polynomial_basis(self, reshape: bool = True) -> typing.List[Function]:
6868
"""
6969
raise ValueError("Polynomial basis not supported for barycentric dual elements.")
7070

71-
@property
72-
def maximum_degree(self) -> int:
73-
"""Get the maximum degree of this polynomial set for the element."""
74-
raise NotImplementedError()
75-
7671
def get_dual_matrix(self) -> sympy.matrices.dense.MutableDenseMatrix:
7772
"""Get the dual matrix.
7873
@@ -287,7 +282,6 @@ def __init__(self, reference: DualPolygon, order: int):
287282
reference: The reference element
288283
order: The polynomial order
289284
"""
290-
assert order == 1
291285
dual_coefficients: typing.List[
292286
typing.List[typing.List[typing.Union[int, sympy.core.expr.Expr]]]
293287
] = [
@@ -339,11 +333,11 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
339333

340334
names = ["Buffa-Christiansen", "BC"]
341335
references = ["dual polygon"]
342-
min_order = 1
343-
max_order = 1
336+
min_order = 0
337+
max_order = 0
344338
continuity = "H(div)"
345339
value_type = "vector dual"
346-
last_updated = "2023.05"
340+
last_updated = "2025.03"
347341

348342

349343
class RotatedBuffaChristiansen(DualCiarletElement):
@@ -356,7 +350,6 @@ def __init__(self, reference: DualPolygon, order: int):
356350
reference: The reference element
357351
order: The polynomial order
358352
"""
359-
assert order == 1
360353
dual_coefficients: typing.List[
361354
typing.List[typing.List[typing.Union[int, sympy.core.expr.Expr]]]
362355
] = [
@@ -408,8 +401,8 @@ def polynomial_superdegree(self) -> typing.Optional[int]:
408401

409402
names = ["rotated Buffa-Christiansen", "RBC"]
410403
references = ["dual polygon"]
411-
min_order = 1
412-
max_order = 1
404+
min_order = 0
405+
max_order = 0
413406
continuity = "H(div)"
414407
value_type = "vector dual"
415-
last_updated = "2023.05"
408+
last_updated = "2025.03"

0 commit comments

Comments
 (0)