|
| 1 | +# Copyright (C) 2024 Robert C. Kirby |
| 2 | +# |
| 3 | +# This file is part of FIAT (https://www.fenicsproject.org) |
| 4 | +# |
| 5 | +# SPDX-License-Identifier: LGPL-3.0-or-later |
| 6 | +# |
| 7 | +# Written by Robert C. Kirby (robert.c.kirby@gmail.com), 2024 |
| 8 | + |
| 9 | +from FIAT import dual_set, finite_element, macro, polynomial_set |
| 10 | +from FIAT.functional import (IntegralMomentOfNormalDerivative, PointDerivative, |
| 11 | + PointEvaluation) |
| 12 | +from FIAT.jacobi import eval_jacobi_batch |
| 13 | +from FIAT.quadrature_schemes import create_quadrature |
| 14 | +from FIAT.reference_element import TRIANGLE, ufc_simplex |
| 15 | + |
| 16 | + |
| 17 | +class QuadraticPowellSabin6DualSet(dual_set.DualSet): |
| 18 | + def __init__(self, ref_complex, degree=2): |
| 19 | + if degree != 2: |
| 20 | + raise ValueError("PS6 only defined for degree = 2") |
| 21 | + ref_el = ref_complex.get_parent() |
| 22 | + if ref_el.get_shape() != TRIANGLE: |
| 23 | + raise ValueError("PS6 only defined on triangles") |
| 24 | + top = ref_el.get_topology() |
| 25 | + verts = ref_el.get_vertices() |
| 26 | + sd = ref_el.get_spatial_dimension() |
| 27 | + entity_ids = {dim: {entity: [] for entity in sorted(top[dim])} for dim in sorted(top)} |
| 28 | + |
| 29 | + # get first order jet at each vertex |
| 30 | + alphas = polynomial_set.mis(sd, 1) |
| 31 | + nodes = [] |
| 32 | + |
| 33 | + for v in sorted(top[0]): |
| 34 | + pt = verts[v] |
| 35 | + cur = len(nodes) |
| 36 | + nodes.append(PointEvaluation(ref_el, pt)) |
| 37 | + nodes.extend(PointDerivative(ref_el, pt, alpha) for alpha in alphas) |
| 38 | + entity_ids[0][v].extend(range(cur, len(nodes))) |
| 39 | + |
| 40 | + super(QuadraticPowellSabin6DualSet, self).__init__( |
| 41 | + nodes, ref_el, entity_ids) |
| 42 | + |
| 43 | + |
| 44 | +class QuadraticPowellSabin6(finite_element.CiarletElement): |
| 45 | + """The PS6 macroelement is a C^1 quadratic macroelement defined |
| 46 | + on the 6-way Powell-Sabin split of a triangle. |
| 47 | + """ |
| 48 | + def __init__(self, ref_el, degree=2): |
| 49 | + if degree != 2: |
| 50 | + raise ValueError("PS6 only defined for degree = 2") |
| 51 | + ref_complex = macro.PowellSabinSplit(ref_el) |
| 52 | + dual = QuadraticPowellSabin6DualSet(ref_complex, degree) |
| 53 | + poly_set = macro.CkPolynomialSet(ref_complex, degree, order=1) |
| 54 | + |
| 55 | + super(QuadraticPowellSabin6, self).__init__(poly_set, dual, degree) |
| 56 | + |
| 57 | + |
| 58 | +class QuadraticPowellSabin12DualSet(dual_set.DualSet): |
| 59 | + def __init__(self, ref_complex, degree=2): |
| 60 | + if degree != 2: |
| 61 | + raise ValueError("PS12 only defined for degree = 2") |
| 62 | + ref_el = ref_complex.get_parent() |
| 63 | + if ref_el.get_shape() != TRIANGLE: |
| 64 | + raise ValueError("PS12 only defined on triangles") |
| 65 | + top = ref_el.get_topology() |
| 66 | + verts = ref_el.get_vertices() |
| 67 | + sd = ref_el.get_spatial_dimension() |
| 68 | + entity_ids = {dim: {entity: [] for entity in sorted(top[dim])} for dim in sorted(top)} |
| 69 | + |
| 70 | + # get first order jet at each vertex |
| 71 | + alphas = polynomial_set.mis(sd, 1) |
| 72 | + nodes = [] |
| 73 | + |
| 74 | + for v in sorted(top[0]): |
| 75 | + pt = verts[v] |
| 76 | + cur = len(nodes) |
| 77 | + nodes.append(PointEvaluation(ref_el, pt)) |
| 78 | + nodes.extend(PointDerivative(ref_el, pt, alpha) for alpha in alphas) |
| 79 | + entity_ids[0][v].extend(range(cur, len(nodes))) |
| 80 | + |
| 81 | + # integral moment of normal derivatives |
| 82 | + rline = macro.AlfeldSplit(ufc_simplex(1)) |
| 83 | + Q = create_quadrature(rline, degree-1) |
| 84 | + qpts = Q.get_points() |
| 85 | + |
| 86 | + x = 2.0*qpts - 1 |
| 87 | + phis = eval_jacobi_batch(1, 1, 0, x) |
| 88 | + for e in sorted(top[1]): |
| 89 | + cur = len(nodes) |
| 90 | + nodes.extend(IntegralMomentOfNormalDerivative(ref_el, e, Q, phi) for phi in phis) |
| 91 | + entity_ids[1][e].extend(range(cur, len(nodes))) |
| 92 | + |
| 93 | + super(QuadraticPowellSabin12DualSet, self).__init__( |
| 94 | + nodes, ref_el, entity_ids) |
| 95 | + |
| 96 | + |
| 97 | +class QuadraticPowellSabin12(finite_element.CiarletElement): |
| 98 | + """The PS12 macroelement is a C^1 quadratic macroelement defined |
| 99 | + on the 12-way Powell-Sabin split of a triangle. |
| 100 | + """ |
| 101 | + def __init__(self, ref_el, degree=2): |
| 102 | + if degree != 2: |
| 103 | + raise ValueError("PS12 only defined for degree = 2") |
| 104 | + ref_complex = macro.PowellSabin12Split(ref_el) |
| 105 | + dual = QuadraticPowellSabin12DualSet(ref_complex, degree) |
| 106 | + poly_set = macro.CkPolynomialSet(ref_complex, degree, order=1) |
| 107 | + |
| 108 | + super(QuadraticPowellSabin12, self).__init__(poly_set, dual, degree) |
0 commit comments