Source code for ufl.cell

# -*- coding: utf-8 -*-
"Types for representing a cell."

# 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 Anders Logg, 2009.
# Modified by Kristian B. Oelgaard, 2009
# Modified by Marie E. Rognes 2012
# Modified by Andrew T. T. McRae, 2014
# Modified by Massimiliano Leoni, 2016

import numbers
import functools

import ufl.cell
from ufl.log import error
from ufl.core.ufl_type import attach_operators_from_hash_data


# Export list for ufl.classes
__all_classes__ = ["AbstractCell", "Cell", "TensorProductCell"]


# --- The most abstract cell class, base class for other cell types

[docs]class AbstractCell(object): """Representation of an abstract finite element cell with only the dimensions known. """ __slots__ = ("_topological_dimension", "_geometric_dimension") def __init__(self, topological_dimension, geometric_dimension): # Validate dimensions if not isinstance(geometric_dimension, numbers.Integral): error("Expecting integer geometric_dimension.") if not isinstance(topological_dimension, numbers.Integral): error("Expecting integer topological_dimension.") if topological_dimension > geometric_dimension: error("Topological dimension cannot be larger than geometric dimension.") # Store validated dimensions self._topological_dimension = topological_dimension self._geometric_dimension = geometric_dimension
[docs] def topological_dimension(self): "Return the dimension of the topology of this cell." return self._topological_dimension
[docs] def geometric_dimension(self): "Return the dimension of the space this cell is embedded in." return self._geometric_dimension
[docs] def is_simplex(self): "Return True if this is a simplex cell." raise NotImplementedError("Implement this to allow important checks and optimizations.")
[docs] def has_simplex_facets(self): "Return True if all the facets of this cell are simplex cells." raise NotImplementedError("Implement this to allow important checks and optimizations.")
def __lt__(self, other): "Define an arbitrarily chosen but fixed sort order for all cells." if not isinstance(other, AbstractCell): return NotImplemented # Sort by gdim first, tdim next, then whatever's left # depending on the subclass s = (self.geometric_dimension(), self.topological_dimension()) o = (other.geometric_dimension(), other.topological_dimension()) if s != o: return s < o return self._ufl_hash_data_() < other._ufl_hash_data_()
# --- Basic topological properties of known basic cells # Mapping from cell name to number of cell entities of each # topological dimension num_cell_entities = {"vertex": (1,), "interval": (2, 1), "triangle": (3, 3, 1), "quadrilateral": (4, 4, 1), "tetrahedron": (4, 6, 4, 1), "prism": (6, 9, 5, 1), "pyramid": (5, 8, 5, 1), "hexahedron": (8, 12, 6, 1)} # Mapping from cell name to topological dimension cellname2dim = dict((k, len(v) - 1) for k, v in num_cell_entities.items()) # --- Basic cell representation classes
[docs]@attach_operators_from_hash_data class Cell(AbstractCell): "Representation of a named finite element cell with known structure." __slots__ = ("_cellname",) def __init__(self, cellname, geometric_dimension=None): "Initialize basic cell description." self._cellname = cellname # The topological dimension is defined by the cell type, so # the cellname must be among the known ones, so we can find # the known dimension, unless we have a product cell, in which # the given dimension is used topological_dimension = len(num_cell_entities[cellname]) - 1 # The geometric dimension defaults to equal the topological # dimension unless overridden for embedded cells if geometric_dimension is None: geometric_dimension = topological_dimension # Initialize and validate dimensions AbstractCell.__init__(self, topological_dimension, geometric_dimension) # --- Overrides of AbstractCell methods ---
[docs] def reconstruct(self, geometric_dimension=None): if geometric_dimension is None: geometric_dimension = self._geometric_dimension return Cell(self._cellname, geometric_dimension=geometric_dimension)
[docs] def is_simplex(self): " Return True if this is a simplex cell." return self.num_vertices() == self.topological_dimension() + 1
[docs] def has_simplex_facets(self): "Return True if all the facets of this cell are simplex cells." return self.is_simplex() or self.cellname() == "quadrilateral"
# --- Specific cell properties ---
[docs] def cellname(self): "Return the cellname of the cell." return self._cellname
[docs] def num_vertices(self): "The number of cell vertices." return num_cell_entities[self.cellname()][0]
[docs] def num_edges(self): "The number of cell edges." return num_cell_entities[self.cellname()][1]
[docs] def num_facets(self): "The number of cell facets." tdim = self.topological_dimension() return num_cell_entities[self.cellname()][tdim - 1]
# --- Facet properties ---
[docs] def facet_types(self): "A tuple of ufl.Cell representing the facets of self." # TODO Move outside method? facet_type_names = {"interval": ("vertex",), "triangle": ("interval",), "quadrilateral": ("interval",), "tetrahedron": ("triangle",), "hexahedron": ("quadrilateral",), "prism": ("triangle", "quadrilateral")} return tuple(ufl.Cell(facet_name, self.geometric_dimension()) for facet_name in facet_type_names[self.cellname()])
# --- Special functions for proper object behaviour --- def __str__(self): gdim = self.geometric_dimension() tdim = self.topological_dimension() s = self.cellname() if gdim > tdim: s += "%dD" % gdim return s def __repr__(self): # For standard cells, return name of builtin cell object if # possible. This reduces the size of the repr strings for # domains, elements, etc. as well gdim = self.geometric_dimension() tdim = self.topological_dimension() name = self.cellname() if gdim == tdim and name in cellname2dim: r = name else: r = "Cell(%s, %s)" % (repr(name), repr(gdim)) return r def _ufl_hash_data_(self): return (self._geometric_dimension, self._topological_dimension, self._cellname)
[docs]@attach_operators_from_hash_data class TensorProductCell(AbstractCell): __slots__ = ("_cells",) def __init__(self, *cells, **kwargs): keywords = list(kwargs.keys()) if keywords and keywords != ["geometric_dimension"]: raise ValueError( "TensorProductCell got an unexpected keyword argument '%s'" % keywords[0]) self._cells = tuple(as_cell(cell) for cell in cells) tdim = sum([cell.topological_dimension() for cell in self._cells]) if kwargs: gdim = kwargs["geometric_dimension"] else: gdim = sum([cell.geometric_dimension() for cell in self._cells]) AbstractCell.__init__(self, tdim, gdim)
[docs] def cellname(self): "Return the cellname of the cell." return " * ".join([cell._cellname for cell in self._cells])
[docs] def reconstruct(self, geometric_dimension=None): if geometric_dimension is None: geometric_dimension = self._geometric_dimension return TensorProductCell(*(self._cells), geometric_dimension=geometric_dimension)
[docs] def is_simplex(self): "Return True if this is a simplex cell." if len(self._cells) == 1: return self._cells[0].is_simplex() return False
[docs] def has_simplex_facets(self): "Return True if all the facets of this cell are simplex cells." if len(self._cells) == 1: return self._cells[0].has_simplex_facets() return False
[docs] def num_vertices(self): "The number of cell vertices." return functools.reduce(lambda x, y: x * y, [c.num_vertices() for c in self._cells])
[docs] def num_edges(self): "The number of cell edges." error("Not defined for TensorProductCell.")
[docs] def num_facets(self): "The number of cell facets." return sum(c.num_facets() for c in self._cells if c.topological_dimension() > 0)
[docs] def sub_cells(self): "Return list of cell factors." return self._cells
def __str__(self): gdim = self.geometric_dimension() tdim = self.topological_dimension() reprs = ", ".join(repr(c) for c in self._cells) if gdim == tdim: gdimstr = "" else: gdimstr = ", geometric_dimension=%d" % gdim r = "TensorProductCell(%s%s)" % (reprs, gdimstr) return r def __repr__(self): return str(self) def _ufl_hash_data_(self): return tuple(c._ufl_hash_data_() for c in self._cells) + (self._geometric_dimension,)
# --- Utility conversion functions # Mapping from topological dimension to reference cell name for # simplices _simplex_dim2cellname = {0: "vertex", 1: "interval", 2: "triangle", 3: "tetrahedron"} # Mapping from topological dimension to reference cell name for # hypercubes _hypercube_dim2cellname = {0: "vertex", 1: "interval", 2: "quadrilateral", 3: "hexahedron"}
[docs]def simplex(topological_dimension, geometric_dimension=None): "Return a simplex cell of given dimension." return Cell(_simplex_dim2cellname[topological_dimension], geometric_dimension)
[docs]def hypercube(topological_dimension, geometric_dimension=None): "Return a hypercube cell of given dimension." return Cell(_hypercube_dim2cellname[topological_dimension], geometric_dimension)
[docs]def as_cell(cell): """Convert any valid object to a Cell or return cell if it is already a Cell. Allows an already valid cell, a known cellname string, or a tuple of cells for a product cell. """ if isinstance(cell, AbstractCell): return cell elif isinstance(cell, str): return Cell(cell) elif isinstance(cell, tuple): return TensorProductCell(cell) else: error("Invalid cell %s." % cell)