Source code for finat.ufl.tensorproductelement
"""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 itertools import chain
from ufl.cell import TensorProductCell, as_cell
from finat.ufl.finiteelementbase import FiniteElementBase
from ufl.sobolevspace import DirectionalSobolevSpace
[docs]class TensorProductElement(FiniteElementBase):
r"""The tensor product of :math:`d` element spaces.
.. math:: V = V_1 \otimes V_2 \otimes ... \otimes V_d
Given bases :math:`\{\phi_{j_i}\}` of the spaces :math:`V_i` for :math:`i = 1, ...., d`,
:math:`\{ \phi_{j_1} \otimes \phi_{j_2} \otimes \cdots \otimes \phi_{j_d}
\}` forms a basis for :math:`V`.
"""
__slots__ = ("_sub_elements", "_cell")
def __init__(self, *elements, **kwargs):
"""Create TensorProductElement from a given list of elements."""
if not elements:
raise ValueError("Cannot create TensorProductElement from empty list.")
keywords = list(kwargs.keys())
if keywords and keywords != ["cell"]:
raise ValueError("TensorProductElement got an unexpected keyword argument '%s'" % keywords[0])
cell = kwargs.get("cell")
family = "TensorProductElement"
if cell is None:
# Define cell as the product of each elements cell
cell = TensorProductCell(*[e.cell for e in elements])
else:
cell = as_cell(cell)
# Define polynomial degree as a tuple of sub-degrees
degree = tuple(e.degree() for e in elements)
# No quadrature scheme defined
quad_scheme = None
# match FIAT implementation
value_shape = tuple(chain(*[e.value_shape for e in elements]))
reference_value_shape = tuple(chain(*[e.reference_value_shape for e in elements]))
if len(value_shape) > 1:
raise ValueError("Product of vector-valued elements not supported")
if len(reference_value_shape) > 1:
raise ValueError("Product of vector-valued elements not supported")
FiniteElementBase.__init__(self, family, cell, degree,
quad_scheme, value_shape,
reference_value_shape)
self._sub_elements = elements
self._cell = cell
def __repr__(self):
"""Doc."""
return "TensorProductElement(" + ", ".join(repr(e) for e in self._sub_elements) + f", cell={repr(self._cell)})"
[docs] def mapping(self):
"""Doc."""
if all(e.mapping() == "identity" for e in self._sub_elements):
return "identity"
elif all(e.mapping() == "L2 Piola" for e in self._sub_elements):
return "L2 Piola"
else:
return "undefined"
@property
def sobolev_space(self):
"""Return the underlying Sobolev space of the TensorProductElement."""
elements = self._sub_elements
if all(e.sobolev_space == elements[0].sobolev_space
for e in elements):
return elements[0].sobolev_space
else:
# Generate a DirectionalSobolevSpace which contains
# continuity information parametrized by spatial index
orders = []
for e in elements:
e_dim = e.cell.geometric_dimension()
e_order = (e.sobolev_space._order,) * e_dim
orders.extend(e_order)
return DirectionalSobolevSpace(orders)
@property
def num_sub_elements(self):
"""Return number of subelements."""
return len(self._sub_elements)
@property
def sub_elements(self):
"""Return subelements (factors)."""
return self._sub_elements
[docs] def reconstruct(self, **kwargs):
"""Doc."""
cell = kwargs.pop("cell", self.cell)
return TensorProductElement(*[e.reconstruct(**kwargs) for e in self.sub_elements], cell=cell)
[docs] def variant(self):
"""Doc."""
try:
variant, = {e.variant() for e in self.sub_elements}
return variant
except ValueError:
return None
def __str__(self):
"""Pretty-print."""
return "TensorProductElement(%s, cell=%s)" \
% (', '.join([str(e) for e in self._sub_elements]), str(self._cell))
[docs] def shortstr(self):
"""Short pretty-print."""
return "TensorProductElement(%s, cell=%s)" \
% (', '.join([e.shortstr() for e in self._sub_elements]), str(self._cell))
@property
def embedded_superdegree(self):
"""Doc."""
return sum(self.degree())
@property
def embedded_subdegree(self):
"""Doc."""
return min(self.degree())