Skip to content

Commit 00fd908

Browse files
authored
Add Direct serendipity element (#83)
* add CiarletElement class * pydocstyle * Add Direct serendipity element * flake
1 parent 32fdeb2 commit 00fd908

34 files changed

+300
-142
lines changed

demo/custom_element.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import symfem
22
import sympy
3-
from symfem.core.finite_element import FiniteElement
3+
from symfem.core.finite_element import CiarletElement
44
from symfem.core.symbolic import x
55
from symfem.core.functionals import PointEvaluation
66

77

8-
class CustomElement(FiniteElement):
8+
class CustomElement(CiarletElement):
99
"""Custom element on a quadrilateral."""
1010

1111
def __init__(self, reference, order, variant):

symfem/core/finite_element.py

Lines changed: 104 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -9,74 +9,22 @@
99
class FiniteElement:
1010
"""Abstract finite element."""
1111

12-
def __init__(self, reference, order, basis, dofs, domain_dim, range_dim,
12+
def __init__(self, reference, order, space_dim, domain_dim, range_dim,
1313
range_shape=None):
14-
assert len(basis) == len(dofs)
1514
self.reference = reference
1615
self.order = order
17-
self.basis = basis
18-
self.dofs = dofs
16+
self.space_dim = space_dim
1917
self.domain_dim = domain_dim
2018
self.range_dim = range_dim
2119
self.range_shape = range_shape
22-
self.space_dim = len(dofs)
23-
self._basis_functions = None
24-
self._reshaped_basis_functions = None
2520

2621
def entity_dofs(self, entity_dim, entity_number):
2722
"""Get the numbers of the DOFs associated with the given entity."""
28-
return [i for i, j in enumerate(self.dofs) if j.entity == (entity_dim, entity_number)]
29-
30-
def get_polynomial_basis(self, reshape=True):
31-
"""Get the polynomial basis for the element."""
32-
if reshape and self.range_shape is not None:
33-
if len(self.range_shape) != 2:
34-
raise NotImplementedError
35-
assert self.range_shape[0] * self.range_shape[1] == self.range_dim
36-
return [sympy.Matrix(
37-
[b[i * self.range_shape[1]: (i + 1) * self.range_shape[1]]
38-
for i in range(self.range_shape[0])]) for b in self.basis]
39-
40-
return self.basis
41-
42-
def get_dual_matrix(self):
43-
"""Get the dual matrix."""
44-
mat = []
45-
for b in self.basis:
46-
row = []
47-
for d in self.dofs:
48-
row.append(d.eval(b))
49-
mat.append(row)
50-
return sympy.Matrix(mat)
23+
raise NotImplementedError()
5124

5225
def get_basis_functions(self, reshape=True):
5326
"""Get the basis functions of the element."""
54-
if self._basis_functions is None:
55-
minv = self.get_dual_matrix().inv("LU")
56-
self._basis_functions = []
57-
if self.range_dim == 1:
58-
# Scalar space
59-
for i, dof in enumerate(self.dofs):
60-
self._basis_functions.append(
61-
sym_sum(c * d for c, d in zip(minv.row(i), self.basis)))
62-
else:
63-
# Vector space
64-
for i, dof in enumerate(self.dofs):
65-
b = [zero for i in self.basis[0]]
66-
for c, d in zip(minv.row(i), self.basis):
67-
for j, d_j in enumerate(d):
68-
b[j] += c * d_j
69-
self._basis_functions.append(b)
70-
71-
if reshape and self.range_shape is not None:
72-
if len(self.range_shape) != 2:
73-
raise NotImplementedError
74-
assert self.range_shape[0] * self.range_shape[1] == self.range_dim
75-
return [sympy.Matrix(
76-
[b[i * self.range_shape[1]: (i + 1) * self.range_shape[1]]
77-
for i in range(self.range_shape[0])]) for b in self._basis_functions]
78-
79-
return self._basis_functions
27+
raise NotImplementedError()
8028

8129
def get_basis_function(self, n):
8230
"""Get a single basis function of the element."""
@@ -147,3 +95,103 @@ def name(self):
14795
return self.names[0]
14896

14997
names = []
98+
99+
100+
class CiarletElement(FiniteElement):
101+
"""Finite element defined using the Ciarlet definition."""
102+
103+
def __init__(self, reference, order, basis, dofs, domain_dim, range_dim,
104+
range_shape=None):
105+
super().__init__(reference, order, len(dofs), domain_dim, range_dim, range_shape)
106+
assert len(basis) == len(dofs)
107+
self.basis = basis
108+
self.dofs = dofs
109+
self._basis_functions = None
110+
self._reshaped_basis_functions = None
111+
112+
def entity_dofs(self, entity_dim, entity_number):
113+
"""Get the numbers of the DOFs associated with the given entity."""
114+
return [i for i, j in enumerate(self.dofs) if j.entity == (entity_dim, entity_number)]
115+
116+
def get_polynomial_basis(self, reshape=True):
117+
"""Get the polynomial basis for the element."""
118+
if reshape and self.range_shape is not None:
119+
if len(self.range_shape) != 2:
120+
raise NotImplementedError
121+
assert self.range_shape[0] * self.range_shape[1] == self.range_dim
122+
return [sympy.Matrix(
123+
[b[i * self.range_shape[1]: (i + 1) * self.range_shape[1]]
124+
for i in range(self.range_shape[0])]) for b in self.basis]
125+
126+
return self.basis
127+
128+
def get_dual_matrix(self):
129+
"""Get the dual matrix."""
130+
mat = []
131+
for b in self.basis:
132+
row = []
133+
for d in self.dofs:
134+
row.append(d.eval(b))
135+
mat.append(row)
136+
return sympy.Matrix(mat)
137+
138+
def get_basis_functions(self, reshape=True):
139+
"""Get the basis functions of the element."""
140+
if self._basis_functions is None:
141+
minv = self.get_dual_matrix().inv("LU")
142+
self._basis_functions = []
143+
if self.range_dim == 1:
144+
# Scalar space
145+
for i, dof in enumerate(self.dofs):
146+
self._basis_functions.append(
147+
sym_sum(c * d for c, d in zip(minv.row(i), self.basis)))
148+
else:
149+
# Vector space
150+
for i, dof in enumerate(self.dofs):
151+
b = [zero for i in self.basis[0]]
152+
for c, d in zip(minv.row(i), self.basis):
153+
for j, d_j in enumerate(d):
154+
b[j] += c * d_j
155+
self._basis_functions.append(b)
156+
157+
if reshape and self.range_shape is not None:
158+
if len(self.range_shape) != 2:
159+
raise NotImplementedError
160+
assert self.range_shape[0] * self.range_shape[1] == self.range_dim
161+
return [sympy.Matrix(
162+
[b[i * self.range_shape[1]: (i + 1) * self.range_shape[1]]
163+
for i in range(self.range_shape[0])]) for b in self._basis_functions]
164+
165+
return self._basis_functions
166+
167+
names = []
168+
169+
170+
class DirectElement(FiniteElement):
171+
"""Finite element defined directly."""
172+
173+
def __init__(self, reference, order, basis_functions, basis_entities, domain_dim, range_dim,
174+
range_shape=None):
175+
super().__init__(reference, order, len(basis_functions), domain_dim, range_dim,
176+
range_shape)
177+
self._basis_entities = basis_entities
178+
self._basis_functions = basis_functions
179+
self._reshaped_basis_functions = None
180+
181+
def entity_dofs(self, entity_dim, entity_number):
182+
"""Get the numbers of the DOFs associated with the given entity."""
183+
return [i for i, j in enumerate(self._basis_entities) if j == (entity_dim, entity_number)]
184+
185+
def get_basis_functions(self, reshape=True):
186+
"""Get the basis functions of the element."""
187+
if reshape and self.range_shape is not None:
188+
if len(self.range_shape) != 2:
189+
raise NotImplementedError
190+
assert self.range_shape[0] * self.range_shape[1] == self.range_dim
191+
return [sympy.Matrix(
192+
[b[i * self.range_shape[1]: (i + 1) * self.range_shape[1]]
193+
for i in range(self.range_shape[0])]) for b in self._basis_functions]
194+
195+
return self._basis_functions
196+
197+
names = []

symfem/create.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def create_element(cell_type, element_type, order, variant="equispaced"):
142142
serendipity, S,
143143
serendipity Hcurl, Scurl, BDMCE, AAE,
144144
serendipity Hdiv, Sdiv, BDMCF, AAF,
145+
direct serendipity,
145146
Regge,
146147
Nedelec, Nedelec1, N1curl,
147148
Nedelec2, N2curl,

symfem/elements/argyris.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
(Arygris, Fried, Scharpf, 1968)
55
"""
66

7-
from ..core.finite_element import FiniteElement
7+
from ..core.finite_element import CiarletElement
88
from ..core.polynomials import polynomial_set
99
from ..core.functionals import (PointEvaluation, PointDirectionalDerivativeEvaluation,
1010
PointNormalDerivativeEvaluation,
1111
PointComponentSecondDerivativeEvaluation)
1212
from ..core.symbolic import sym_sum
1313

1414

15-
class Argyris(FiniteElement):
15+
class Argyris(CiarletElement):
1616
"""Argyris finite element."""
1717

1818
def __init__(self, reference, order, variant):

symfem/elements/aw.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
"""
66

77
import sympy
8-
from ..core.finite_element import FiniteElement
8+
from ..core.finite_element import CiarletElement
99
from ..core.polynomials import polynomial_set
1010
from ..core.functionals import (PointInnerProduct, InnerProductIntegralMoment,
1111
VecIntegralMoment, IntegralMoment)
1212
from ..core.symbolic import zero, one, x
1313
from .lagrange import DiscontinuousLagrange
1414

1515

16-
class ArnoldWinther(FiniteElement):
16+
class ArnoldWinther(CiarletElement):
1717
"""An Arnold-Winther element."""
1818

1919
def __init__(self, reference, order, variant):

symfem/elements/bddm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
(Brezzi, Douglas, Duran, Fortin, 1987)
55
"""
66

7-
from ..core.finite_element import FiniteElement
7+
from ..core.finite_element import CiarletElement
88
from ..core.moments import make_integral_moment_dofs
99
from ..core.polynomials import polynomial_set
1010
from ..core.symbolic import x, zero
@@ -30,7 +30,7 @@ def bddf_polyset(reference, order):
3030
return pset
3131

3232

33-
class BDDF(FiniteElement):
33+
class BDDF(CiarletElement):
3434
"""Brezzi-Douglas-Duran-Fortin Hdiv finite element."""
3535

3636
def __init__(self, reference, order, variant):

symfem/elements/bdfm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
(Brezzi, Douglas, Fortin, Marini, 1987)
55
"""
66

7-
from ..core.finite_element import FiniteElement
7+
from ..core.finite_element import CiarletElement
88
from ..core.moments import make_integral_moment_dofs
99
from ..core.polynomials import polynomial_set
1010
from ..core.symbolic import x, zero
@@ -44,7 +44,7 @@ def bdfm_polyset(reference, order):
4444
return pset
4545

4646

47-
class BDFM(FiniteElement):
47+
class BDFM(CiarletElement):
4848
"""Brezzi-Douglas-Fortin-Marini Hdiv finite element."""
4949

5050
def __init__(self, reference, order, variant):

symfem/elements/bdm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
(Brezzi, Douglas, Marini, 1985)
55
"""
66

7-
from ..core.finite_element import FiniteElement
7+
from ..core.finite_element import CiarletElement
88
from ..core.moments import make_integral_moment_dofs
99
from ..core.polynomials import polynomial_set
1010
from ..core.functionals import NormalIntegralMoment, IntegralMoment
1111
from .lagrange import DiscontinuousLagrange
1212
from .nedelec import NedelecFirstKind
1313

1414

15-
class BDM(FiniteElement):
15+
class BDM(CiarletElement):
1616
"""Brezzi-Douglas-Marini Hdiv finite element."""
1717

1818
def __init__(self, reference, order, variant):

symfem/elements/bell.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@
33
This element's definition is given in https://doi.org/10.1002/nme.1620010108 (Bell, 1969)
44
"""
55

6-
from ..core.finite_element import FiniteElement
6+
from ..core.finite_element import CiarletElement
77
from ..core.moments import make_integral_moment_dofs
88
from ..core.polynomials import polynomial_set
99
from ..core.functionals import (PointEvaluation, NormalDerivativeIntegralMoment,
1010
DerivativePointEvaluation)
1111
from .lagrange import DiscontinuousLagrange
1212

1313

14-
class Bell(FiniteElement):
14+
class Bell(CiarletElement):
1515
"""Bell finite element."""
1616

1717
def __init__(self, reference, order, variant):

symfem/elements/bernardi_raugel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
"""
66

77
from ..core import mappings
8-
from ..core.finite_element import FiniteElement
8+
from ..core.finite_element import CiarletElement
99
from ..core.moments import make_integral_moment_dofs
1010
from ..core.polynomials import polynomial_set
1111
from ..core.functionals import NormalIntegralMoment, DotPointEvaluation
1212
from .lagrange import Lagrange, DiscontinuousLagrange
1313

1414

15-
class BernardiRaugel(FiniteElement):
15+
class BernardiRaugel(CiarletElement):
1616
"""Bernardi-Raugel Hdiv finite element."""
1717

1818
def __init__(self, reference, order, variant):

0 commit comments

Comments
 (0)