|
17 | 17 | from FIAT.quadrature_schemes import create_quadrature |
18 | 18 |
|
19 | 19 |
|
20 | | -def DivergenceDubinerMoments(cell, start_deg, stop_deg, comp_deg): |
21 | | - sd = cell.get_spatial_dimension() |
22 | | - P = polynomial_set.ONPolynomialSet(cell, stop_deg) |
23 | | - Q = create_quadrature(cell, comp_deg + stop_deg) |
| 20 | +def DivergenceDubinerMoments(ref_el, start_deg, stop_deg, comp_deg): |
| 21 | + sd = ref_el.get_spatial_dimension() |
| 22 | + P = polynomial_set.ONPolynomialSet(ref_el, stop_deg) |
| 23 | + Q = create_quadrature(ref_el, comp_deg + stop_deg) |
24 | 24 |
|
25 | | - dim0 = expansions.polynomial_dimension(cell, start_deg-1) |
26 | | - dim1 = expansions.polynomial_dimension(cell, stop_deg) |
| 25 | + dim0 = expansions.polynomial_dimension(ref_el, start_deg-1) |
| 26 | + dim1 = expansions.polynomial_dimension(ref_el, stop_deg) |
27 | 27 | indices = list(range(dim0, dim1)) |
28 | 28 | phis = P.take(indices).tabulate(Q.get_points())[(0,)*sd] |
29 | | - return [IntegralMomentOfDivergence(cell, Q, phi) for phi in phis] |
| 29 | + for phi in phis: |
| 30 | + yield IntegralMomentOfDivergence(ref_el, Q, phi) |
30 | 31 |
|
31 | 32 |
|
32 | 33 | class MardalTaiWintherDual(dual_set.DualSet): |
33 | 34 | """Degrees of freedom for Mardal-Tai-Winther elements.""" |
34 | | - def __init__(self, cell, degree): |
35 | | - dim = cell.get_spatial_dimension() |
36 | | - if not dim == 2: |
37 | | - raise ValueError("Mardal-Tai-Winther elements are only" |
38 | | - "defined in dimension 2.") |
39 | | - |
40 | | - if not degree == 3: |
41 | | - raise ValueError("Mardal-Tai-Winther elements are only defined" |
42 | | - "for degree 3.") |
43 | | - |
44 | | - # construct the degrees of freedoms |
45 | | - dofs = [] # list of functionals |
46 | | - |
47 | | - # dof_ids[i][j] contains the indices of dofs that are associated with |
48 | | - # entity j in dim i |
49 | | - dof_ids = {} |
50 | | - |
51 | | - # no vertex dof |
52 | | - dof_ids[0] = {i: [] for i in range(dim + 1)} |
53 | | - |
54 | | - # edge dofs |
55 | | - (_dofs, _dof_ids) = self._generate_edge_dofs(cell, degree) |
56 | | - dofs.extend(_dofs) |
57 | | - dof_ids[1] = _dof_ids |
58 | | - |
59 | | - # no cell dofs |
60 | | - dof_ids[2] = {} |
61 | | - dof_ids[2][0] = [] |
62 | | - |
63 | | - # extra dofs for enforcing div(v) constant over the cell and |
64 | | - # v.n linear on edges |
65 | | - (_dofs, _edge_dof_ids, _cell_dof_ids) = self._generate_constraint_dofs(cell, degree, len(dofs)) |
66 | | - dofs.extend(_dofs) |
67 | | - |
68 | | - for entity_id in range(3): |
69 | | - dof_ids[1][entity_id].extend(_edge_dof_ids[entity_id]) |
70 | | - |
71 | | - dof_ids[2][0].extend(_cell_dof_ids) |
72 | | - |
73 | | - super(MardalTaiWintherDual, self).__init__(dofs, cell, dof_ids) |
74 | | - |
75 | | - @staticmethod |
76 | | - def _generate_edge_dofs(cell, degree): |
77 | | - """Generate dofs on edges. |
78 | | - On each edge, let n be its normal. We need to integrate |
79 | | - u.n and u.t against the first Legendre polynomial (constant) |
80 | | - and u.n against the second (linear). |
81 | | - """ |
82 | | - dofs = [] |
83 | | - dof_ids = {} |
84 | | - offset = 0 |
85 | | - sd = 2 |
86 | | - |
87 | | - facet = cell.get_facet_element() |
88 | | - # Facet nodes are \int_F v\cdot n p ds where p \in P_{q-1} |
| 35 | + def __init__(self, ref_el, degree): |
| 36 | + sd = ref_el.get_spatial_dimension() |
| 37 | + top = ref_el.get_topology() |
| 38 | + |
| 39 | + if sd != 2: |
| 40 | + raise ValueError("Mardal-Tai-Winther elements are only defined in dimension 2.") |
| 41 | + |
| 42 | + if degree != 3: |
| 43 | + raise ValueError("Mardal-Tai-Winther elements are only defined for degree 3.") |
| 44 | + |
| 45 | + entity_ids = {dim: {entity: [] for entity in top[dim]} for dim in top} |
| 46 | + nodes = [] |
| 47 | + |
| 48 | + # no vertex dofs |
| 49 | + |
| 50 | + # On each facet, let n be its normal. We need to integrate |
| 51 | + # u.n and u.t against the first Legendre polynomial (constant) |
| 52 | + # and u.n against the second (linear). |
| 53 | + facet = ref_el.get_facet_element() |
| 54 | + # Facet nodes are \int_F v.n p ds where p \in P_{q-1} |
89 | 55 | # degree is q - 1 |
90 | 56 | Q = create_quadrature(facet, degree+1) |
91 | 57 | Pq = polynomial_set.ONPolynomialSet(facet, 1) |
92 | 58 | phis = Pq.tabulate(Q.get_points())[(0,)*(sd - 1)] |
93 | | - for f in range(3): |
94 | | - dofs.append(IntegralMomentOfNormalEvaluation(cell, Q, phis[0], f)) |
95 | | - dofs.append(IntegralMomentOfTangentialEvaluation(cell, Q, phis[0], f)) |
96 | | - dofs.append(IntegralMomentOfNormalEvaluation(cell, Q, phis[1], f)) |
97 | | - |
98 | | - num_new_dofs = 3 |
99 | | - dof_ids[f] = list(range(offset, offset + num_new_dofs)) |
100 | | - offset += num_new_dofs |
101 | | - |
102 | | - return (dofs, dof_ids) |
103 | | - |
104 | | - @staticmethod |
105 | | - def _generate_constraint_dofs(cell, degree, offset): |
106 | | - """ |
107 | | - Generate constraint dofs on the cell and edges |
108 | | - * div(v) must be constant on the cell. Since v is a cubic and |
109 | | - div(v) is quadratic, we need the integral of div(v) against the |
110 | | - linear and quadratic Dubiner polynomials to vanish. |
111 | | - There are two linear and three quadratics, so these are five |
112 | | - constraints |
113 | | - * v.n must be linear on each edge. Since v.n is cubic, we need |
114 | | - the integral of v.n against the cubic and quadratic Legendre |
115 | | - polynomial to vanish on each edge. |
116 | | -
|
117 | | - So we introduce functionals whose kernel describes this property, |
118 | | - as described in the FIAT paper. |
119 | | - """ |
120 | | - dofs = [] |
121 | | - edge_dof_ids = {} |
122 | | - |
| 59 | + for f in sorted(top[sd-1]): |
| 60 | + cur = len(nodes) |
| 61 | + nodes.append(IntegralMomentOfNormalEvaluation(ref_el, Q, phis[0], f)) |
| 62 | + nodes.append(IntegralMomentOfTangentialEvaluation(ref_el, Q, phis[0], f)) |
| 63 | + nodes.append(IntegralMomentOfNormalEvaluation(ref_el, Q, phis[1], f)) |
| 64 | + entity_ids[sd-1][f].extend(range(cur, len(nodes))) |
| 65 | + |
| 66 | + # Generate constraint nodes on the cell and facets |
| 67 | + # * div(v) must be constant on the cell. Since v is a cubic and |
| 68 | + # div(v) is quadratic, we need the integral of div(v) against the |
| 69 | + # linear and quadratic Dubiner polynomials to vanish. |
| 70 | + # There are two linear and three quadratics, so these are five |
| 71 | + # constraints |
| 72 | + # * v.n must be linear on each facet. Since v.n is cubic, we need |
| 73 | + # the integral of v.n against the cubic and quadratic Legendre |
| 74 | + # polynomial to vanish on each facet. |
| 75 | + |
| 76 | + # So we introduce functionals whose kernel describes this property, |
| 77 | + # as described in the FIAT paper. |
123 | 78 | start_order = 2 |
124 | 79 | stop_order = 3 |
125 | 80 | qdegree = degree + stop_order |
126 | | - for entity_id in range(3): |
127 | | - cur = len(dofs) |
128 | | - dofs.extend(IntegralLegendreNormalMoment(cell, entity_id, order, qdegree) |
129 | | - for order in range(start_order, stop_order+1)) |
130 | | - |
131 | | - edge_dof_ids[entity_id] = list(range(offset+cur, offset+len(dofs))) |
| 81 | + for f in sorted(top[sd-1]): |
| 82 | + cur = len(nodes) |
| 83 | + nodes.extend(IntegralLegendreNormalMoment(ref_el, f, order, qdegree) |
| 84 | + for order in range(start_order, stop_order+1)) |
| 85 | + entity_ids[sd-1][f].extend(range(cur, len(nodes))) |
132 | 86 |
|
133 | | - cur = len(dofs) |
134 | | - dofs.extend(DivergenceDubinerMoments(cell, start_order-1, stop_order-1, degree)) |
135 | | - cell_dof_ids = list(range(offset+cur, offset+len(dofs))) |
| 87 | + cur = len(nodes) |
| 88 | + nodes.extend(DivergenceDubinerMoments(ref_el, start_order-1, stop_order-1, degree)) |
| 89 | + entity_ids[sd][0].extend(range(cur, len(nodes))) |
136 | 90 |
|
137 | | - return (dofs, edge_dof_ids, cell_dof_ids) |
| 91 | + super().__init__(nodes, ref_el, entity_ids) |
138 | 92 |
|
139 | 93 |
|
140 | 94 | class MardalTaiWinther(finite_element.CiarletElement): |
141 | 95 | """The definition of the Mardal-Tai-Winther element. |
142 | 96 | """ |
143 | | - def __init__(self, cell, degree=3): |
| 97 | + def __init__(self, ref_el, degree=3): |
| 98 | + sd = ref_el.get_spatial_dimension() |
144 | 99 | assert degree == 3, "Only defined for degree 3" |
145 | | - assert cell.get_spatial_dimension() == 2, "Only defined for dimension 2" |
146 | | - # polynomial space |
147 | | - Ps = polynomial_set.ONPolynomialSet(cell, degree, (2,)) |
148 | | - |
149 | | - # degrees of freedom |
150 | | - Ls = MardalTaiWintherDual(cell, degree) |
151 | | - |
152 | | - # mapping under affine transformation |
| 100 | + assert sd == 2, "Only defined for dimension 2" |
| 101 | + poly_set = polynomial_set.ONPolynomialSet(ref_el, degree, (sd,)) |
| 102 | + dual = MardalTaiWintherDual(ref_el, degree) |
| 103 | + formdegree = sd-1 |
153 | 104 | mapping = "contravariant piola" |
154 | | - |
155 | | - super(MardalTaiWinther, self).__init__(Ps, Ls, degree, |
156 | | - mapping=mapping) |
| 105 | + super().__init__(poly_set, dual, degree, formdegree, mapping=mapping) |
0 commit comments