Пример #1
0
def get_bt_masks(mesh, key, finat_element):
    """Get masks for top and bottom dofs.

    :arg mesh: The mesh to use.
    :arg key: Canonicalised entity_dofs (see :func:`entity_dofs_key`).
    :arg finat_element: The FInAT element.
    :returns: A dict mapping ``"topological"`` and ``"geometric"``
        keys to bottom and top dofs (extruded) or ``None``.
    """
    if not bool(mesh.layers):
        return None
    bt_masks = {}
    # Compute the top and bottom masks to identify boundary dofs
    #
    # Sorting the keys of the closure entity dofs, the whole cell
    # comes last [-1], before that the horizontal facet [-2], before
    # that vertical facets [-3]. We need the horizontal facets here.
    closure_dofs = finat_element.entity_closure_dofs()
    horiz_facet_dim = sorted(closure_dofs.keys())[-2]
    b_mask = closure_dofs[horiz_facet_dim][0]
    t_mask = closure_dofs[horiz_facet_dim][1]
    bt_masks["topological"] = (b_mask, t_mask)  # conversion to tuple
    # Geometric facet dofs
    facet_dofs = entity_support_dofs(finat_element, horiz_facet_dim)
    bt_masks["geometric"] = (facet_dofs[0], facet_dofs[1])
    return bt_masks
Пример #2
0
def test_hex(hex_mesh, args, kwargs, horiz_expected, vert_expected):
    if not kwargs:
        fe = FiniteElement(args[0],
                           hex_mesh.ufl_cell(),
                           args[1],
                           variant='equispaced')
    else:
        A, B = hex_mesh.ufl_cell().sub_cells()
        hfe = FiniteElement(args[0], A, args[1], variant='equispaced')
        vfe = FiniteElement(kwargs["vfamily"],
                            B,
                            kwargs['vdegree'],
                            variant='equispaced')
        fe = TensorProductElement(hfe, vfe)
    V = FunctionSpace(hex_mesh, fe, **kwargs)
    assert horiz_expected == entity_support_dofs(V.finat_element, (2, 0))
    assert vert_expected == entity_support_dofs(V.finat_element, (1, 1))
Пример #3
0
    def exterior_facet_boundary_node_map(self, V, method):
        """Return the :class:`pyop2.Map` from exterior facets to nodes
        on the boundary.

        :arg V: The function space.
        :arg method:  The method for determining boundary nodes.  See
           :class:`~.DirichletBC` for details.
        """
        try:
            return self.map_caches["boundary_node"][method]
        except KeyError:
            pass
        el = V.finat_element

        dim = self.mesh.facet_dimension()

        if method == "topological":
            boundary_dofs = el.entity_closure_dofs()[dim]
        elif method == "geometric":
            # This function is only called on extruded meshes when
            # asking for the nodes that live on the "vertical"
            # exterior facets.
            boundary_dofs = entity_support_dofs(el, dim)

        nodes_per_facet = \
            len(boundary_dofs[0])

        # HACK ALERT
        # The facet set does not have a halo associated with it, since
        # we only construct halos for DoF sets.  Fortunately, this
        # loop is direct and we already have all the correct
        # information available locally.  So We fake a set of the
        # correct size and carry out a direct loop
        facet_set = op2.Set(self.mesh.exterior_facets.set.total_size,
                            comm=self.mesh.comm)

        fs_dat = op2.Dat(
            facet_set**el.space_dimension(),
            data=V.exterior_facet_node_map().values_with_halo.view())

        facet_dat = op2.Dat(facet_set**nodes_per_facet, dtype=IntType)

        # Ensure these come out in sorted order.
        local_facet_nodes = numpy.array(
            [boundary_dofs[e] for e in sorted(boundary_dofs.keys())])

        # Helper function to turn the inner index of an array into c
        # array literals.
        c_array = lambda xs: "{" + ", ".join(map(str, xs)) + "}"

        # AST for: l_nodes[facet[0]][n]
        rank_ast = ast.Symbol("l_nodes",
                              rank=(ast.Symbol("facet", rank=(0, )), "n"))

        body = ast.Block([
            ast.Decl("int",
                     ast.Symbol("l_nodes",
                                (len(el.cell.topology[dim]), nodes_per_facet)),
                     init=ast.ArrayInit(
                         c_array(map(c_array, local_facet_nodes))),
                     qualifiers=["const"]),
            ast.For(
                ast.Decl("int", "n", 0), ast.Less("n", nodes_per_facet),
                ast.Incr("n", 1),
                ast.Assign(ast.Symbol("facet_nodes", ("n", )),
                           ast.Symbol("cell_nodes", (rank_ast, ))))
        ])

        kernel = op2.Kernel(
            ast.FunDecl("void", "create_bc_node_map", [
                ast.Decl("%s*" % as_cstr(fs_dat.dtype), "cell_nodes"),
                ast.Decl("%s*" % as_cstr(facet_dat.dtype), "facet_nodes"),
                ast.Decl("unsigned int*", "facet")
            ], body), "create_bc_node_map")

        local_facet_dat = op2.Dat(
            facet_set**self.mesh.exterior_facets._rank,
            self.mesh.exterior_facets.local_facet_dat.data_ro_with_halos,
            dtype=numpy.uintc)
        op2.par_loop(kernel, facet_set, fs_dat(op2.READ), facet_dat(op2.WRITE),
                     local_facet_dat(op2.READ))

        if self.extruded:
            offset = self.offset[boundary_dofs[0]]
        else:
            offset = None
        val = op2.Map(facet_set,
                      self.node_set,
                      nodes_per_facet,
                      facet_dat.data_ro_with_halos,
                      name="exterior_facet_boundary_node",
                      offset=offset)
        self.map_caches["boundary_node"][method] = val
        return val
def test_hex(hex_mesh, args, kwargs, horiz_expected, vert_expected):
    V = FunctionSpace(hex_mesh, *args, **kwargs)
    assert horiz_expected == entity_support_dofs(V.finat_element, (2, 0))
    assert vert_expected == entity_support_dofs(V.finat_element, (1, 1))
Пример #5
0
    def compute_bounds(self, field):
        """
        Re-compute min/max values of all neighbouring centroids

        :arg field: :class:`Function` to limit
        """
        # Call general-purpose bound computation.
        # super(VertexBasedP1DGLimiter, self).compute_bounds(field)
        self._update_centroids(field)
        self.max_field.assign(-1.0e10)  # small number
        self.min_field.assign(1.0e10)  # big number
        par_loop(self._min_max_loop,
                 dx, {
                     "maxq": (self.max_field, MAX),
                     "minq": (self.min_field, MIN),
                     "q": (self.centroids, READ)
                 },
                 is_loopy_kernel=False)

        # Add the average of lateral boundary facets to min/max fields
        # NOTE this just computes the arithmetic mean of nodal values on the facet,
        # which in general is not equivalent to the mean of the field over the bnd facet.
        # This is OK for P1DG triangles, but not exact for the extruded case (quad facets)
        from finat.finiteelementbase import entity_support_dofs

        if self.is_2d:
            entity_dim = 1  # get 1D facets
            # for vertical 2d in non-hydrostatic (nh) extension, WPan 2020-02-11
            if self.mesh_is_extruded:
                entity_dim = (
                    1, 0
                )  # TODO separate horizontal 2d and vertical 2d properly
        else:
            entity_dim = (1, 1)  # get vertical facets
        boundary_dofs = entity_support_dofs(self.P1DG.finat_element,
                                            entity_dim)
        local_facet_nodes = np.array(
            [boundary_dofs[e] for e in sorted(boundary_dofs.keys())])
        n_bnd_nodes = local_facet_nodes.shape[1]
        local_facet_idx = op2.Global(local_facet_nodes.shape,
                                     local_facet_nodes,
                                     dtype=np.int32,
                                     name='local_facet_idx')
        code = """
            void my_kernel(double *qmax, double *qmin, double *field, unsigned int *facet, unsigned int *local_facet_idx)
            {
                double face_mean = 0.0;
                for (int i = 0; i < %(nnodes)d; i++) {
                    unsigned int idx = local_facet_idx[facet[0]*%(nnodes)d + i];
                    face_mean += field[idx];
                }
                face_mean /= %(nnodes)d;
                for (int i = 0; i < %(nnodes)d; i++) {
                    unsigned int idx = local_facet_idx[facet[0]*%(nnodes)d + i];
                    qmax[idx] = fmax(qmax[idx], face_mean);
                    qmin[idx] = fmin(qmin[idx], face_mean);
                }
            }"""
        bnd_kernel = op2.Kernel(code % {'nnodes': n_bnd_nodes}, 'my_kernel')
        op2.par_loop(
            bnd_kernel,
            self.P1DG.mesh().exterior_facets.set,
            self.max_field.dat(op2.MAX,
                               self.max_field.exterior_facet_node_map()),
            self.min_field.dat(op2.MIN,
                               self.min_field.exterior_facet_node_map()),
            field.dat(op2.READ, field.exterior_facet_node_map()),
            self.P1DG.mesh().exterior_facets.local_facet_dat(op2.READ),
            local_facet_idx(op2.READ))
        if not self.is_2d or self.mesh_is_extruded:  # add 'or self.mesh_is_extruded' for vertical 2d, TODO verify, WPan 2020-02-11
            # Add nodal values from surface/bottom boundaries
            # NOTE calling firedrake par_loop with measure=ds_t raises an error
            bottom_nodes = get_facet_mask(self.P1CG, 'geometric', 'bottom')
            top_nodes = get_facet_mask(self.P1CG, 'geometric', 'top')
            bottom_idx = op2.Global(len(bottom_nodes),
                                    bottom_nodes,
                                    dtype=np.int32,
                                    name='node_idx')
            top_idx = op2.Global(len(top_nodes),
                                 top_nodes,
                                 dtype=np.int32,
                                 name='node_idx')
            code = """
                void my_kernel(double *qmax, double *qmin, double *field, int *idx) {
                    double face_mean = 0;
                    for (int i=0; i<%(nnodes)d; i++) {
                        face_mean += field[idx[i]];
                    }
                    face_mean /= %(nnodes)d;
                    for (int i=0; i<%(nnodes)d; i++) {
                        qmax[idx[i]] = fmax(qmax[idx[i]], face_mean);
                        qmin[idx[i]] = fmin(qmin[idx[i]], face_mean);
                    }
                }"""
            kernel = op2.Kernel(code % {'nnodes': len(bottom_nodes)},
                                'my_kernel')

            op2.par_loop(kernel,
                         self.mesh.cell_set,
                         self.max_field.dat(
                             op2.MAX,
                             self.max_field.function_space().cell_node_map()),
                         self.min_field.dat(
                             op2.MIN,
                             self.min_field.function_space().cell_node_map()),
                         field.dat(op2.READ,
                                   field.function_space().cell_node_map()),
                         bottom_idx(op2.READ),
                         iterate=op2.ON_BOTTOM)

            op2.par_loop(kernel,
                         self.mesh.cell_set,
                         self.max_field.dat(
                             op2.MAX,
                             self.max_field.function_space().cell_node_map()),
                         self.min_field.dat(
                             op2.MIN,
                             self.min_field.function_space().cell_node_map()),
                         field.dat(op2.READ,
                                   field.function_space().cell_node_map()),
                         top_idx(op2.READ),
                         iterate=op2.ON_TOP)
def test_hex(hex_mesh, args, kwargs, horiz_expected, vert_expected):
    V = FunctionSpace(hex_mesh, *args, **kwargs)
    assert horiz_expected == entity_support_dofs(V.finat_element, (2, 0))
    assert vert_expected == entity_support_dofs(V.finat_element, (1, 1))
Пример #7
0
    def compute_bounds(self, field):
        """
        Re-compute min/max values of all neighbouring centroids

        :arg field: :class:`Function` to limit
        """
        # Call general-purpose bound computation.
        super(VertexBasedP1DGLimiter, self).compute_bounds(field)

        # Add the average of lateral boundary facets to min/max fields
        # NOTE this just computes the arithmetic mean of nodal values on the facet,
        # which in general is not equivalent to the mean of the field over the bnd facet.
        # This is OK for P1DG triangles, but not exact for the extruded case (quad facets)
        from finat.finiteelementbase import entity_support_dofs

        if self.extruded:
            entity_dim = (self.dim - 2, 1)  # get vertical facets
        else:
            entity_dim = self.dim - 1
        boundary_dofs = entity_support_dofs(self.P1DG.finat_element,
                                            entity_dim)
        local_facet_nodes = np.array(
            [boundary_dofs[e] for e in sorted(boundary_dofs.keys())])
        n_bnd_nodes = local_facet_nodes.shape[1]
        local_facet_idx = op2.Global(local_facet_nodes.shape,
                                     local_facet_nodes,
                                     dtype=np.int32,
                                     name='local_facet_idx')
        code = """
            void my_kernel(double *qmax, double *qmin, double *field, unsigned int *facet, unsigned int *local_facet_idx)
            {
                double face_mean = 0.0;
                for (int i = 0; i < %(nnodes)d; i++) {
                    unsigned int idx = local_facet_idx[facet[0]*%(nnodes)d + i];
                    face_mean += field[idx];
                }
                face_mean /= %(nnodes)d;
                for (int i = 0; i < %(nnodes)d; i++) {
                    unsigned int idx = local_facet_idx[facet[0]*%(nnodes)d + i];
                    qmax[idx] = fmax(qmax[idx], face_mean);
                    qmin[idx] = fmin(qmin[idx], face_mean);
                }
            }"""
        bnd_kernel = op2.Kernel(code % {'nnodes': n_bnd_nodes}, 'my_kernel')
        op2.par_loop(
            bnd_kernel,
            self.P1DG.mesh().exterior_facets.set,
            self.max_field.dat(op2.MAX,
                               self.max_field.exterior_facet_node_map()),
            self.min_field.dat(op2.MIN,
                               self.min_field.exterior_facet_node_map()),
            field.dat(op2.READ, field.exterior_facet_node_map()),
            self.P1DG.mesh().exterior_facets.local_facet_dat(op2.READ),
            local_facet_idx(op2.READ))
        if self.extruded:
            # Add nodal values from surface/bottom boundaries
            # NOTE calling firedrake par_loop with measure=ds_t raises an error
            bottom_nodes = get_facet_mask(self.P1CG, 'bottom')
            top_nodes = get_facet_mask(self.P1CG, 'top')
            bottom_idx = op2.Global(len(bottom_nodes),
                                    bottom_nodes,
                                    dtype=np.int32,
                                    name='node_idx')
            top_idx = op2.Global(len(top_nodes),
                                 top_nodes,
                                 dtype=np.int32,
                                 name='node_idx')
            code = """
                void my_kernel(double *qmax, double *qmin, double *field, int *idx) {
                    double face_mean = 0;
                    for (int i=0; i<%(nnodes)d; i++) {
                        face_mean += field[idx[i]];
                    }
                    face_mean /= %(nnodes)d;
                    for (int i=0; i<%(nnodes)d; i++) {
                        qmax[idx[i]] = fmax(qmax[idx[i]], face_mean);
                        qmin[idx[i]] = fmin(qmin[idx[i]], face_mean);
                    }
                }"""
            kernel = op2.Kernel(code % {'nnodes': len(bottom_nodes)},
                                'my_kernel')

            op2.par_loop(kernel,
                         self.mesh.cell_set,
                         self.max_field.dat(
                             op2.MAX,
                             self.max_field.function_space().cell_node_map()),
                         self.min_field.dat(
                             op2.MIN,
                             self.min_field.function_space().cell_node_map()),
                         field.dat(op2.READ,
                                   field.function_space().cell_node_map()),
                         bottom_idx(op2.READ),
                         iteration_region=op2.ON_BOTTOM)

            op2.par_loop(kernel,
                         self.mesh.cell_set,
                         self.max_field.dat(
                             op2.MAX,
                             self.max_field.function_space().cell_node_map()),
                         self.min_field.dat(
                             op2.MIN,
                             self.min_field.function_space().cell_node_map()),
                         field.dat(op2.READ,
                                   field.function_space().cell_node_map()),
                         top_idx(op2.READ),
                         iteration_region=op2.ON_TOP)
        if self.squeezed_triangles:
            code = """
                void my_kernel(double *qmax, double *qmin, double *marker) {
                    float min_val, max_val;
                    for (int i=0; i<%(nnodes)d; i++) {
                        if (marker[i] > 0) {
                            max_val = qmax[i];
                            min_val = qmin[i];
                            break;
                        }
                    }
                    for (int i=i+1; i<%(nnodes)d; i++) {
                        if (marker[i] > 0) {
                            max_val = fmax(qmax[i], max_val);
                            min_val = fmin(qmin[i], min_val);
                        }
                    }
                    for (int i=0; i<%(nnodes)d; i++) {
                        if (marker[i] > 0) {
                            qmax[i] = max_val;
                            qmin[i] = min_val;
                        }
                    }
                }"""
            cnode_map = self.min_field.function_space().cell_node_map()
            kernel = op2.Kernel(code % {'nnodes': cnode_map.shape[1]},
                                'my_kernel')

            marker = self.squeezed_filter.marker

            # NOTE: for multiple squeezed triangle on top (e.g. ice front!) this currently only
            # works at the top, under the assumption that cells are iterated
            # over in each column bottom to top:
            op2.par_loop(
                kernel, self.mesh.cell_set,
                self.max_field.dat(
                    op2.MAX,
                    self.max_field.function_space().cell_node_map()),
                self.min_field.dat(
                    op2.MIN,
                    self.min_field.function_space().cell_node_map()),
                marker.dat(op2.READ,
                           marker.function_space().cell_node_map()))