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 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)
def __init__(self, mesh, layers, kernel=None, layer_height=None, extrusion_type='uniform'): # A cache of function spaces that have been built on this mesh self._cache = {} if kernel is None and extrusion_type is None: raise RuntimeError("Please provide a kernel or a preset extrusion_type ('uniform' or 'radial') for extruding the mesh") 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._cells = mesh._cells 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._cell_closure = mesh._cell_closure 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) 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) self._ufl_cell = ufl.OuterProductCell(mesh._ufl_cell, ufl.Cell("interval", 1)) self._ufl_domain = ufl.Domain(self.ufl_cell(), data=self) flat_temp = fiat_element.flattened_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 self._coordinate_fs = functionspace.VectorFunctionSpace(self, mesh._coordinate_fs.ufl_element().family(), mesh._coordinate_fs.ufl_element().degree(), 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) # 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) self._dx = ufl.Measure('cell', domain=self, subdomain_data=self.coordinates) self._ds = ufl.Measure('exterior_facet', domain=self, subdomain_data=self.coordinates) self._dS = ufl.Measure('interior_facet', domain=self, subdomain_data=self.coordinates) self._ds_t = ufl.Measure('exterior_facet_top', domain=self, subdomain_data=self.coordinates) self._ds_b = ufl.Measure('exterior_facet_bottom', domain=self, subdomain_data=self.coordinates) self._ds_v = ufl.Measure('exterior_facet_vert', domain=self, subdomain_data=self.coordinates) self._dS_h = ufl.Measure('interior_facet_horiz', domain=self, subdomain_data=self.coordinates) self._dS_v = ufl.Measure('interior_facet_vert', domain=self, subdomain_data=self.coordinates) # Set the subdomain_data on all the default measures to this coordinate field. 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 = self.coordinates measure._domain = self.ufl_domain()