Exemplo n.º 1
0
def flip_simplex_element_group(vertices, grp, grp_flip_flags):
    from meshmode.mesh import SimplexElementGroup

    if not isinstance(grp, SimplexElementGroup):
        raise NotImplementedError(
            "flips only supported on "
            "exclusively SimplexElementGroup-based meshes")

    # Swap the first two vertices on elements to be flipped.

    new_vertex_indices = grp.vertex_indices.copy()
    new_vertex_indices[grp_flip_flags, 0] \
            = grp.vertex_indices[grp_flip_flags, 1]
    new_vertex_indices[grp_flip_flags, 1] \
            = grp.vertex_indices[grp_flip_flags, 0]

    # Apply the flip matrix to the nodes.
    flip_matrix = get_simplex_element_flip_matrix(grp.order, grp.unit_nodes)
    new_nodes = grp.nodes.copy()
    new_nodes[:, grp_flip_flags] = np.einsum("ij,dej->dei", flip_matrix,
                                             grp.nodes[:, grp_flip_flags])

    return SimplexElementGroup(grp.order,
                               new_vertex_indices,
                               new_nodes,
                               unit_nodes=grp.unit_nodes)
Exemplo n.º 2
0
def make_curve_mesh(curve_f, element_boundaries, order,
        unit_nodes=None,
        node_vertex_consistency_tolerance=None,
        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 unit_nodes: if given, the unit nodes to use. Must have shape
        ``(dim, nnoodes)``.
    :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)

    vertices = curve_f(element_boundaries)

    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=np.vstack([
                np.arange(nelements, dtype=np.int32),
                np.arange(1, nelements+1, dtype=np.int32) % nelements,
                ]).T,
            nodes=nodes,
            unit_nodes=unit_nodes)

    mesh = Mesh(
            vertices=vertices, groups=[egroup],
            nodal_adjacency=None,
            facial_adjacency_groups=None,
            node_vertex_consistency_tolerance=node_vertex_consistency_tolerance)

    if return_parametrization_points:
        return mesh, t
    else:
        return mesh
Exemplo n.º 3
0
    def get_discr(self, actx) -> meshmode.discretization.Discretization:
        """Get a discretization with nodes exactly matching the ones used
        by the nodal-DG code.

        The returned discretization contains a new :class:`~meshmode.mesh.Mesh`
        object constructed from the global Octave state.
        """
        # find dim as number of vertices in the simplex - 1
        etov_size = self.octave.eval("size(EToV)", verbose=False)
        dim = int(etov_size[0, 1] - 1)

        if dim == 1:
            unit_nodes = self.octave.eval("JacobiGL(0, 0, N)", verbose=False).T
        else:
            unit_nodes_arrays = self.octave.eval(f"Nodes{dim}D(N)",
                                                 nout=dim,
                                                 verbose=False)

            equilat_to_biunit_func_name = ("".join(self.AXES[:dim] + ["to"] +
                                                   self.REF_AXES[:dim]))

            unit_nodes_arrays = self.octave.feval(equilat_to_biunit_func_name,
                                                  *unit_nodes_arrays,
                                                  nout=dim,
                                                  verbose=False)

            unit_nodes = np.array([a.reshape(-1) for a in unit_nodes_arrays])

        vertices = np.array([
            self.octave.pull(f"V{self.AXES[ax].upper()}").reshape(-1)
            for ax in range(dim)
        ])
        nodes = np.array(
            [self.octave.pull(self.AXES[ax]).T for ax in range(dim)])
        vertex_indices = (self.octave.pull("EToV")).astype(np.int32) - 1

        from meshmode.mesh import Mesh, SimplexElementGroup
        order = int(self.octave.pull("N"))
        egroup = SimplexElementGroup(order,
                                     vertex_indices=vertex_indices,
                                     nodes=nodes,
                                     unit_nodes=unit_nodes)

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

        from meshmode.discretization import Discretization
        from meshmode.discretization.poly_element import (
            PolynomialGivenNodesGroupFactory)
        return Discretization(
            actx, mesh, PolynomialGivenNodesGroupFactory(order, unit_nodes))
Exemplo n.º 4
0
def flip_simplex_element_group(vertices, grp, grp_flip_flags):
    from modepy.tools import barycentric_to_unit, unit_to_barycentric

    from meshmode.mesh import SimplexElementGroup

    if not isinstance(grp, SimplexElementGroup):
        raise NotImplementedError(
            "flips only supported on "
            "exclusively SimplexElementGroup-based meshes")

    # Swap the first two vertices on elements to be flipped.

    new_vertex_indices = grp.vertex_indices.copy()
    new_vertex_indices[grp_flip_flags, 0] \
            = grp.vertex_indices[grp_flip_flags, 1]
    new_vertex_indices[grp_flip_flags, 1] \
            = grp.vertex_indices[grp_flip_flags, 0]

    # Generate a resampling matrix that corresponds to the
    # first two barycentric coordinates being swapped.

    bary_unit_nodes = unit_to_barycentric(grp.unit_nodes)

    flipped_bary_unit_nodes = bary_unit_nodes.copy()
    flipped_bary_unit_nodes[0, :] = bary_unit_nodes[1, :]
    flipped_bary_unit_nodes[1, :] = bary_unit_nodes[0, :]
    flipped_unit_nodes = barycentric_to_unit(flipped_bary_unit_nodes)

    flip_matrix = mp.resampling_matrix(
        mp.simplex_best_available_basis(grp.dim, grp.order),
        flipped_unit_nodes, grp.unit_nodes)

    flip_matrix[np.abs(flip_matrix) < 1e-15] = 0

    # Flipping twice should be the identity
    assert la.norm(
        np.dot(flip_matrix, flip_matrix) - np.eye(len(flip_matrix))) < 1e-13

    # Apply the flip matrix to the nodes.
    new_nodes = grp.nodes.copy()
    new_nodes[:, grp_flip_flags] = np.einsum("ij,dej->dei", flip_matrix,
                                             grp.nodes[:, grp_flip_flags])

    return SimplexElementGroup(grp.order,
                               new_vertex_indices,
                               new_nodes,
                               unit_nodes=grp.unit_nodes)
Exemplo n.º 5
0
    def group(self):
        if self._group is None:
            from meshmode.mesh import SimplexElementGroup
            from meshmode.mesh.processing import flip_simplex_element_group

            finat_element_a = self.coordinates_a.function_space_a().finat_element_a

            # IMPORTANT that set :attr:`_group` because
            # :meth:`orientations` may call :meth:`group`
            self._group = SimplexElementGroup(
                finat_element_a.analog().degree,
                self.vertex_indices(),
                self.nodes(),
                dim=self.cell_dimension(),
                unit_nodes=finat_element_a.unit_nodes())

            self._group = flip_simplex_element_group(self.vertices(), self._group,
                                                     self.orientations() < 0)

        return self._group
Exemplo n.º 6
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
Exemplo n.º 7
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.FRESTR_INTERIOR_FACES`
        to indicate interior faces, or
        :class:`meshmode.discretization.connection.FRESTR_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 = FACE_RESTR_INTERIOR
        from warnings import warn
        warn(
            "passing *None* for boundary_tag is deprecated--pass "
            "FRESTR_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 FACE_RESTR_INTERIOR:
            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 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]))

        # }}}

        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
Exemplo n.º 8
0
def test_sanity_single_element(ctx_getter, dim, order, visualize=False):
    pytest.importorskip("pytential")

    cl_ctx = ctx_getter()
    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],
                nodal_adjacency=None,
                facial_adjacency_groups=None)

    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)])

    from pytential import bind, sym
    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()
Exemplo n.º 9
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 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 in enumerate(self.element_vertices):
            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(*vertex_gmsh_index_to_mine.items()))
        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 el_type_hist.items():
            if group_el_type.dimensions != mesh_bulk_dim:
                continue

            bulk_el_types.add(group_el_type)

            nodes = np.empty(
                    (ambient_dim, ngroup_elements, group_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 el_vertices, el_nodes, el_type in 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

            import modepy as mp
            if isinstance(group_el_type, GmshSimplexElementBase):
                shape = mp.Simplex(group_el_type.dimensions)
            elif isinstance(group_el_type, GmshTensorProductElementBase):
                shape = mp.Hypercube(group_el_type.dimensions)
            else:
                raise NotImplementedError(
                        f"gmsh element type: {type(group_el_type).__name__}")

            space = mp.space_for_shape(shape, group_el_type.order)
            unit_nodes = mp.equispaced_nodes_for_space(space, shape)

            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, bool))

            elif isinstance(group_el_type, GmshTensorProductElementBase):
                vertex_shuffle = type(group_el_type)(
                        order=1).get_lexicographic_gmsh_node_indices()

                group = TensorProductElementGroup(
                    group_el_type.order,
                    vertex_indices[:, vertex_shuffle],
                    nodes,
                    unit_nodes=unit_nodes
                    )
            else:
                # NOTE: already checked above
                raise AssertionError()

            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)
Exemplo n.º 10
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)
Exemplo n.º 11
0
def _get_firedrake_facial_adjacency_groups(fdrake_mesh_topology,
                                           cells_to_use=None):
    """
    Return facial_adjacency_groups corresponding to
    the given firedrake mesh topology. Note that as we do not
    have geometric information, elements may need to be
    flipped later.

    :arg fdrake_mesh_topology: A :mod:`firedrake` instance of class
        `firedrake.mesh.MeshTopology` or `firedrake.mesh.MeshGeometry`.
    :arg cells_to_use: If *None*, then this argument is ignored.
        Otherwise, assumed to be a numpy array of unique firedrake
        cell ids indicating which cells of the mesh to include,
        as well as inducing a new cell index for those cells.
        Also, in this case boundary faces are tagged
        with :class:`meshmode.mesh.BTAG_INDUCED_BOUNDARY`
        if they are not a boundary face in *fdrake_mesh_topology* but become a
        boundary because the opposite cell is not in *cells_to_use*.  Boundary
        faces in *fdrake_mesh_topology* are marked with :class:`BTAG_ALL`. Both
        are marked with :class:`BTAG_REALLY_ALL`.

    :return: A list of maps to :class:`FacialAdjacencyGroup`s as required
        by a :mod:`meshmode` :class:`Mesh`.
    """
    top = fdrake_mesh_topology.topology
    # We only need one group
    # for interconnectivity and one for boundary connectivity.
    # The tricky part is moving from firedrake local facet numbering
    # (ordered lexicographically by the vertex excluded from the face,
    #  search for "local facet number" in the following paper for
    #  a reference on this...
    # https://spiral.imperial.ac.uk/bitstream/10044/1/28819/2/mlange-firedrake-dmplex-accepted.pdf  # noqa : E501
    # )
    # and meshmode's facet ordering: obtained from a simplex element
    # group
    mm_simp_group = SimplexElementGroup(1, None, None,
                                        dim=top.cell_dimension())
    mm_face_vertex_indices = mm_simp_group.face_vertex_indices()
    # map firedrake local face number to meshmode local face number
    fd_loc_fac_nr_to_mm = {}
    # Figure out which vertex is excluded to get the corresponding
    # firedrake local index
    all_local_facet_nrs = set(range(top.ufl_cell().num_vertices()))
    for mm_local_facet_nr, face in enumerate(mm_face_vertex_indices):
        fd_local_facet_nr = all_local_facet_nrs - set(face)
        assert len(fd_local_facet_nr) == 1
        (fd_local_facet_nr,) = fd_local_facet_nr  # extract id from set({id})
        fd_loc_fac_nr_to_mm[fd_local_facet_nr] = mm_local_facet_nr

    # build a look-up table from firedrake markers to the appropriate values
    # in the neighbors array for the external and internal facial adjacency
    # groups
    bdy_tags = _get_firedrake_boundary_tags(
        top, tag_induced_boundary=cells_to_use is not None)
    boundary_tag_to_index = {bdy_tag: i for i, bdy_tag in enumerate(bdy_tags)}
    marker_to_neighbor_value = {}
    from meshmode.mesh import _boundary_tag_bit
    # None for no marker
    marker_to_neighbor_value[None] = \
        -(_boundary_tag_bit(bdy_tags, boundary_tag_to_index, BTAG_REALLY_ALL)
          | _boundary_tag_bit(bdy_tags, boundary_tag_to_index, BTAG_ALL))
    for marker in top.exterior_facets.unique_markers:
        marker_to_neighbor_value[marker] = \
            -(_boundary_tag_bit(bdy_tags, boundary_tag_to_index, marker)
              | -marker_to_neighbor_value[None])

    # {{{ build the FacialAdjacencyGroup for internal connectivity

    # Get the firedrake cells associated to each interior facet
    int_facet_cell = top.interior_facets.facet_cell
    # Get the firedrake local facet numbers and map them to the
    # meshmode local facet numbers
    int_fac_loc_nr = top.interior_facets.local_facet_dat.data
    int_fac_loc_nr = \
        np.array([[fd_loc_fac_nr_to_mm[fac_nr] for fac_nr in fac_nrs]
                  for fac_nrs in int_fac_loc_nr])
    # elements neighbors element_faces neighbor_faces are as required
    # for a :class:`FacialAdjacencyGroup`.

    int_elements = int_facet_cell.flatten()
    int_neighbors = np.concatenate((int_facet_cell[:, 1], int_facet_cell[:, 0]))
    int_element_faces = int_fac_loc_nr.flatten().astype(Mesh.face_id_dtype)
    int_neighbor_faces = np.concatenate((int_fac_loc_nr[:, 1],
                                         int_fac_loc_nr[:, 0]))
    int_neighbor_faces = int_neighbor_faces.astype(Mesh.face_id_dtype)
    # If only using some of the cells
    from pyop2.datatypes import IntType
    if cells_to_use is not None:
        to_keep = np.isin(int_elements, cells_to_use)
        cells_to_use_inv = dict(zip(cells_to_use,
                                    np.arange(np.size(cells_to_use),
                                              dtype=IntType)))

        # Keep the cells that we are using and change old cell index
        # to new cell index
        int_elements = np.vectorize(cells_to_use_inv.__getitem__)(
            int_elements[to_keep])
        int_element_faces = int_element_faces[to_keep]
        int_neighbors = int_neighbors[to_keep]
        int_neighbor_faces = int_neighbor_faces[to_keep]
        # For neighbor cells, change to new cell index or record
        # as a new boundary (if the neighbor cell is not being used)
        newly_created_exterior_facs = []
        for ndx, icell in enumerate(int_neighbors):
            try:
                int_neighbors[ndx] = cells_to_use_inv[icell]
            except KeyError:
                newly_created_exterior_facs.append(ndx)
        # Make boolean array: 1 if a newly created exterior facet, 0 if
        #                     remains an interior facet
        newly_created_exterior_facs = np.isin(np.arange(np.size(int_elements)),
                                              newly_created_exterior_facs)
        new_ext_elements = int_elements[newly_created_exterior_facs]
        new_ext_element_faces = int_element_faces[newly_created_exterior_facs]
        new_ext_neighbor_tag = -(_boundary_tag_bit(bdy_tags,
                                                  boundary_tag_to_index,
                                                  BTAG_REALLY_ALL)
                                | _boundary_tag_bit(bdy_tags,
                                                    boundary_tag_to_index,
                                                    BTAG_INDUCED_BOUNDARY))
        new_ext_neighbors = np.full(new_ext_elements.shape,
                                    new_ext_neighbor_tag,
                                    dtype=IntType)
        new_ext_neighbor_faces = np.full(new_ext_elements.shape,
                                         0,
                                         dtype=Mesh.face_id_dtype)
        # Remove any (previously) interior facets that have become exterior
        # facets
        remaining_int_facs = np.logical_not(newly_created_exterior_facs)
        int_elements = int_elements[remaining_int_facs]
        int_element_faces = int_element_faces[remaining_int_facs]
        int_neighbors = int_neighbors[remaining_int_facs]
        int_neighbor_faces = int_neighbor_faces[remaining_int_facs]

    interconnectivity_grp = FacialAdjacencyGroup(igroup=0, ineighbor_group=0,
                                                 elements=int_elements,
                                                 neighbors=int_neighbors,
                                                 element_faces=int_element_faces,
                                                 neighbor_faces=int_neighbor_faces)

    # }}}

    # {{{ build the FacialAdjacencyGroup for boundary faces

    # We can get the elements directly from exterior facets
    ext_elements = top.exterior_facets.facet_cell.flatten()

    ext_element_faces = np.array([fd_loc_fac_nr_to_mm[fac_nr] for fac_nr in
                                  top.exterior_facets.local_facet_dat.data],
                                 dtype=Mesh.face_id_dtype)
    ext_neighbor_faces = np.zeros(ext_element_faces.shape,
                                  dtype=Mesh.face_id_dtype)
    # If only using some of the cells, throw away unused cells and
    # move to new cell index
    if cells_to_use is not None:
        to_keep = np.isin(ext_elements, cells_to_use)
        ext_elements = np.vectorize(cells_to_use_inv.__getitem__)(
            ext_elements[to_keep])
        ext_element_faces = ext_element_faces[to_keep]
        ext_neighbor_faces = ext_neighbor_faces[to_keep]

    # tag the boundary, making sure to record custom tags
    # (firedrake "markers") if present
    if top.exterior_facets.markers is not None:
        ext_neighbors = np.zeros(ext_elements.shape, dtype=IntType)
        for ifac, marker in enumerate(top.exterior_facets.markers):
            ext_neighbors[ifac] = marker_to_neighbor_value[marker]
    else:
        ext_neighbors = np.full(ext_elements.shape,
                                marker_to_neighbor_value[None],
                                dtype=IntType)

    # If not using all the cells, some interior facets may have become
    # exterior facets:
    if cells_to_use is not None:
        # Record any newly created exterior facets
        ext_elements = np.concatenate((ext_elements, new_ext_elements))
        ext_element_faces = np.concatenate((ext_element_faces,
                                            new_ext_element_faces))
        ext_neighbor_faces = np.concatenate((ext_neighbor_faces,
                                             new_ext_neighbor_faces))
        ext_neighbors = np.concatenate((ext_neighbors, new_ext_neighbors))

    exterior_grp = FacialAdjacencyGroup(igroup=0, ineighbor=None,
                                        elements=ext_elements,
                                        element_faces=ext_element_faces,
                                        neighbors=ext_neighbors,
                                        neighbor_faces=ext_neighbor_faces)

    # }}}

    return [{0: interconnectivity_grp, None: exterior_grp}]
Exemplo n.º 12
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

        vertex_index_gmsh_to_mine = {}
        for el_vertices, el_type in zip(self.element_vertices,
                                        self.element_types):
            for gmsh_vertex_nr in el_vertices:
                if gmsh_vertex_nr not in vertex_index_gmsh_to_mine:
                    vertex_index_gmsh_to_mine[gmsh_vertex_nr] = \
                            len(vertex_index_gmsh_to_mine)

        # }}}

        # {{{ build vertex array

        gmsh_vertex_indices, my_vertex_indices = \
                list(zip(*six.iteritems(vertex_index_gmsh_to_mine)))
        vertices = np.empty((ambient_dim, len(vertex_index_gmsh_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)

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

            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 el_vertices, el_nodes, el_type in 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_index_gmsh_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__)

            # Gmsh seems to produce elements in the opposite orientation
            # of what we like. Flip them all.

            groups.append(group)

        return Mesh(vertices,
                    groups,
                    nodal_adjacency=None,
                    facial_adjacency_groups=None)
Exemplo n.º 13
0
def make_curve_mesh(
        curve_f: Callable[[np.ndarray], np.ndarray],
        element_boundaries: np.ndarray,
        order: int,
        *,
        unit_nodes: Optional[np.ndarray] = None,
        node_vertex_consistency_tolerance: Optional[Union[float, bool]] = None,
        closed: bool = True,
        return_parametrization_points: bool = False):
    """
    :param curve_f: parametrization for a curve, accepting a vector of
        point locations and returning an array of shape ``(2, npoints)``.
    :param element_boundaries: a vector of element boundary locations in
        :math:`[0, 1]`, in order. :math:`0` must be the first entry, :math:`1`
        the last one.
    :param order: order of the (simplex) elements. If *unit_nodes* is also
        provided, the orders should match.
    :param unit_nodes: if given, the unit nodes to use. Must have shape
        ``(2, nnodes)``.
    :param node_vertex_consistency_tolerance: passed to the
        :class:`~meshmode.mesh.Mesh` constructor. If *False*, no checks are
        performed.
    :param closed: if *True*, the curve is assumed closed and the first and
        last of the *element_boundaries* must match.
    :param return_parametrization_points: if *True*, the parametrization points
        at which all the nodes in the mesh were evaluated are also returned.
    :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