def __init__(self, mesh_hierarchy, family, degree=None, name=None, vfamily=None, vdegree=None): """ :arg mesh_hierarchy: a :class:`~.MeshHierarchy` to build the function spaces on. :arg family: the function space family :arg degree: the degree of the function space See :class:`~.FunctionSpace` for more details on the form of the remaining parameters. """ fses = [ functionspace.FunctionSpace(m, family, degree=degree, name=name, vfamily=vfamily, vdegree=vdegree) for m in mesh_hierarchy ] self.dim = 1 super(FunctionSpaceHierarchy, self).__init__(mesh_hierarchy, fses)
def sanitise_input(v, V): if isinstance(v, expression.Expression): shape = v.value_shape() # Build a function space that supports PointEvaluation so that # we can interpolate into it. deg = max(as_tuple(V.ufl_element().degree())) if v.rank() == 0: fs = functionspace.FunctionSpace(V.mesh(), 'DG', deg+1) elif v.rank() == 1: fs = functionspace.VectorFunctionSpace(V.mesh(), 'DG', deg+1, dim=shape[0]) else: fs = functionspace.TensorFunctionSpace(V.mesh(), 'DG', deg+1, shape=shape) f = function.Function(fs) f.interpolate(v) return f elif isinstance(v, function.Function): return v elif isinstance(v, ufl.classes.Expr): return v else: raise ValueError("Can't project from source object %r" % v)
def errornorm(u, uh, norm_type="L2", degree_rise=3, mesh=None): """Compute the error :math:`e = u - u_h` in the specified norm. :arg u: a :class:`.Function` containing an "exact" solution :arg uh: a :class:`.Function` containing the approximate solution :arg norm_type: the type of norm to compute, see :func:`.norm` for details of supported norm types. :arg degree_rise: increase in polynomial degree to use as the approximation space for computing the error. :arg mesh: an optional mesh on which to compute the error norm (currently ignored). This function works by :func:`.project`\ing ``u`` and ``uh`` into a space of degree ``degree_rise`` higher than the degree of ``uh`` and computing the error there. """ urank = len(u.ufl_shape) uhrank = len(uh.ufl_shape) rank = urank if urank != uhrank: raise RuntimeError("Mismatching rank between u and uh") degree = uh.function_space().ufl_element().degree() if isinstance(degree, tuple): degree = max(degree) + degree_rise else: degree += degree_rise # The exact solution might be an expression, in which case this test is irrelevant. if isinstance(u, function.Function): degree_u = u.function_space().ufl_element().degree() if degree > degree_u: warning("Degree of exact solution less than approximation degree") mesh = uh.function_space().mesh() if rank == 0: V = functionspace.FunctionSpace(mesh, 'DG', degree) elif rank == 1: V = functionspace.VectorFunctionSpace(mesh, 'DG', degree, dim=u.ufl_shape[0]) else: raise RuntimeError( "Don't know how to compute error norm for tensor valued functions") u_ = projection.project(u, V) uh_ = projection.project(uh, V) uh_ -= u_ return norm(uh_, norm_type=norm_type, mesh=mesh)
def SubDomainData(geometric_expr): """Creates a subdomain data object from a boolean-valued UFL expression. The result can be attached as the subdomain_data field of a :class:`ufl.Measure`. For example: x = mesh.coordinates sd = SubDomainData(x[0] < 0.5) assemble(f*dx(subdomain_data=sd)) """ import firedrake.functionspace as functionspace import firedrake.projection as projection # Find domain from expression m = geometric_expr.ufl_domain() # Find selected cells fs = functionspace.FunctionSpace(m, 'DG', 0) f = projection.project(ufl.conditional(geometric_expr, 1, 0), fs) # Create cell subset indices, = np.nonzero(f.dat.data_ro_with_halos > 0.5) return op2.Subset(m.cell_set, indices)
def __init__(self, m, refinement_levels, refinements_per_level=1, reorder=None): """Build a hierarchy of meshes by uniformly refining a coarse mesh. :arg m: the coarse :func:`~.Mesh` to refine :arg refinement_levels: the number of levels of refinement :arg refinements_per_level: Optional number of refinements per level in the resulting hierarchy. Note that the intermediate meshes are still kept, but iteration over the mesh hierarchy skips them. :arg reorder: optional flag indicating whether to reorder the refined meshes. """ from firedrake.citations import Citations Citations().register("Mitchell2016") if m.ufl_cell().cellname() not in ["triangle", "interval"]: raise NotImplementedError( "Only supported on intervals and triangles") if refinements_per_level < 1: raise ValueError( "Must provide positive number of refinements per level") m._plex.setRefinementUniform(True) dm_hierarchy = [] cdm = m._plex self.comm = m.comm fpoint_ises = [] if m.comm.size > 1 and m._grown_halos: raise RuntimeError( "Cannot refine parallel overlapped meshes (make sure the MeshHierarchy is built immediately after the Mesh)" ) for i in range(refinement_levels * refinements_per_level): rdm = cdm.refine() fpoint_ises.append(cdm.createCoarsePointIS()) # Remove interior facet label (re-construct from # complement of exterior facets). Necessary because the # refinement just marks points "underneath" the refined # facet with the appropriate label. This works for # exterior, but not marked interior facets rdm.removeLabel("interior_facets") # Remove vertex (and edge) points from labels on exterior # facets. Interior facets will be relabeled in Mesh # construction below. impl.filter_exterior_facet_labels(rdm) rdm.removeLabel("op2_core") rdm.removeLabel("op2_non_core") rdm.removeLabel("op2_exec_halo") rdm.removeLabel("op2_non_exec_halo") dm_hierarchy.append(rdm) cdm = rdm # Fix up coords if refining embedded circle or sphere if hasattr(m, '_radius'): # FIXME, really we need some CAD-like representation # of the boundary we're trying to conform to. This # doesn't DTRT really for cubed sphere meshes (the # refined meshes are no longer gnonomic). coords = cdm.getCoordinatesLocal().array.reshape( -1, m.geometric_dimension()) scale = m._radius / np.linalg.norm(coords, axis=1).reshape( -1, 1) coords *= scale hierarchy = [m] + [ mesh.Mesh(dm, dim=m.ufl_cell().geometric_dimension(), distribute=False, reorder=reorder) for i, dm in enumerate(dm_hierarchy) ] for m in hierarchy: m._non_overlapped_lgmap = impl.create_lgmap(m._plex) m._non_overlapped_nent = [] for d in range(m._plex.getDimension() + 1): m._non_overlapped_nent.append(m._plex.getDepthStratum(d)) m.init() m._overlapped_lgmap = impl.create_lgmap(m._plex) # On coarse mesh n, a map of consistent cell orientations and # vertex permutations for the fine cells on each coarse cell. self._cells_vperm = [] for mc, mf, fpointis in zip(hierarchy[:-1], hierarchy[1:], fpoint_ises): mc._fpointIS = fpointis c2f = impl.coarse_to_fine_cells(mc, mf) P1c = functionspace.FunctionSpace(mc, 'CG', 1) P1f = functionspace.FunctionSpace(mf, 'CG', 1) self._cells_vperm.append(impl.compute_orientations(P1c, P1f, c2f)) self._hierarchy = tuple(hierarchy[::refinements_per_level]) self._unskipped_hierarchy = tuple(hierarchy) for level, m in enumerate(self): set_level(m, self, level) # Attach fractional levels to skipped parts # This allows us to do magic under the hood so that multigrid # on skipping hierarchies still works! for level, m in enumerate(hierarchy): if level % refinements_per_level == 0: continue set_level(m, self, Fraction(level, refinements_per_level)) self.refinements_per_level = refinements_per_level
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.OuterProductCell(ufl.Cell('interval'), ufl.Cell('interval'), gdim=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
def __lshift__(self, data): """It allows file << function syntax for writing data out to disk. In the case of parallel, it would also accept (function, timestep) tuple as an argument. If only function is given, then the timestep will be automatically generated.""" # If parallel, it needs to keep track of its timestep. if MPI.parallel: # if statements to keep the consistency of how to update the # timestep. if isinstance(data, tuple): if self._time_step == -1 or not self._generate_time: function = data[0] self._time_step = data[1] else: raise TypeError("Expected function, got tuple.") else: if self._time_step != -1 and not self._generate_time: raise TypeError("Expected tuple, got function.") function = data self._time_step += 1 self._generate_time = True else: function = data def is_family1(e, family): import ufl.finiteelement.hdivcurl as hc if isinstance(e, (hc.HDivElement, hc.HCurlElement)): return False if e.family() == 'OuterProductElement': if e.degree() == (1, 1): if e._A.family() == family \ and e._B.family() == family: return True elif e.family() == family and e.degree() == 1: return True return False def is_cgN(e): import ufl.finiteelement.hdivcurl as hc if isinstance(e, (hc.HDivElement, hc.HCurlElement)): return False if e.family() == 'OuterProductElement': if e._A.family() in ('Lagrange', 'Q') \ and e._B.family() == 'Lagrange': return True elif e.family() in ('Lagrange', 'Q'): return True return False mesh = function.function_space().mesh() e = function.function_space().ufl_element() if len(e.value_shape()) > 1: raise RuntimeError("Can't output tensor valued functions") ce = mesh.coordinates.function_space().ufl_element() coords_p1 = is_family1(ce, 'Lagrange') or is_family1(ce, 'Q') coords_p1dg = is_family1(ce, 'Discontinuous Lagrange') or is_family1(ce, 'DQ') coords_cgN = is_cgN(ce) function_p1 = is_family1(e, 'Lagrange') or is_family1(e, 'Q') function_p1dg = is_family1(e, 'Discontinuous Lagrange') or is_family1(e, 'DQ') function_cgN = is_cgN(e) project_coords = False project_function = False discontinuous = False # We either output in P1 or P1dg. if coords_cgN and function_cgN: family = 'CG' project_coords = not coords_p1 project_function = not function_p1 else: family = 'DG' project_coords = not coords_p1dg project_function = not function_p1dg discontinuous = True if project_function: if len(e.value_shape()) == 0: Vo = fs.FunctionSpace(mesh, family, 1) elif len(e.value_shape()) == 1: Vo = fs.VectorFunctionSpace(mesh, family, 1, dim=e.value_shape()[0]) else: # Never reached Vo = None if not self._warnings[0]: warning(RED % "*** Projecting output function to %s1", family) self._warnings[0] = True output = projection.project(function, Vo, name=function.name()) else: output = function Vo = output.function_space() if project_coords: Vc = fs.VectorFunctionSpace(mesh, family, 1, dim=mesh._coordinate_fs.dim) if not self._warnings[1]: warning(RED % "*** Projecting coordinates to %s1", family) self._warnings[1] = True coordinates = projection.project(mesh.coordinates, Vc, name=mesh.coordinates.name()) else: coordinates = mesh.coordinates Vc = coordinates.function_space() num_points = Vo.node_count layers = mesh.layers - 1 if isinstance(e.cell(), OuterProductCell) else 1 num_cells = mesh.num_cells() * layers if not isinstance(e.cell(), OuterProductCell) and e.cell().cellname() != "quadrilateral": connectivity = Vc.cell_node_map().values_with_halo.flatten() else: # Connectivity of bottom cell in extruded mesh base = Vc.cell_node_map().values_with_halo if _cells[mesh.ufl_cell()] == hl.VtkQuad: # Quad is # # 1--3 # | | # 0--2 # # needs to be # # 3--2 # | | # 0--1 base = base[:, [0, 2, 3, 1]] points_per_cell = 4 elif _cells[mesh.ufl_cell()] == hl.VtkWedge: # Wedge is # # 5 # /|\ # / | \ # 1----3 # | 4 | # | /\ | # |/ \| # 0----2 # # needs to be # # 5 # /|\ # / | \ # 3----4 # | 2 | # | /\ | # |/ \| # 0----1 # base = base[:, [0, 2, 4, 1, 3, 5]] points_per_cell = 6 elif _cells[mesh.ufl_cell()] == hl.VtkHexahedron: # Hexahedron is # # 5----7 # /| /| # 4----6 | # | 1--|-3 # |/ |/ # 0----2 # # needs to be # # 7----6 # /| /| # 4----5 | # | 3--|-2 # |/ |/ # 0----1 # base = base[:, [0, 2, 3, 1, 4, 6, 7, 5]] points_per_cell = 8 # Repeat up the column connectivity_temp = np.repeat(base, layers, axis=0) if discontinuous: scale = points_per_cell else: scale = 1 offsets = np.arange(layers) * scale # Add offsets going up the column connectivity_temp += np.tile(offsets.reshape(-1, 1), (mesh.num_cells(), 1)) connectivity = connectivity_temp.flatten() if isinstance(output.function_space(), fs.VectorFunctionSpace): tmp = output.dat.data_ro_with_halos vdata = [None]*3 if output.dat.dim[0] == 1: vdata[0] = tmp.flatten() else: for i in range(output.dat.dim[0]): vdata[i] = tmp[:, i].flatten() for i in range(output.dat.dim[0], 3): vdata[i] = np.zeros_like(vdata[0]) data = tuple(vdata) # only for checking large file size flat_data = {function.name(): tmp.flatten()} else: data = output.dat.data_ro_with_halos.flatten() flat_data = {function.name(): data} coordinates = self._fd_to_evtk_coord(coordinates.dat.data_ro_with_halos) cell_types = np.empty(num_cells, dtype="uint8") # Assume that all cells are of same shape. cell_types[:] = _cells[mesh.ufl_cell()].tid p_c = _points_per_cell[mesh.ufl_cell()] # This tells which are the last nodes of each cell. offsets = np.arange(start=p_c, stop=p_c * (num_cells + 1), step=p_c, dtype='int32') large_file_flag = _requiresLargeVTKFileSize("VtkUnstructuredGrid", numPoints=num_points, numCells=num_cells, pointData=flat_data, cellData=None) new_name = self._filename # When vtu file makes part of a parallel process, aggregated by a # pvtu file, the output is : filename_timestep_rank.vtu if MPI.parallel: new_name += "_" + str(self._time_step) + "_" + str(MPI.comm.rank) self._writer = hl.VtkFile( new_name, hl.VtkUnstructuredGrid, large_file_flag) self._writer.openGrid() self._writer.openPiece(ncells=num_cells, npoints=num_points) # openElement allows the stuff in side of the tag <arg></arg> # to be editted. self._writer.openElement("Points") # addData adds the DataArray in the tag <arg1> self._writer.addData("Points", coordinates) self._writer.closeElement("Points") self._writer.openElement("Cells") self._writer.addData("connectivity", connectivity) self._writer.addData("offsets", offsets) self._writer.addData("types", cell_types) self._writer.closeElement("Cells") self._writer.openData("Point", scalars=function.name()) self._writer.addData(function.name(), data) self._writer.closeData("Point") self._writer.closePiece() self._writer.closeGrid() # Create the AppendedData self._writer.appendData(coordinates) self._writer.appendData(connectivity) self._writer.appendData(offsets) self._writer.appendData(cell_types) self._writer.appendData(data) self._writer.save()
def make_extruded_coords(extruded_topology, base_coords, ext_coords, layer_height, extrusion_type='uniform', kernel=None): """ Given either a kernel or a (fixed) layer_height, compute an extruded coordinate field for an extruded mesh. :arg extruded_topology: an :class:`~.ExtrudedMeshTopology` to extrude a coordinate field for. :arg base_coords: a :class:`~.Function` to read the base coordinates from. :arg ext_coords: a :class:`~.Function` to write the extruded coordinates into. :arg layer_height: an equi-spaced height for each layer. :arg extrusion_type: the type of extrusion to use. Predefined options are either "uniform" (creating equi-spaced layers by extruding in the (n+1)dth direction), "radial" (creating equi-spaced layers by extruding in the outward direction from the origin) or "radial_hedgehog" (creating equi-spaced layers by extruding coordinates in the outward cell-normal direction, needs a P1dgxP1 coordinate field). :arg kernel: an optional kernel to carry out coordinate extrusion. The kernel signature (if provided) is:: void kernel(double **base_coords, double **ext_coords, int **layer, double *layer_height) The kernel iterates over the cells of the mesh and receives as arguments the coordinates of the base cell (to read), the coordinates on the extruded cell (to write to), the layer number of each cell and the fixed layer height. """ _, vert_space = ext_coords.function_space().ufl_element().sub_elements( )[0].sub_elements() if kernel is None and not (vert_space.degree() == 1 and vert_space.family() in ['Lagrange', 'Discontinuous Lagrange']): raise RuntimeError( 'Extrusion of coordinates is only possible for a P1 or P1dg interval unless a custom kernel is provided' ) if kernel is not None: pass elif extrusion_type == 'uniform': kernel = op2.Kernel( """ void uniform_extrusion_kernel(double **base_coords, double **ext_coords, int **layer, double *layer_height) { for ( int d = 0; d < %(base_map_arity)d; d++ ) { for ( int c = 0; c < %(base_coord_dim)d; c++ ) { ext_coords[2*d][c] = base_coords[d][c]; ext_coords[2*d+1][c] = base_coords[d][c]; } ext_coords[2*d][%(base_coord_dim)d] = *layer_height * (layer[0][0]); ext_coords[2*d+1][%(base_coord_dim)d] = *layer_height * (layer[0][0] + 1); } }""" % { 'base_map_arity': base_coords.cell_node_map().arity, 'base_coord_dim': base_coords.function_space().dim }, "uniform_extrusion_kernel") elif extrusion_type == 'radial': kernel = op2.Kernel( """ void radial_extrusion_kernel(double **base_coords, double **ext_coords, int **layer, double *layer_height) { for ( int d = 0; d < %(base_map_arity)d; d++ ) { double norm = 0.0; for ( int c = 0; c < %(base_coord_dim)d; c++ ) { norm += base_coords[d][c] * base_coords[d][c]; } norm = sqrt(norm); for ( int c = 0; c < %(base_coord_dim)d; c++ ) { ext_coords[2*d][c] = base_coords[d][c] * (1 + (*layer_height * layer[0][0])/norm); ext_coords[2*d+1][c] = base_coords[d][c] * (1 + (*layer_height * (layer[0][0]+1))/norm); } } }""" % { 'base_map_arity': base_coords.cell_node_map().arity, 'base_coord_dim': base_coords.function_space().dim }, "radial_extrusion_kernel") elif extrusion_type == 'radial_hedgehog': # Only implemented for interval in 2D and triangle in 3D. # gdim != tdim already checked in ExtrudedMesh constructor. if base_coords.ufl_domain().ufl_cell().topological_dimension() not in [ 1, 2 ]: raise NotImplementedError( "Hedgehog extrusion not implemented for %s" % base_coords.ufl_domain().ufl_cell()) kernel = op2.Kernel( """ void radial_hedgehog_extrusion_kernel(double **base_coords, double **ext_coords, int **layer, double *layer_height) { double v0[%(base_coord_dim)d]; double v1[%(base_coord_dim)d]; double n[%(base_coord_dim)d]; double x[%(base_coord_dim)d] = {0}; double dot = 0.0; double norm = 0.0; int i, c, d; if (%(base_coord_dim)d == 2) { /* * normal is: * (0 -1) (x2 - x1) * (1 0) (y2 - y1) */ n[0] = -(base_coords[1][1] - base_coords[0][1]); n[1] = base_coords[1][0] - base_coords[0][0]; } else if (%(base_coord_dim)d == 3) { /* * normal is * v0 x v1 * * /\\ * v0/ \\ * / \\ * /------\\ * v1 */ for (i = 0; i < 3; ++i) { v0[i] = base_coords[1][i] - base_coords[0][i]; v1[i] = base_coords[2][i] - base_coords[0][i]; } n[0] = v0[1] * v1[2] - v0[2] * v1[1]; n[1] = v0[2] * v1[0] - v0[0] * v1[2]; n[2] = v0[0] * v1[1] - v0[1] * v1[0]; } for (i = 0; i < %(base_map_arity)d; ++i) { for (c = 0; c < %(base_coord_dim)d; ++c) { x[c] += base_coords[i][c]; } } for (i = 0; i < %(base_coord_dim)d; ++i) { dot += x[i] * n[i]; norm += n[i] * n[i]; } /* * Make inward-pointing normals point out */ norm = sqrt(norm); norm *= (dot < 0 ? -1 : 1); for (d = 0; d < %(base_map_arity)d; ++d) { for (c = 0; c < %(base_coord_dim)d; ++c ) { ext_coords[2*d][c] = base_coords[d][c] + n[c] * layer_height[0] * layer[0][0] / norm; ext_coords[2*d+1][c] = base_coords[d][c] + n[c] * layer_height[0] * (layer[0][0] + 1)/ norm; } } }""" % { 'base_map_arity': base_coords.cell_node_map().arity, 'base_coord_dim': base_coords.function_space().dim }, "radial_hedgehog_extrusion_kernel") else: raise NotImplementedError('Unsupported extrusion type "%s"' % extrusion_type) # Dat to hold layer number import firedrake.functionspace as fs layer_fs = fs.FunctionSpace(extruded_topology, 'DG', 0) layers = extruded_topology.layers layer = op2.Dat( layer_fs.dof_dset, np.repeat(np.arange(layers - 1, dtype=np.int32), extruded_topology.cell_set.total_size).reshape( layers - 1, extruded_topology.cell_set.total_size).T.ravel(), dtype=np.int32) height = op2.Global(1, layer_height, dtype=float) op2.par_loop(kernel, ext_coords.cell_set, base_coords.dat(op2.READ, base_coords.cell_node_map()), ext_coords.dat(op2.WRITE, ext_coords.cell_node_map()), layer(op2.READ, layer_fs.cell_node_map()), height(op2.READ))
def project(v, V, bcs=None, mesh=None, solver_parameters=None, form_compiler_parameters=None, name=None): """Project an :class:`.Expression` or :class:`.Function` into a :class:`.FunctionSpace` :arg v: the :class:`.Expression`, :class:`ufl.Expr` or :class:`.Function` to project :arg V: the :class:`.FunctionSpace` or :class:`.Function` to project into :arg bcs: boundary conditions to apply in the projection :arg mesh: the mesh to project into :arg solver_parameters: parameters to pass to the solver used when projecting. :arg form_compiler_parameters: parameters to the form compiler :arg name: name of the resulting :class:`.Function` If ``V`` is a :class:`.Function` then ``v`` is projected into ``V`` and ``V`` is returned. If `V` is a :class:`.FunctionSpace` then ``v`` is projected into a new :class:`.Function` and that :class:`.Function` is returned. The ``bcs``, ``mesh`` and ``form_compiler_parameters`` are currently ignored.""" from firedrake import function if isinstance(V, functionspace.FunctionSpaceBase): ret = function.Function(V, name=name) elif isinstance(V, function.Function): ret = V V = V.function_space() else: raise RuntimeError( 'Can only project into functions and function spaces, not %r' % type(V)) if isinstance(v, expression.Expression): shape = v.value_shape() # Build a function space that supports PointEvaluation so that # we can interpolate into it. if isinstance(V.ufl_element().degree(), tuple): deg = max(V.ufl_element().degree()) else: deg = V.ufl_element().degree() if v.rank() == 0: fs = functionspace.FunctionSpace(V.mesh(), 'DG', deg + 1) elif v.rank() == 1: fs = functionspace.VectorFunctionSpace(V.mesh(), 'DG', deg + 1, dim=shape[0]) else: fs = functionspace.TensorFunctionSpace(V.mesh(), 'DG', deg + 1, shape=shape) f = function.Function(fs) f.interpolate(v) v = f elif isinstance(v, function.Function): if v.function_space().mesh() != ret.function_space().mesh(): raise RuntimeError("Can't project between mismatching meshes") elif not isinstance(v, ufl.core.expr.Expr): raise RuntimeError( "Can't only project from expressions and functions, not %r" % type(v)) if v.ufl_shape != ret.ufl_shape: raise RuntimeError( 'Shape mismatch between source %s and target function spaces %s in project' % (v.ufl_shape, ret.ufl_shape)) p = ufl_expr.TestFunction(V) q = ufl_expr.TrialFunction(V) a = ufl.inner(p, q) * ufl.dx(domain=V.mesh()) L = ufl.inner(p, v) * ufl.dx(domain=V.mesh()) # Default to 1e-8 relative tolerance if solver_parameters is None: solver_parameters = {'ksp_type': 'cg', 'ksp_rtol': 1e-8} else: solver_parameters.setdefault('ksp_type', 'cg') solver_parameters.setdefault('ksp_rtol', 1e-8) _solve(a == L, ret, bcs=bcs, solver_parameters=solver_parameters, form_compiler_parameters=form_compiler_parameters) return ret
def __init__(self, m, refinement_levels, reorder=None): """Build a hierarchy of meshes by uniformly refining a coarse mesh. :arg m: the coarse :func:`~.Mesh` to refine :arg refinement_levels: the number of levels of refinement :arg reorder: optional flag indicating whether to reorder the refined meshes. """ if m.ufl_cell().cellname() not in ["triangle", "interval"]: raise NotImplementedError( "Only supported on intervals and triangles") m._plex.setRefinementUniform(True) dm_hierarchy = [] cdm = m._plex fpoint_ises = [] if MPI.comm.size > 1 and m._grown_halos: raise RuntimeError( "Cannot refine parallel overlapped meshes (make sure the MeshHierarchy is built immediately after the Mesh)" ) for i in range(refinement_levels): rdm = cdm.refine() fpoint_ises.append(cdm.createCoarsePointIS()) # Remove interior facet label (re-construct from # complement of exterior facets). Necessary because the # refinement just marks points "underneath" the refined # facet with the appropriate label. This works for # exterior, but not marked interior facets rdm.removeLabel("interior_facets") # Remove vertex (and edge) points from labels on exterior # facets. Interior facets will be relabeled in Mesh # construction below. impl.filter_exterior_facet_labels(rdm) rdm.removeLabel("op2_core") rdm.removeLabel("op2_non_core") rdm.removeLabel("op2_exec_halo") rdm.removeLabel("op2_non_exec_halo") dm_hierarchy.append(rdm) cdm = rdm # Fix up coords if refining embedded circle or sphere if hasattr(m, '_circle_manifold'): coords = cdm.getCoordinatesLocal().array.reshape(-1, 2) scale = m._circle_manifold / np.linalg.norm( coords, axis=1).reshape(-1, 1) coords *= scale elif hasattr(m, '_icosahedral_sphere'): coords = cdm.getCoordinatesLocal().array.reshape(-1, 3) scale = m._icosahedral_sphere / np.linalg.norm( coords, axis=1).reshape(-1, 1) coords *= scale hierarchy = [m] + [ mesh.Mesh(dm, dim=m.ufl_cell().geometric_dimension(), distribute=False, reorder=reorder) for i, dm in enumerate(dm_hierarchy) ] self._hierarchy = tuple( [set_level(o, self, lvl) for lvl, o in enumerate(hierarchy)]) for m in self: m._non_overlapped_lgmap = impl.create_lgmap(m._plex) m._non_overlapped_nent = [] for d in range(m._plex.getDimension() + 1): m._non_overlapped_nent.append(m._plex.getDepthStratum(d)) m.init() m._overlapped_lgmap = impl.create_lgmap(m._plex) # On coarse mesh n, a map of consistent cell orientations and # vertex permutations for the fine cells on each coarse cell. self._cells_vperm = [] for mc, mf, fpointis in zip(self._hierarchy[:-1], self._hierarchy[1:], fpoint_ises): mc._fpointIS = fpointis c2f = impl.coarse_to_fine_cells(mc, mf) P1c = functionspace.FunctionSpace(mc, 'CG', 1) P1f = functionspace.FunctionSpace(mf, 'CG', 1) self._cells_vperm.append(impl.compute_orientations(P1c, P1f, c2f))