Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    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
Ejemplo n.º 7
0
    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()
Ejemplo n.º 8
0
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))
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
    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))