Ejemplo n.º 1
0
def generate_torus_and_cycle_vertices(r_major,
                                      r_minor,
                                      n_major=20,
                                      n_minor=10,
                                      order=1):
    a = r_major
    b = r_minor
    u, v = np.mgrid[0:2 * np.pi:2 * np.pi / n_major,
                    0:2 * np.pi:2 * np.pi / n_minor]

    # https://web.archive.org/web/20160410151837/https://www.math.hmc.edu/~gu/curves_and_surfaces/surfaces/torus.html  # noqa
    x = np.cos(u) * (a + b * np.cos(v))
    y = np.sin(u) * (a + b * np.cos(v))
    z = b * np.sin(v)
    vertices = (np.vstack((x[np.newaxis], y[np.newaxis],
                           z[np.newaxis])).transpose(0, 2,
                                                     1).copy().reshape(3, -1))

    def idx(i, j):
        return (i % n_major) + (j % n_minor) * n_major

    vertex_indices = ([(idx(i, j), idx(i + 1, j), idx(i, j + 1))
                       for i in range(n_major) for j in range(n_minor)] +
                      [(idx(i + 1, j), idx(i + 1, j + 1), idx(i, j + 1))
                       for i in range(n_major) for j in range(n_minor)])

    vertex_indices = np.array(vertex_indices, dtype=np.int32)
    grp = make_group_from_vertices(vertices, vertex_indices, order)

    # ambient_dim, nelements, nunit_nodes
    nodes = grp.nodes.copy()

    major_theta = np.arctan2(nodes[1], nodes[0])
    rvec = np.array(
        [np.cos(major_theta),
         np.sin(major_theta),
         np.zeros_like(major_theta)])

    #               ^
    #               |
    # --------------+----.
    #           /   |     \
    #          /    |  _-- \
    #         |     |.^  | | y
    #         |     +------+--->
    #         |       x    |
    #          \          /
    #           \        /
    # ------------------'

    x = np.sum(nodes * rvec, axis=0) - a

    minor_theta = np.arctan2(nodes[2], x)

    nodes[0] = np.cos(major_theta) * (a + b * np.cos(minor_theta))
    nodes[1] = np.sin(major_theta) * (a + b * np.cos(minor_theta))
    nodes[2] = b * np.sin(minor_theta)

    from meshmode.mesh import Mesh
    return (Mesh(vertices, [grp.copy(nodes=nodes)],
                 is_conforming=True), [idx(i, 0) for i in range(n_major)],
            [idx(0, j) for j in range(n_minor)])
Ejemplo n.º 2
0
def test_sanity_single_element(ctx_factory, dim, order, visualize=False):
    pytest.importorskip("pytential")

    cl_ctx = ctx_factory()
    queue = cl.CommandQueue(cl_ctx)

    from modepy.tools import unit_vertices
    vertices = unit_vertices(dim).T.copy()

    center = np.empty(dim, np.float64)
    center.fill(-0.5)

    import modepy as mp
    from meshmode.mesh import SimplexElementGroup, Mesh, BTAG_ALL
    mg = SimplexElementGroup(
            order=order,
            vertex_indices=np.arange(dim+1, dtype=np.int32).reshape(1, -1),
            nodes=mp.warp_and_blend_nodes(dim, order).reshape(dim, 1, -1),
            dim=dim)

    mesh = Mesh(vertices, [mg], is_conforming=True)

    from meshmode.discretization import Discretization
    from meshmode.discretization.poly_element import \
            PolynomialWarpAndBlendGroupFactory
    vol_discr = Discretization(cl_ctx, mesh,
            PolynomialWarpAndBlendGroupFactory(order+3))

    # {{{ volume calculation check

    vol_x = vol_discr.nodes().with_queue(queue)

    vol_one = vol_x[0].copy()
    vol_one.fill(1)
    from pytential import norm, integral  # noqa

    from pytools import factorial
    true_vol = 1/factorial(dim) * 2**dim

    comp_vol = integral(vol_discr, queue, vol_one)
    rel_vol_err = abs(true_vol - comp_vol) / true_vol

    assert rel_vol_err < 1e-12

    # }}}

    # {{{ boundary discretization

    from meshmode.discretization.connection import make_face_restriction
    bdry_connection = make_face_restriction(
            vol_discr, PolynomialWarpAndBlendGroupFactory(order + 3),
            BTAG_ALL)
    bdry_discr = bdry_connection.to_discr

    # }}}

    # {{{ visualizers

    from meshmode.discretization.visualization import make_visualizer
    #vol_vis = make_visualizer(queue, vol_discr, 4)
    bdry_vis = make_visualizer(queue, bdry_discr, 4)

    # }}}

    from pytential import bind, sym
    bdry_normals = bind(bdry_discr, sym.normal(dim))(queue).as_vector(dtype=object)

    if visualize:
        bdry_vis.write_vtk_file("boundary.vtu", [
            ("bdry_normals", bdry_normals)
            ])

    normal_outward_check = bind(bdry_discr,
            sym.normal(dim)
            | (sym.nodes(dim) + 0.5*sym.ones_vec(dim)),
            )(queue).as_scalar() > 0

    assert normal_outward_check.get().all(), normal_outward_check.get()
Ejemplo n.º 3
0
def make_curve_mesh(curve_f,
                    element_boundaries,
                    order,
                    unit_nodes=None,
                    node_vertex_consistency_tolerance=None,
                    closed=True,
                    return_parametrization_points=False):
    """
    :arg curve_f: A callable representing a parametrization for a curve,
        accepting a vector of point locations and returning
        an array of shape *(2, npoints)*.
    :arg element_boundaries: a vector of element boundary locations in
        :math:`[0,1]`, in order. 0 must be the first entry, 1 the
        last one.
    :arg closed: if *True*, the curve is assumed closed and the first and
        last of the *element_boundaries* must match.
    :arg unit_nodes: if given, the unit nodes to use. Must have shape
        ``(dim, nnodes)``.
    :returns: a :class:`meshmode.mesh.Mesh`, or if *return_parametrization_points*
        is *True*, a tuple ``(mesh, par_points)``, where *par_points* is an array of
        parametrization points.
    """

    assert element_boundaries[0] == 0
    assert element_boundaries[-1] == 1
    nelements = len(element_boundaries) - 1

    if unit_nodes is None:
        unit_nodes = mp.warp_and_blend_nodes(1, order)
    nodes_01 = 0.5 * (unit_nodes + 1)

    wrap = nelements
    if not closed:
        wrap += 1

    vertices = curve_f(element_boundaries)[:, :wrap]
    vertex_indices = np.vstack([
        np.arange(0, nelements, dtype=np.int32),
        np.arange(1, nelements + 1, dtype=np.int32) % wrap
    ]).T

    assert vertices.shape[1] == np.max(vertex_indices) + 1
    if closed:
        start_end_par = np.array([0, 1], dtype=np.float64)
        start_end_curve = curve_f(start_end_par)

        assert la.norm(start_end_curve[:, 0] - start_end_curve[:, 1]) < 1.0e-12

    el_lengths = np.diff(element_boundaries)
    el_starts = element_boundaries[:-1]

    # (el_nr, node_nr)
    t = el_starts[:, np.newaxis] + el_lengths[:, np.newaxis] * nodes_01
    t = t.ravel()
    nodes = curve_f(t).reshape(vertices.shape[0], nelements, -1)

    from meshmode.mesh import Mesh, SimplexElementGroup
    egroup = SimplexElementGroup(order,
                                 vertex_indices=vertex_indices,
                                 nodes=nodes,
                                 unit_nodes=unit_nodes)

    mesh = Mesh(
        vertices=vertices,
        groups=[egroup],
        node_vertex_consistency_tolerance=node_vertex_consistency_tolerance,
        is_conforming=True)

    if return_parametrization_points:
        return mesh, t
    else:
        return mesh
Ejemplo n.º 4
0
    def refine(self, refine_flags):
        """
        :arg refine_flags: a :class:`numpy.ndarray` of dtype bool of length ``mesh.nelements``
            indicating which elements should be split.
        """

        if len(refine_flags) != self.last_mesh.nelements:
            raise ValueError("length of refine_flags does not match "
                             "element count of last generated mesh")

        #vertices and groups for next generation
        nvertices = len(self.last_mesh.vertices[0])

        groups = []

        midpoint_already = set()
        grpn = 0
        totalnelements = 0

        for grp in self.last_mesh.groups:
            iel_base = grp.element_nr_base
            nelements = 0
            for iel_grp in range(grp.nelements):
                nelements += 1
                vertex_indices = grp.vertex_indices[iel_grp]
                if refine_flags[iel_base + iel_grp]:
                    cur_dim = len(grp.vertex_indices[iel_grp]) - 1
                    nelements += len(self.simplex_result[cur_dim]) - 1
                    for i in range(len(vertex_indices)):
                        for j in range(i + 1, len(vertex_indices)):
                            i_index = vertex_indices[i]
                            j_index = vertex_indices[j]
                            index_tuple = (i_index,
                                           j_index) if i_index < j_index else (
                                               j_index, i_index)
                            if index_tuple not in midpoint_already and \
                                self.pair_map[index_tuple].midpoint is None:
                                nvertices += 1
                                midpoint_already.add(index_tuple)
            groups.append(
                np.empty([nelements, len(grp.vertex_indices[0])],
                         dtype=np.int32))
            grpn += 1
            totalnelements += nelements

        vertices = np.empty([len(self.last_mesh.vertices), nvertices])

        new_hanging_vertex_element = [[] for i in range(nvertices)]

        #        def remove_element_from_connectivity(vertices, new_hanging_vertex_elements, to_remove):
        #            #print(vertices)
        #            import itertools
        #            if len(vertices) == 2:
        #                min_vertex = min(vertices[0], vertices[1])
        #                max_vertex = max(vertices[0], vertices[1])
        #                ray = self.pair_map[(min_vertex, max_vertex)]
        #                self.remove_from_subtree(ray, new_hanging_vertex_elements, to_remove)
        #                return
        #
        #            cur_dim = len(vertices)-1
        #            element_rays = []
        #            midpoints = []
        #            split_possible = True
        #            for i in range(len(vertices)):
        #                for j in range(i+1, len(vertices)):
        #                    min_vertex = min(vertices[i], vertices[j])
        #                    max_vertex = max(vertices[i], vertices[j])
        #                    element_rays.append(self.pair_map[(min_vertex, max_vertex)])
        #                    if element_rays[len(element_rays)-1].midpoint is not None:
        #                        midpoints.append(element_rays[len(element_rays)-1].midpoint)
        #                    else:
        #                        split_possible = False

        #for node in element_rays:
        #self.remove_from_subtree(node, new_hanging_vertex_elements, to_remove)
        #if split_possible:
        #            if split_possible:
        #                node_tuple_to_coord = {}
        #                for node_index, node_tuple in enumerate(self.index_to_node_tuple[cur_dim]):
        #                    node_tuple_to_coord[node_tuple] = vertices[node_index]
        #                for midpoint_index, midpoint_tuple in enumerate(self.index_to_midpoint_tuple[cur_dim]):
        #                    node_tuple_to_coord[midpoint_tuple] = midpoints[midpoint_index]
        #                for i in range(len(self.simplex_result[cur_dim])):
        #                    next_vertices = []
        #                    for j in range(len(self.simplex_result[cur_dim][i])):
        #                        next_vertices.append(node_tuple_to_coord[self.simplex_node_tuples[cur_dim][self.simplex_result[cur_dim][i][j]]])
        #                    all_rays_present = True
        #                    for v1 in range(len(next_vertices)):
        #                        for v2 in range(v1+1, len(next_vertices)):
        #                            vertex_tuple = (min(next_vertices[v1], next_vertices[v2]), max(next_vertices[v1], next_vertices[v2]))
        #                            if vertex_tuple not in self.pair_map:
        #                                all_rays_present = False
        #                    if all_rays_present:
        #                        remove_element_from_connectivity(next_vertices, new_hanging_vertex_elements, to_remove)
        #                    else:
        #                        split_possible = False
        #            if not split_possible:
        #                next_vertices_list = list(itertools.combinations(vertices, len(vertices)-1))
        #                for next_vertices in next_vertices_list:
        #                    remove_element_from_connectivity(next_vertices, new_hanging_vertex_elements, to_remove)

        # {{{ Add element to connectivity

        def add_element_to_connectivity(vertices, new_hanging_vertex_elements,
                                        to_add):
            if len(vertices) == 2:
                min_vertex = min(vertices[0], vertices[1])
                max_vertex = max(vertices[0], vertices[1])
                ray = self.pair_map[(min_vertex, max_vertex)]
                self.add_to_subtree(ray, new_hanging_vertex_elements, to_add)
                return

            cur_dim = len(vertices) - 1
            element_rays = []
            midpoints = []
            split_possible = True
            for i in range(len(vertices)):
                for j in range(i + 1, len(vertices)):
                    min_vertex = min(vertices[i], vertices[j])
                    max_vertex = max(vertices[i], vertices[j])
                    element_rays.append(self.pair_map[(min_vertex,
                                                       max_vertex)])
                    if element_rays[len(element_rays) -
                                    1].midpoint is not None:
                        midpoints.append(element_rays[len(element_rays) -
                                                      1].midpoint)
                    else:
                        split_possible = False
            #for node in element_rays:
            #self.add_to_subtree(node, new_hanging_vertex_elements, to_add)
            if split_possible:
                node_tuple_to_coord = {}
                for node_index, node_tuple in enumerate(
                        self.index_to_node_tuple[cur_dim]):
                    node_tuple_to_coord[node_tuple] = vertices[node_index]
                for midpoint_index, midpoint_tuple in enumerate(
                        self.index_to_midpoint_tuple[cur_dim]):
                    node_tuple_to_coord[midpoint_tuple] = midpoints[
                        midpoint_index]
                for i in range(len(self.simplex_result[cur_dim])):
                    next_vertices = []
                    for j in range(len(self.simplex_result[cur_dim][i])):
                        next_vertices.append(node_tuple_to_coord[
                            self.simplex_node_tuples[cur_dim][
                                self.simplex_result[cur_dim][i][j]]])
                    all_rays_present = True
                    for v1 in range(len(next_vertices)):
                        for v2 in range(v1 + 1, len(next_vertices)):
                            vertex_tuple = (min(next_vertices[v1],
                                                next_vertices[v2]),
                                            max(next_vertices[v1],
                                                next_vertices[v2]))
                            if vertex_tuple not in self.pair_map:
                                all_rays_present = False
                    if all_rays_present:
                        add_element_to_connectivity(
                            next_vertices, new_hanging_vertex_elements, to_add)
                    else:
                        split_possible = False
            if not split_possible:
                next_vertices_list = list(
                    itertools.combinations(vertices,
                                           len(vertices) - 1))
                for next_vertices in next_vertices_list:
                    add_element_to_connectivity(next_vertices,
                                                new_hanging_vertex_elements,
                                                to_add)
#            for node in element_rays:
#                self.add_element_to_connectivity(node, new_hanging_vertex_elements, to_add)
#               leaves = self.get_subtree(node)
#               for leaf in leaves:
#                   if to_add not in leaf.adjacent_elements:
#                       leaf.adjacent_elements.append(to_add)
#                   if to_add not in new_hanging_vertex_elements[leaf.left_vertex]:
#                       new_hanging_vertex_elements[leaf.left_vertex].append(to_add)
#                   if to_add not in new_hanging_vertex_elements[leaf.right_vertex]:
#                       new_hanging_vertex_elements[leaf.right_vertex].append(to_add)

#            next_element_rays = []
#            for i in range(len(element_rays)):
#                for j in range(i+1, len(element_rays)):
#                    if element_rays[i].midpoint is not None and element_rays[j].midpoint is not None:
#                        min_midpoint = min(element_rays[i].midpoint, element_rays[j].midpoint)
#                        max_midpoint = max(element_rays[i].midpoint, element_rays[j].midpoint)
#                        vertex_pair = (min_midpoint, max_midpoint)
#                        if vertex_pair in self.pair_map:
#                            next_element_rays.append(self.pair_map[vertex_pair])
#                            cur_next_rays = []
#                            if element_rays[i].left_vertex == element_rays[j].left_vertex:
#                                cur_next_rays = [element_rays[i].left, element_rays[j].left, self.pair_map[vertex_pair]]
#                            if element_rays[i].right_vertex == element_rays[j].right_vertex:
#                                cur_next_rays = [element_rays[i].right, element_rays[j].right, self.pair_map[vertex_pair]]
#                            if element_rays[i].left_vertex == element_rays[j].right_vertex:
#                                cur_next_rays = [element_rays[i].left, element_rays[j].right, self.pair_map[vertex_pair]]
#                            if element_rays[i].right_vertex == element_rays[j].left_vertex:
#                                cur_next_rays = [element_rays[i].right, element_rays[j].left, self.pair_map[vertex_pair]]
#                            assert (cur_next_rays != [])
#                            #print cur_next_rays
#                            add_element_to_connectivity(cur_next_rays, new_hanging_vertex_elements, to_add)
#                        else:
#                            return
#                    else:
#                        return
#            add_element_to_connectivity(next_element_rays, new_hanging_vertex_elements, to_add)

# }}}

# {{{ Add hanging vertex element

        def add_hanging_vertex_el(v_index, el):
            assert not (v_index == 37 and el == 48)

            new_hanging_vertex_element[v_index].append(el)

        # }}}

#        def remove_ray_el(ray, el):
#            ray.remove(el)

# {{{ Check adjacent elements

        def check_adjacent_elements(groups, new_hanging_vertex_elements,
                                    nelements_in_grp):
            for grp in groups:
                iel_base = 0
                for iel_grp in range(nelements_in_grp):
                    vertex_indices = grp[iel_grp]
                    for i in range(len(vertex_indices)):
                        for j in range(i + 1, len(vertex_indices)):
                            min_index = min(vertex_indices[i],
                                            vertex_indices[j])
                            max_index = max(vertex_indices[i],
                                            vertex_indices[j])
                            cur_node = self.pair_map[(min_index, max_index)]
                            #print iel_base+iel_grp, cur_node.left_vertex, cur_node.right_vertex
                            #if (iel_base + iel_grp) not in cur_node.adjacent_elements:
                            #print min_index, max_index
                            #print iel_base + iel_grp, cur_node.left_vertex, cur_node.right_vertex, cur_node.adjacent_elements
                            #assert (4 in new_hanging_vertex_elements[cur_node.left_vertex] or 4 in new_hanging_vertex_elements[cur_node.right_vertex])
                            assert ((iel_base + iel_grp)
                                    in cur_node.adjacent_elements)
                            assert ((iel_base + iel_grp)
                                    in new_hanging_vertex_elements[
                                        cur_node.left_vertex])
                            assert ((iel_base + iel_grp)
                                    in new_hanging_vertex_elements[
                                        cur_node.right_vertex])

        # }}}

        for i in range(len(self.last_mesh.vertices)):
            for j in range(len(self.last_mesh.vertices[i])):
                vertices[i, j] = self.last_mesh.vertices[i, j]
                import copy
                if i == 0:
                    new_hanging_vertex_element[j] = copy.deepcopy(
                        self.hanging_vertex_element[j])
        grpn = 0
        for grp in self.last_mesh.groups:
            for iel_grp in range(grp.nelements):
                for i in range(len(grp.vertex_indices[iel_grp])):
                    groups[grpn][iel_grp][i] = grp.vertex_indices[iel_grp][i]
            grpn += 1

        grpn = 0
        vertices_index = len(self.last_mesh.vertices[0])
        nelements_in_grp = grp.nelements
        del self.group_refinement_records[:]

        for grp_idx, grp in enumerate(self.last_mesh.groups):
            iel_base = grp.element_nr_base
            # List of lists mapping element number to new element number(s).
            element_mapping = []
            tesselation = None

            # {{{ get midpoint coordinates for vertices

            midpoints_to_find = []
            resampler = None
            for iel_grp in range(grp.nelements):
                if refine_flags[iel_base + iel_grp]:
                    # if simplex
                    if len(grp.vertex_indices[iel_grp]) == grp.dim + 1:
                        midpoints_to_find.append(iel_grp)
                        if not resampler:
                            from meshmode.mesh.refinement.resampler import (
                                SimplexResampler)
                            resampler = SimplexResampler()
                            tesselation = _Tesselation(
                                self.simplex_result[grp.dim],
                                self.simplex_node_tuples[grp.dim])
                    else:
                        raise NotImplementedError(
                            "unimplemented: midpoint finding"
                            "for non simplex elements")

            if midpoints_to_find:
                midpoints = resampler.get_midpoints(grp, tesselation,
                                                    midpoints_to_find)
                midpoint_order = resampler.get_vertex_pair_to_midpoint_order(
                    grp.dim)

            del midpoints_to_find

            # }}}

            for iel_grp in range(grp.nelements):
                element_mapping.append([iel_grp])
                if refine_flags[iel_base + iel_grp]:
                    midpoint_vertices = []
                    vertex_indices = grp.vertex_indices[iel_grp]
                    # if simplex
                    if len(vertex_indices) == grp.dim + 1:
                        for i in range(len(vertex_indices)):
                            for j in range(i + 1, len(vertex_indices)):
                                min_index = min(vertex_indices[i],
                                                vertex_indices[j])
                                max_index = max(vertex_indices[i],
                                                vertex_indices[j])
                                cur_node = self.pair_map[(min_index,
                                                          max_index)]
                                if cur_node.midpoint is None:
                                    cur_node.midpoint = vertices_index
                                    import copy
                                    cur_node.left = TreeRayNode(
                                        min_index, vertices_index,
                                        copy.deepcopy(
                                            cur_node.adjacent_elements))
                                    cur_node.left.parent = cur_node
                                    cur_node.right = TreeRayNode(
                                        max_index, vertices_index,
                                        copy.deepcopy(
                                            cur_node.adjacent_elements))
                                    cur_node.right.parent = cur_node
                                    vertex_pair1 = (min_index, vertices_index)
                                    vertex_pair2 = (max_index, vertices_index)
                                    self.pair_map[vertex_pair1] = cur_node.left
                                    self.pair_map[
                                        vertex_pair2] = cur_node.right
                                    midpoint_idx = midpoint_order[(i, j)]
                                    vertices[:, vertices_index] = \
                                            midpoints[iel_grp][:, midpoint_idx]
                                    midpoint_vertices.append(vertices_index)
                                    vertices_index += 1
                                else:
                                    cur_midpoint = cur_node.midpoint
                                    midpoint_vertices.append(cur_midpoint)

                        #generate new rays
                        cur_dim = len(grp.vertex_indices[0]) - 1
                        for i in range(len(midpoint_vertices)):
                            for j in range(i + 1, len(midpoint_vertices)):
                                min_index = min(midpoint_vertices[i],
                                                midpoint_vertices[j])
                                max_index = max(midpoint_vertices[i],
                                                midpoint_vertices[j])
                                vertex_pair = (min_index, max_index)
                                if vertex_pair in self.pair_map:
                                    continue
                                self.pair_map[vertex_pair] = TreeRayNode(
                                    min_index, max_index, [])
                        node_tuple_to_coord = {}
                        for node_index, node_tuple in enumerate(
                                self.index_to_node_tuple[cur_dim]):
                            node_tuple_to_coord[
                                node_tuple] = grp.vertex_indices[iel_grp][
                                    node_index]
                        for midpoint_index, midpoint_tuple in enumerate(
                                self.index_to_midpoint_tuple[cur_dim]):
                            node_tuple_to_coord[
                                midpoint_tuple] = midpoint_vertices[
                                    midpoint_index]
                        for i in range(len(self.simplex_result[cur_dim])):
                            if i == 0:
                                iel = iel_grp
                            else:
                                iel = nelements_in_grp + i - 1
                                element_mapping[-1].append(iel)
                            for j in range(len(
                                    self.simplex_result[cur_dim][i])):
                                groups[grpn][iel][j] = \
                                    node_tuple_to_coord[self.simplex_node_tuples[cur_dim][self.simplex_result[cur_dim][i][j]]]
                        nelements_in_grp += len(
                            self.simplex_result[cur_dim]) - 1
                    #assuming quad otherwise
                    else:
                        #quadrilateral
                        raise NotImplementedError("unimplemented: "
                                                  "support for quad elements")


#                        node_tuple_to_coord = {}
#                        for node_index, node_tuple in enumerate(self.index_to_node_tuple[cur_dim]):
#                            node_tuple_to_coord[node_tuple] = grp.vertex_indices[iel_grp][node_index]
#                        def generate_all_tuples(cur_list):
#                            if len(cur_list[len(cur_list)-1])

            self.group_refinement_records.append(
                _GroupRefinementRecord(tesselation, element_mapping))

        #clear connectivity data
        for grp in self.last_mesh.groups:
            iel_base = grp.element_nr_base
            for iel_grp in range(grp.nelements):
                for i in range(len(grp.vertex_indices[iel_grp])):
                    for j in range(i + 1, len(grp.vertex_indices[iel_grp])):
                        min_vert = min(grp.vertex_indices[iel_grp][i],
                                       grp.vertex_indices[iel_grp][j])
                        max_vert = max(grp.vertex_indices[iel_grp][i],
                                       grp.vertex_indices[iel_grp][j])
                        vertex_pair = (min_vert, max_vert)
                        root_ray = self.get_root(self.pair_map[vertex_pair])
                        if root_ray not in self.seen_tuple:
                            self.seen_tuple[root_ray] = True
                            cur_tree = self.get_subtree(root_ray)
                            for node in cur_tree:
                                node.adjacent_elements = []
                                new_hanging_vertex_element[
                                    node.left_vertex] = []
                                new_hanging_vertex_element[
                                    node.right_vertex] = []

        self.seen_tuple.clear()

        nelements_in_grp = grp.nelements
        for grp in groups:
            for iel_grp in range(len(grp)):
                add_verts = []
                for i in range(len(grp[iel_grp])):
                    add_verts.append(grp[iel_grp][i])
                add_element_to_connectivity(add_verts,
                                            new_hanging_vertex_element,
                                            iel_base + iel_grp)
        #assert ray connectivity
        #check_adjacent_elements(groups, new_hanging_vertex_element, nelements_in_grp)

        self.hanging_vertex_element = new_hanging_vertex_element

        # {{{ make new groups

        new_mesh_el_groups = []

        for refinement_record, group, prev_group in zip(
                self.group_refinement_records, groups, self.last_mesh.groups):
            is_simplex = len(
                prev_group.vertex_indices[0]) == prev_group.dim + 1
            ambient_dim = len(prev_group.nodes)
            nelements = len(group)
            nunit_nodes = len(prev_group.unit_nodes[0])

            nodes = np.empty((ambient_dim, nelements, nunit_nodes),
                             dtype=prev_group.nodes.dtype)

            element_mapping = refinement_record.element_mapping

            to_resample = [
                elem for elem in range(len(element_mapping))
                if len(element_mapping[elem]) > 1
            ]

            if to_resample:
                # if simplex
                if is_simplex:
                    from meshmode.mesh.refinement.resampler import SimplexResampler
                    resampler = SimplexResampler()
                    new_nodes = resampler.get_tesselated_nodes(
                        prev_group, refinement_record.tesselation, to_resample)
                else:
                    raise NotImplementedError(
                        "unimplemented: node resampling for non simplex elements"
                    )

            for elem, mapped_elems in enumerate(element_mapping):
                if len(mapped_elems) == 1:
                    # No resampling required, just copy over
                    nodes[:, mapped_elems[0]] = prev_group.nodes[:, elem]
                    n = nodes[:, mapped_elems[0]]
                else:
                    nodes[:, mapped_elems] = new_nodes[elem]

            if is_simplex:
                new_mesh_el_groups.append(
                    type(prev_group)(order=prev_group.order,
                                     vertex_indices=group,
                                     nodes=nodes,
                                     unit_nodes=prev_group.unit_nodes))
            else:
                raise NotImplementedError("unimplemented: support for creating"
                                          "non simplex element groups")

        # }}}

        from meshmode.mesh import Mesh

        refine_flags = refine_flags.astype(np.bool)

        self.previous_mesh = self.last_mesh
        self.last_mesh = Mesh(vertices,
                              new_mesh_el_groups,
                              nodal_adjacency=self.generate_nodal_adjacency(
                                  totalnelements, nvertices, groups),
                              vertex_id_dtype=self.last_mesh.vertex_id_dtype,
                              element_id_dtype=self.last_mesh.element_id_dtype,
                              is_conforming=(self.last_mesh.is_conforming
                                             and (refine_flags.all() or
                                                  (~refine_flags).all())))
        return self.last_mesh
Ejemplo n.º 5
0
    def refine(self, refine_flags):
        """
        :arg refine_flags: a :class:`numpy.ndarray` of dtype bool of length
            ``mesh.nelements`` indicating which elements should be split.
        """

        mesh = self._current_mesh
        refine_flags = np.asarray(refine_flags, dtype=np.bool)

        if len(refine_flags) != mesh.nelements:
            raise ValueError("length of refine_flags does not match "
                             "element count of last generated mesh")

        perform_vertex_updates = mesh.vertices is not None

        new_el_groups = []
        group_refinement_records = []
        additional_vertices = []
        if perform_vertex_updates:
            inew_vertex = mesh.nvertices

        for igrp, group in enumerate(mesh.groups):
            bisection_info = self._get_bisection_tesselation_info(
                type(group), group.dim)

            # {{{ compute counts and index arrays

            grp_flags = refine_flags[group.
                                     element_nr_base:group.element_nr_base +
                                     group.nelements]

            nchildren = len(bisection_info.children)
            nchild_elements = np.ones(group.nelements,
                                      dtype=mesh.element_id_dtype)
            nchild_elements[grp_flags] = nchildren

            child_el_indices = np.empty(group.nelements + 1,
                                        dtype=mesh.element_id_dtype)
            child_el_indices[0] = 0
            child_el_indices[1:] = np.cumsum(nchild_elements)

            unrefined_el_new_indices = child_el_indices[:-1][~grp_flags]
            refining_el_old_indices, = np.where(grp_flags)

            new_nelements = child_el_indices[-1]

            # }}}

            group_refinement_records.append(
                _GroupRefinementRecord(tesselation=bisection_info,
                                       element_mapping=[
                                           list(
                                               range(
                                                   child_el_indices[iel],
                                                   child_el_indices[iel] +
                                                   nchild_elements[iel]))
                                           for iel in range(group.nelements)
                                       ]))

            # {{{ get new vertices together

            if perform_vertex_updates:
                midpoints = bisection_info.resampler.get_midpoints(
                    group, bisection_info, refining_el_old_indices)

                new_vertex_indices = np.empty(
                    (new_nelements, group.vertex_indices.shape[1]),
                    dtype=mesh.vertex_id_dtype)
                new_vertex_indices.fill(-17)

                # copy over unchanged vertices
                new_vertex_indices[unrefined_el_new_indices] = \
                        group.vertex_indices[~grp_flags]

                for old_iel in refining_el_old_indices:
                    new_iel_base = child_el_indices[old_iel]

                    refining_vertices = np.empty(len(
                        bisection_info.ref_vertices),
                                                 dtype=mesh.vertex_id_dtype)
                    refining_vertices.fill(-17)

                    # carry over old vertices
                    refining_vertices[bisection_info.orig_vertex_indices] = \
                            group.vertex_indices[old_iel]

                    for imidpoint, (iref_midpoint, (v1, v2)) in enumerate(
                            zip(bisection_info.midpoint_indices,
                                bisection_info.midpoint_vertex_pairs)):

                        global_v1 = group.vertex_indices[old_iel, v1]
                        global_v2 = group.vertex_indices[old_iel, v2]

                        if global_v1 > global_v2:
                            global_v1, global_v2 = global_v2, global_v1

                        try:
                            global_midpoint = self.global_vertex_pair_to_midpoint[
                                global_v1, global_v2]
                        except KeyError:
                            global_midpoint = inew_vertex
                            additional_vertices.append(
                                midpoints[old_iel][:, imidpoint])
                            inew_vertex += 1

                        refining_vertices[iref_midpoint] = global_midpoint

                    assert (refining_vertices >= 0).all()

                    new_vertex_indices[new_iel_base:new_iel_base+nchildren] = \
                            refining_vertices[bisection_info.children]

                assert (new_vertex_indices >= 0).all()
            else:
                new_vertex_indices = None

            # }}}

            # {{{ get new nodes together

            new_nodes = np.empty(
                (mesh.ambient_dim, new_nelements, group.nunit_nodes),
                dtype=group.nodes.dtype)

            new_nodes.fill(float("nan"))

            # copy over unchanged nodes
            new_nodes[:, unrefined_el_new_indices] = group.nodes[:, ~grp_flags]

            tesselated_nodes = bisection_info.resampler.get_tesselated_nodes(
                group, bisection_info, refining_el_old_indices)

            for old_iel in refining_el_old_indices:
                new_iel_base = child_el_indices[old_iel]
                new_nodes[:, new_iel_base:new_iel_base+nchildren, :] = \
                        tesselated_nodes[old_iel]

            assert (~np.isnan(new_nodes)).all()

            # }}}

            new_el_groups.append(
                type(group)(order=group.order,
                            vertex_indices=new_vertex_indices,
                            nodes=new_nodes,
                            unit_nodes=group.unit_nodes))

        if perform_vertex_updates:
            new_vertices = np.empty(
                (mesh.ambient_dim, mesh.nvertices + len(additional_vertices)),
                mesh.vertices.dtype)
            new_vertices[:, :mesh.nvertices] = mesh.vertices
            new_vertices[:, mesh.nvertices:] = np.array(additional_vertices).T
        else:
            new_vertices = None

        from meshmode.mesh import Mesh
        new_mesh = Mesh(new_vertices,
                        new_el_groups,
                        is_conforming=(mesh.is_conforming
                                       and (refine_flags.all() or
                                            (~refine_flags).all())))

        self.group_refinement_records = group_refinement_records
        self._current_mesh = new_mesh
        self._previous_mesh = mesh

        return new_mesh
Ejemplo n.º 6
0
def partition_mesh(mesh, part_per_element, part_num):
    """
    :arg mesh: A :class:`~meshmode.mesh.Mesh` to be partitioned.
    :arg part_per_element: A :class:`numpy.ndarray` containing one
        integer per element of *mesh* indicating which part of the
        partitioned mesh the element is to become a part of.
    :arg part_num: The part number of the mesh to return.

    :returns: A tuple ``(part_mesh, part_to_global)``, where *part_mesh*
        is a :class:`~meshmode.mesh.Mesh` that is a partition of mesh, and
        *part_to_global* is a :class:`numpy.ndarray` mapping element
        numbers on *part_mesh* to ones in *mesh*.

    .. versionadded:: 2017.1
    """
    assert len(part_per_element) == mesh.nelements, (
        "part_per_element must have shape (mesh.nelements,)")

    # Contains the indices of the elements requested.
    queried_elems = np.where(np.array(part_per_element) == part_num)[0]

    global_elem_to_part_elem = _compute_global_elem_to_part_elem(part_per_element,
                {part_num}, mesh.element_id_dtype)

    # Create new mesh groups that mimick the original mesh's groups but only contain
    # the local partition's elements
    part_mesh_groups, global_group_to_part_group, required_vertex_indices =\
                _filter_mesh_groups(mesh.groups, queried_elems, mesh.vertex_id_dtype)

    part_vertices = np.zeros((mesh.ambient_dim, len(required_vertex_indices)))
    for dim in range(mesh.ambient_dim):
        part_vertices[dim] = mesh.vertices[dim][required_vertex_indices]

    part_mesh_group_elem_base = [0 for _ in part_mesh_groups]
    el_nr = 0
    for i_part_grp, grp in enumerate(part_mesh_groups):
        part_mesh_group_elem_base[i_part_grp] = el_nr
        el_nr += grp.nelements

    local_to_local_adj_groups = _create_local_to_local_adjacency_groups(mesh,
                global_elem_to_part_elem, part_mesh_groups,
                global_group_to_part_group, part_mesh_group_elem_base)

    nonlocal_adj_data = _collect_nonlocal_adjacency_data(mesh,
                np.array(part_per_element), global_elem_to_part_elem,
                part_mesh_groups, global_group_to_part_group,
                part_mesh_group_elem_base)

    bdry_data = _collect_bdry_data(mesh, global_elem_to_part_elem, part_mesh_groups,
                global_group_to_part_group, part_mesh_group_elem_base)

    group_neighbor_parts = [adj.neighbor_parts for adj in nonlocal_adj_data if
                adj is not None]
    all_neighbor_parts = set(np.unique(np.concatenate(group_neighbor_parts))) if\
                group_neighbor_parts else set()

    boundary_tags = mesh.boundary_tags[:]
    btag_to_index = {tag: i for i, tag in enumerate(boundary_tags)}

    def boundary_tag_bit(boundary_tag):
        from meshmode.mesh import _boundary_tag_bit
        return _boundary_tag_bit(boundary_tags, btag_to_index, boundary_tag)

    from meshmode.mesh import BTAG_PARTITION
    for i_neighbor_part in all_neighbor_parts:
        part_tag = BTAG_PARTITION(i_neighbor_part)
        boundary_tags.append(part_tag)
        btag_to_index[part_tag] = len(boundary_tags)-1

    inter_partition_adj_groups = _create_inter_partition_adjacency_groups(mesh,
                part_per_element, part_mesh_groups, all_neighbor_parts,
                nonlocal_adj_data, bdry_data, boundary_tag_bit)

    # Combine local and inter-partition/boundary adjacency groups
    part_facial_adj_groups = local_to_local_adj_groups
    for igrp, facial_adj in enumerate(inter_partition_adj_groups):
        part_facial_adj_groups[igrp][None] = facial_adj

    from meshmode.mesh import Mesh
    part_mesh = Mesh(
            part_vertices,
            part_mesh_groups,
            facial_adjacency_groups=part_facial_adj_groups,
            boundary_tags=boundary_tags,
            is_conforming=mesh.is_conforming)

    return part_mesh, queried_elems
Ejemplo n.º 7
0
def merge_disjoint_meshes(meshes, skip_tests=False, single_group=False):
    if not meshes:
        raise ValueError("must pass at least one mesh")

    from pytools import is_single_valued
    if not is_single_valued(mesh.ambient_dim for mesh in meshes):
        raise ValueError("all meshes must share the same ambient dimension")

    # {{{ assemble combined vertex array

    ambient_dim = meshes[0].ambient_dim
    nvertices = sum(
            mesh.vertices.shape[-1]
            for mesh in meshes)

    vert_dtype = np.find_common_type(
            [mesh.vertices.dtype for mesh in meshes],
            [])
    vertices = np.empty(
            (ambient_dim, nvertices), vert_dtype)

    current_vert_base = 0
    vert_bases = []
    for mesh in meshes:
        mesh_nvert = mesh.vertices.shape[-1]
        vertices[:, current_vert_base:current_vert_base+mesh_nvert] = \
                mesh.vertices

        vert_bases.append(current_vert_base)
        current_vert_base += mesh_nvert

    # }}}

    # {{{ assemble new groups list

    nodal_adjacency = None
    facial_adjacency_groups = None

    if single_group:
        grp_cls = None
        order = None
        unit_nodes = None
        nodal_adjacency = None
        facial_adjacency_groups = None

        for mesh in meshes:
            if mesh._nodal_adjacency is not None:
                nodal_adjacency = False
            if mesh._facial_adjacency_groups is not None:
                facial_adjacency_groups = False

            for group in mesh.groups:
                if grp_cls is None:
                    grp_cls = type(group)
                    order = group.order
                    unit_nodes = group.unit_nodes
                else:
                    assert type(group) == grp_cls
                    assert group.order == order
                    assert np.array_equal(unit_nodes, group.unit_nodes)

        vertex_indices = np.vstack([
            group.vertex_indices + vert_base
            for mesh, vert_base in zip(meshes, vert_bases)
            for group in mesh.groups])
        nodes = np.hstack([
            group.nodes
            for mesh in meshes
            for group in mesh.groups])

        if not nodes.flags.c_contiguous:
            # hstack stopped producing C-contiguous arrays in numpy 1.14
            nodes = nodes.copy(order="C")

        new_groups = [
                grp_cls(order, vertex_indices, nodes, unit_nodes=unit_nodes)]

    else:
        new_groups = []
        nodal_adjacency = None
        facial_adjacency_groups = None

        for mesh, vert_base in zip(meshes, vert_bases):
            if mesh._nodal_adjacency is not None:
                nodal_adjacency = False
            if mesh._facial_adjacency_groups is not None:
                facial_adjacency_groups = False

            for group in mesh.groups:
                new_vertex_indices = group.vertex_indices + vert_base
                new_group = group.copy(vertex_indices=new_vertex_indices)
                new_groups.append(new_group)

    # }}}

    from meshmode.mesh import Mesh
    return Mesh(vertices, new_groups, skip_tests=skip_tests,
            nodal_adjacency=nodal_adjacency,
            facial_adjacency_groups=facial_adjacency_groups,
            is_conforming=all(
                mesh.is_conforming
                for mesh in meshes))
Ejemplo n.º 8
0
    def get_mesh(self):
        el_type_hist = {}
        for el_type in self.element_types:
            el_type_hist[el_type] = el_type_hist.get(el_type, 0) + 1

        if not el_type_hist:
            raise RuntimeError("empty mesh in gmsh input")

        groups = self.groups = []

        ambient_dim = self.points.shape[-1]

        mesh_bulk_dim = max(el_type.dimensions
                            for el_type in six.iterkeys(el_type_hist))

        # {{{ build vertex numbering

        # map set of face vertex indices to list of tags associated to face
        face_vertex_indices_to_tags = {}
        vertex_gmsh_index_to_mine = {}
        for element, (el_vertices, el_type) in enumerate(
                zip(self.element_vertices, self.element_types)):
            for gmsh_vertex_nr in el_vertices:
                if gmsh_vertex_nr not in vertex_gmsh_index_to_mine:
                    vertex_gmsh_index_to_mine[gmsh_vertex_nr] = \
                            len(vertex_gmsh_index_to_mine)
            if self.tags:
                el_tag_indexes = [
                    self.gmsh_tag_index_to_mine[t]
                    for t in self.element_markers[element]
                ]
                # record tags of boundary dimension
                el_tags = [
                    self.tags[i][0] for i in el_tag_indexes
                    if self.tags[i][1] == mesh_bulk_dim - 1
                ]
                el_grp_verts = {
                    vertex_gmsh_index_to_mine[e]
                    for e in el_vertices
                }
                face_vertex_indices = frozenset(el_grp_verts)
                if face_vertex_indices not in face_vertex_indices_to_tags:
                    face_vertex_indices_to_tags[face_vertex_indices] = []
                face_vertex_indices_to_tags[face_vertex_indices] += el_tags

        # }}}

        # {{{ build vertex array

        gmsh_vertex_indices, my_vertex_indices = \
                list(zip(*six.iteritems(vertex_gmsh_index_to_mine)))
        vertices = np.empty((ambient_dim, len(vertex_gmsh_index_to_mine)),
                            dtype=np.float64)
        vertices[:, np.array(my_vertex_indices, np.intp)] = \
                self.points[np.array(gmsh_vertex_indices, np.intp)].T

        # }}}

        from meshmode.mesh import (Mesh, SimplexElementGroup,
                                   TensorProductElementGroup)

        bulk_el_types = set()

        for group_el_type, ngroup_elements in six.iteritems(el_type_hist):
            if group_el_type.dimensions != mesh_bulk_dim:
                continue

            bulk_el_types.add(group_el_type)

            nodes = np.empty(
                (ambient_dim, ngroup_elements, el_type.node_count()),
                np.float64)
            el_vertex_count = group_el_type.vertex_count()
            vertex_indices = np.empty((ngroup_elements, el_vertex_count),
                                      np.int32)
            i = 0

            for element, (el_vertices, el_nodes, el_type) in enumerate(
                    zip(self.element_vertices, self.element_nodes,
                        self.element_types)):
                if el_type is not group_el_type:
                    continue

                nodes[:, i] = self.points[el_nodes].T
                vertex_indices[i] = [
                    vertex_gmsh_index_to_mine[v_nr] for v_nr in el_vertices
                ]

                i += 1

            unit_nodes = (np.array(group_el_type.lexicographic_node_tuples(),
                                   dtype=np.float64).T /
                          group_el_type.order) * 2 - 1

            if isinstance(group_el_type, GmshSimplexElementBase):
                group = SimplexElementGroup(group_el_type.order,
                                            vertex_indices,
                                            nodes,
                                            unit_nodes=unit_nodes)

                if group.dim == 2:
                    from meshmode.mesh.processing import flip_simplex_element_group
                    group = flip_simplex_element_group(
                        vertices, group, np.ones(ngroup_elements, np.bool))

            elif isinstance(group_el_type, GmshTensorProductElementBase):
                gmsh_vertex_tuples = type(group_el_type)(
                    order=1).gmsh_node_tuples()
                gmsh_vertex_tuples_loc_dict = dict(
                    (gvt, i) for i, gvt in enumerate(gmsh_vertex_tuples))

                from pytools import (generate_nonnegative_integer_tuples_below
                                     as gnitb)
                vertex_shuffle = np.array([
                    gmsh_vertex_tuples_loc_dict[vt]
                    for vt in gnitb(2, group_el_type.dimensions)
                ])

                group = TensorProductElementGroup(
                    group_el_type.order,
                    vertex_indices[:, vertex_shuffle],
                    nodes,
                    unit_nodes=unit_nodes)
            else:
                raise NotImplementedError("gmsh element type: %s" %
                                          type(group_el_type).__name__)

            groups.append(group)

        # FIXME: This is heuristic.
        if len(bulk_el_types) == 1:
            is_conforming = True
        else:
            is_conforming = mesh_bulk_dim < 3

        # construct boundary tags for mesh
        from meshmode.mesh import BTAG_ALL, BTAG_REALLY_ALL
        boundary_tags = [BTAG_ALL, BTAG_REALLY_ALL]
        if self.tags:
            boundary_tags += [
                tag for tag, dim in self.tags if dim == mesh_bulk_dim - 1
            ]

        # compute facial adjacency for Mesh if there is tag information
        facial_adjacency_groups = None
        if is_conforming and self.tags:
            from meshmode.mesh import _compute_facial_adjacency_from_vertices
            facial_adjacency_groups = _compute_facial_adjacency_from_vertices(
                groups, boundary_tags, np.int32, np.int8,
                face_vertex_indices_to_tags)

        return Mesh(vertices,
                    groups,
                    is_conforming=is_conforming,
                    facial_adjacency_groups=facial_adjacency_groups,
                    boundary_tags=boundary_tags,
                    **self.mesh_construction_kwargs)
Ejemplo n.º 9
0
def generate_box_mesh(axis_coords, order=1, coord_dtype=np.float64,
        group_factory=None):
    """Create a semi-structured mesh.

    :param axis_coords: a tuple with a number of entries corresponding
        to the number of dimensions, with each entry a numpy array
        specifying the coordinates to be used along that axis.
    :param group_factory: One of :class:`meshmode.mesh.SimplexElementGroup`
        or :class:`meshmode.mesh.TensorProductElementGroup`.

    .. versionchanged:: 2017.1

        *group_factory* parameter added.
    """

    for iaxis, axc in enumerate(axis_coords):
        if len(axc) < 2:
            raise ValueError("need at least two points along axis %d"
                    % (iaxis+1))

    dim = len(axis_coords)

    shape = tuple(len(axc) for axc in axis_coords)

    from pytools import product
    nvertices = product(shape)

    vertex_indices = np.arange(nvertices).reshape(*shape, order="F")

    vertices = np.empty((dim,)+shape, dtype=coord_dtype)
    for idim in range(dim):
        vshape = (shape[idim],) + (1,)*idim
        vertices[idim] = axis_coords[idim].reshape(*vshape)

    vertices = vertices.reshape(dim, -1)

    from meshmode.mesh import SimplexElementGroup, TensorProductElementGroup
    if group_factory is None:
        group_factory = SimplexElementGroup

    if issubclass(group_factory, SimplexElementGroup):
        is_tp = False
    elif issubclass(group_factory, TensorProductElementGroup):
        is_tp = True
    else:
        raise ValueError("unsupported value for 'group_factory': %s"
                % group_factory)

    el_vertices = []

    if dim == 1:
        for i in range(shape[0]-1):
            # a--b

            a = vertex_indices[i]
            b = vertex_indices[i+1]

            el_vertices.append((a, b,))

    elif dim == 2:
        for i in range(shape[0]-1):
            for j in range(shape[1]-1):

                # c--d
                # |  |
                # a--b

                a = vertex_indices[i, j]
                b = vertex_indices[i+1, j]
                c = vertex_indices[i, j+1]
                d = vertex_indices[i+1, j+1]

                if is_tp:
                    el_vertices.append((a, b, c, d))
                else:
                    el_vertices.append((a, b, c))
                    el_vertices.append((d, c, b))

    elif dim == 3:
        for i in range(shape[0]-1):
            for j in range(shape[1]-1):
                for k in range(shape[2]-1):

                    a000 = vertex_indices[i, j, k]
                    a001 = vertex_indices[i, j, k+1]
                    a010 = vertex_indices[i, j+1, k]
                    a011 = vertex_indices[i, j+1, k+1]

                    a100 = vertex_indices[i+1, j, k]
                    a101 = vertex_indices[i+1, j, k+1]
                    a110 = vertex_indices[i+1, j+1, k]
                    a111 = vertex_indices[i+1, j+1, k+1]

                    if is_tp:
                        el_vertices.append(
                                (a000, a001, a010, a011,
                                    a100, a101, a110, a111))

                    else:
                        el_vertices.append((a000, a100, a010, a001))
                        el_vertices.append((a101, a100, a001, a010))
                        el_vertices.append((a101, a011, a010, a001))

                        el_vertices.append((a100, a010, a101, a110))
                        el_vertices.append((a011, a010, a110, a101))
                        el_vertices.append((a011, a111, a101, a110))

    else:
        raise NotImplementedError("box meshes of dimension %d"
                % dim)

    el_vertices = np.array(el_vertices, dtype=np.int32)

    grp = make_group_from_vertices(
            vertices.reshape(dim, -1), el_vertices, order,
            group_factory=group_factory)

    from meshmode.mesh import Mesh
    return Mesh(vertices, [grp],
            nodal_adjacency=None,
            facial_adjacency_groups=None)
Ejemplo n.º 10
0
def partition_mesh(mesh, part_per_element, part_num):
    """
    :arg mesh: A :class:`~meshmode.mesh.Mesh` to be partitioned.
    :arg part_per_element: A :class:`numpy.ndarray` containing one
        integer per element of *mesh* indicating which part of the
        partitioned mesh the element is to become a part of.
    :arg part_num: The part number of the mesh to return.

    :returns: A tuple ``(part_mesh, part_to_global)``, where *part_mesh*
        is a :class:`~meshmode.mesh.Mesh` that is a partition of mesh, and
        *part_to_global* is a :class:`numpy.ndarray` mapping element
        numbers on *part_mesh* to ones in *mesh*.

    .. versionadded:: 2017.1
    """
    assert len(part_per_element) == mesh.nelements, (
        "part_per_element must have shape (mesh.nelements,)")

    # Contains the indices of the elements requested.
    queried_elems = np.where(np.array(part_per_element) == part_num)[0]

    num_groups = len(mesh.groups)
    new_indices = []
    new_nodes = []

    # The set of vertex indices we need.
    # NOTE: There are two methods for producing required_indices.
    #   Optimizations may come from further exploring these options.
    #index_set = np.array([], dtype=int)
    index_sets = np.array([], dtype=set)

    skip_groups = []
    num_prev_elems = 0
    start_idx = 0
    for group_num in range(num_groups):
        mesh_group = mesh.groups[group_num]

        # Find the index of first element in the next group.
        end_idx = len(queried_elems)
        for idx in range(start_idx, len(queried_elems)):
            if queried_elems[idx] - num_prev_elems >= mesh_group.nelements:
                end_idx = idx
                break

        if start_idx == end_idx:
            skip_groups.append(group_num)
            new_indices.append(np.array([]))
            new_nodes.append(np.array([]))
            num_prev_elems += mesh_group.nelements
            continue

        elems = queried_elems[start_idx:end_idx] - num_prev_elems
        new_indices.append(mesh_group.vertex_indices[elems])

        new_nodes.append(
            np.zeros((mesh.ambient_dim, end_idx - start_idx,
                      mesh_group.nunit_nodes)))
        for i in range(mesh.ambient_dim):
            for j in range(start_idx, end_idx):
                elems = queried_elems[j] - num_prev_elems
                new_idx = j - start_idx
                new_nodes[group_num][i,
                                     new_idx, :] = mesh_group.nodes[i,
                                                                    elems, :]

        #index_set = np.append(index_set, new_indices[group_num].ravel())
        index_sets = np.append(index_sets, set(new_indices[group_num].ravel()))

        num_prev_elems += mesh_group.nelements
        start_idx = end_idx

    # A sorted np.array of vertex indices we need (without duplicates).
    #required_indices = np.unique(np.sort(index_set))
    required_indices = np.array(list(set.union(*index_sets)))

    new_vertices = np.zeros((mesh.ambient_dim, len(required_indices)))
    for dim in range(mesh.ambient_dim):
        new_vertices[dim] = mesh.vertices[dim][required_indices]

    # Our indices need to be in range [0, len(mesh.nelements)].
    for group_num in range(num_groups):
        if group_num not in skip_groups:
            for i in range(len(new_indices[group_num])):
                for j in range(len(new_indices[group_num][0])):
                    original_index = new_indices[group_num][i, j]
                    new_indices[group_num][i, j] = np.where(
                        required_indices == original_index)[0]

    new_mesh_groups = []
    for group_num, mesh_group in enumerate(mesh.groups):
        if group_num not in skip_groups:
            new_mesh_groups.append(
                type(mesh_group)(mesh_group.order,
                                 new_indices[group_num],
                                 new_nodes[group_num],
                                 unit_nodes=mesh_group.unit_nodes))

    from meshmode.mesh import BTAG_ALL, BTAG_PARTITION
    boundary_tags = [BTAG_PARTITION(n) for n in np.unique(part_per_element)]

    from meshmode.mesh import Mesh
    part_mesh = Mesh(new_vertices,
                     new_mesh_groups,
                     facial_adjacency_groups=None,
                     boundary_tags=boundary_tags,
                     is_conforming=mesh.is_conforming)

    adj_data = [[] for _ in range(len(part_mesh.groups))]

    for igrp, grp in enumerate(part_mesh.groups):
        elem_base = grp.element_nr_base
        boundary_adj = part_mesh.facial_adjacency_groups[igrp][None]
        boundary_elems = boundary_adj.elements
        boundary_faces = boundary_adj.element_faces
        p_meshwide_elems = queried_elems[boundary_elems + elem_base]
        parent_igrps = find_group_indices(mesh.groups, p_meshwide_elems)
        for adj_idx, elem in enumerate(boundary_elems):
            face = boundary_faces[adj_idx]
            tag = -boundary_adj.neighbors[adj_idx]
            assert tag >= 0, "Expected boundary tag in adjacency group."

            parent_igrp = parent_igrps[adj_idx]
            parent_elem_base = mesh.groups[parent_igrp].element_nr_base
            parent_elem = p_meshwide_elems[adj_idx] - parent_elem_base

            parent_adj = mesh.facial_adjacency_groups[parent_igrp]

            for parent_facial_group in parent_adj.values():
                indices, = np.nonzero(
                    parent_facial_group.elements == parent_elem)
                for idx in indices:
                    if (parent_facial_group.neighbors[idx] >= 0 and
                            parent_facial_group.element_faces[idx] == face):
                        rank_neighbor = (parent_facial_group.neighbors[idx] +
                                         parent_elem_base)
                        n_face = parent_facial_group.neighbor_faces[idx]

                        n_part_num = part_per_element[rank_neighbor]
                        tag = tag & ~part_mesh.boundary_tag_bit(BTAG_ALL)
                        tag = tag | part_mesh.boundary_tag_bit(
                            BTAG_PARTITION(n_part_num))
                        boundary_adj.neighbors[adj_idx] = -tag

                        # Find the neighbor element from the other partition.
                        n_meshwide_elem = np.count_nonzero(
                            part_per_element[:rank_neighbor] == n_part_num)

                        adj_data[igrp].append(
                            (elem, face, n_part_num, n_meshwide_elem, n_face))

    connected_mesh = part_mesh.copy()

    from meshmode.mesh import InterPartitionAdjacencyGroup
    for igrp, adj in enumerate(adj_data):
        if adj:
            bdry = connected_mesh.facial_adjacency_groups[igrp][None]
            # Initialize connections
            n_parts = np.zeros_like(bdry.elements)
            n_parts.fill(-1)
            global_n_elems = np.copy(n_parts)
            n_faces = np.copy(n_parts)

            # Sort both sets of elements so that we can quickly merge
            # the two data structures
            bdry_perm = np.lexsort([bdry.element_faces, bdry.elements])
            elems = bdry.elements[bdry_perm]
            faces = bdry.element_faces[bdry_perm]
            neighbors = bdry.neighbors[bdry_perm]
            adj_elems, adj_faces, adj_n_parts, adj_gl_n_elems, adj_n_faces =\
                                    np.array(adj).T
            adj_perm = np.lexsort([adj_faces, adj_elems])
            adj_elems = adj_elems[adj_perm]
            adj_faces = adj_faces[adj_perm]
            adj_n_parts = adj_n_parts[adj_perm]
            adj_gl_n_elems = adj_gl_n_elems[adj_perm]
            adj_n_faces = adj_n_faces[adj_perm]

            # Merge interpartition adjacency data with FacialAdjacencyGroup
            adj_idx = 0
            for bdry_idx in range(len(elems)):
                if adj_idx >= len(adj_elems):
                    break
                if (adj_elems[adj_idx] == elems[bdry_idx]
                        and adj_faces[adj_idx] == faces[bdry_idx]):
                    n_parts[bdry_idx] = adj_n_parts[adj_idx]
                    global_n_elems[bdry_idx] = adj_gl_n_elems[adj_idx]
                    n_faces[bdry_idx] = adj_n_faces[adj_idx]
                    adj_idx += 1

            connected_mesh.facial_adjacency_groups[igrp][None] =\
                    InterPartitionAdjacencyGroup(elements=elems,
                                                 element_faces=faces,
                                                 neighbors=neighbors,
                                                 igroup=bdry.igroup,
                                                 ineighbor_group=None,
                                                 neighbor_partitions=n_parts,
                                                 global_neighbors=global_n_elems,
                                                 neighbor_faces=n_faces)

    return connected_mesh, queried_elems
Ejemplo n.º 11
0
def import_firedrake_mesh(fdrake_mesh,
                          cells_to_use=None,
                          normals=None,
                          no_normals_warn=None):
    """
    Create a :class:`meshmode.mesh.Mesh`
    from a `firedrake.mesh.MeshGeometry`
    with the same cells/elements, vertices, nodes,
    mesh order, and facial adjacency.

    The vertex and node coordinates will be the same, as well
    as the cell/element ordering. However, :mod:`firedrake`
    does not require elements to be positively oriented,
    so any negative elements are flipped
    as in :func:`meshmode.mesh.processing.flip_simplex_element_group`.

    The flipped cells/elements are identified by the returned
    *firedrake_orient* array

    :arg fdrake_mesh: `firedrake.mesh.MeshGeometry`.
        This mesh **must** be in a space of ambient dimension
        1, 2, or 3 and have co-dimension of 0 or 1.
        It must use a simplex as a reference element.

        In the case of a 2-dimensional mesh embedded in 3-space,
        the method ``fdrake_mesh.init_cell_orientations`` must
        have been called.

        In the case of a 1-dimensional mesh embedded in 2-space,
        see parameters *normals* and *no_normals_warn*.

        Finally, the ``coordinates`` attribute must have a function
        space whose *finat_element* associates a degree
        of freedom with each vertex. In particular,
        this means that the vertices of the mesh must have well-defined
        coordinates.
        For those unfamiliar with :mod:`firedrake`, you can
        verify this by looking at

        .. code-block:: python

            coords_fspace = fdrake_mesh.coordinates.function_space()
            vertex_entity_dofs = coords_fspace.finat_element.entity_dofs()[0]
            for entity, dof_list in vertex_entity_dofs.items():
                assert len(dof_list) > 0

    :arg cells_to_use: *cells_to_use* is primarily intended for use
        internally by :func:`~meshmode.interop.firedrake.connection.\
build_connection_from_firedrake`.
        *cells_to_use* must be either

        1. *None*, in which case this argument is ignored, or
        2. a numpy array of unique firedrake cell indexes.

        In case (2.),
        only cells whose index appears in *cells_to_use* are included
        in the resultant mesh, and their index in *cells_to_use*
        becomes the element index in the resultant mesh element group.
        Any faces or vertices which do not touch a cell in
        *cells_to_use* are also ignored.
        Note that in this latter case, some faces that are not
        boundaries in *fdrake_mesh* may become boundaries in the
        returned mesh. These "induced" boundaries are marked with
        :class:`~meshmode.mesh.BTAG_INDUCED_BOUNDARY`
        instead of :class:`~meshmode.mesh.BTAG_ALL`.

    :arg normals: **Only** used if *fdrake_mesh* is a 1-surface
        embedded in 2-space. In this case,

            - If *None* then
              all elements are assumed to be positively oriented.
            - Else, should be a list/array whose *i*\\ th entry
              is the normal for the *i*\\ th element (*i*\\ th
              in *mesh.coordinate.function_space()*'s
              *cell_node_list*)

    :arg no_normals_warn: If *True* (the default), raises a warning
        if *fdrake_mesh* is a 1-surface embedded in 2-space
        and *normals* is *None*.

    :return: A tuple *(meshmode mesh, firedrake_orient)*.
         ``firedrake_orient < 0`` is *True* for any negatively
         oriented firedrake cell (which was flipped by meshmode)
         and False for any positively oriented firedrake cell
         (which was not flipped by meshmode).
    """
    # Type validation
    from firedrake.mesh import MeshGeometry
    if not isinstance(fdrake_mesh, MeshGeometry):
        raise TypeError("'fdrake_mesh_topology' must be an instance of "
                        "firedrake.mesh.MeshGeometry, "
                        "not '%s'." % type(fdrake_mesh))
    if cells_to_use is not None:
        if not isinstance(cells_to_use, np.ndarray):
            raise TypeError("'cells_to_use' must be a np.ndarray or " "*None*")
        assert len(cells_to_use.shape) == 1
        assert np.size(np.unique(cells_to_use)) == np.size(cells_to_use), \
            ":arg:`cells_to_use` must have unique entries"
        assert np.all(
            np.logical_and(cells_to_use >= 0,
                           cells_to_use < fdrake_mesh.num_cells()))
    assert fdrake_mesh.ufl_cell().is_simplex(), "Mesh must use simplex cells"
    gdim = fdrake_mesh.geometric_dimension()
    tdim = fdrake_mesh.topological_dimension()
    assert gdim in [1, 2, 3], "Mesh must be in space of ambient dim 1, 2, or 3"
    assert gdim - tdim in [0, 1], "Mesh co-dimension must be 0 or 1"
    # firedrake meshes are not guaranteed be fully instantiated until
    # the .init() method is called. In particular, the coordinates function
    # may not be accessible if we do not call init(). If the mesh has
    # already been initialized, nothing will change. For more details
    # on why we need a second initialization, see
    # this pull request:
    # https://github.com/firedrakeproject/firedrake/pull/627
    # which details how Firedrake implements a mesh's coordinates
    # as a function on that very same mesh
    fdrake_mesh.init()

    # Get all the nodal information we can from the topology
    bdy_tags = _get_firedrake_boundary_tags(fdrake_mesh,
                                            tag_induced_boundary=cells_to_use
                                            is not None)

    with ProcessLogger(
            logger, "Retrieving vertex indices and computing "
            "NodalAdjacency from firedrake mesh"):
        vertex_indices, nodal_adjacency = \
            _get_firedrake_nodal_info(fdrake_mesh, cells_to_use=cells_to_use)

        # If only using some cells, vertices may need new indices as many
        # will be removed
        if cells_to_use is not None:
            vert_ndx_new2old = np.unique(vertex_indices.flatten())
            vert_ndx_old2new = dict(
                zip(
                    vert_ndx_new2old,
                    np.arange(np.size(vert_ndx_new2old),
                              dtype=vertex_indices.dtype)))
            vertex_indices = \
                np.vectorize(vert_ndx_old2new.__getitem__)(vertex_indices)

    with ProcessLogger(
            logger, "Building (possibly) unflipped "
            "SimplexElementGroup from firedrake unit nodes/nodes"):

        # Grab the mesh reference element and cell dimension
        coord_finat_elt = fdrake_mesh.coordinates.function_space(
        ).finat_element
        cell_dim = fdrake_mesh.cell_dimension()

        # Get finat unit nodes and map them onto the meshmode reference simplex
        finat_unit_nodes = get_finat_element_unit_nodes(coord_finat_elt)
        fd_ref_to_mm = get_affine_reference_simplex_mapping(cell_dim, True)
        finat_unit_nodes = fd_ref_to_mm(finat_unit_nodes)

        # Now grab the nodes
        coords = fdrake_mesh.coordinates
        cell_node_list = coords.function_space().cell_node_list
        if cells_to_use is not None:
            cell_node_list = cell_node_list[cells_to_use]
        nodes = np.real(coords.dat.data[cell_node_list])
        # Add extra dim in 1D for shape (nelements, nunit_nodes, dim)
        if tdim == 1:
            nodes = np.reshape(nodes, nodes.shape + (1, ))
        # Transpose nodes to have shape (dim, nelements, nunit_nodes)
        nodes = np.transpose(nodes, (2, 0, 1))

        # make a group (possibly with some elements that need to be flipped)
        unflipped_group = SimplexElementGroup(coord_finat_elt.degree,
                                              vertex_indices,
                                              nodes,
                                              dim=cell_dim,
                                              unit_nodes=finat_unit_nodes)

    # Next get the vertices (we'll need these for the orientations)
    with ProcessLogger(logger, "Obtaining vertex coordinates"):
        coord_finat = fdrake_mesh.coordinates.function_space().finat_element
        # unit_vertex_indices are the element-local indices of the nodes
        # which coincide with the vertices, i.e. for element *i*,
        # vertex 0's coordinates would be nodes[i][unit_vertex_indices[0]].
        # This assumes each vertex has some node which coincides with it...
        # which is normally fine to assume for firedrake meshes.
        unit_vertex_indices = []
        # iterate through the dofs associated to each vertex on the
        # reference element
        for _, dofs in sorted(coord_finat.entity_dofs()[0].items()):
            assert len(dofs) == 1, \
                "The function space of the mesh coordinates must have" \
                " exactly one degree of freedom associated with " \
                " each vertex in order to determine vertex coordinates"
            dof, = dofs
            unit_vertex_indices.append(dof)

        # Now get the vertex coordinates as *(dim, nvertices)*-shaped array
        if cells_to_use is not None:
            nvertices = np.size(vert_ndx_new2old)
        else:
            nvertices = fdrake_mesh.num_vertices()
        vertices = np.ndarray((gdim, nvertices), dtype=nodes.dtype)
        recorded_verts = set()
        for icell, cell_vertex_indices in enumerate(vertex_indices):
            for local_vert_id, global_vert_id in enumerate(
                    cell_vertex_indices):
                if global_vert_id not in recorded_verts:
                    recorded_verts.add(global_vert_id)
                    local_node_nr = unit_vertex_indices[local_vert_id]
                    vertices[:, global_vert_id] = nodes[:, icell,
                                                        local_node_nr]

    # Use the vertices to compute the orientations and flip the group
    with ProcessLogger(logger, "Computing cell orientations"):
        orient = _get_firedrake_orientations(fdrake_mesh,
                                             unflipped_group,
                                             vertices,
                                             cells_to_use=cells_to_use,
                                             normals=normals,
                                             no_normals_warn=no_normals_warn)

    with ProcessLogger(logger, "Flipping group"):
        from meshmode.mesh.processing import flip_simplex_element_group
        group = flip_simplex_element_group(vertices, unflipped_group,
                                           orient < 0)

    # Now, any flipped element had its 0 vertex and 1 vertex exchanged.
    # This changes the local facet nr, so we need to create and then
    # fix our facial adjacency groups. To do that, we need to figure
    # out which local facet numbers switched.
    face_vertex_indices = group.face_vertex_indices()
    # face indices of the faces not containing vertex 0 and not
    # containing vertex 1, respectively
    no_zero_face_ndx, no_one_face_ndx = None, None
    for iface, face in enumerate(face_vertex_indices):
        if 0 not in face:
            no_zero_face_ndx = iface
        elif 1 not in face:
            no_one_face_ndx = iface

    with ProcessLogger(
            logger, "Building (possibly) unflipped "
            "FacialAdjacencyGroups"):
        unflipped_facial_adjacency_groups = \
            _get_firedrake_facial_adjacency_groups(fdrake_mesh,
                                                   cells_to_use=cells_to_use)

    # applied below to take elements and element_faces
    # (or neighbors and neighbor_faces) and flip in any faces that need to
    # be flipped.
    def flip_local_face_indices(faces, elements):
        faces = np.copy(faces)
        neg_elements = np.full(elements.shape, False)
        # To handle neighbor case, we only need to flip at elements
        # who have a neighbor, i.e. where neighbors is not a negative
        # bitmask of bdy tags
        neg_elements[elements >= 0] = (orient[elements[elements >= 0]] < 0)
        no_zero = np.logical_and(neg_elements, faces == no_zero_face_ndx)
        no_one = np.logical_and(neg_elements, faces == no_one_face_ndx)
        faces[no_zero], faces[no_one] = no_one_face_ndx, no_zero_face_ndx
        return faces

    # Create new facial adjacency groups that have been flipped
    with ProcessLogger(logger, "Flipping FacialAdjacencyGroups"):
        facial_adjacency_groups = []
        for igroup, fagrps in enumerate(unflipped_facial_adjacency_groups):
            facial_adjacency_groups.append({})
            for ineighbor_group, fagrp in fagrps.items():
                new_element_faces = flip_local_face_indices(
                    fagrp.element_faces, fagrp.elements)
                new_neighbor_faces = flip_local_face_indices(
                    fagrp.neighbor_faces, fagrp.neighbors)
                new_fagrp = FacialAdjacencyGroup(
                    igroup=igroup,
                    ineighbor_group=ineighbor_group,
                    elements=fagrp.elements,
                    element_faces=new_element_faces,
                    neighbors=fagrp.neighbors,
                    neighbor_faces=new_neighbor_faces)
                facial_adjacency_groups[igroup][ineighbor_group] = new_fagrp

    return (Mesh(vertices, [group],
                 boundary_tags=bdy_tags,
                 nodal_adjacency=nodal_adjacency,
                 facial_adjacency_groups=facial_adjacency_groups), orient)
Ejemplo n.º 12
0
def make_face_restriction(discr,
                          group_factory,
                          boundary_tag,
                          per_face_groups=False):
    """Create a mesh, a discretization and a connection to restrict
    a function on *discr* to its values on the edges of element faces
    denoted by *boundary_tag*.

    :arg boundary_tag: The boundary tag for which to create a face
        restriction. May be
        :class:`meshmode.discretization.connection.INTERIOR_FACES`
        to indicate interior faces, or
        :class:`meshmode.discretization.connection.ALL_FACES`
        to make a discretization consisting of all (interior and
        boundary) faces.

    :arg per_face_groups: If *True*, the resulting discretization is
        guaranteed to have groups organized as::

            (grp0, face0), (grp0, face1), ... (grp0, faceN),
            (grp1, face0), (grp1, face1), ... (grp1, faceN), ...

        each with the elements in the same order as the originating
        group. If *False*, volume and boundary groups correspond with
        each other one-to-one, and an interpolation batch is created
        per face.

    :return: a
        :class:`meshmode.discretization.connection.DirectDiscretizationConnection`
        representing the new connection. The new boundary discretization can be
        obtained from the
        :attr:`meshmode.discretization.connection.DirectDiscretizationConnection.to_discr`
        attribute of the return value, and the corresponding new boundary mesh
        from that.

    """

    if boundary_tag is None:
        boundary_tag = FRESTR_INTERIOR_FACES
        from warnings import warn
        warn(
            "passing *None* for boundary_tag is deprecated--pass "
            "INTERIOR_FACES instead",
            DeprecationWarning,
            stacklevel=2)

    logger.info("building face restriction: start")

    # {{{ gather boundary vertices

    bdry_vertex_vol_nrs = _get_face_vertices(discr.mesh, boundary_tag)

    vol_to_bdry_vertices = np.empty(discr.mesh.vertices.shape[-1],
                                    discr.mesh.vertices.dtype)
    vol_to_bdry_vertices.fill(-1)
    vol_to_bdry_vertices[bdry_vertex_vol_nrs] = np.arange(
        len(bdry_vertex_vol_nrs), dtype=np.intp)

    bdry_vertices = discr.mesh.vertices[:, bdry_vertex_vol_nrs]

    # }}}

    from meshmode.mesh import Mesh, SimplexElementGroup
    bdry_mesh_groups = []
    connection_data = {}

    btag_bit = discr.mesh.boundary_tag_bit(boundary_tag)

    for igrp, (grp, fagrp_map) in enumerate(
            zip(discr.groups, discr.mesh.facial_adjacency_groups)):

        mgrp = grp.mesh_el_group

        if not isinstance(mgrp, SimplexElementGroup):
            raise NotImplementedError("can only take boundary of "
                                      "SimplexElementGroup-based meshes")

        # {{{ pull together per-group face lists

        group_boundary_faces = []

        if boundary_tag is FRESTR_INTERIOR_FACES:
            for fagrp in six.itervalues(fagrp_map):
                if fagrp.ineighbor_group is None:
                    # boundary faces -> not looking for those
                    continue

                group_boundary_faces.extend(
                    zip(fagrp.elements, fagrp.element_faces))

        elif boundary_tag is FRESTR_ALL_FACES:
            group_boundary_faces.extend(
                (iel, iface) for iface in range(grp.mesh_el_group.nfaces)
                for iel in range(grp.nelements))

        else:
            bdry_grp = fagrp_map.get(None)
            if bdry_grp is not None:
                nb_el_bits = -bdry_grp.neighbors
                face_relevant_flags = (nb_el_bits & btag_bit) != 0

                group_boundary_faces.extend(
                    zip(bdry_grp.elements[face_relevant_flags],
                        bdry_grp.element_faces[face_relevant_flags]))

        # }}}

        grp_face_vertex_indices = mgrp.face_vertex_indices()
        grp_vertex_unit_coordinates = mgrp.vertex_unit_coordinates()

        batch_base = 0

        # group by face_id

        for face_id in range(mgrp.nfaces):
            batch_boundary_el_numbers_in_grp = np.array([
                ibface_el for ibface_el, ibface_face in group_boundary_faces
                if ibface_face == face_id
            ],
                                                        dtype=np.intp)

            # {{{ Preallocate arrays for mesh group

            nbatch_elements = len(batch_boundary_el_numbers_in_grp)

            if per_face_groups or face_id == 0:
                if per_face_groups:
                    ngroup_bdry_elements = nbatch_elements
                else:
                    ngroup_bdry_elements = len(group_boundary_faces)

                vertex_indices = np.empty(
                    (ngroup_bdry_elements, mgrp.dim + 1 - 1),
                    mgrp.vertex_indices.dtype)

                bdry_unit_nodes = mp.warp_and_blend_nodes(
                    mgrp.dim - 1, mgrp.order)
                bdry_unit_nodes_01 = (bdry_unit_nodes + 1) * 0.5

                vol_basis = mp.simplex_onb(mgrp.dim, mgrp.order)
                nbdry_unit_nodes = bdry_unit_nodes_01.shape[-1]
                nodes = np.empty((discr.ambient_dim, ngroup_bdry_elements,
                                  nbdry_unit_nodes),
                                 dtype=np.float64)

            # }}}

            new_el_numbers = batch_base + np.arange(nbatch_elements)
            if not per_face_groups:
                batch_base += nbatch_elements

            # {{{ no per-element axes in these computations

            # Find boundary vertex indices
            loc_face_vertices = list(grp_face_vertex_indices[face_id])

            # Find unit nodes for boundary element
            face_vertex_unit_coordinates = \
                    grp_vertex_unit_coordinates[loc_face_vertices]

            # Find A, b such that A [e_1 e_2] + b = [r_1 r_2]
            # (Notation assumes that the volume is 3D and the face is 2D.
            # Code does not.)

            b = face_vertex_unit_coordinates[0]
            A = (  # noqa
                face_vertex_unit_coordinates[1:] -
                face_vertex_unit_coordinates[0]).T

            face_unit_nodes = (np.dot(A, bdry_unit_nodes_01).T + b).T

            resampling_mat = mp.resampling_matrix(vol_basis, face_unit_nodes,
                                                  mgrp.unit_nodes)

            # }}}

            # {{{ build information for mesh element group

            # Find vertex_indices
            glob_face_vertices = mgrp.vertex_indices[
                batch_boundary_el_numbers_in_grp][:, loc_face_vertices]
            vertex_indices[new_el_numbers] = \
                    vol_to_bdry_vertices[glob_face_vertices]

            # Find nodes
            nodes[:, new_el_numbers, :] = np.einsum(
                "ij,dej->dei", resampling_mat,
                mgrp.nodes[:, batch_boundary_el_numbers_in_grp, :])

            # }}}

            connection_data[igrp, face_id] = _ConnectionBatchData(
                group_source_element_indices=batch_boundary_el_numbers_in_grp,
                group_target_element_indices=new_el_numbers,
                A=A,
                b=b,
            )

            is_last_face = face_id + 1 == mgrp.nfaces

            if per_face_groups or is_last_face:
                bdry_mesh_group = SimplexElementGroup(
                    mgrp.order,
                    vertex_indices,
                    nodes,
                    unit_nodes=bdry_unit_nodes)
                bdry_mesh_groups.append(bdry_mesh_group)

    bdry_mesh = Mesh(bdry_vertices, bdry_mesh_groups)

    from meshmode.discretization import Discretization
    bdry_discr = Discretization(discr.cl_context, bdry_mesh, group_factory)

    with cl.CommandQueue(discr.cl_context) as queue:
        connection = _build_boundary_connection(queue, discr, bdry_discr,
                                                connection_data,
                                                per_face_groups)

    logger.info("building face restriction: done")

    return connection
Ejemplo n.º 13
0
def make_face_restriction(actx,
                          discr,
                          group_factory,
                          boundary_tag,
                          per_face_groups=False):
    """Create a mesh, a discretization and a connection to restrict
    a function on *discr* to its values on the edges of element faces
    denoted by *boundary_tag*.

    :arg boundary_tag: The boundary tag for which to create a face
        restriction. May be
        :class:`FACE_RESTR_INTERIOR`
        to indicate interior faces, or
        :class:`FACE_RESTR_ALL`
        to make a discretization consisting of all (interior and
        boundary) faces.

    :arg per_face_groups: If *True*, the resulting discretization is
        guaranteed to have groups organized as::

            (grp0, face0), (grp0, face1), ... (grp0, faceN),
            (grp1, face0), (grp1, face1), ... (grp1, faceN), ...

        each with the elements in the same order as the originating
        group. If *False*, volume and boundary groups correspond with
        each other one-to-one, and an interpolation batch is created
        per face.

    :return: a
        :class:`meshmode.discretization.connection.DirectDiscretizationConnection`
        representing the new connection. The new boundary discretization can be
        obtained from the
        :attr:`meshmode.discretization.connection.DirectDiscretizationConnection.to_discr`
        attribute of the return value, and the corresponding new boundary mesh
        from that.

    """

    if boundary_tag is None:
        boundary_tag = FACE_RESTR_INTERIOR
        from warnings import warn
        warn(
            "passing *None* for boundary_tag is deprecated--pass "
            "FACE_RESTR_INTERIOR instead",
            DeprecationWarning,
            stacklevel=2)

    logger.info("building face restriction: start")

    # {{{ gather boundary vertices

    bdry_vertex_vol_nrs = _get_face_vertices(discr.mesh, boundary_tag)

    vol_to_bdry_vertices = np.empty(discr.mesh.vertices.shape[-1],
                                    discr.mesh.vertices.dtype)
    vol_to_bdry_vertices.fill(-1)
    vol_to_bdry_vertices[bdry_vertex_vol_nrs] = np.arange(
        len(bdry_vertex_vol_nrs), dtype=np.intp)

    bdry_vertices = discr.mesh.vertices[:, bdry_vertex_vol_nrs]

    # }}}

    from meshmode.mesh import Mesh, _ModepyElementGroup
    bdry_mesh_groups = []
    connection_data = {}

    if boundary_tag not in [FACE_RESTR_ALL, FACE_RESTR_INTERIOR]:
        btag_bit = discr.mesh.boundary_tag_bit(boundary_tag)

    for igrp, (grp, fagrp_map) in enumerate(
            zip(discr.groups, discr.mesh.facial_adjacency_groups)):

        mgrp = grp.mesh_el_group

        if not isinstance(mgrp, _ModepyElementGroup):
            raise NotImplementedError(
                "can only take boundary of "
                "meshes based on SimplexElementGroup and "
                "TensorProductElementGroup")

        # {{{ pull together per-group face lists

        group_boundary_faces = []

        if boundary_tag is FACE_RESTR_INTERIOR:
            for fagrp in fagrp_map.values():
                if fagrp.ineighbor_group is None:
                    # boundary faces -> not looking for those
                    continue

                group_boundary_faces.extend(
                    zip(fagrp.elements, fagrp.element_faces))

        elif boundary_tag is FACE_RESTR_ALL:
            group_boundary_faces.extend(
                (iel, iface) for iface in range(grp.mesh_el_group.nfaces)
                for iel in range(grp.nelements))

        else:
            bdry_grp = fagrp_map.get(None)
            if bdry_grp is not None:
                nb_el_bits = -bdry_grp.neighbors
                face_relevant_flags = (nb_el_bits & btag_bit) != 0

                group_boundary_faces.extend(
                    zip(bdry_grp.elements[face_relevant_flags],
                        bdry_grp.element_faces[face_relevant_flags]))

        # }}}

        batch_base = 0

        # group by face_index

        for face in mgrp._modepy_faces:
            batch_boundary_el_numbers_in_grp = np.array([
                ibface_el for ibface_el, ibface_face in group_boundary_faces
                if ibface_face == face.face_index
            ],
                                                        dtype=np.intp)

            # {{{ preallocate arrays for mesh group

            nbatch_elements = len(batch_boundary_el_numbers_in_grp)

            if per_face_groups or face.face_index == 0:
                if per_face_groups:
                    ngroup_bdry_elements = nbatch_elements
                else:
                    ngroup_bdry_elements = len(group_boundary_faces)

                # make up some not-terrible nodes for the boundary Mesh
                space = mp.space_for_shape(face, mgrp.order)
                bdry_unit_nodes = mp.edge_clustered_nodes_for_space(
                    space, face)

                vol_basis = mp.basis_for_space(mgrp._modepy_space,
                                               mgrp._modepy_shape).functions

                vertex_indices = np.empty(
                    (ngroup_bdry_elements, face.nvertices),
                    mgrp.vertex_indices.dtype)

                nbdry_unit_nodes = bdry_unit_nodes.shape[-1]
                nodes = np.empty((discr.ambient_dim, ngroup_bdry_elements,
                                  nbdry_unit_nodes),
                                 dtype=np.float64)
            # }}}

            new_el_numbers = batch_base + np.arange(nbatch_elements)
            if not per_face_groups:
                batch_base += nbatch_elements

            # {{{ no per-element axes in these computations

            face_unit_nodes = face.map_to_volume(bdry_unit_nodes)
            resampling_mat = mp.resampling_matrix(vol_basis, face_unit_nodes,
                                                  mgrp.unit_nodes)

            # }}}

            # {{{ build information for mesh element group

            # Find vertex_indices
            glob_face_vertices = mgrp.vertex_indices[
                batch_boundary_el_numbers_in_grp][:,
                                                  face.volume_vertex_indices]
            vertex_indices[new_el_numbers] = \
                    vol_to_bdry_vertices[glob_face_vertices]

            # Find nodes
            nodes[:, new_el_numbers, :] = np.einsum(
                "ij,dej->dei", resampling_mat,
                mgrp.nodes[:, batch_boundary_el_numbers_in_grp, :])

            # }}}

            connection_data[igrp, face.face_index] = _ConnectionBatchData(
                group_source_element_indices=batch_boundary_el_numbers_in_grp,
                group_target_element_indices=new_el_numbers,
                face=face,
            )

            is_last_face = face.face_index + 1 == mgrp.nfaces

            if per_face_groups or is_last_face:
                bdry_mesh_group = type(mgrp)(mgrp.order,
                                             vertex_indices,
                                             nodes,
                                             unit_nodes=bdry_unit_nodes)
                bdry_mesh_groups.append(bdry_mesh_group)

    bdry_mesh = Mesh(bdry_vertices, bdry_mesh_groups)

    bdry_discr = discr.copy(actx=actx,
                            mesh=bdry_mesh,
                            group_factory=group_factory)

    connection = _build_boundary_connection(actx, discr, bdry_discr,
                                            connection_data, per_face_groups)

    logger.info("building face restriction: done")

    return connection
Ejemplo n.º 14
0
def partition_mesh(mesh, part_per_element, part_num):
    """
    :arg mesh: A :class:`meshmode.mesh.Mesh` to be partitioned.
    :arg part_per_element: A :class:`numpy.ndarray` containing one
        integer per element of *mesh* indicating which part of the
        partitioned mesh the element is to become a part of.
    :arg part_num: The part number of the mesh to return.

    :returns: A tuple ``(part_mesh, part_to_global)``, where *part_mesh*
        is a :class:`meshmode.mesh.Mesh` that is a partition of mesh, and
        *part_to_global* is a :class:`numpy.ndarray` mapping element
        numbers on *part_mesh* to ones in *mesh*.

    .. versionadded:: 2017.1
    """
    assert len(part_per_element) == mesh.nelements, (
        "part_per_element must have shape (mesh.nelements,)")

    # Contains the indices of the elements requested.
    queried_elems = np.where(np.array(part_per_element) == part_num)[0]

    num_groups = len(mesh.groups)
    new_indices = []
    new_nodes = []

    # The set of vertex indices we need.
    # NOTE: There are two methods for producing required_indices.
    #   Optimizations may come from further exploring these options.
    #index_set = np.array([], dtype=int)
    index_sets = np.array([], dtype=set)

    skip_groups = []
    num_prev_elems = 0
    start_idx = 0
    for group_num in range(num_groups):
        mesh_group = mesh.groups[group_num]

        # Find the index of first element in the next group.
        end_idx = len(queried_elems)
        for idx in range(start_idx, len(queried_elems)):
            if queried_elems[idx] - num_prev_elems >= mesh_group.nelements:
                end_idx = idx
                break

        if start_idx == end_idx:
            skip_groups.append(group_num)
            new_indices.append(np.array([]))
            new_nodes.append(np.array([]))
            num_prev_elems += mesh_group.nelements
            continue

        elems = queried_elems[start_idx:end_idx] - num_prev_elems
        new_indices.append(mesh_group.vertex_indices[elems])

        new_nodes.append(
            np.zeros(
                (mesh.ambient_dim, end_idx - start_idx, mesh_group.nunit_nodes)))
        for i in range(mesh.ambient_dim):
            for j in range(start_idx, end_idx):
                elems = queried_elems[j] - num_prev_elems
                new_idx = j - start_idx
                new_nodes[group_num][i, new_idx, :] = mesh_group.nodes[i, elems, :]

        #index_set = np.append(index_set, new_indices[group_num].ravel())
        index_sets = np.append(index_sets, set(new_indices[group_num].ravel()))

        num_prev_elems += mesh_group.nelements
        start_idx = end_idx

    # A sorted np.array of vertex indices we need (without duplicates).
    #required_indices = np.unique(np.sort(index_set))
    required_indices = np.array(list(set.union(*index_sets)))

    new_vertices = np.zeros((mesh.ambient_dim, len(required_indices)))
    for dim in range(mesh.ambient_dim):
        new_vertices[dim] = mesh.vertices[dim][required_indices]

    # Our indices need to be in range [0, len(mesh.nelements)].
    for group_num in range(num_groups):
        if group_num not in skip_groups:
            for i in range(len(new_indices[group_num])):
                for j in range(len(new_indices[group_num][0])):
                    original_index = new_indices[group_num][i, j]
                    new_indices[group_num][i, j] = np.where(
                            required_indices == original_index)[0]

    new_mesh_groups = []
    for group_num, mesh_group in enumerate(mesh.groups):
        if group_num not in skip_groups:
            new_mesh_groups.append(
                type(mesh_group)(
                    mesh_group.order, new_indices[group_num],
                    new_nodes[group_num],
                    unit_nodes=mesh_group.unit_nodes))

    from meshmode.mesh import BTAG_ALL, BTAG_PARTITION
    boundary_tags = [BTAG_PARTITION(n) for n in np.unique(part_per_element)]

    from meshmode.mesh import Mesh
    part_mesh = Mesh(
            new_vertices,
            new_mesh_groups,
            facial_adjacency_groups=None,
            boundary_tags=boundary_tags,
            is_conforming=mesh.is_conforming)

    adj_data = [[] for _ in range(len(part_mesh.groups))]

    for igrp, grp in enumerate(part_mesh.groups):
        elem_base = grp.element_nr_base
        boundary_adj = part_mesh.facial_adjacency_groups[igrp][None]
        boundary_elems = boundary_adj.elements
        boundary_faces = boundary_adj.element_faces
        p_meshwide_elems = queried_elems[boundary_elems + elem_base]
        parent_igrps = find_group_indices(mesh.groups, p_meshwide_elems)
        for adj_idx, elem in enumerate(boundary_elems):
            face = boundary_faces[adj_idx]
            tag = -boundary_adj.neighbors[adj_idx]
            assert tag >= 0, "Expected boundary tag in adjacency group."

            parent_igrp = parent_igrps[adj_idx]
            parent_elem_base = mesh.groups[parent_igrp].element_nr_base
            parent_elem = p_meshwide_elems[adj_idx] - parent_elem_base

            parent_adj = mesh.facial_adjacency_groups[parent_igrp]

            for parent_facial_group in parent_adj.values():
                indices, = np.nonzero(parent_facial_group.elements == parent_elem)
                for idx in indices:
                    if (parent_facial_group.neighbors[idx] >= 0
                            and parent_facial_group.element_faces[idx] == face):
                        rank_neighbor = (parent_facial_group.neighbors[idx]
                                            + parent_elem_base)
                        n_face = parent_facial_group.neighbor_faces[idx]

                        n_part_num = part_per_element[rank_neighbor]
                        tag = tag & ~part_mesh.boundary_tag_bit(BTAG_ALL)
                        tag = tag | part_mesh.boundary_tag_bit(
                                                    BTAG_PARTITION(n_part_num))
                        boundary_adj.neighbors[adj_idx] = -tag

                        # Find the neighbor element from the other partition.
                        n_meshwide_elem = np.count_nonzero(
                                    part_per_element[:rank_neighbor] == n_part_num)

                        adj_data[igrp].append((elem, face,
                                               n_part_num, n_meshwide_elem, n_face))

    connected_mesh = part_mesh.copy()

    from meshmode.mesh import InterPartitionAdjacencyGroup
    for igrp, adj in enumerate(adj_data):
        if adj:
            bdry = connected_mesh.facial_adjacency_groups[igrp][None]
            # Initialize connections
            n_parts = np.zeros_like(bdry.elements)
            n_parts.fill(-1)
            global_n_elems = np.copy(n_parts)
            n_faces = np.copy(n_parts)

            # Sort both sets of elements so that we can quickly merge
            # the two data structures
            bdry_perm = np.lexsort([bdry.element_faces, bdry.elements])
            elems = bdry.elements[bdry_perm]
            faces = bdry.element_faces[bdry_perm]
            neighbors = bdry.neighbors[bdry_perm]
            adj_elems, adj_faces, adj_n_parts, adj_gl_n_elems, adj_n_faces =\
                                    np.array(adj).T
            adj_perm = np.lexsort([adj_faces, adj_elems])
            adj_elems = adj_elems[adj_perm]
            adj_faces = adj_faces[adj_perm]
            adj_n_parts = adj_n_parts[adj_perm]
            adj_gl_n_elems = adj_gl_n_elems[adj_perm]
            adj_n_faces = adj_n_faces[adj_perm]

            # Merge interpartition adjacency data with FacialAdjacencyGroup
            adj_idx = 0
            for bdry_idx in range(len(elems)):
                if adj_idx >= len(adj_elems):
                    break
                if (adj_elems[adj_idx] == elems[bdry_idx]
                        and adj_faces[adj_idx] == faces[bdry_idx]):
                    n_parts[bdry_idx] = adj_n_parts[adj_idx]
                    global_n_elems[bdry_idx] = adj_gl_n_elems[adj_idx]
                    n_faces[bdry_idx] = adj_n_faces[adj_idx]
                    adj_idx += 1

            connected_mesh.facial_adjacency_groups[igrp][None] =\
                    InterPartitionAdjacencyGroup(elements=elems,
                                                 element_faces=faces,
                                                 neighbors=neighbors,
                                                 igroup=bdry.igroup,
                                                 ineighbor_group=None,
                                                 neighbor_partitions=n_parts,
                                                 global_neighbors=global_n_elems,
                                                 neighbor_faces=n_faces)

    return connected_mesh, queried_elems