Codebase list fiat / 4e49719
New upstream version 2019.2.0~git20200722.e16d9f3 Drew Parsons 3 years ago
14 changed file(s) with 654 addition(s) and 160 deletion(s). Raw diff Collapse all Expand all
7272
7373 Cyrus Cheng
7474 email: cyruscycheng21@gmail.com
75
76 Reuben W. Hill
77 email: reuben.hill10@imperial.ac.uk
1818 entity_ids = {}
1919 nodes = []
2020 vs = numpy.array(ref_el.get_vertices())
21 bary = tuple(numpy.average(vs, 0))
21 if ref_el.get_dimension() == 0:
22 bary = ()
23 else:
24 bary = tuple(numpy.average(vs, 0))
2225
2326 nodes = [functional.PointEvaluation(ref_el, bary)]
2427 entity_ids = {}
66
77 from FIAT import (finite_element, quadrature, functional, dual_set,
88 polynomial_set, nedelec)
9 from FIAT.check_format_variant import check_format_variant
910
1011
1112 class BDMDualSet(dual_set.DualSet):
12 def __init__(self, ref_el, degree):
13 def __init__(self, ref_el, degree, variant, quad_deg):
1314
1415 # Initialize containers for map: mesh_entity -> dof number and
1516 # dual basis
1920 sd = ref_el.get_spatial_dimension()
2021 t = ref_el.get_topology()
2122
22 # Define each functional for the dual set
23 # codimension 1 facets
24 for i in range(len(t[sd - 1])):
25 pts_cur = ref_el.make_points(sd - 1, i, sd + degree)
26 for j in range(len(pts_cur)):
27 pt_cur = pts_cur[j]
28 f = functional.PointScaledNormalEvaluation(ref_el, i, pt_cur)
29 nodes.append(f)
23 if variant == "integral":
24 facet = ref_el.get_facet_element()
25 # Facet nodes are \int_F v\cdot n p ds where p \in P_{q-1}
26 # degree is q - 1
27 Q = quadrature.make_quadrature(facet, quad_deg)
28 Pq = polynomial_set.ONPolynomialSet(facet, degree)
29 Pq_at_qpts = Pq.tabulate(Q.get_points())[tuple([0]*(sd - 1))]
30 for f in range(len(t[sd - 1])):
31 for i in range(Pq_at_qpts.shape[0]):
32 phi = Pq_at_qpts[i, :]
33 nodes.append(functional.IntegralMomentOfScaledNormalEvaluation(ref_el, Q, phi, f))
3034
31 # internal nodes
32 if degree > 1:
33 Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))
34 qpts = Q.get_points()
35 Nedel = nedelec.Nedelec(ref_el, degree - 1)
36 Nedfs = Nedel.get_nodal_basis()
37 zero_index = tuple([0 for i in range(sd)])
38 Ned_at_qpts = Nedfs.tabulate(qpts)[zero_index]
35 # internal nodes
36 if degree > 1:
37 Q = quadrature.make_quadrature(ref_el, quad_deg)
38 qpts = Q.get_points()
39 Nedel = nedelec.Nedelec(ref_el, degree - 1, variant)
40 Nedfs = Nedel.get_nodal_basis()
41 zero_index = tuple([0 for i in range(sd)])
42 Ned_at_qpts = Nedfs.tabulate(qpts)[zero_index]
3943
40 for i in range(len(Ned_at_qpts)):
41 phi_cur = Ned_at_qpts[i, :]
42 l_cur = functional.FrobeniusIntegralMoment(ref_el, Q, phi_cur)
43 nodes.append(l_cur)
44 for i in range(len(Ned_at_qpts)):
45 phi_cur = Ned_at_qpts[i, :]
46 l_cur = functional.FrobeniusIntegralMoment(ref_el, Q, phi_cur)
47 nodes.append(l_cur)
48
49 elif variant == "point":
50 # Define each functional for the dual set
51 # codimension 1 facets
52 for i in range(len(t[sd - 1])):
53 pts_cur = ref_el.make_points(sd - 1, i, sd + degree)
54 for j in range(len(pts_cur)):
55 pt_cur = pts_cur[j]
56 f = functional.PointScaledNormalEvaluation(ref_el, i, pt_cur)
57 nodes.append(f)
58
59 # internal nodes
60 if degree > 1:
61 Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))
62 qpts = Q.get_points()
63 Nedel = nedelec.Nedelec(ref_el, degree - 1, variant)
64 Nedfs = Nedel.get_nodal_basis()
65 zero_index = tuple([0 for i in range(sd)])
66 Ned_at_qpts = Nedfs.tabulate(qpts)[zero_index]
67
68 for i in range(len(Ned_at_qpts)):
69 phi_cur = Ned_at_qpts[i, :]
70 l_cur = functional.FrobeniusIntegralMoment(ref_el, Q, phi_cur)
71 nodes.append(l_cur)
4472
4573 # sets vertices (and in 3d, edges) to have no nodes
4674 for i in range(sd - 1):
7098
7199
72100 class BrezziDouglasMarini(finite_element.CiarletElement):
73 """The BDM element"""
101 """
102 The BDM element
74103
75 def __init__(self, ref_el, degree):
104 :arg ref_el: The reference element.
105 :arg k: The degree.
106 :arg variant: optional variant specifying the types of nodes.
76107
77 if degree < 1:
108 variant can be chosen from ["point", "integral", "integral(quadrature_degree)"]
109 "point" -> dofs are evaluated by point evaluation. Note that this variant has suboptimal
110 convergence order in the H(div)-norm
111 "integral" -> dofs are evaluated by quadrature rule. The quadrature degree is chosen to integrate
112 polynomials of degree 5*k so that most expressions will be interpolated exactly. This is important
113 when you want to have (nearly) divergence-preserving interpolation.
114 "integral(quadrature_degree)" -> dofs are evaluated by quadrature rule of degree quadrature_degree
115 """
116
117 def __init__(self, ref_el, k, variant=None):
118
119 (variant, quad_deg) = check_format_variant(variant, k, "BDM")
120
121 if k < 1:
78122 raise Exception("BDM_k elements only valid for k >= 1")
79123
80124 sd = ref_el.get_spatial_dimension()
81 poly_set = polynomial_set.ONPolynomialSet(ref_el, degree, (sd, ))
82 dual = BDMDualSet(ref_el, degree)
125 poly_set = polynomial_set.ONPolynomialSet(ref_el, k, (sd, ))
126 dual = BDMDualSet(ref_el, k, variant, quad_deg)
83127 formdegree = sd - 1 # (n-1)-form
84 super(BrezziDouglasMarini, self).__init__(poly_set, dual, degree, formdegree,
128 super(BrezziDouglasMarini, self).__init__(poly_set, dual, k, formdegree,
85129 mapping="contravariant piola")
0 import re
1 import warnings
2
3
4 def check_format_variant(variant, degree, element):
5 if variant is None:
6 variant = "point"
7 warnings.simplefilter('always', DeprecationWarning)
8 warnings.warn('Variant of ' + element + ' element will change from point evaluation to integral evaluation.'
9 ' You should project into variant="integral"', DeprecationWarning)
10
11 match = re.match(r"^integral(?:\((\d+)\))?$", variant)
12 if match:
13 variant = "integral"
14 quad_degree, = match.groups()
15 quad_degree = int(quad_degree) if quad_degree is not None else 5*(degree + 1)
16 if quad_degree < degree + 1:
17 raise ValueError("Warning, quadrature degree should be at least %s" % (degree + 1))
18 elif variant == "point":
19 quad_degree = None
20 else:
21 raise ValueError('Choose either variant="point" or variant="integral"'
22 'or variant="integral(Quadrature degree)"')
23
24 return (variant, quad_degree)
108108 return xi1, xi2, xi3
109109
110110
111 class PointExpansionSet(object):
112 """Evaluates the point basis on a point reference element."""
113
114 def __init__(self, ref_el):
115 if ref_el.get_spatial_dimension() != 0:
116 raise ValueError("Must have a point")
117 self.ref_el = ref_el
118 self.base_ref_el = reference_element.Point()
119
120 def get_num_members(self, n):
121 return 1
122
123 def tabulate(self, n, pts):
124 """Returns a numpy array A[i,j] = phi_i(pts[j]) = 1.0."""
125 assert n == 0
126 return numpy.ones((1, len(pts)))
127
128 def tabulate_derivatives(self, n, pts):
129 """Returns a numpy array of size A where A[i,j] = phi_i(pts[j])
130 but where each element is an empty tuple (). This maintains
131 compatibility with the interfaces of the interval, triangle and
132 tetrahedron expansions."""
133 deriv_vals = numpy.empty_like(self.tabulate(n, pts), dtype=tuple)
134 deriv_vals.fill(())
135
136 return deriv_vals
137
138
111139 class LineExpansionSet(object):
112140 """Evaluates the Legendre basis on a line reference element."""
113141
376404 def get_expansion_set(ref_el):
377405 """Returns an ExpansionSet instance appopriate for the given
378406 reference element."""
379 if ref_el.get_shape() == reference_element.LINE:
407 if ref_el.get_shape() == reference_element.POINT:
408 return PointExpansionSet(ref_el)
409 elif ref_el.get_shape() == reference_element.LINE:
380410 return LineExpansionSet(ref_el)
381411 elif ref_el.get_shape() == reference_element.TRIANGLE:
382412 return TriangleExpansionSet(ref_el)
389419 def polynomial_dimension(ref_el, degree):
390420 """Returns the dimension of the space of polynomials of degree no
391421 greater than degree on the reference element."""
392 if ref_el.get_shape() == reference_element.LINE:
422 if ref_el.get_shape() == reference_element.POINT:
423 if degree > 0:
424 raise ValueError("Only degree zero polynomials supported on point elements.")
425 return 1
426 elif ref_el.get_shape() == reference_element.LINE:
393427 return max(0, degree + 1)
394428 elif ref_el.get_shape() == reference_element.TRIANGLE:
395429 return max((degree + 1) * (degree + 2) // 2, 0)
396430 elif ref_el.get_shape() == reference_element.TETRAHEDRON:
397431 return max(0, (degree + 1) * (degree + 2) * (degree + 3) // 6)
398432 else:
399 raise Exception("Unknown reference element type.")
433 raise ValueError("Unknown reference element type.")
387387 return "(u.t)(%s)" % (','.join(x),)
388388
389389
390 class IntegralMomentOfEdgeTangentEvaluation(Functional):
391 r"""
392 \int_e v\cdot t p ds
393
394 p \in Polynomials
395
396 :arg ref_el: reference element for which e is a dim-1 entity
397 :arg Q: quadrature rule on the face
398 :arg P_at_qpts: polynomials evaluated at quad points
399 :arg edge: which edge.
400 """
401 def __init__(self, ref_el, Q, P_at_qpts, edge):
402 t = ref_el.compute_edge_tangent(edge)
403 sd = ref_el.get_spatial_dimension()
404 transform = ref_el.get_entity_transform(1, edge)
405 pts = tuple(map(lambda p: tuple(transform(p)), Q.get_points()))
406 weights = Q.get_weights()
407 pt_dict = OrderedDict()
408 for pt, wgt, phi in zip(pts, weights, P_at_qpts):
409 pt_dict[pt] = [(wgt*phi*t[i], (i, )) for i in range(sd)]
410 super().__init__(ref_el, (sd, ), pt_dict, {}, "IntegralMomentOfEdgeTangentEvaluation")
411
412
390413 class PointFaceTangentEvaluation(Functional):
391414 """Implements the evaluation of a tangential component of a
392415 vector at a point on a facet of codimension 1."""
405428 return "(u.t%d)(%s)" % (self.tno, ','.join(x),)
406429
407430
431 class IntegralMomentOfFaceTangentEvaluation(Functional):
432 r"""
433 \int_F v \times n \cdot p ds
434
435 p \in Polynomials
436
437 :arg ref_el: reference element for which F is a codim-1 entity
438 :arg Q: quadrature rule on the face
439 :arg P_at_qpts: polynomials evaluated at quad points
440 :arg facet: which facet.
441 """
442 def __init__(self, ref_el, Q, P_at_qpts, facet):
443 P_at_qpts = [[P_at_qpts[0][i], P_at_qpts[1][i], P_at_qpts[2][i]]
444 for i in range(P_at_qpts.shape[1])]
445 n = ref_el.compute_scaled_normal(facet)
446 sd = ref_el.get_spatial_dimension()
447 transform = ref_el.get_entity_transform(sd-1, facet)
448 pts = tuple(map(lambda p: tuple(transform(p)), Q.get_points()))
449 weights = Q.get_weights()
450 pt_dict = OrderedDict()
451 for pt, wgt, phi in zip(pts, weights, P_at_qpts):
452 phixn = [phi[1]*n[2] - phi[2]*n[1],
453 phi[2]*n[0] - phi[0]*n[2],
454 phi[0]*n[1] - phi[1]*n[0]]
455 pt_dict[pt] = [(wgt*(-n[2]*phixn[1]+n[1]*phixn[2]), (0, )),
456 (wgt*(n[2]*phixn[0]-n[0]*phixn[2]), (1, )),
457 (wgt*(-n[1]*phixn[0]+n[0]*phixn[1]), (2, ))]
458 super().__init__(ref_el, (sd, ), pt_dict, {}, "IntegralMomentOfFaceTangentEvaluation")
459
460
461 class MonkIntegralMoment(Functional):
462 r"""
463 face nodes are \int_F v\cdot p dA where p \in P_{q-2}(f)^3 with p \cdot n = 0
464 (cmp. Peter Monk - Finite Element Methods for Maxwell's equations p. 129)
465 Note that we don't scale by the area of the facet
466
467 :arg ref_el: reference element for which F is a codim-1 entity
468 :arg Q: quadrature rule on the face
469 :arg P_at_qpts: polynomials evaluated at quad points
470 :arg facet: which facet.
471 """
472
473 def __init__(self, ref_el, Q, P_at_qpts, facet):
474 sd = ref_el.get_spatial_dimension()
475 weights = Q.get_weights()
476 pt_dict = OrderedDict()
477 transform = ref_el.get_entity_transform(sd-1, facet)
478 pts = tuple(map(lambda p: tuple(transform(p)), Q.get_points()))
479 for pt, wgt, phi in zip(pts, weights, P_at_qpts):
480 pt_dict[pt] = [(wgt*phi[i], (i, )) for i in range(sd)]
481 super().__init__(ref_el, (sd, ), pt_dict, {}, "MonkIntegralMoment")
482
483
408484 class PointScaledNormalEvaluation(Functional):
409485 """Implements the evaluation of the normal component of a vector at a
410486 point on a facet of codimension 1, where the normal is scaled by
421497 def tostr(self):
422498 x = list(map(str, list(self.pt_dict.keys())[0]))
423499 return "(u.n)(%s)" % (','.join(x),)
500
501
502 class IntegralMomentOfScaledNormalEvaluation(Functional):
503 r"""
504 \int_F v\cdot n p ds
505
506 p \in Polynomials
507
508 :arg ref_el: reference element for which F is a codim-1 entity
509 :arg Q: quadrature rule on the face
510 :arg P_at_qpts: polynomials evaluated at quad points
511 :arg facet: which facet.
512 """
513 def __init__(self, ref_el, Q, P_at_qpts, facet):
514 n = ref_el.compute_scaled_normal(facet)
515 sd = ref_el.get_spatial_dimension()
516 transform = ref_el.get_entity_transform(sd - 1, facet)
517 pts = tuple(map(lambda p: tuple(transform(p)), Q.get_points()))
518 weights = Q.get_weights()
519 pt_dict = OrderedDict()
520 for pt, wgt, phi in zip(pts, weights, P_at_qpts):
521 pt_dict[pt] = [(wgt*phi*n[i], (i, )) for i in range(sd)]
522 super().__init__(ref_el, (sd, ), pt_dict, {}, "IntegralMomentOfScaledNormalEvaluation")
424523
425524
426525 class PointwiseInnerProductEvaluation(Functional):
88 finite_element, functional)
99 from itertools import chain
1010 import numpy
11 from FIAT.check_format_variant import check_format_variant
1112
1213
1314 def NedelecSpace2D(ref_el, k):
142143 class NedelecDual2D(dual_set.DualSet):
143144 """Dual basis for first-kind Nedelec in 2D."""
144145
145 def __init__(self, ref_el, degree):
146 def __init__(self, ref_el, degree, variant, quad_deg):
146147 sd = ref_el.get_spatial_dimension()
147148 if sd != 2:
148149 raise Exception("Nedelec2D only works on triangles")
151152
152153 t = ref_el.get_topology()
153154
154 num_edges = len(t[1])
155
156 # edge tangents
157 for i in range(num_edges):
158 pts_cur = ref_el.make_points(1, i, degree + 2)
159 for j in range(len(pts_cur)):
160 pt_cur = pts_cur[j]
161 f = functional.PointEdgeTangentEvaluation(ref_el, i, pt_cur)
162 nodes.append(f)
163
164 # internal moments
165 if degree > 0:
166 Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))
167 qpts = Q.get_points()
168 Pkm1 = polynomial_set.ONPolynomialSet(ref_el, degree - 1)
169 zero_index = tuple([0 for i in range(sd)])
170 Pkm1_at_qpts = Pkm1.tabulate(qpts)[zero_index]
171
172 for d in range(sd):
173 for i in range(Pkm1_at_qpts.shape[0]):
174 phi_cur = Pkm1_at_qpts[i, :]
175 l_cur = functional.IntegralMoment(ref_el, Q, phi_cur, (d,))
176 nodes.append(l_cur)
155 if variant == "integral":
156 # edge nodes are \int_F v\cdot t p ds where p \in P_{q-1}(edge)
157 # degree is q - 1
158 edge = ref_el.get_facet_element()
159 Q = quadrature.make_quadrature(edge, quad_deg)
160 Pq = polynomial_set.ONPolynomialSet(edge, degree)
161 Pq_at_qpts = Pq.tabulate(Q.get_points())[tuple([0]*(sd - 1))]
162 for e in range(len(t[sd - 1])):
163 for i in range(Pq_at_qpts.shape[0]):
164 phi = Pq_at_qpts[i, :]
165 nodes.append(functional.IntegralMomentOfEdgeTangentEvaluation(ref_el, Q, phi, e))
166
167 # internal nodes. These are \int_T v \cdot p dx where p \in P_{q-2}^2
168 if degree > 0:
169 Q = quadrature.make_quadrature(ref_el, quad_deg)
170 qpts = Q.get_points()
171 Pkm1 = polynomial_set.ONPolynomialSet(ref_el, degree - 1)
172 zero_index = tuple([0 for i in range(sd)])
173 Pkm1_at_qpts = Pkm1.tabulate(qpts)[zero_index]
174
175 for d in range(sd):
176 for i in range(Pkm1_at_qpts.shape[0]):
177 phi_cur = Pkm1_at_qpts[i, :]
178 l_cur = functional.IntegralMoment(ref_el, Q, phi_cur, (d,), (sd,))
179 nodes.append(l_cur)
180
181 elif variant == "point":
182 num_edges = len(t[1])
183
184 # edge tangents
185 for i in range(num_edges):
186 pts_cur = ref_el.make_points(1, i, degree + 2)
187 for j in range(len(pts_cur)):
188 pt_cur = pts_cur[j]
189 f = functional.PointEdgeTangentEvaluation(ref_el, i, pt_cur)
190 nodes.append(f)
191
192 # internal moments
193 if degree > 0:
194 Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))
195 qpts = Q.get_points()
196 Pkm1 = polynomial_set.ONPolynomialSet(ref_el, degree - 1)
197 zero_index = tuple([0 for i in range(sd)])
198 Pkm1_at_qpts = Pkm1.tabulate(qpts)[zero_index]
199
200 for d in range(sd):
201 for i in range(Pkm1_at_qpts.shape[0]):
202 phi_cur = Pkm1_at_qpts[i, :]
203 l_cur = functional.IntegralMoment(ref_el, Q, phi_cur, (d,))
204 nodes.append(l_cur)
177205
178206 entity_ids = {}
179207
203231 class NedelecDual3D(dual_set.DualSet):
204232 """Dual basis for first-kind Nedelec in 3D."""
205233
206 def __init__(self, ref_el, degree):
234 def __init__(self, ref_el, degree, variant, quad_deg):
207235 sd = ref_el.get_spatial_dimension()
208236 if sd != 3:
209237 raise Exception("NedelecDual3D only works on tetrahedra")
212240
213241 t = ref_el.get_topology()
214242
215 # how many edges
216 num_edges = len(t[1])
217
218 for i in range(num_edges):
219 # points to specify P_k on each edge
220 pts_cur = ref_el.make_points(1, i, degree + 2)
221 for j in range(len(pts_cur)):
222 pt_cur = pts_cur[j]
223 f = functional.PointEdgeTangentEvaluation(ref_el, i, pt_cur)
224 nodes.append(f)
225
226 if degree > 0: # face tangents
227 num_faces = len(t[2])
228 for i in range(num_faces): # loop over faces
229 pts_cur = ref_el.make_points(2, i, degree + 2)
230 for j in range(len(pts_cur)): # loop over points
243 if variant == "integral":
244 # edge nodes are \int_F v\cdot t p ds where p \in P_{q-1}(edge)
245 # degree is q - 1
246 edge = ref_el.get_facet_element().get_facet_element()
247 Q = quadrature.make_quadrature(edge, quad_deg)
248 Pq = polynomial_set.ONPolynomialSet(edge, degree)
249 Pq_at_qpts = Pq.tabulate(Q.get_points())[tuple([0]*(1))]
250 for e in range(len(t[1])):
251 for i in range(Pq_at_qpts.shape[0]):
252 phi = Pq_at_qpts[i, :]
253 nodes.append(functional.IntegralMomentOfEdgeTangentEvaluation(ref_el, Q, phi, e))
254
255 # face nodes are \int_F v\cdot p dA where p \in P_{q-2}(f)^3 with p \cdot n = 0 (cmp. Monk)
256 # these are equivalent to dofs from Fenics book defined by
257 # \int_F v\times n \cdot p ds where p \in P_{q-2}(f)^2
258 if degree > 0:
259 facet = ref_el.get_facet_element()
260 Q = quadrature.make_quadrature(facet, quad_deg)
261 Pq = polynomial_set.ONPolynomialSet(facet, degree-1, (sd,))
262 Pq_at_qpts = Pq.tabulate(Q.get_points())[(0, 0)]
263
264 for f in range(len(t[2])):
265 # R is used to map [1,0,0] to tangent1 and [0,1,0] to tangent2
266 R = ref_el.compute_face_tangents(f)
267
268 # Skip last functionals because we only want p with p \cdot n = 0
269 for i in range(2 * Pq.get_num_members() // 3):
270 phi = Pq_at_qpts[i, ...]
271 phi = numpy.matmul(phi[:-1, ...].T, R)
272 nodes.append(functional.MonkIntegralMoment(ref_el, Q, phi, f))
273
274 # internal nodes. These are \int_T v \cdot p dx where p \in P_{q-3}^3(T)
275 if degree > 1:
276 Q = quadrature.make_quadrature(ref_el, quad_deg)
277 qpts = Q.get_points()
278 Pkm2 = polynomial_set.ONPolynomialSet(ref_el, degree - 2)
279 zero_index = tuple([0 for i in range(sd)])
280 Pkm2_at_qpts = Pkm2.tabulate(qpts)[zero_index]
281
282 for d in range(sd):
283 for i in range(Pkm2_at_qpts.shape[0]):
284 phi_cur = Pkm2_at_qpts[i, :]
285 l_cur = functional.IntegralMoment(ref_el, Q, phi_cur, (d,), (sd,))
286 nodes.append(l_cur)
287
288 elif variant == "point":
289 num_edges = len(t[1])
290
291 for i in range(num_edges):
292 # points to specify P_k on each edge
293 pts_cur = ref_el.make_points(1, i, degree + 2)
294 for j in range(len(pts_cur)):
231295 pt_cur = pts_cur[j]
232 for k in range(2): # loop over tangents
233 f = functional.PointFaceTangentEvaluation(ref_el, i, k, pt_cur)
296 f = functional.PointEdgeTangentEvaluation(ref_el, i, pt_cur)
297 nodes.append(f)
298
299 if degree > 0: # face tangents
300 num_faces = len(t[2])
301 for i in range(num_faces): # loop over faces
302 pts_cur = ref_el.make_points(2, i, degree + 2)
303 for j in range(len(pts_cur)): # loop over points
304 pt_cur = pts_cur[j]
305 for k in range(2): # loop over tangents
306 f = functional.PointFaceTangentEvaluation(ref_el, i, k, pt_cur)
307 nodes.append(f)
308
309 if degree > 1: # internal moments
310 Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))
311 qpts = Q.get_points()
312 Pkm2 = polynomial_set.ONPolynomialSet(ref_el, degree - 2)
313 zero_index = tuple([0 for i in range(sd)])
314 Pkm2_at_qpts = Pkm2.tabulate(qpts)[zero_index]
315
316 for d in range(sd):
317 for i in range(Pkm2_at_qpts.shape[0]):
318 phi_cur = Pkm2_at_qpts[i, :]
319 f = functional.IntegralMoment(ref_el, Q, phi_cur, (d,))
234320 nodes.append(f)
235
236 if degree > 1: # internal moments
237 Q = quadrature.make_quadrature(ref_el, 2 * (degree + 1))
238 qpts = Q.get_points()
239 Pkm2 = polynomial_set.ONPolynomialSet(ref_el, degree - 2)
240 zero_index = tuple([0 for i in range(sd)])
241 Pkm2_at_qpts = Pkm2.tabulate(qpts)[zero_index]
242
243 for d in range(sd):
244 for i in range(Pkm2_at_qpts.shape[0]):
245 phi_cur = Pkm2_at_qpts[i, :]
246 f = functional.IntegralMoment(ref_el, Q, phi_cur, (d,))
247 nodes.append(f)
248321
249322 entity_ids = {}
250323 # set to empty
276349
277350
278351 class Nedelec(finite_element.CiarletElement):
279 """Nedelec finite element"""
280
281 def __init__(self, ref_el, q):
282
283 degree = q - 1
352 """
353 Nedelec finite element
354
355 :arg ref_el: The reference element.
356 :arg k: The degree.
357 :arg variant: optional variant specifying the types of nodes.
358
359 variant can be chosen from ["point", "integral", "integral(quadrature_degree)"]
360 "point" -> dofs are evaluated by point evaluation. Note that this variant has suboptimal
361 convergence order in the H(curl)-norm
362 "integral" -> dofs are evaluated by quadrature rule. The quadrature degree is chosen to integrate
363 polynomials of degree 5*k so that most expressions will be interpolated exactly. This is important
364 when you want to have (nearly) curl-preserving interpolation.
365 "integral(quadrature_degree)" -> dofs are evaluated by quadrature rule of degree quadrature_degree
366 """
367
368 def __init__(self, ref_el, k, variant=None):
369
370 degree = k - 1
371
372 (variant, quad_deg) = check_format_variant(variant, degree, "Nedelec")
284373
285374 if ref_el.get_spatial_dimension() == 3:
286375 poly_set = NedelecSpace3D(ref_el, degree)
287 dual = NedelecDual3D(ref_el, degree)
376 dual = NedelecDual3D(ref_el, degree, variant, quad_deg)
288377 elif ref_el.get_spatial_dimension() == 2:
289378 poly_set = NedelecSpace2D(ref_el, degree)
290 dual = NedelecDual2D(ref_el, degree)
379 dual = NedelecDual2D(ref_el, degree, variant, quad_deg)
291380 else:
292381 raise Exception("Not implemented")
293382 formdegree = 1 # 1-form
1414 from FIAT.raviart_thomas import RaviartThomas
1515 from FIAT.quadrature import make_quadrature, UFCTetrahedronFaceQuadratureRule
1616 from FIAT.reference_element import UFCTetrahedron
17 from FIAT.check_format_variant import check_format_variant
18
19 from FIAT import polynomial_set, quadrature, functional
1720
1821
1922 class NedelecSecondKindDual(DualSet):
4548 these elements coincide with the CG_k elements.)
4649 """
4750
48 def __init__(self, cell, degree):
51 def __init__(self, cell, degree, variant, quad_deg):
4952
5053 # Define degrees of freedom
51 (dofs, ids) = self.generate_degrees_of_freedom(cell, degree)
52
54 (dofs, ids) = self.generate_degrees_of_freedom(cell, degree, variant, quad_deg)
5355 # Call init of super-class
5456 super(NedelecSecondKindDual, self).__init__(dofs, cell, ids)
5557
56 def generate_degrees_of_freedom(self, cell, degree):
58 def generate_degrees_of_freedom(self, cell, degree, variant, quad_deg):
5759 "Generate dofs and geometry-to-dof maps (ids)."
5860
5961 dofs = []
6769 ids[0] = dict(list(zip(list(range(d + 1)), ([] for i in range(d + 1)))))
6870
6971 # (d+1) degrees of freedom per entity of codimension 1 (edges)
70 (edge_dofs, edge_ids) = self._generate_edge_dofs(cell, degree, 0)
72 (edge_dofs, edge_ids) = self._generate_edge_dofs(cell, degree, 0, variant, quad_deg)
7173 dofs.extend(edge_dofs)
7274 ids[1] = edge_ids
7375
7476 # Include face degrees of freedom if 3D
7577 if d == 3:
7678 (face_dofs, face_ids) = self._generate_face_dofs(cell, degree,
77 len(dofs))
79 len(dofs), variant, quad_deg)
7880 dofs.extend(face_dofs)
7981 ids[2] = face_ids
8082
8183 # Varying degrees of freedom (possibly zero) per cell
82 (cell_dofs, cell_ids) = self._generate_cell_dofs(cell, degree, len(dofs))
84 (cell_dofs, cell_ids) = self._generate_cell_dofs(cell, degree, len(dofs), variant, quad_deg)
8385 dofs.extend(cell_dofs)
8486 ids[d] = cell_ids
8587
8688 return (dofs, ids)
8789
88 def _generate_edge_dofs(self, cell, degree, offset):
90 def _generate_edge_dofs(self, cell, degree, offset, variant, quad_deg):
8991 """Generate degrees of freedoms (dofs) for entities of
9092 codimension 1 (edges)."""
9193
9395 # freedom per entity of codimension 1 (edges)
9496 dofs = []
9597 ids = {}
96 for edge in range(len(cell.get_topology()[1])):
97
98 # Create points for evaluation of tangential components
99 points = cell.make_points(1, edge, degree + 2)
100
101 # A tangential component evaluation for each point
102 dofs += [Tangent(cell, edge, point) for point in points]
103
104 # Associate these dofs with this edge
105 i = len(points) * edge
106 ids[edge] = list(range(offset + i, offset + i + len(points)))
107
108 return (dofs, ids)
109
110 def _generate_face_dofs(self, cell, degree, offset):
98
99 if variant == "integral":
100 edge = cell.construct_subelement(1)
101 Q = quadrature.make_quadrature(edge, quad_deg)
102 Pq = polynomial_set.ONPolynomialSet(edge, degree)
103 Pq_at_qpts = Pq.tabulate(Q.get_points())[tuple([0]*(1))]
104 for e in range(len(cell.get_topology()[1])):
105 for i in range(Pq_at_qpts.shape[0]):
106 phi = Pq_at_qpts[i, :]
107 dofs.append(functional.IntegralMomentOfEdgeTangentEvaluation(cell, Q, phi, e))
108 jj = Pq_at_qpts.shape[0] * e
109 ids[e] = list(range(offset + jj, offset + jj + Pq_at_qpts.shape[0]))
110
111 elif variant == "point":
112 for edge in range(len(cell.get_topology()[1])):
113
114 # Create points for evaluation of tangential components
115 points = cell.make_points(1, edge, degree + 2)
116
117 # A tangential component evaluation for each point
118 dofs += [Tangent(cell, edge, point) for point in points]
119
120 # Associate these dofs with this edge
121 i = len(points) * edge
122 ids[edge] = list(range(offset + i, offset + i + len(points)))
123
124 return (dofs, ids)
125
126 def _generate_face_dofs(self, cell, degree, offset, variant, quad_deg):
111127 """Generate degrees of freedoms (dofs) for faces."""
112128
113129 # Initialize empty dofs and identifiers (ids)
133149 # Construct Raviart-Thomas of (degree - 1) on the
134150 # reference face
135151 reference_face = Q_face.reference_rule().ref_el
136 RT = RaviartThomas(reference_face, degree - 1)
152 RT = RaviartThomas(reference_face, degree - 1, variant)
137153 num_rts = RT.space_dimension()
138154
139155 # Evaluate RT basis functions at reference quadrature
167183
168184 return (dofs, ids)
169185
170 def _generate_cell_dofs(self, cell, degree, offset):
186 def _generate_cell_dofs(self, cell, degree, offset, variant, quad_deg):
171187 """Generate degrees of freedoms (dofs) for entities of
172188 codimension d (cells)."""
173189
181197 qs = Q.get_points()
182198
183199 # Create Raviart-Thomas nodal basis
184 RT = RaviartThomas(cell, degree + 1 - d)
200 RT = RaviartThomas(cell, degree + 1 - d, variant)
185201 phi = RT.get_nodal_basis()
186202
187203 # Evaluate Raviart-Thomas basis at quadrature points
202218 tetrahedra: the polynomial space described by the full polynomials
203219 of degree k, with a suitable set of degrees of freedom to ensure
204220 H(curl) conformity.
221
222 :arg ref_el: The reference element.
223 :arg k: The degree.
224 :arg variant: optional variant specifying the types of nodes.
225
226 variant can be chosen from ["point", "integral", "integral(quadrature_degree)"]
227 "point" -> dofs are evaluated by point evaluation. Note that this variant has suboptimal
228 convergence order in the H(curl)-norm
229 "integral" -> dofs are evaluated by quadrature rule. The quadrature degree is chosen to integrate
230 polynomials of degree 5*k so that most expressions will be interpolated exactly. This is important
231 when you want to have (nearly) curl-preserving interpolation.
232 "integral(quadrature_degree)" -> dofs are evaluated by quadrature rule of degree quadrature_degree
205233 """
206234
207 def __init__(self, cell, degree):
235 def __init__(self, cell, k, variant=None):
236
237 (variant, quad_deg) = check_format_variant(variant, k, "Nedelec Second Kind")
208238
209239 # Check degree
210 assert degree >= 1, "Second kind Nedelecs start at 1!"
240 assert k >= 1, "Second kind Nedelecs start at 1!"
211241
212242 # Get dimension
213243 d = cell.get_spatial_dimension()
214244
215245 # Construct polynomial basis for d-vector fields
216 Ps = ONPolynomialSet(cell, degree, (d, ))
246 Ps = ONPolynomialSet(cell, k, (d, ))
217247
218248 # Construct dual space
219 Ls = NedelecSecondKindDual(cell, degree)
249 Ls = NedelecSecondKindDual(cell, k, variant, quad_deg)
220250
221251 # Set form degree
222252 formdegree = 1 # 1-form
225255 mapping = "covariant piola"
226256
227257 # Call init of super-class
228 super(NedelecSecondKind, self).__init__(Ps, Ls, degree, formdegree, mapping=mapping)
258 super(NedelecSecondKind, self).__init__(Ps, Ls, k, formdegree, mapping=mapping)
7474 for i in range(jet_order + 1):
7575 alphas = mis(self.ref_el.get_spatial_dimension(), i)
7676 for alpha in alphas:
77 D = form_matrix_product(self.dmats, alpha)
77 if len(self.dmats) > 0:
78 D = form_matrix_product(self.dmats, alpha)
79 else:
80 # special for vertex without defined point location
81 assert pts == [()]
82 D = numpy.eye(1)
7883 result[alpha] = numpy.dot(self.coeffs,
7984 numpy.dot(numpy.transpose(D),
8085 base_vals))
88 finite_element, functional)
99 import numpy
1010 from itertools import chain
11 from FIAT.check_format_variant import check_format_variant
1112
1213
1314 def RTSpace(ref_el, deg):
6465 evaluation of normals on facets of codimension 1 and internal
6566 moments against polynomials"""
6667
67 def __init__(self, ref_el, degree):
68 def __init__(self, ref_el, degree, variant, quad_deg):
6869 entity_ids = {}
6970 nodes = []
7071
7172 sd = ref_el.get_spatial_dimension()
7273 t = ref_el.get_topology()
7374
74 # codimension 1 facets
75 for i in range(len(t[sd - 1])):
76 pts_cur = ref_el.make_points(sd - 1, i, sd + degree)
77 for j in range(len(pts_cur)):
78 pt_cur = pts_cur[j]
79 f = functional.PointScaledNormalEvaluation(ref_el, i, pt_cur)
80 nodes.append(f)
75 if variant == "integral":
76 facet = ref_el.get_facet_element()
77 # Facet nodes are \int_F v\cdot n p ds where p \in P_{q-1}
78 # degree is q - 1
79 Q = quadrature.make_quadrature(facet, quad_deg)
80 Pq = polynomial_set.ONPolynomialSet(facet, degree)
81 Pq_at_qpts = Pq.tabulate(Q.get_points())[tuple([0]*(sd - 1))]
82 for f in range(len(t[sd - 1])):
83 for i in range(Pq_at_qpts.shape[0]):
84 phi = Pq_at_qpts[i, :]
85 nodes.append(functional.IntegralMomentOfScaledNormalEvaluation(ref_el, Q, phi, f))
8186
82 # internal nodes. Let's just use points at a lattice
83 if degree > 0:
84 cpe = functional.ComponentPointEvaluation
85 pts = ref_el.make_points(sd, 0, degree + sd)
86 for d in range(sd):
87 for i in range(len(pts)):
88 l_cur = cpe(ref_el, d, (sd,), pts[i])
89 nodes.append(l_cur)
87 # internal nodes. These are \int_T v \cdot p dx where p \in P_{q-2}^d
88 if degree > 0:
89 Q = quadrature.make_quadrature(ref_el, quad_deg)
90 qpts = Q.get_points()
91 Pkm1 = polynomial_set.ONPolynomialSet(ref_el, degree - 1)
92 zero_index = tuple([0 for i in range(sd)])
93 Pkm1_at_qpts = Pkm1.tabulate(qpts)[zero_index]
9094
91 # Q = quadrature.make_quadrature(ref_el, 2 * ( degree + 1 ))
92 # qpts = Q.get_points()
93 # Pkm1 = polynomial_set.ONPolynomialSet(ref_el, degree - 1)
94 # zero_index = tuple([0 for i in range(sd)])
95 # Pkm1_at_qpts = Pkm1.tabulate(qpts)[zero_index]
95 for d in range(sd):
96 for i in range(Pkm1_at_qpts.shape[0]):
97 phi_cur = Pkm1_at_qpts[i, :]
98 l_cur = functional.IntegralMoment(ref_el, Q, phi_cur, (d,), (sd,))
99 nodes.append(l_cur)
96100
97 # for d in range(sd):
98 # for i in range(Pkm1_at_qpts.shape[0]):
99 # phi_cur = Pkm1_at_qpts[i, :]
100 # l_cur = functional.IntegralMoment(ref_el, Q, phi_cur, (d,), (sd,))
101 # nodes.append(l_cur)
101 elif variant == "point":
102 # codimension 1 facets
103 for i in range(len(t[sd - 1])):
104 pts_cur = ref_el.make_points(sd - 1, i, sd + degree)
105 for j in range(len(pts_cur)):
106 pt_cur = pts_cur[j]
107 f = functional.PointScaledNormalEvaluation(ref_el, i, pt_cur)
108 nodes.append(f)
109
110 # internal nodes. Let's just use points at a lattice
111 if degree > 0:
112 cpe = functional.ComponentPointEvaluation
113 pts = ref_el.make_points(sd, 0, degree + sd)
114 for d in range(sd):
115 for i in range(len(pts)):
116 l_cur = cpe(ref_el, d, (sd,), pts[i])
117 nodes.append(l_cur)
102118
103119 # sets vertices (and in 3d, edges) to have no nodes
104120 for i in range(sd - 1):
127143
128144
129145 class RaviartThomas(finite_element.CiarletElement):
130 """The Raviart-Thomas finite element"""
146 """
147 The Raviart Thomas element
131148
132 def __init__(self, ref_el, q):
149 :arg ref_el: The reference element.
150 :arg k: The degree.
151 :arg variant: optional variant specifying the types of nodes.
133152
134 degree = q - 1
153 variant can be chosen from ["point", "integral", "integral(quadrature_degree)"]
154 "point" -> dofs are evaluated by point evaluation. Note that this variant has suboptimal
155 convergence order in the H(div)-norm
156 "integral" -> dofs are evaluated by quadrature rule. The quadrature degree is chosen to integrate
157 polynomials of degree 5*k so that most expressions will be interpolated exactly. This is important
158 when you want to have (nearly) divergence-preserving interpolation.
159 "integral(quadrature_degree)" -> dofs are evaluated by quadrature rule of degree quadrature_degree
160 """
161
162 def __init__(self, ref_el, k, variant=None):
163
164 degree = k - 1
165
166 (variant, quad_deg) = check_format_variant(variant, degree, "Raviart Thomas")
167
135168 poly_set = RTSpace(ref_el, degree)
136 dual = RTDualSet(ref_el, degree)
169 dual = RTDualSet(ref_el, degree, variant, quad_deg)
137170 formdegree = ref_el.get_spatial_dimension() - 1 # (n-1)-form
138171 super(RaviartThomas, self).__init__(poly_set, dual, degree, formdegree,
139172 mapping="contravariant piola")
55 #
66 # Modified by David A. Ham (david.ham@imperial.ac.uk), 2014
77 # Modified by Lizao Li (lzlarryli@gmail.com), 2016
8
89 """
910 Abstract class and particular implementations of finite element
1011 reference simplex geometry/topology.
480481 verts = ((),)
481482 topology = {0: {0: (0,)}}
482483 super(Point, self).__init__(POINT, verts, topology)
484
485 def construct_subelement(self, dimension):
486 """Constructs the reference element of a cell subentity
487 specified by subelement dimension.
488
489 :arg dimension: subentity dimension (integer). Must be zero.
490 """
491 assert dimension == 0
492 return self
483493
484494
485495 class DefaultLine(Simplex):
967977 return UFCQuadrilateral()
968978 elif celltype == "hexahedron":
969979 return UFCHexahedron()
980 elif celltype == "vertex":
981 return ufc_simplex(0)
970982 elif celltype == "interval":
971983 return ufc_simplex(1)
972984 elif celltype == "triangle":
149149 raise NotImplementedError('no tabulate method for serendipity elements of dimension 1 or less.')
150150 if dim >= 4:
151151 raise NotImplementedError('tabulate does not support higher dimensions than 3.')
152 points = np.asarray(points)
153 npoints, pointdim = points.shape
152154 for o in range(order + 1):
153155 alphas = mis(dim, o)
154156 for alpha in alphas:
159161 callable = lambdify(variables[:dim], polynomials, modules="numpy", dummify=True)
160162 self.basis[alpha] = polynomials
161163 self.basis_callable[alpha] = callable
162 points = np.asarray(points)
163 T = np.asarray(callable(*(points[:, i] for i in range(points.shape[1]))))
164 tabulation = callable(*(points[:, i] for i in range(pointdim)))
165 T = np.asarray([np.broadcast_to(tab, (npoints, ))
166 for tab in tabulation])
164167 phivals[alpha] = T
165168 return phivals
166169
1919 import pytest
2020
2121 from FIAT.reference_element import LINE, ReferenceElement
22 from FIAT.reference_element import UFCInterval, UFCTriangle, UFCTetrahedron
22 from FIAT.reference_element import Point, UFCInterval, UFCTriangle, UFCTetrahedron
2323 from FIAT.lagrange import Lagrange
2424 from FIAT.discontinuous_lagrange import DiscontinuousLagrange # noqa: F401
2525 from FIAT.discontinuous_taylor import DiscontinuousTaylor # noqa: F401
4848 from FIAT.enriched import EnrichedElement # noqa: F401
4949 from FIAT.nodal_enriched import NodalEnrichedElement
5050
51
51 P = Point()
5252 I = UFCInterval() # noqa: E741
5353 T = UFCTriangle()
5454 S = UFCTetrahedron()
109109 "P0(I)",
110110 "P0(T)",
111111 "P0(S)",
112 "DiscontinuousLagrange(P, 0)",
112113 "DiscontinuousLagrange(I, 0)",
113114 "DiscontinuousLagrange(I, 1)",
114115 "DiscontinuousLagrange(I, 2)",
136137 "RaviartThomas(S, 1)",
137138 "RaviartThomas(S, 2)",
138139 "RaviartThomas(S, 3)",
140 'RaviartThomas(T, 1, variant="integral")',
141 'RaviartThomas(T, 2, variant="integral")',
142 'RaviartThomas(T, 3, variant="integral")',
143 'RaviartThomas(S, 1, variant="integral")',
144 'RaviartThomas(S, 2, variant="integral")',
145 'RaviartThomas(S, 3, variant="integral")',
146 'RaviartThomas(T, 1, variant="integral(2)")',
147 'RaviartThomas(T, 2, variant="integral(3)")',
148 'RaviartThomas(T, 3, variant="integral(4)")',
149 'RaviartThomas(S, 1, variant="integral(2)")',
150 'RaviartThomas(S, 2, variant="integral(3)")',
151 'RaviartThomas(S, 3, variant="integral(4)")',
152 'RaviartThomas(T, 1, variant="point")',
153 'RaviartThomas(T, 2, variant="point")',
154 'RaviartThomas(T, 3, variant="point")',
155 'RaviartThomas(S, 1, variant="point")',
156 'RaviartThomas(S, 2, variant="point")',
157 'RaviartThomas(S, 3, variant="point")',
139158 "DiscontinuousRaviartThomas(T, 1)",
140159 "DiscontinuousRaviartThomas(T, 2)",
141160 "DiscontinuousRaviartThomas(T, 3)",
148167 "BrezziDouglasMarini(S, 1)",
149168 "BrezziDouglasMarini(S, 2)",
150169 "BrezziDouglasMarini(S, 3)",
170 'BrezziDouglasMarini(T, 1, variant="integral")',
171 'BrezziDouglasMarini(T, 2, variant="integral")',
172 'BrezziDouglasMarini(T, 3, variant="integral")',
173 'BrezziDouglasMarini(S, 1, variant="integral")',
174 'BrezziDouglasMarini(S, 2, variant="integral")',
175 'BrezziDouglasMarini(S, 3, variant="integral")',
176 'BrezziDouglasMarini(T, 1, variant="integral(2)")',
177 'BrezziDouglasMarini(T, 2, variant="integral(3)")',
178 'BrezziDouglasMarini(T, 3, variant="integral(4)")',
179 'BrezziDouglasMarini(S, 1, variant="integral(2)")',
180 'BrezziDouglasMarini(S, 2, variant="integral(3)")',
181 'BrezziDouglasMarini(S, 3, variant="integral(4)")',
182 'BrezziDouglasMarini(T, 1, variant="point")',
183 'BrezziDouglasMarini(T, 2, variant="point")',
184 'BrezziDouglasMarini(T, 3, variant="point")',
185 'BrezziDouglasMarini(S, 1, variant="point")',
186 'BrezziDouglasMarini(S, 2, variant="point")',
187 'BrezziDouglasMarini(S, 3, variant="point")',
151188 "Nedelec(T, 1)",
152189 "Nedelec(T, 2)",
153190 "Nedelec(T, 3)",
154191 "Nedelec(S, 1)",
155192 "Nedelec(S, 2)",
156193 "Nedelec(S, 3)",
194 'Nedelec(T, 1, variant="integral")',
195 'Nedelec(T, 2, variant="integral")',
196 'Nedelec(T, 3, variant="integral")',
197 'Nedelec(S, 1, variant="integral")',
198 'Nedelec(S, 2, variant="integral")',
199 'Nedelec(S, 3, variant="integral")',
200 'Nedelec(T, 1, variant="integral(2)")',
201 'Nedelec(T, 2, variant="integral(3)")',
202 'Nedelec(T, 3, variant="integral(4)")',
203 'Nedelec(S, 1, variant="integral(2)")',
204 'Nedelec(S, 2, variant="integral(3)")',
205 'Nedelec(S, 3, variant="integral(4)")',
206 'Nedelec(T, 1, variant="point")',
207 'Nedelec(T, 2, variant="point")',
208 'Nedelec(T, 3, variant="point")',
209 'Nedelec(S, 1, variant="point")',
210 'Nedelec(S, 2, variant="point")',
211 'Nedelec(S, 3, variant="point")',
157212 "NedelecSecondKind(T, 1)",
158213 "NedelecSecondKind(T, 2)",
159214 "NedelecSecondKind(T, 3)",
160215 "NedelecSecondKind(S, 1)",
161216 "NedelecSecondKind(S, 2)",
162217 "NedelecSecondKind(S, 3)",
218 'NedelecSecondKind(T, 1, variant="integral")',
219 'NedelecSecondKind(T, 2, variant="integral")',
220 'NedelecSecondKind(T, 3, variant="integral")',
221 'NedelecSecondKind(S, 1, variant="integral")',
222 'NedelecSecondKind(S, 2, variant="integral")',
223 'NedelecSecondKind(S, 3, variant="integral")',
224 'NedelecSecondKind(T, 1, variant="integral(2)")',
225 'NedelecSecondKind(T, 2, variant="integral(3)")',
226 'NedelecSecondKind(T, 3, variant="integral(4)")',
227 'NedelecSecondKind(S, 1, variant="integral(2)")',
228 'NedelecSecondKind(S, 2, variant="integral(3)")',
229 'NedelecSecondKind(S, 3, variant="integral(4)")',
230 'NedelecSecondKind(T, 1, variant="point")',
231 'NedelecSecondKind(T, 2, variant="point")',
232 'NedelecSecondKind(T, 3, variant="point")',
233 'NedelecSecondKind(S, 1, variant="point")',
234 'NedelecSecondKind(S, 2, variant="point")',
235 'NedelecSecondKind(S, 3, variant="point")',
163236 "Regge(T, 0)",
164237 "Regge(T, 1)",
165238 "Regge(T, 2)",
422495 assert np.isclose(basis[i], 1.0 if i == j else 0.0)
423496
424497
498 @pytest.mark.parametrize('element', [
499 'Nedelec(S, 3, variant="integral(2)")',
500 'NedelecSecondKind(S, 3, variant="integral(3)")'
501 ])
502 def test_error_quadrature_degree(element):
503 with pytest.raises(ValueError):
504 eval(element)
505
506
425507 if __name__ == '__main__':
426508 import os
427509 pytest.main(os.path.abspath(__file__))
0 from FIAT.reference_element import UFCQuadrilateral
1 from FIAT import Serendipity
2 import numpy as np
3 import sympy
4
5
6 def test_serendipity_derivatives():
7 cell = UFCQuadrilateral()
8 S = Serendipity(cell, 2)
9
10 x = sympy.DeferredVector("X")
11 X, Y = x[0], x[1]
12 basis_functions = [
13 (1 - X)*(1 - Y),
14 Y*(1 - X),
15 X*(1 - Y),
16 X*Y,
17 Y*(1 - X)*(Y - 1),
18 X*Y*(Y - 1),
19 X*(1 - Y)*(X - 1),
20 X*Y*(X - 1),
21 ]
22 points = [[0.5, 0.5], [0.25, 0.75]]
23 for alpha, actual in S.tabulate(2, points).items():
24 expect = list(sympy.diff(basis, *zip([X, Y], alpha))
25 for basis in basis_functions)
26 expect = list([basis.subs(dict(zip([X, Y], point)))
27 for point in points]
28 for basis in expect)
29 assert actual.shape == (8, 2)
30 assert np.allclose(np.asarray(expect, dtype=float),
31 actual.reshape(8, 2))