Source code for finat.ufl.enrichedelement
"""This module defines the UFL finite element classes."""
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
# This file was originally part of UFL (https://www.fenicsproject.org)
#
# SPDX-License-Identifier: LGPL-3.0-or-later
#
# Modified by Kristian B. Oelgaard
# Modified by Marie E. Rognes 2010, 2012
# Modified by Massimiliano Leoni, 2016
# Modified by Matthew Scroggs, 2023
from finat.ufl.finiteelementbase import FiniteElementBase
[docs]class EnrichedElementBase(FiniteElementBase):
"""The vector sum of several finite element spaces."""
def __init__(self, *elements):
"""Doc."""
self._elements = elements
cell = elements[0].cell
if not all(e.cell == cell for e in elements[1:]):
raise ValueError("Cell mismatch for sub elements of enriched element.")
if isinstance(elements[0].degree(), int):
degrees = {e.degree() for e in elements} - {None}
degree = max(degrees) if degrees else None
else:
degree = tuple(map(max, zip(*[e.degree() for e in elements])))
# We can allow the scheme not to be defined, but all defined
# should be equal
quad_schemes = [e.quadrature_scheme() for e in elements]
quad_schemes = [qs for qs in quad_schemes if qs is not None]
quad_scheme = quad_schemes[0] if quad_schemes else None
if not all(qs == quad_scheme for qs in quad_schemes):
raise ValueError("Quadrature scheme mismatch.")
value_shape = elements[0].value_shape
if not all(e.value_shape == value_shape for e in elements[1:]):
raise ValueError("Element value shape mismatch.")
reference_value_shape = elements[0].reference_value_shape
if not all(e.reference_value_shape == reference_value_shape for e in elements[1:]):
raise ValueError("Element reference value shape mismatch.")
# mapping = elements[0].mapping() # FIXME: This fails for a mixed subelement here.
# if not all(e.mapping() == mapping for e in elements[1:]):
# raise ValueError("Element mapping mismatch.")
# Get name of subclass: EnrichedElement or NodalEnrichedElement
class_name = self.__class__.__name__
# Initialize element data
FiniteElementBase.__init__(self, class_name, cell, degree,
quad_scheme, value_shape,
reference_value_shape)
[docs] def mapping(self):
"""Doc."""
return self._elements[0].mapping()
@property
def sobolev_space(self):
"""Return the underlying Sobolev space."""
elements = [e for e in self._elements]
if all(e.sobolev_space == elements[0].sobolev_space
for e in elements):
return elements[0].sobolev_space
else:
# Find smallest shared Sobolev space over all sub elements
spaces = [e.sobolev_space for e in elements]
superspaces = [{s} | set(s.parents) for s in spaces]
intersect = set.intersection(*superspaces)
for s in intersect.copy():
for parent in s.parents:
intersect.discard(parent)
sobolev_space, = intersect
return sobolev_space
[docs] def variant(self):
"""Doc."""
try:
variant, = {e.variant() for e in self._elements}
return variant
except ValueError:
return None
[docs] def reconstruct(self, **kwargs):
"""Doc."""
return type(self)(*[e.reconstruct(**kwargs) for e in self._elements])
@property
def embedded_subdegree(self):
"""Return embedded subdegree."""
if isinstance(self._degree, int):
return self._degree
else:
return min(e.embedded_subdegree for e in self._elements)
@property
def embedded_superdegree(self):
"""Return embedded superdegree."""
if isinstance(self._degree, int):
return self._degree
else:
return max(e.embedded_superdegree for e in self._elements)
[docs]class EnrichedElement(EnrichedElementBase):
r"""The vector sum of several finite element spaces.
.. math:: \\textrm{EnrichedElement}(V, Q) = \\{v + q | v \\in V, q \\in Q\\}.
Dual basis is a concatenation of subelements dual bases;
primal basis is a concatenation of subelements primal bases;
resulting element is not nodal even when subelements are.
Structured basis may be exploited in form compilers.
"""
[docs] def is_cellwise_constant(self):
"""Return whether the basis functions of this element is spatially constant over each cell."""
return all(e.is_cellwise_constant() for e in self._elements)
def __repr__(self):
"""Doc."""
return "EnrichedElement(" + ", ".join(repr(e) for e in self._elements) + ")"
def __str__(self):
"""Format as string for pretty printing."""
return "<%s>" % " + ".join(str(e) for e in self._elements)
[docs] def shortstr(self):
"""Format as string for pretty printing."""
return "<%s>" % " + ".join(e.shortstr() for e in self._elements)
[docs]class NodalEnrichedElement(EnrichedElementBase):
r"""The vector sum of several finite element spaces.
.. math:: \\textrm{EnrichedElement}(V, Q) = \\{v + q | v \\in V, q \\in Q\\}.
Primal basis is reorthogonalized to dual basis which is
a concatenation of subelements dual bases; resulting
element is nodal.
"""
[docs] def is_cellwise_constant(self):
"""Return whether the basis functions of this element is spatially constant over each cell."""
return False
def __repr__(self):
"""Doc."""
return "NodalEnrichedElement(" + ", ".join(repr(e) for e in self._elements) + ")"
def __str__(self):
"""Format as string for pretty printing."""
return "<Nodal enriched element(%s)>" % ", ".join(str(e) for e in self._elements)
[docs] def shortstr(self):
"""Format as string for pretty printing."""
return "NodalEnriched(%s)" % ", ".join(e.shortstr() for e in self._elements)