Source code for finat.hct

import FIAT
import numpy
from gem import ListTensor, Literal, partial_indexed

from finat.argyris import _edge_transform
from finat.fiat_elements import ScalarFiatElement
from finat.physically_mapped import Citations, PhysicallyMappedElement
from copy import deepcopy


[docs]class HsiehCloughTocher(PhysicallyMappedElement, ScalarFiatElement): def __init__(self, cell, degree=3, avg=False): if degree < 3: raise ValueError("HCT only defined for degree >= 3") if Citations is not None: Citations().register("Clough1965") if degree > 3: Citations().register("Groselj2022") self.avg = avg super().__init__(FIAT.HsiehCloughTocher(cell, degree))
[docs] def basis_transformation(self, coordinate_mapping): # Jacobians at cell center J = coordinate_mapping.jacobian_at([1/3, 1/3]) ndof = self.space_dimension() V = numpy.eye(ndof, dtype=object) for multiindex in numpy.ndindex(V.shape): V[multiindex] = Literal(V[multiindex]) sd = self.cell.get_dimension() top = self.cell.get_topology() voffset = 1 + sd for v in sorted(top[0]): s = voffset * v for i in range(sd): for j in range(sd): V[s+1+i, s+1+j] = J[j, i] vorder = 1 eorder = self.degree - 3 _edge_transform(V, vorder, eorder, self.cell, coordinate_mapping, avg=self.avg) # Patch up conditioning h = coordinate_mapping.cell_size() for v in sorted(top[0]): for k in range(sd): V[:, voffset*v+1+k] /= h[v] return ListTensor(V.T)
[docs]class ReducedHsiehCloughTocher(PhysicallyMappedElement, ScalarFiatElement): def __init__(self, cell, degree=3): if degree != 3: raise ValueError("Degree must be 3 for reduced HCT element") if Citations is not None: Citations().register("Clough1965") super().__init__(FIAT.HsiehCloughTocher(cell, reduced=True)) reduced_dofs = deepcopy(self._element.entity_dofs()) sd = cell.get_spatial_dimension() for entity in reduced_dofs[sd-1]: reduced_dofs[sd-1][entity] = [] self._entity_dofs = reduced_dofs
[docs] def basis_transformation(self, coordinate_mapping): # Jacobian at barycenter J = coordinate_mapping.jacobian_at([1/3, 1/3]) numbf = self._element.space_dimension() ndof = self.space_dimension() # rectangular to toss out the constraint dofs V = numpy.eye(numbf, ndof, dtype=object) for multiindex in numpy.ndindex(V.shape): V[multiindex] = Literal(V[multiindex]) sd = self.cell.get_spatial_dimension() top = self.cell.get_topology() voffset = sd + 1 for v in sorted(top[0]): s = voffset * v for i in range(sd): for j in range(sd): V[s+1+i, s+1+j] = J[j, i] rns = coordinate_mapping.reference_normals() pts = coordinate_mapping.physical_tangents() pel = coordinate_mapping.physical_edge_lengths() for e in sorted(top[1]): s = len(top[0]) * voffset + e v0id, v1id = (v * voffset for v in top[1][e]) nhat = partial_indexed(rns, (e, )) t = partial_indexed(pts, (e, )) Bnt = (J @ nhat) @ t V[s, v0id] = Literal(1/5) * Bnt / pel[e] V[s, v1id] = Literal(-1) * V[s, v0id] R = Literal(1/10) * Bnt * t V[s, v0id + 1] = R[0] V[s, v0id + 2] = R[1] V[s, v1id + 1] = R[0] V[s, v1id + 2] = R[1] # Patch up conditioning h = coordinate_mapping.cell_size() for v in sorted(top[0]): s = voffset * v for k in range(sd): V[:, s+1+k] /= h[v] return ListTensor(V.T)
[docs] def entity_dofs(self): return self._entity_dofs
@property def index_shape(self): return (9,)
[docs] def space_dimension(self): return 9