# -*- coding: utf-8 -*-
"This module defines the UFL finite element classes."
# Copyright (C) 2008-2016 Martin Sandve Alnæs
#
# This file is 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
from ufl.utils.sequences import product
from ufl.utils.dicts import EmptyDict
from ufl.log import error
from ufl.cell import AbstractCell, as_cell
[docs]class FiniteElementBase(object):
"Base class for all finite elements."
__slots__ = ("_family",
"_cell",
"_degree",
"_quad_scheme",
"_value_shape",
"_reference_value_shape",
"_repr",
"__weakref__")
# TODO: Not all these should be in the base class! In particular
# family, degree, and quad_scheme do not belong here.
def __init__(self, family, cell, degree, quad_scheme, value_shape,
reference_value_shape):
"Initialize basic finite element data."
if not isinstance(family, str):
error("Invalid family type.")
if not (degree is None or isinstance(degree, (int, tuple))):
error("Invalid degree type.")
if not isinstance(value_shape, tuple):
error("Invalid value_shape type.")
if not isinstance(reference_value_shape, tuple):
error("Invalid reference_value_shape type.")
if cell is not None:
cell = as_cell(cell)
if not isinstance(cell, AbstractCell):
error("Invalid cell type.")
self._family = family
self._cell = cell
self._degree = degree
self._value_shape = value_shape
self._reference_value_shape = reference_value_shape
self._quad_scheme = quad_scheme
def __repr__(self):
"""Format as string for evaluation as Python object.
NB! Assumes subclass has assigned its repr string to self._repr.
"""
return self._repr
def _ufl_hash_data_(self):
return repr(self)
def _ufl_signature_data_(self):
return repr(self)
def __hash__(self):
"Compute hash code for insertion in hashmaps."
return hash(self._ufl_hash_data_())
def __eq__(self, other):
"Compute element equality for insertion in hashmaps."
return type(self) == type(other) and self._ufl_hash_data_() == other._ufl_hash_data_()
def __ne__(self, other):
"Compute element inequality for insertion in hashmaps."
return not self.__eq__(other)
def __lt__(self, other):
"Compare elements by repr, to give a natural stable sorting."
return repr(self) < repr(other)
[docs] def family(self): # FIXME: Undefined for base?
"Return finite element family."
return self._family
[docs] def variant(self):
"""Return the variant used to initialise the element."""
return None
[docs] def degree(self, component=None):
"Return polynomial degree of finite element."
# FIXME: Consider embedded_degree concept for more accurate
# degree, see blueprint
return self._degree
[docs] def quadrature_scheme(self):
"Return quadrature scheme of finite element."
return self._quad_scheme
[docs] def mapping(self):
"Not implemented."
error("Missing implementation of mapping().")
[docs] def cell(self):
"Return cell of finite element."
return self._cell
[docs] def is_cellwise_constant(self, component=None):
"""Return whether the basis functions of this
element is spatially constant over each cell."""
return self.family() == "Real" or self.degree() == 0
[docs] def value_shape(self):
"Return the shape of the value space on the global domain."
return self._value_shape
[docs] def reference_value_shape(self):
"Return the shape of the value space on the reference cell."
return self._reference_value_shape
[docs] def value_size(self):
"Return the integer product of the value shape."
return product(self.value_shape())
[docs] def reference_value_size(self):
"Return the integer product of the reference value shape."
return product(self.reference_value_shape())
[docs] def symmetry(self): # FIXME: different approach
"""Return the symmetry dict, which is a mapping :math:`c_0 \\to c_1`
meaning that component :math:`c_0` is represented by component
:math:`c_1`.
A component is a tuple of one or more ints."""
return EmptyDict
def _check_component(self, i):
"Check that component index i is valid"
sh = self.value_shape()
r = len(sh)
if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))):
error(("Illegal component index '%s' (value rank %d)" +
"for element (value rank %d).") % (i, len(i), r))
def _check_reference_component(self, i):
"Check that reference component index i is valid."
sh = self.value_shape()
r = len(sh)
if not (len(i) == r and all(j < k for (j, k) in zip(i, sh))):
error(("Illegal component index '%s' (value rank %d)" +
"for element (value rank %d).") % (i, len(i), r))
[docs] def num_sub_elements(self):
"Return number of sub-elements."
return 0
[docs] def sub_elements(self):
"Return list of sub-elements."
return []
def __add__(self, other):
"Add two elements, creating an enriched element"
if not isinstance(other, FiniteElementBase):
error("Can't add element and %s." % other.__class__)
from ufl.finiteelement import EnrichedElement
return EnrichedElement(self, other)
def __mul__(self, other):
"Multiply two elements, creating a mixed element"
if not isinstance(other, FiniteElementBase):
error("Can't multiply element and %s." % other.__class__)
from ufl.finiteelement import MixedElement
return MixedElement(self, other)
def __getitem__(self, index):
"Restrict finite element to a subdomain, subcomponent or topology (cell)."
if index in ("facet", "interior"):
from ufl.finiteelement import RestrictedElement
return RestrictedElement(self, index)
return NotImplemented