Beispiel #1
0
    def __init__(self, mesh, layers, layer_height=None, extrusion_type='uniform', kernel=None, gdim=None):
        # A cache of function spaces that have been built on this mesh
        import firedrake.function as function
        import firedrake.functionspace as functionspace

        self._cache = {}
        mesh.init()
        self._old_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.parent = mesh.parent
        self.uid = mesh.uid
        self.name = mesh.name
        self._plex = mesh._plex
        self._plex_renumbering = mesh._plex_renumbering
        self._cell_numbering = mesh._cell_numbering
        self._entity_classes = mesh._entity_classes

        interior_f = self._old_mesh.interior_facets
        self._interior_facets = _Facets(self, interior_f.classes,
                                        "interior",
                                        interior_f.facet_cell,
                                        interior_f.local_facet_number)
        exterior_f = self._old_mesh.exterior_facets
        self._exterior_facets = _Facets(self, exterior_f.classes,
                                        "exterior",
                                        exterior_f.facet_cell,
                                        exterior_f.local_facet_number,
                                        exterior_f.markers,
                                        unique_markers=exterior_f.unique_markers)

        self.ufl_cell_element = ufl.FiniteElement("Lagrange",
                                                  domain=mesh.ufl_cell(),
                                                  degree=1)
        self.ufl_interval_element = ufl.FiniteElement("Lagrange",
                                                      domain=ufl.Cell("interval", 1),
                                                      degree=1)

        self.fiat_base_element = fiat_utils.fiat_from_ufl_element(self.ufl_cell_element)
        self.fiat_vert_element = fiat_utils.fiat_from_ufl_element(self.ufl_interval_element)

        fiat_element = FIAT.tensor_finite_element.TensorFiniteElement(self.fiat_base_element, self.fiat_vert_element)

        if extrusion_type == "uniform":
            # *must* add a new dimension
            self._ufl_cell = ufl.OuterProductCell(mesh.ufl_cell(), ufl.Cell("interval", 1), gdim=mesh.ufl_cell().geometric_dimension() + 1)

        elif extrusion_type in ("radial", "radial_hedgehog"):
            # do not allow radial extrusion if tdim = gdim
            if mesh.ufl_cell().geometric_dimension() == mesh.ufl_cell().topological_dimension():
                raise RuntimeError("Cannot radially-extrude a mesh with equal geometric and topological dimension")
            # otherwise, all is fine, so make cell
            self._ufl_cell = ufl.OuterProductCell(mesh.ufl_cell(), ufl.Cell("interval", 1))

        else:
            # check for kernel
            if kernel is None:
                raise RuntimeError("If the custom extrusion_type is used, a kernel must be provided")
            # otherwise, use the gdim that was passed in
            if gdim is None:
                raise RuntimeError("The geometric dimension of the mesh must be specified if a custom extrusion kernel is used")
            self._ufl_cell = ufl.OuterProductCell(mesh.ufl_cell(), ufl.Cell("interval", 1), gdim=gdim)

        self._ufl_domain = ufl.Domain(self.ufl_cell(), data=self)
        flat_temp = fiat_utils.FlattenedElement(fiat_element)

        # Calculated dofs_per_column from flattened_element and layers.
        # The mirrored elements have to be counted only once.
        # Then multiply by layers and layers - 1 accordingly.
        self.dofs_per_column = eutils.compute_extruded_dofs(fiat_element, flat_temp.entity_dofs(),
                                                            layers)

        # Compute Coordinates of the extruded mesh
        if layer_height is None:
            # Default to unit
            layer_height = 1.0 / layers

        if extrusion_type == 'radial_hedgehog':
            hfamily = "DG"
        else:
            hfamily = mesh.coordinates.element().family()
        hdegree = mesh.coordinates.element().degree()

        self._coordinate_fs = functionspace.VectorFunctionSpace(self, hfamily,
                                                                hdegree,
                                                                vfamily="CG",
                                                                vdegree=1)

        self.coordinates = function.Function(self._coordinate_fs)
        self._ufl_domain = ufl.Domain(self.coordinates)
        eutils.make_extruded_coords(self, layer_height, extrusion_type=extrusion_type,
                                    kernel=kernel)
        if extrusion_type == "radial_hedgehog":
            fs = functionspace.VectorFunctionSpace(self, "CG", hdegree, vfamily="CG", vdegree=1)
            self.radial_coordinates = function.Function(fs)
            eutils.make_extruded_coords(self, layer_height, extrusion_type="radial",
                                        output_coords=self.radial_coordinates)

        # Build a new ufl element for this function space with the
        # correct domain.  This is necessary since this function space
        # is in the cache and will be picked up by later
        # VectorFunctionSpace construction.
        self._coordinate_fs._ufl_element = self._coordinate_fs.ufl_element().reconstruct(domain=self.ufl_domain())
        # HACK alert!
        # Replace coordinate Function by one that has a real domain on it (but don't copy values)
        self.coordinates = function.Function(self._coordinate_fs, val=self.coordinates.dat)
        # Add subdomain_data to the measure objects we store with
        # the mesh.  These are weakrefs for consistency with the
        # "global" measure objects
        self._dx = ufl.Measure('cell', subdomain_data=weakref.ref(self.coordinates))
        self._ds = ufl.Measure('exterior_facet', subdomain_data=weakref.ref(self.coordinates))
        self._dS = ufl.Measure('interior_facet', subdomain_data=weakref.ref(self.coordinates))
        self._ds_t = ufl.Measure('exterior_facet_top', subdomain_data=weakref.ref(self.coordinates))
        self._ds_b = ufl.Measure('exterior_facet_bottom', subdomain_data=weakref.ref(self.coordinates))
        self._ds_v = ufl.Measure('exterior_facet_vert', subdomain_data=weakref.ref(self.coordinates))
        self._dS_h = ufl.Measure('interior_facet_horiz', subdomain_data=weakref.ref(self.coordinates))
        self._dS_v = ufl.Measure('interior_facet_vert', subdomain_data=weakref.ref(self.coordinates))
        # Set the subdomain_data on all the default measures to this
        # coordinate field.  We don't set the domain on the measure
        # since this causes an uncollectable reference in the global
        # space (dx is global).  Furthermore, it's never used anyway.
        for measure in [ufl.ds, ufl.dS, ufl.dx, ufl.ds_t, ufl.ds_b, ufl.ds_v, ufl.dS_h, ufl.dS_v]:
            measure._subdomain_data = weakref.ref(self.coordinates)
Beispiel #2
0
def ExtrudedMesh(mesh, layers, layer_height=None, extrusion_type='uniform', kernel=None, gdim=None):
    """Build an extruded mesh from an input mesh

    :arg mesh:           the unstructured base mesh
    :arg layers:         number of extruded cell layers in the "vertical"
                         direction.
    :arg layer_height:   the layer height, assuming all layers are evenly
                         spaced. If this is omitted, the value defaults to
                         1/layers (i.e. the extruded mesh has total height 1.0)
                         unless a custom kernel is used.
    :arg extrusion_type: the algorithm to employ to calculate the extruded
                         coordinates. One of "uniform", "radial",
                         "radial_hedgehog" or "custom". See below.
    :arg kernel:         a :class:`pyop2.Kernel` to produce coordinates for
                         the extruded mesh. See :func:`~.make_extruded_coords`
                         for more details.
    :arg gdim:           number of spatial dimensions of the
                         resulting mesh (this is only used if a
                         custom kernel is provided)

    The various values of ``extrusion_type`` have the following meanings:

    ``"uniform"``
        the extruded mesh has an extra spatial
        dimension compared to the base mesh. The layers exist
        in this dimension only.

    ``"radial"``
        the extruded mesh has the same number of
        spatial dimensions as the base mesh; the cells are
        radially extruded outwards from the origin. This
        requires the base mesh to have topological dimension
        strictly smaller than geometric dimension.
    ``"radial_hedgehog"``
        similar to `radial`, but the cells
        are extruded in the direction of the outward-pointing
        cell normal (this produces a P1dgxP1 coordinate field).
        In this case, a radially extruded coordinate field
        (generated with ``extrusion_type="radial"``) is
        available in the :attr:`radial_coordinates` attribute.
    ``"custom"``
        use a custom kernel to generate the extruded coordinates

    For more details see the :doc:`manual section on extruded meshes <extruded-meshes>`.
    """
    import firedrake.functionspace as functionspace
    import firedrake.function as function

    mesh.init()
    topology = ExtrudedMeshTopology(mesh.topology, layers)

    if extrusion_type == "uniform":
        pass
    elif extrusion_type in ("radial", "radial_hedgehog"):
        # do not allow radial extrusion if tdim = gdim
        if mesh.ufl_cell().geometric_dimension() == mesh.ufl_cell().topological_dimension():
            raise RuntimeError("Cannot radially-extrude a mesh with equal geometric and topological dimension")
    else:
        # check for kernel
        if kernel is None:
            raise RuntimeError("If the custom extrusion_type is used, a kernel must be provided")
        # otherwise, use the gdim that was passed in
        if gdim is None:
            raise RuntimeError("The geometric dimension of the mesh must be specified if a custom extrusion kernel is used")

    # Compute Coordinates of the extruded mesh
    if layer_height is None:
        # Default to unit
        layer_height = 1.0 / layers

    if extrusion_type == 'radial_hedgehog':
        hfamily = "DG"
    else:
        hfamily = mesh._coordinates.ufl_element().family()
    hdegree = mesh._coordinates.ufl_element().degree()

    if gdim is None:
        gdim = mesh.ufl_cell().geometric_dimension() + (extrusion_type == "uniform")
    coordinates_fs = functionspace.VectorFunctionSpace(topology, hfamily, hdegree, dim=gdim,
                                                       vfamily="Lagrange", vdegree=1)

    coordinates = function.CoordinatelessFunction(coordinates_fs, name="Coordinates")

    eutils.make_extruded_coords(topology, mesh._coordinates, coordinates,
                                layer_height, extrusion_type=extrusion_type, kernel=kernel)

    self = make_mesh_from_coordinates(coordinates)
    self._base_mesh = mesh

    if extrusion_type == "radial_hedgehog":
        fs = functionspace.VectorFunctionSpace(self, "CG", hdegree, dim=gdim,
                                               vfamily="CG", vdegree=1)
        self.radial_coordinates = function.Function(fs)
        eutils.make_extruded_coords(topology, mesh._coordinates, self.radial_coordinates,
                                    layer_height, extrusion_type="radial", kernel=kernel)

    return self
Beispiel #3
0
    def __init__(self,
                 mesh,
                 layers,
                 layer_height=None,
                 extrusion_type='uniform',
                 kernel=None,
                 gdim=None):
        # A cache of function spaces that have been built on this mesh
        import firedrake.function as function
        import firedrake.functionspace as functionspace

        self._cache = {}
        mesh.init()
        self._old_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.parent = mesh.parent
        self.uid = mesh.uid
        self.name = mesh.name
        self._plex = mesh._plex
        self._plex_renumbering = mesh._plex_renumbering
        self._cell_numbering = mesh._cell_numbering
        self._entity_classes = mesh._entity_classes

        interior_f = self._old_mesh.interior_facets
        self._interior_facets = _Facets(self, interior_f.classes, "interior",
                                        interior_f.facet_cell,
                                        interior_f.local_facet_number)
        exterior_f = self._old_mesh.exterior_facets
        self._exterior_facets = _Facets(
            self,
            exterior_f.classes,
            "exterior",
            exterior_f.facet_cell,
            exterior_f.local_facet_number,
            exterior_f.markers,
            unique_markers=exterior_f.unique_markers)

        self.ufl_cell_element = ufl.FiniteElement("Lagrange",
                                                  domain=mesh.ufl_cell(),
                                                  degree=1)
        self.ufl_interval_element = ufl.FiniteElement("Lagrange",
                                                      domain=ufl.Cell(
                                                          "interval", 1),
                                                      degree=1)

        self.fiat_base_element = fiat_utils.fiat_from_ufl_element(
            self.ufl_cell_element)
        self.fiat_vert_element = fiat_utils.fiat_from_ufl_element(
            self.ufl_interval_element)

        fiat_element = FIAT.tensor_finite_element.TensorFiniteElement(
            self.fiat_base_element, self.fiat_vert_element)

        if extrusion_type == "uniform":
            # *must* add a new dimension
            self._ufl_cell = ufl.OuterProductCell(
                mesh.ufl_cell(),
                ufl.Cell("interval", 1),
                gdim=mesh.ufl_cell().geometric_dimension() + 1)

        elif extrusion_type in ("radial", "radial_hedgehog"):
            # do not allow radial extrusion if tdim = gdim
            if mesh.ufl_cell().geometric_dimension() == mesh.ufl_cell(
            ).topological_dimension():
                raise RuntimeError(
                    "Cannot radially-extrude a mesh with equal geometric and topological dimension"
                )
            # otherwise, all is fine, so make cell
            self._ufl_cell = ufl.OuterProductCell(mesh.ufl_cell(),
                                                  ufl.Cell("interval", 1))

        else:
            # check for kernel
            if kernel is None:
                raise RuntimeError(
                    "If the custom extrusion_type is used, a kernel must be provided"
                )
            # otherwise, use the gdim that was passed in
            if gdim is None:
                raise RuntimeError(
                    "The geometric dimension of the mesh must be specified if a custom extrusion kernel is used"
                )
            self._ufl_cell = ufl.OuterProductCell(mesh.ufl_cell(),
                                                  ufl.Cell("interval", 1),
                                                  gdim=gdim)

        self._ufl_domain = ufl.Domain(self.ufl_cell(), data=self)
        flat_temp = fiat_utils.FlattenedElement(fiat_element)

        # Calculated dofs_per_column from flattened_element and layers.
        # The mirrored elements have to be counted only once.
        # Then multiply by layers and layers - 1 accordingly.
        self.dofs_per_column = eutils.compute_extruded_dofs(
            fiat_element, flat_temp.entity_dofs(), layers)

        # Compute Coordinates of the extruded mesh
        if layer_height is None:
            # Default to unit
            layer_height = 1.0 / layers

        if extrusion_type == 'radial_hedgehog':
            hfamily = "DG"
        else:
            hfamily = mesh.coordinates.element().family()
        hdegree = mesh.coordinates.element().degree()

        self._coordinate_fs = functionspace.VectorFunctionSpace(self,
                                                                hfamily,
                                                                hdegree,
                                                                vfamily="CG",
                                                                vdegree=1)

        self.coordinates = function.Function(self._coordinate_fs)
        self._ufl_domain = ufl.Domain(self.coordinates)
        eutils.make_extruded_coords(self,
                                    layer_height,
                                    extrusion_type=extrusion_type,
                                    kernel=kernel)
        if extrusion_type == "radial_hedgehog":
            fs = functionspace.VectorFunctionSpace(self,
                                                   "CG",
                                                   hdegree,
                                                   vfamily="CG",
                                                   vdegree=1)
            self.radial_coordinates = function.Function(fs)
            eutils.make_extruded_coords(self,
                                        layer_height,
                                        extrusion_type="radial",
                                        output_coords=self.radial_coordinates)

        # Build a new ufl element for this function space with the
        # correct domain.  This is necessary since this function space
        # is in the cache and will be picked up by later
        # VectorFunctionSpace construction.
        self._coordinate_fs._ufl_element = self._coordinate_fs.ufl_element(
        ).reconstruct(domain=self.ufl_domain())
        # HACK alert!
        # Replace coordinate Function by one that has a real domain on it (but don't copy values)
        self.coordinates = function.Function(self._coordinate_fs,
                                             val=self.coordinates.dat)
        # Add subdomain_data to the measure objects we store with
        # the mesh.  These are weakrefs for consistency with the
        # "global" measure objects
        self._dx = ufl.Measure('cell',
                               subdomain_data=weakref.ref(self.coordinates))
        self._ds = ufl.Measure('exterior_facet',
                               subdomain_data=weakref.ref(self.coordinates))
        self._dS = ufl.Measure('interior_facet',
                               subdomain_data=weakref.ref(self.coordinates))
        self._ds_t = ufl.Measure('exterior_facet_top',
                                 subdomain_data=weakref.ref(self.coordinates))
        self._ds_b = ufl.Measure('exterior_facet_bottom',
                                 subdomain_data=weakref.ref(self.coordinates))
        self._ds_v = ufl.Measure('exterior_facet_vert',
                                 subdomain_data=weakref.ref(self.coordinates))
        self._dS_h = ufl.Measure('interior_facet_horiz',
                                 subdomain_data=weakref.ref(self.coordinates))
        self._dS_v = ufl.Measure('interior_facet_vert',
                                 subdomain_data=weakref.ref(self.coordinates))
        # Set the subdomain_data on all the default measures to this
        # coordinate field.  We don't set the domain on the measure
        # since this causes an uncollectable reference in the global
        # space (dx is global).  Furthermore, it's never used anyway.
        for measure in [
                ufl.ds, ufl.dS, ufl.dx, ufl.ds_t, ufl.ds_b, ufl.ds_v, ufl.dS_h,
                ufl.dS_v
        ]:
            measure._subdomain_data = weakref.ref(self.coordinates)