def __init__(self, mesh, layers): """Build an extruded mesh topology from an input mesh topology :arg mesh: the unstructured base mesh topology :arg layers: number of extruded cell layers in the "vertical" direction. """ from firedrake.citations import Citations Citations().register("McRae2014") # A cache of shared function space data on this mesh self._shared_data_cache = defaultdict(dict) mesh.init() self._base_mesh = mesh if layers < 1: raise RuntimeError("Must have at least one layer of extruded cells (not %d)" % layers) # All internal logic works with layers of base mesh (not layers of cells) self._layers = layers + 1 self._ufl_cell = ufl.TensorProductCell(mesh.ufl_cell(), ufl.interval) # TODO: These attributes are copied so that FunctionSpaceBase can # access them directly. Eventually we would want a better refactoring # of responsibilities between mesh and function space. self._plex = mesh._plex self._plex_renumbering = mesh._plex_renumbering self._entity_classes = mesh._entity_classes
def _create_fiat_element(ufl_element): """Create FIAT element corresponding to given finite element.""" # Get element data family = ufl_element.family() cell = ufl_element.cell() cellname = cell.cellname() degree = ufl_element.degree() # Check that FFC supports this element if family not in supported_families: raise RuntimeError("This element family (%s) is not supported by FFC." % family) # Create FIAT cell fiat_cell = reference_cell(cellname) # Handle the space of the constant if family == "Real": element = _create_fiat_element(ufl.FiniteElement("DG", cell, 0)) element.__class__ = type('SpaceOfReals', (type(element), SpaceOfReals), {}) return element if cellname == "quadrilateral": # Handle quadrilateral case by reconstructing the element with # cell TensorProductCell (interval x interval) quadrilateral_tpc = ufl.TensorProductCell(ufl.Cell("interval"), ufl.Cell("interval")) return FlattenedDimensions( _create_fiat_element(ufl_element.reconstruct(cell=quadrilateral_tpc))) elif cellname == "hexahedron": # Handle hexahedron case by reconstructing the element with cell # TensorProductCell (quadrilateral x interval). This creates # TensorProductElement(TensorProductElement(interval, interval), # interval) Therefore dof entities consists of nested tuples, # example: ((0, 1), 1) hexahedron_tpc = ufl.TensorProductCell(ufl.Cell("quadrilateral"), ufl.Cell("interval")) return FlattenedDimensions( _create_fiat_element(ufl_element.reconstruct(cell=hexahedron_tpc))) # FIXME: AL: Should this really be here? # Handle QuadratureElement if family == "Quadrature": # Compute number of points per axis from the degree of the element scheme = ufl_element.quadrature_scheme() assert degree is not None assert scheme is not None # Create quadrature (only interested in points) # TODO: KBO: What should we do about quadrature functions that live on ds, dS? # Get cell and facet names. points, weights = create_quadrature(cellname, degree, scheme) # Make element element = QuadratureElement(fiat_cell, points) else: # Check if finite element family is supported by FIAT if family not in FIAT.supported_elements: raise RuntimeError("Sorry, finite element of type \"%s\" are not supported by FIAT.", family) ElementClass = FIAT.supported_elements[family] # Create tensor product FIAT finite element if isinstance(ufl_element, ufl.TensorProductElement): A = create_element(ufl_element.sub_elements()[0]) B = create_element(ufl_element.sub_elements()[1]) element = ElementClass(A, B) # Create normal FIAT finite element else: if degree is None: element = ElementClass(fiat_cell) else: element = ElementClass(fiat_cell, degree) if element.value_shape() != ufl_element.reference_value_shape(): # Consistency check between UFL and FIAT elements. raise RuntimeError("Something went wrong in the construction of FIAT element from UFL element." + "Shapes are {} and {}.".format(element.value_shape(), ufl_element.reference_value_shape())) return element
elements = [] def rec(eles): for ele in eles: if isinstance(ele, ufl.MixedElement): rec(ele.sub_elements()) else: elements.append(ele) rec(element.sub_elements()) fiat_elements = map( partial(create_element, vector_is_mixed=vector_is_mixed), elements) return FIAT.MixedElement(fiat_elements) quad_tpc = ufl.TensorProductCell(ufl.Cell("interval"), ufl.Cell("interval")) _cache = weakref.WeakKeyDictionary() def create_element(element, vector_is_mixed=True): """Create a FIAT element (suitable for tabulating with) given a UFL element. :arg element: The UFL element to create a FIAT element from. :arg vector_is_mixed: indicate whether VectorElement (or TensorElement) should be treated as a MixedElement. Maybe useful if you want a FIAT element that tells you how many "nodes" the finite element has. """ try: cache = _cache[element]
finat_elem, deps = _create_element(element._element, **kwargs) return finat.HCurlElement(finat_elem), deps @convert.register(ufl.RestrictedElement) def convert_restrictedelement(element, **kwargs): # Fall back on FIAT return fiat_compat(element), set() @convert.register(ufl.NodalEnrichedElement) def convert_nodalenrichedelement(element, **kwargs): return fiat_compat(element), set() hexahedron_tpc = ufl.TensorProductCell(ufl.quadrilateral, ufl.interval) quadrilateral_tpc = ufl.TensorProductCell(ufl.interval, ufl.interval) _cache = weakref.WeakKeyDictionary() def create_element(ufl_element, shape_innermost=True): """Create a FInAT element (suitable for tabulating with) given a UFL element. :arg ufl_element: The UFL element to create a FInAT element from. :arg shape_innermost: Vector/tensor indices come after basis function indices """ finat_element, deps = _create_element(ufl_element, shape_innermost=shape_innermost) return finat_element
VTK_INTERVAL = 3 VTK_TRIANGLE = 5 VTK_QUADRILATERAL = 9 VTK_TETRAHEDRON = 10 VTK_HEXAHEDRON = 12 VTK_WEDGE = 13 cells = { ufl.Cell("interval"): VTK_INTERVAL, ufl.Cell("triangle"): VTK_TRIANGLE, ufl.Cell("quadrilateral"): VTK_QUADRILATERAL, ufl.TensorProductCell(ufl.Cell("interval"), ufl.Cell("interval")): VTK_QUADRILATERAL, ufl.Cell("tetrahedron"): VTK_TETRAHEDRON, ufl.TensorProductCell(ufl.Cell("triangle"), ufl.Cell("interval")): VTK_WEDGE, ufl.TensorProductCell(ufl.Cell("quadrilateral"), ufl.Cell("interval")): VTK_HEXAHEDRON } OFunction = collections.namedtuple("OFunction", ["array", "name", "function"]) def is_cg(V): """Is the provided space continuous?
VTK_INTERVAL = 3 VTK_TRIANGLE = 5 VTK_QUADRILATERAL = 9 VTK_TETRAHEDRON = 10 VTK_HEXAHEDRON = 12 VTK_WEDGE = 13 # Lagrange VTK cells: VTK_LAGRANGE_CURVE = 68 VTK_LAGRANGE_TRIANGLE = 69 VTK_LAGRANGE_QUADRILATERAL = 70 VTK_LAGRANGE_TETRAHEDRON = 71 VTK_LAGRANGE_HEXAHEDRON = 72 VTK_LAGRANGE_WEDGE = 73 ufl_quad = ufl.TensorProductCell(ufl.Cell("interval"), ufl.Cell("interval")) ufl_wedge = ufl.TensorProductCell(ufl.Cell("triangle"), ufl.Cell("interval")) ufl_hex = ufl.TensorProductCell(ufl.Cell("quadrilateral"), ufl.Cell("interval")) cells = { (ufl.Cell("interval"), False): VTK_INTERVAL, (ufl.Cell("interval"), True): VTK_LAGRANGE_CURVE, (ufl.Cell("triangle"), False): VTK_TRIANGLE, (ufl.Cell("triangle"), True): VTK_LAGRANGE_TRIANGLE, (ufl.Cell("quadrilateral"), False): VTK_QUADRILATERAL, (ufl.Cell("quadrilateral"), True): VTK_LAGRANGE_QUADRILATERAL, (ufl_quad, True): VTK_LAGRANGE_QUADRILATERAL, (ufl_quad, False): VTK_QUADRILATERAL, (ufl.Cell("tetrahedron"), False): VTK_TETRAHEDRON, (ufl.Cell("tetrahedron"), True): VTK_LAGRANGE_TETRAHEDRON,
logger = logging.getLogger("ffcx") # Element families supported by FFCX supported_families = ("Brezzi-Douglas-Marini", "Brezzi-Douglas-Fortin-Marini", "Crouzeix-Raviart", "Discontinuous Lagrange", "Discontinuous Raviart-Thomas", "HDiv Trace", "Lagrange", "Lobatto", "Nedelec 1st kind H(curl)", "Nedelec 2nd kind H(curl)", "Radau", "Raviart-Thomas", "Real", "Bubble", "Quadrature", "Regge", "Hellan-Herrmann-Johnson", "Q", "DQ", "TensorProductElement", "Gauss-Lobatto-Legendre") # Cache for computed elements _cache = {} _tpc_quadrilateral = ufl.TensorProductCell(ufl.interval, ufl.interval) _tpc_hexahedron = ufl.TensorProductCell(ufl.quadrilateral, ufl.interval) class SpaceOfReals(object): """Constant over the entire domain, rather than just cellwise.""" def reference_cell_vertices(cellname): """Return dict of coordinates of reference cell vertices for this 'cellname'.""" return FIAT.ufc_cell(cellname).get_vertices() @functools.singledispatch def _create_element(element): raise ValueError("Element type is not supported.")
__all__ = ("File", ) VTK_INTERVAL = 3 VTK_TRIANGLE = 5 VTK_QUADRILATERAL = 9 VTK_TETRAHEDRON = 10 VTK_HEXAHEDRON = 12 VTK_WEDGE = 13 cells = { ufl.Cell("interval"): VTK_INTERVAL, ufl.Cell("triangle"): VTK_TRIANGLE, ufl.Cell("quadrilateral"): VTK_QUADRILATERAL, ufl.TensorProductCell(ufl.Cell("interval"), ufl.Cell("interval")): VTK_QUADRILATERAL, ufl.Cell("tetrahedron"): VTK_TETRAHEDRON, ufl.TensorProductCell(ufl.Cell("triangle"), ufl.Cell("interval")): VTK_WEDGE, ufl.TensorProductCell(ufl.Cell("quadrilateral"), ufl.Cell("interval")): VTK_HEXAHEDRON } OFunction = collections.namedtuple("OFunction", ["array", "name", "function"]) def is_cg(V): """Is the provided space continuous? :arg V: A FunctionSpace.
return finat.HDivElement(finat_elem), deps @convert.register(ufl.HCurlElement) def convert_hcurlelement(element, **kwargs): finat_elem, deps = _create_element(element._element, **kwargs) return finat.HCurlElement(finat_elem), deps @convert.register(ufl.RestrictedElement) def convert_restrictedelement(element, **kwargs): # Fall back on FIAT return fiat_compat(element), set() quad_tpc = ufl.TensorProductCell(ufl.interval, ufl.interval) _cache = weakref.WeakKeyDictionary() def create_element(ufl_element, shape_innermost=True): """Create a FInAT element (suitable for tabulating with) given a UFL element. :arg ufl_element: The UFL element to create a FInAT element from. :arg shape_innermost: Vector/tensor indices come after basis function indices """ finat_element, deps = _create_element(ufl_element, shape_innermost=shape_innermost) return finat_element def _create_element(ufl_element, **kwargs):
def init_cell_orientations(self, expr): """Compute and initialise :attr:`cell_orientations` relative to a specified orientation. :arg expr: an :class:`.Expression` evaluated to produce a reference normal direction. """ import firedrake.function as function import firedrake.functionspace as functionspace if expr.value_shape()[0] != 3: raise NotImplementedError('Only implemented for 3-vectors') if self.ufl_cell() not in (ufl.Cell('triangle', 3), ufl.Cell("quadrilateral", 3), ufl.TensorProductCell(ufl.Cell('interval'), ufl.Cell('interval'), geometric_dimension=3)): raise NotImplementedError('Only implemented for triangles and quadrilaterals embedded in 3d') if hasattr(self.topology, '_cell_orientations'): raise RuntimeError("init_cell_orientations already called, did you mean to do so again?") v0 = lambda x: ast.Symbol("v0", (x,)) v1 = lambda x: ast.Symbol("v1", (x,)) n = lambda x: ast.Symbol("n", (x,)) x = lambda x: ast.Symbol("x", (x,)) coords = lambda x, y: ast.Symbol("coords", (x, y)) body = [] body += [ast.Decl("double", v(3)) for v in [v0, v1, n, x]] body.append(ast.Decl("double", "dot")) body.append(ast.Assign("dot", 0.0)) body.append(ast.Decl("int", "i")) # if triangle, use v0 = x1 - x0, v1 = x2 - x0 # otherwise, for the various quads, use v0 = x2 - x0, v1 = x1 - x0 # recall reference element ordering: # triangle: 2 quad: 1 3 # 0 1 0 2 if self.ufl_cell() == ufl.Cell('triangle', 3): body.append(ast.For(ast.Assign("i", 0), ast.Less("i", 3), ast.Incr("i", 1), [ast.Assign(v0("i"), ast.Sub(coords(1, "i"), coords(0, "i"))), ast.Assign(v1("i"), ast.Sub(coords(2, "i"), coords(0, "i"))), ast.Assign(x("i"), 0.0)])) else: body.append(ast.For(ast.Assign("i", 0), ast.Less("i", 3), ast.Incr("i", 1), [ast.Assign(v0("i"), ast.Sub(coords(2, "i"), coords(0, "i"))), ast.Assign(v1("i"), ast.Sub(coords(1, "i"), coords(0, "i"))), ast.Assign(x("i"), 0.0)])) # n = v0 x v1 body.append(ast.Assign(n(0), ast.Sub(ast.Prod(v0(1), v1(2)), ast.Prod(v0(2), v1(1))))) body.append(ast.Assign(n(1), ast.Sub(ast.Prod(v0(2), v1(0)), ast.Prod(v0(0), v1(2))))) body.append(ast.Assign(n(2), ast.Sub(ast.Prod(v0(0), v1(1)), ast.Prod(v0(1), v1(0))))) body.append(ast.For(ast.Assign("i", 0), ast.Less("i", 3), ast.Incr("i", 1), [ast.Incr(x(j), coords("i", j)) for j in range(3)])) body.extend([ast.FlatBlock("dot += (%(x)s) * n[%(i)d];\n" % {"x": x_, "i": i}) for i, x_ in enumerate(expr.code)]) body.append(ast.Assign("orientation[0][0]", ast.Ternary(ast.Less("dot", 0), 1, 0))) kernel = op2.Kernel(ast.FunDecl("void", "cell_orientations", [ast.Decl("int**", "orientation"), ast.Decl("double**", "coords")], ast.Block(body)), "cell_orientations") # Build the cell orientations as a DG0 field (so that we can # pass it in for facet integrals and the like) fs = functionspace.FunctionSpace(self, 'DG', 0) cell_orientations = function.Function(fs, name="cell_orientations", dtype=np.int32) op2.par_loop(kernel, self.cell_set, cell_orientations.dat(op2.WRITE, cell_orientations.cell_node_map()), self.coordinates.dat(op2.READ, self.coordinates.cell_node_map())) self.topology._cell_orientations = cell_orientations