|
| 1 | +"""Lederer-Schoberl elements on simplices. |
| 2 | +
|
| 3 | +This element's definition appears in https://doi.org/10.34726/hss.2019.62042 |
| 4 | +(Lederer, 2019). |
| 5 | +""" |
| 6 | + |
| 7 | +import typing |
| 8 | + |
| 9 | + |
| 10 | +from symfem.elements.lagrange import Lagrange |
| 11 | +from symfem.finite_element import CiarletElement |
| 12 | +from symfem.functionals import ( |
| 13 | + InnerProductIntegralMoment, |
| 14 | + IntegralMoment, |
| 15 | + TraceIntegralMoment, |
| 16 | + ListOfFunctionals, |
| 17 | +) |
| 18 | +from symfem.functions import FunctionInput, MatrixFunction |
| 19 | +from symfem.moments import make_integral_moment_dofs |
| 20 | +from symfem.polynomials import polynomial_set_vector |
| 21 | +from symfem.references import Reference |
| 22 | +from symfem.symbols import t, x |
| 23 | + |
| 24 | +__all__ = ["LedererSchoberl"] |
| 25 | + |
| 26 | + |
| 27 | +class LedererSchoberl(CiarletElement): |
| 28 | + """A Lederer-Schoberl element on a simplex.""" |
| 29 | + |
| 30 | + def __init__(self, reference: Reference, order: int): |
| 31 | + """Create the element. |
| 32 | +
|
| 33 | + Args: |
| 34 | + reference: The reference element |
| 35 | + order: The polynomial order |
| 36 | + variant: The variant of the element |
| 37 | + """ |
| 38 | + from symfem import create_reference |
| 39 | + |
| 40 | + tdim = reference.tdim |
| 41 | + poly: typing.List[FunctionInput] = [ |
| 42 | + tuple(tuple(p[i * tdim : (i + 1) * tdim]) for i in range(tdim)) |
| 43 | + for p in polynomial_set_vector(tdim, tdim**2, order) |
| 44 | + ] |
| 45 | + |
| 46 | + dofs: ListOfFunctionals = [] |
| 47 | + facet_dim = reference.tdim - 1 |
| 48 | + space = Lagrange( |
| 49 | + create_reference(["point", "interval", "triangle"][facet_dim]), order, "equispaced" |
| 50 | + ) |
| 51 | + basis = [f.subs(x, t) for f in space.get_basis_functions()] |
| 52 | + for facet_n in range(reference.sub_entity_count(facet_dim)): |
| 53 | + facet = reference.sub_entity(facet_dim, facet_n) |
| 54 | + for tangent in facet.axes: |
| 55 | + for f, dof in zip(basis, space.dofs): |
| 56 | + dofs.append( |
| 57 | + InnerProductIntegralMoment( |
| 58 | + reference, |
| 59 | + f, |
| 60 | + tuple(i / facet.volume() for i in tangent), |
| 61 | + facet.normal(), |
| 62 | + dof, |
| 63 | + entity=(facet_dim, facet_n), |
| 64 | + mapping="co_contravariant", |
| 65 | + ) |
| 66 | + ) |
| 67 | + |
| 68 | + dofs += make_integral_moment_dofs( |
| 69 | + reference, |
| 70 | + cells=( |
| 71 | + TraceIntegralMoment, |
| 72 | + Lagrange, |
| 73 | + order, |
| 74 | + "co_contravariant", |
| 75 | + ), |
| 76 | + ) |
| 77 | + |
| 78 | + if order > 0: |
| 79 | + if reference.tdim == 2: |
| 80 | + functions = [ |
| 81 | + MatrixFunction(i) |
| 82 | + for i in [ |
| 83 | + (((x[0] + x[1] - 1) / 2, 0), (0, (1 - x[0] - x[1]) / 2)), |
| 84 | + ((x[0] / 2, 0), (x[0], -x[0] / 2)), |
| 85 | + ((x[1] / 2, -x[1]), (0, -x[1] / 2)), |
| 86 | + ] |
| 87 | + ] |
| 88 | + else: |
| 89 | + assert reference.tdim == 3 |
| 90 | + functions = [ |
| 91 | + MatrixFunction(i) |
| 92 | + for i in [ |
| 93 | + ( |
| 94 | + (2 * (1 - x[0] - x[1] - x[2]) / 3, 0, 0), |
| 95 | + (0, (x[0] + x[1] + x[2] - 1) / 3, 0), |
| 96 | + (0, 0, (x[0] + x[1] + x[2] - 1) / 3), |
| 97 | + ), |
| 98 | + ( |
| 99 | + ((x[0] + x[1] + x[2] - 1) / 3, 0, 0), |
| 100 | + (0, 2 * (1 - x[0] - x[1] - x[2]) / 3, 0), |
| 101 | + (0, 0, (x[0] + x[1] + x[2] - 1) / 3), |
| 102 | + ), |
| 103 | + ((x[0] / 3, 0, 0), (x[0], -2 * x[0] / 3, 0), (0, 0, x[0] / 3)), |
| 104 | + ((x[0] / 3, 0, 0), (0, x[0] / 3, 0), (x[0], 0, -2 * x[0] / 3)), |
| 105 | + ((-x[1] / 3, 0, 0), (0, -x[1] / 3, 0), (0, -x[1], 2 * x[1] / 3)), |
| 106 | + ((-x[1] / 3, x[1], 0), (0, 2 * x[1] / 3, 0), (0, x[1], -x[1] / 3)), |
| 107 | + ((x[2] / 3, 0, -x[2]), (0, x[2] / 3, -x[2]), (0, 0, -2 * x[2] / 3)), |
| 108 | + ((-2 * x[2] / 3, 0, x[2]), (0, x[2] / 3, 0), (0, 0, x[2] / 3)), |
| 109 | + ] |
| 110 | + ] |
| 111 | + |
| 112 | + lagrange = Lagrange(reference, order - 1) |
| 113 | + for dof, f in zip(lagrange.dofs, lagrange.get_basis_functions()): |
| 114 | + for g in functions: |
| 115 | + dofs.append( |
| 116 | + IntegralMoment( |
| 117 | + reference, |
| 118 | + f * g, |
| 119 | + dof, |
| 120 | + (reference.tdim, 0), |
| 121 | + "co_contravariant", |
| 122 | + ) |
| 123 | + ) |
| 124 | + |
| 125 | + super().__init__( |
| 126 | + reference, |
| 127 | + order, |
| 128 | + poly, |
| 129 | + dofs, |
| 130 | + reference.tdim, |
| 131 | + reference.tdim**2, |
| 132 | + (reference.tdim, reference.tdim), |
| 133 | + ) |
| 134 | + |
| 135 | + def init_kwargs(self) -> typing.Dict[str, typing.Any]: |
| 136 | + """Return the kwargs used to create this element. |
| 137 | +
|
| 138 | + Returns: |
| 139 | + Keyword argument dictionary |
| 140 | + """ |
| 141 | + return {} |
| 142 | + |
| 143 | + @property |
| 144 | + def lagrange_subdegree(self) -> int: |
| 145 | + return self.order |
| 146 | + |
| 147 | + @property |
| 148 | + def lagrange_superdegree(self) -> typing.Optional[int]: |
| 149 | + return self.order |
| 150 | + |
| 151 | + @property |
| 152 | + def polynomial_subdegree(self) -> int: |
| 153 | + return self.order |
| 154 | + |
| 155 | + @property |
| 156 | + def polynomial_superdegree(self) -> typing.Optional[int]: |
| 157 | + return self.order |
| 158 | + |
| 159 | + names = ["Lederer-Schoberl"] |
| 160 | + references = ["triangle", "tetrahedron"] |
| 161 | + min_order = 0 |
| 162 | + continuity = "inner H(curl div)" |
| 163 | + value_type = "matrix" |
| 164 | + last_updated = "2024.03" |
0 commit comments