|  | 
|  | 1 | +"""TiNiest Tensor product (TNT) elements. | 
|  | 2 | +
 | 
|  | 3 | +These elements' definitions appear in https://doi.org/10.1090/S0025-5718-2013-02729-9 | 
|  | 4 | +(Cockburn, Qiu, 2013) | 
|  | 5 | +""" | 
|  | 6 | + | 
|  | 7 | +from itertools import product | 
|  | 8 | +from ..finite_element import CiarletElement | 
|  | 9 | +from ..polynomials import quolynomial_set | 
|  | 10 | +from ..functionals import (TangentIntegralMoment, IntegralAgainst, | 
|  | 11 | +                           NormalIntegralMoment, PointEvaluation, | 
|  | 12 | +                           DerivativeIntegralMoment) | 
|  | 13 | +from ..symbolic import x, t, subs | 
|  | 14 | +from ..calculus import grad, curl | 
|  | 15 | +from ..moments import make_integral_moment_dofs | 
|  | 16 | +from ..vectors import vcross | 
|  | 17 | +from ..legendre import get_legendre_basis | 
|  | 18 | +from ..references import Interval | 
|  | 19 | +from .q import Q | 
|  | 20 | + | 
|  | 21 | + | 
|  | 22 | +def P(k, v): | 
|  | 23 | +    """Return the kth Legendre polynomial.""" | 
|  | 24 | +    return subs( | 
|  | 25 | +        get_legendre_basis([x[0] ** i for i in range(k + 1)], Interval())[-1], | 
|  | 26 | +        x[0], v) | 
|  | 27 | + | 
|  | 28 | + | 
|  | 29 | +def B(k, v): | 
|  | 30 | +    """ | 
|  | 31 | +    Return the function B_k. | 
|  | 32 | +
 | 
|  | 33 | +    This function is defined on page 4 (606) of | 
|  | 34 | +    https://doi.org/10.1090/S0025-5718-2013-02729-9 (Cockburn, Qiu, 2013). | 
|  | 35 | +    """ | 
|  | 36 | +    if k == 1: | 
|  | 37 | +        return 0 | 
|  | 38 | +    return (P(k, v) - P(k - 2, v)) / (4 + k - 2) | 
|  | 39 | + | 
|  | 40 | + | 
|  | 41 | +class TNT(CiarletElement): | 
|  | 42 | +    """TiNiest Tensor scalar finite element.""" | 
|  | 43 | + | 
|  | 44 | +    def __init__(self, reference, order, variant="equispaced"): | 
|  | 45 | +        poly = quolynomial_set(reference.tdim, 1, order) | 
|  | 46 | +        if reference.tdim == 2: | 
|  | 47 | +            for i in range(2): | 
|  | 48 | +                vars = [x[j] for j in range(2) if j != i] | 
|  | 49 | +                for f in [1 - vars[0], vars[0]]: | 
|  | 50 | +                    poly.append(f * B(order + 1, x[i])) | 
|  | 51 | + | 
|  | 52 | +        elif reference.tdim == 3: | 
|  | 53 | +            for i in range(3): | 
|  | 54 | +                vars = [x[j] for j in range(3) if j != i] | 
|  | 55 | +                for f0 in [1 - vars[0], vars[0]]: | 
|  | 56 | +                    for f1 in [1 - vars[1], vars[1]]: | 
|  | 57 | +                        poly.append(f0 * f1 * B(order + 1, x[i])) | 
|  | 58 | + | 
|  | 59 | +        dofs = [] | 
|  | 60 | +        for i, v in enumerate(reference.vertices): | 
|  | 61 | +            dofs.append(PointEvaluation(v, entity=(0, i))) | 
|  | 62 | + | 
|  | 63 | +        for i in range(1, order + 1): | 
|  | 64 | +            f = i * t[0] ** (i - 1) | 
|  | 65 | +            for edge_n in range(reference.sub_entity_count(1)): | 
|  | 66 | +                edge = reference.sub_entity(1, edge_n) | 
|  | 67 | +                dofs.append(IntegralAgainst( | 
|  | 68 | +                    edge, f, entity=(1, edge_n), mapping="identity")) | 
|  | 69 | + | 
|  | 70 | +        for i in range(1, order): | 
|  | 71 | +            for j in range(1, order): | 
|  | 72 | +                f = t[0] ** i * (t[0] - 1) * t[1] ** j * (t[1] - 1) | 
|  | 73 | +                delta_f = (f.diff(t[0]).diff(t[0]) + f.diff(t[1]).diff(t[1])).expand() | 
|  | 74 | +                for face_n in range(reference.sub_entity_count(2)): | 
|  | 75 | +                    face = reference.sub_entity(2, face_n) | 
|  | 76 | +                    dofs.append(IntegralAgainst( | 
|  | 77 | +                        face, delta_f, entity=(2, face_n), mapping="identity")) | 
|  | 78 | + | 
|  | 79 | +        if reference.tdim == 3: | 
|  | 80 | +            for i in product(range(1, order), repeat=3): | 
|  | 81 | +                f = 1 | 
|  | 82 | +                for j, k in zip(i, x): | 
|  | 83 | +                    f *= k ** j * (k - 1) | 
|  | 84 | +                grad_f = tuple(j.expand() for j in grad(f, 3)) | 
|  | 85 | +                dofs.append(DerivativeIntegralMoment( | 
|  | 86 | +                    reference, 1, grad_f, None, entity=(3, 0), mapping="identity")) | 
|  | 87 | + | 
|  | 88 | +        super().__init__( | 
|  | 89 | +            reference, order, poly, dofs, reference.tdim, 1 | 
|  | 90 | +        ) | 
|  | 91 | +        self.variant = variant | 
|  | 92 | + | 
|  | 93 | +    def init_kwargs(self): | 
|  | 94 | +        """Return the kwargs used to create this element.""" | 
|  | 95 | +        return {"variant": self.variant} | 
|  | 96 | + | 
|  | 97 | +    names = ["tiniest tensor", "TNT"] | 
|  | 98 | +    references = ["quadrilateral", "hexahedron"] | 
|  | 99 | +    min_order = 1 | 
|  | 100 | +    continuity = "C0" | 
|  | 101 | + | 
|  | 102 | + | 
|  | 103 | +class TNTcurl(CiarletElement): | 
|  | 104 | +    """TiNiest Tensor Hcurl finite element.""" | 
|  | 105 | + | 
|  | 106 | +    def __init__(self, reference, order, variant="equispaced"): | 
|  | 107 | +        poly = quolynomial_set(reference.tdim, reference.tdim, order) | 
|  | 108 | +        if reference.tdim == 2: | 
|  | 109 | +            for i in product([0, 1], repeat=2): | 
|  | 110 | +                if sum(i) != 0: | 
|  | 111 | +                    poly.append([j.expand() for j in [ | 
|  | 112 | +                        P(order, i[0] * x[0]) * B(order + 1, i[1] * x[1]), | 
|  | 113 | +                        -B(order + 1, i[0] * x[0]) * P(order, i[1] * x[1]), | 
|  | 114 | +                    ]]) | 
|  | 115 | +        else: | 
|  | 116 | +            face_poly = [] | 
|  | 117 | +            for i in product([0, 1], repeat=2): | 
|  | 118 | +                if sum(i) != 0: | 
|  | 119 | +                    face_poly.append([j.expand() for j in [ | 
|  | 120 | +                        B(order + 1, i[0] * t[0]) * P(order, i[1] * t[1]), | 
|  | 121 | +                        P(order, i[0] * t[0]) * B(order + 1, i[1] * t[1]), | 
|  | 122 | +                    ]]) | 
|  | 123 | +            for lamb_n in [(x[0], 0, 0), (1 - x[0], 0, 0), | 
|  | 124 | +                           (0, x[1], 0), (0, 1 - x[1], 0), | 
|  | 125 | +                           (0, 0, x[2]), (0, 0, 1 - x[2])]: | 
|  | 126 | +                vars = tuple(i for i, j in enumerate(lamb_n) if j == 0) | 
|  | 127 | +                poly += [vcross(lamb_n, [ | 
|  | 128 | +                    subs(p[0], t[:2], [x[j] for j in vars]) if i == vars[0] | 
|  | 129 | +                    else (subs(p[1], t[:2], [x[j] for j in vars]) if i == vars[1] else 0) | 
|  | 130 | +                    for i in range(3) | 
|  | 131 | +                ]) for p in face_poly] | 
|  | 132 | + | 
|  | 133 | +        dofs = [] | 
|  | 134 | +        dofs += make_integral_moment_dofs( | 
|  | 135 | +            reference, | 
|  | 136 | +            edges=(TangentIntegralMoment, Q, order, {"variant": variant}), | 
|  | 137 | +        ) | 
|  | 138 | + | 
|  | 139 | +        # Face moments | 
|  | 140 | +        face_moments = [] | 
|  | 141 | +        for i in product(range(order + 1), repeat=2): | 
|  | 142 | +            if sum(i) > 0: | 
|  | 143 | +                f = x[0] ** i[0] * x[1] ** i[1] | 
|  | 144 | +                grad_f = grad(f, 2) | 
|  | 145 | +                grad_f = subs((grad_f[1], -grad_f[0]), x[:2], t[:2]) | 
|  | 146 | +                face_moments.append(grad_f) | 
|  | 147 | + | 
|  | 148 | +        for i in range(2, order + 1): | 
|  | 149 | +            for j in range(2, order + 1): | 
|  | 150 | +                face_moments.append(( | 
|  | 151 | +                    t[1] ** (j - 1) * (1 - t[1]) * t[0] ** (i - 2) * (i * t[0] - i + 1), | 
|  | 152 | +                    -t[0] ** (i - 1) * (1 - t[0]) * t[1] ** (j - 2) * (j - 1 - j * t[1]))) | 
|  | 153 | +        if reference.tdim == 2: | 
|  | 154 | +            for f in face_moments: | 
|  | 155 | +                dofs.append(IntegralAgainst( | 
|  | 156 | +                    reference, f, entity=(2, 0), mapping="contravariant")) | 
|  | 157 | +        elif reference.tdim == 3: | 
|  | 158 | +            for face_n in range(6): | 
|  | 159 | +                face = reference.sub_entity(2, face_n) | 
|  | 160 | +                for f in face_moments: | 
|  | 161 | +                    dofs.append(IntegralAgainst( | 
|  | 162 | +                        face, f, entity=(2, face_n), mapping="contravariant")) | 
|  | 163 | + | 
|  | 164 | +        # Interior Moments | 
|  | 165 | +        if reference.tdim == 3: | 
|  | 166 | +            for i in range(1, order): | 
|  | 167 | +                for j in range(1, order): | 
|  | 168 | +                    for k in range(order + 1): | 
|  | 169 | +                        f = (x[0] ** k * x[1] ** i * (1 - x[1]) * x[2] ** j * (1 - x[2]), 0, 0) | 
|  | 170 | +                        dofs.append(IntegralAgainst( | 
|  | 171 | +                            reference, curl(curl(f)), entity=(3, 0), mapping="covariant")) | 
|  | 172 | + | 
|  | 173 | +                        f = (0, x[1] ** k * x[0] ** i * (1 - x[0]) * x[2] ** j * (1 - x[2]), 0, 0) | 
|  | 174 | +                        dofs.append(IntegralAgainst( | 
|  | 175 | +                            reference, curl(curl(f)), entity=(3, 0), mapping="covariant")) | 
|  | 176 | + | 
|  | 177 | +                        if k in [0, 2]: | 
|  | 178 | +                            f = (0, 0,  x[2] ** k * x[0] ** i * (1 - x[0]) * x[1] ** j * (1 - x[1])) | 
|  | 179 | +                            dofs.append(IntegralAgainst( | 
|  | 180 | +                                reference, curl(curl(f)), entity=(3, 0), mapping="covariant")) | 
|  | 181 | + | 
|  | 182 | +            for i in range(2, order + 1): | 
|  | 183 | +                for j in range(2, order + 1): | 
|  | 184 | +                    for k in range(2, order + 1): | 
|  | 185 | +                        f = x[0] ** (i - 1) * x[0] ** i | 
|  | 186 | +                        f *= x[1] ** (j - 1) * x[1] ** j | 
|  | 187 | +                        f *= x[2] ** (k - 1) * x[2] ** k | 
|  | 188 | +                        grad_f = grad(f, 3) | 
|  | 189 | +                        dofs.append(IntegralAgainst( | 
|  | 190 | +                            reference, grad_f, entity=(3, 0), mapping="contravariant")) | 
|  | 191 | + | 
|  | 192 | +        super().__init__( | 
|  | 193 | +            reference, order, poly, dofs, reference.tdim, reference.tdim | 
|  | 194 | +        ) | 
|  | 195 | +        self.variant = variant | 
|  | 196 | + | 
|  | 197 | +    def init_kwargs(self): | 
|  | 198 | +        """Return the kwargs used to create this element.""" | 
|  | 199 | +        return {"variant": self.variant} | 
|  | 200 | + | 
|  | 201 | +    names = ["tiniest tensor Hcurl", "TNTcurl"] | 
|  | 202 | +    references = ["quadrilateral", "hexahedron"] | 
|  | 203 | +    min_order = 1 | 
|  | 204 | +    continuity = "H(curl)" | 
|  | 205 | + | 
|  | 206 | + | 
|  | 207 | +class TNTdiv(CiarletElement): | 
|  | 208 | +    """TiNiest Tensor Hdiv finite element.""" | 
|  | 209 | + | 
|  | 210 | +    def __init__(self, reference, order, variant="equispaced"): | 
|  | 211 | +        poly = quolynomial_set(reference.tdim, reference.tdim, order) | 
|  | 212 | +        if reference.tdim == 2: | 
|  | 213 | +            for i in product([0, 1], repeat=2): | 
|  | 214 | +                if sum(i) != 0: | 
|  | 215 | +                    poly.append([j.expand() for j in [ | 
|  | 216 | +                        B(order + 1, i[0] * x[0]) * P(order, i[1] * x[1]), | 
|  | 217 | +                        P(order, i[0] * x[0]) * B(order + 1, i[1] * x[1]), | 
|  | 218 | +                    ]]) | 
|  | 219 | +        else: | 
|  | 220 | +            for i in product([0, 1], repeat=3): | 
|  | 221 | +                if sum(i) != 0: | 
|  | 222 | +                    poly.append([ | 
|  | 223 | +                        B(order + 1, i[0] * x[0]) * P(order, i[1] * x[1]) * P(order, i[2] * x[2]), | 
|  | 224 | +                        P(order, i[0] * x[0]) * B(order + 1, i[1] * x[1]) * P(order, i[2] * x[2]), | 
|  | 225 | +                        P(order, i[0] * x[0]) * P(order, i[1] * x[1]) * B(order + 1, i[2] * x[2]), | 
|  | 226 | +                    ]) | 
|  | 227 | + | 
|  | 228 | +        dofs = [] | 
|  | 229 | +        dofs += make_integral_moment_dofs( | 
|  | 230 | +            reference, | 
|  | 231 | +            facets=(NormalIntegralMoment, Q, order, {"variant": variant}), | 
|  | 232 | +        ) | 
|  | 233 | + | 
|  | 234 | +        for i in product(range(order + 1), repeat=reference.tdim): | 
|  | 235 | +            if sum(i) > 0: | 
|  | 236 | +                if reference.tdim == 2: | 
|  | 237 | +                    f = x[0] ** i[0] * x[1] ** i[1] | 
|  | 238 | +                else: | 
|  | 239 | +                    f = x[0] ** i[0] * x[1] ** i[1] * x[2] ** i[2] | 
|  | 240 | +                grad_f = grad(f, reference.tdim) | 
|  | 241 | +                dofs.append(IntegralAgainst(reference, grad_f, entity=(reference.tdim, 0), | 
|  | 242 | +                                            mapping="covariant")) | 
|  | 243 | + | 
|  | 244 | +        if reference.tdim == 2: | 
|  | 245 | +            for i in range(2, order + 1): | 
|  | 246 | +                for j in range(2, order + 1): | 
|  | 247 | +                    f = (x[0] ** (i - 1) * (1 - x[0]) * x[1] ** (j - 2) * (j - 1 - j * x[1]), | 
|  | 248 | +                         x[1] ** (j - 1) * (1 - x[1]) * x[0] ** (i - 2) * (i * x[0] - i + 1)) | 
|  | 249 | +                    dofs.append(IntegralAgainst( | 
|  | 250 | +                        reference, f, entity=(reference.tdim, 0), mapping="covariant")) | 
|  | 251 | +        if reference.tdim == 3: | 
|  | 252 | +            for i in range(2, order + 1): | 
|  | 253 | +                for j in range(2, order + 1): | 
|  | 254 | +                    for k in range(order + 1): | 
|  | 255 | +                        f = ( | 
|  | 256 | +                            x[2] ** k * x[0] ** (i - 1) * (1 - x[0]) * x[2] ** (j - 2) * ( | 
|  | 257 | +                                j - 1 - j * x[1]), | 
|  | 258 | +                            x[2] ** k * x[1] ** (j - 1) * (1 - x[1]) * x[0] ** (i - 2) * ( | 
|  | 259 | +                                i * x[0] - i + 1), | 
|  | 260 | +                            0 | 
|  | 261 | +                        ) | 
|  | 262 | +                        dofs.append(IntegralAgainst( | 
|  | 263 | +                            reference, f, entity=(reference.tdim, 0), mapping="covariant")) | 
|  | 264 | +                        f = ( | 
|  | 265 | +                            x[1] ** k * x[0] ** (i - 1) * (1 - x[0]) * x[2] ** (j - 2) * ( | 
|  | 266 | +                                j - 1 - j * x[2]), | 
|  | 267 | +                            0, | 
|  | 268 | +                            x[1] ** k * x[2] ** (j - 1) * (1 - x[2]) * x[0] ** (i - 2) * ( | 
|  | 269 | +                                i * x[0] - i + 1) | 
|  | 270 | +                        ) | 
|  | 271 | +                        dofs.append(IntegralAgainst( | 
|  | 272 | +                            reference, f, entity=(reference.tdim, 0), mapping="covariant")) | 
|  | 273 | +                        if k in [0, 2]: | 
|  | 274 | +                            f = ( | 
|  | 275 | +                                0, | 
|  | 276 | +                                x[0] ** k * x[1] ** (i - 1) * (1 - x[1]) * x[2] ** (j - 2) * ( | 
|  | 277 | +                                    j - 1 - j * x[2]), | 
|  | 278 | +                                x[0] ** k * x[2] ** (j - 1) * (1 - x[2]) * x[1] ** (i - 2) * ( | 
|  | 279 | +                                    i * x[1] - i + 1) | 
|  | 280 | +                            ) | 
|  | 281 | +                            dofs.append(IntegralAgainst( | 
|  | 282 | +                                reference, f, entity=(reference.tdim, 0), mapping="covariant")) | 
|  | 283 | + | 
|  | 284 | +        super().__init__( | 
|  | 285 | +            reference, order, poly, dofs, reference.tdim, reference.tdim | 
|  | 286 | +        ) | 
|  | 287 | +        self.variant = variant | 
|  | 288 | + | 
|  | 289 | +    def init_kwargs(self): | 
|  | 290 | +        """Return the kwargs used to create this element.""" | 
|  | 291 | +        return {"variant": self.variant} | 
|  | 292 | + | 
|  | 293 | +    names = ["tiniest tensor Hdiv", "TNTdiv"] | 
|  | 294 | +    references = ["quadrilateral", "hexahedron"] | 
|  | 295 | +    min_order = 1 | 
|  | 296 | +    continuity = "H(div)" | 
0 commit comments